agsmith
v0.2.1
Published
Smith CLI — your extensible agent toolkit
Downloads
346
Readme
Agent Smith
Smith CLI — your extensible agent toolkit.
Installation
npm install
npm run build
npm linkRequires Node.js >= 18.
Configuration
All config and credentials live in ~/.agsmith/ (override the directory by setting AGSMITH_CONFIG_DIR).
~/.agsmith/
config.json # Slack + OpenClaw + Outlook settings
credentials/
gmail-client.json # Gmail OAuth client secret
gmail-user-readonly-token.json # Gmail read-only token
gmail-user-modify-token.json # Gmail modify token
outlook-token-cache.json # Outlook (Microsoft Graph) MSAL token cacheCommands
agsmith config
agsmith config show Show current config with secrets masked
agsmith config path Print the path to the config fileagsmith slack
Setup
agsmith slack set api-key <value> Set the Slack bot/user OAuth token (xoxb-... or xoxp-...)
agsmith slack set main-user-id <value> Set the primary Slack user ID (e.g. U0123456789)Commands
agsmith slack channel list List all channels in the workspace
--include-archived Include archived channels
--private-only Show only private channels
--public-only Show only public channels
--wrap Wrap cell content in table output
--truncate Truncate cell content to column widths
agsmith slack channel create <name> Create a channel in the workspace
--private Create as a private channel
agsmith slack send <channel> <text...> Send a message to a channel<channel> accepts a channel name with or without a leading #, or a channel ID. Names are
resolved to an ID via conversations.list before posting, so the bot token needs
channels:read (and groups:read for private channels) in addition to chat:write.
agsmith gmail
Setup
agsmith gmail import-credentials <path> Copy OAuth client secret to ~/.agsmith/credentials/gmail-client.json
agsmith gmail auth Run the OAuth flow (read-only, default)
--readonly Request read-only access (default)
--modify Request modify access (required for label operations)Tokens are saved automatically to ~/.agsmith/credentials/.
Commands
agsmith gmail list List Gmail messages
--filter <filter> Gmail search query (e.g. "is:unread")
--limit <n> Max messages to fetch (default: 20)
--offset <n> Number of messages to skip (default: 0)
--json Output JSON
--wrap Wrap cell content in table output
--truncate Truncate cell content to column widths
agsmith gmail message <message-id> Show a single message by ID
--json Output JSON
--pdf [output-path] Render the message to a PDF file
agsmith gmail labels List all Gmail labels
--json Output JSON
--wrap Wrap cell content in table output
--truncate Truncate cell content to column widths
agsmith gmail update-labels [message-ids...] Add or remove labels on messages
--filter <filter> Gmail search query to select messages
--add <label-id> Label ID to add (repeatable)
--remove <label-id> Label ID to remove (repeatable)
--add-name <label-name> Label name to add (repeatable)
--remove-name <label-name> Label name to remove (repeatable)
--json Output JSON
agsmith gmail attachment list <message-id> List attachments on a message
--json Output JSON
agsmith gmail attachment download <message-id> <attachment-id> Download an attachment
--output-dir <dir> Directory to write the file into (default: .)
--filename <name> Override the output filename
--json Output JSONagsmith outlook
Read-only access to Outlook / Microsoft 365 mail via Microsoft Graph.
Setup
You'll need an Azure Entra app registration (free — see the Microsoft docs for "App registrations"). Register a public client / native app with the redirect URI http://localhost, enable "Allow public client flows", and grant the delegated permissions Mail.Read, User.Read, and offline_access. Then save your client ID:
agsmith outlook set client-id <azure-app-client-id> Required
agsmith outlook set tenant-id <tenant-id> Optional (default: "common"); set to a tenant
GUID if your app is single-tenant
agsmith outlook set token-cache-path <path> Optional; override the MSAL token cache location
agsmith outlook auth Run the OAuth flow in the browserThe MSAL token cache is saved automatically to ~/.agsmith/credentials/outlook-token-cache.json.
Commands
agsmith outlook list List Outlook messages via Microsoft Graph
--filter <odata> Raw OData $filter expression
--search <query> Free-text $search (subject/body/from/etc.)
--from <address> Shortcut for $filter on from/emailAddress/address
--to <address> Shortcut for $filter on toRecipients
--subject <text> Shortcut for $filter contains(subject, ...)
--in <folder> Well-known folder (inbox, sentitems, drafts,
deleteditems, junkemail, archive)
--unread $filter isRead eq false
--starred $filter flag/flagStatus eq 'flagged'
--has-attachment $filter hasAttachments eq true
--before <date> $filter receivedDateTime lt <date> (YYYY-MM-DD)
--after <date> $filter receivedDateTime ge <date> (YYYY-MM-DD)
--on <date> $filter receivedDateTime within a single day
--limit <n> Max messages to fetch (default: 20)
--offset <n> Number of messages to skip (default: 0)
--json Output JSON
--wrap Wrap cell content in table output
--truncate Truncate cell content to column widths
agsmith outlook message <message-id> Show a single message by Graph ID
--json Output JSON
--pdf [output-path] Render the message to a PDF file (inline cid: images
and image attachments are embedded)agsmith openclaw
Setup
agsmith openclaw set config-dir <path> Set the path to the OpenClaw config directory
agsmith openclaw set default-model <model> Set the default model for new agentsCommands
agsmith openclaw agent create <agent-name>Creates an OpenClaw agent, a matching Slack channel, and binds them together. If the channel or agent already exists, the existing resources are reused.
agsmith iterm
Requires macOS with iTerm2 installed.
agsmith iterm list List all iTerm2 windows, tabs, and sessions
--json Output JSON
agsmith iterm new <session> [cwd] [command] [args...]
Create a new tab, optionally cd and run a command
agsmith iterm read <session> Read the visible screen contents of a named session
agsmith iterm rename <session> <new-name>
Rename a named session
agsmith iterm run <session> <command> [args...]
Run a command in a named session (sends Ctrl+C first)
agsmith iterm send <session> <text...> Type raw text into a session (no Ctrl+C, no auto-Enter)
-e, --enter Press Enter after the text
agsmith iterm send-ctrl <session> <keys...>
Send control codes / named keys, in order
Single letters become Ctrl+<letter> (`c` = Ctrl+C);
named keys: enter, return, esc, tab, space,
backspace, bs, newline
--delay <ms> Delay between keys (default 400)
agsmith iterm split <direction> <target> [session]
Split a named session horizontally or vertically
<direction> is `horizontal` or `vertical`Sessions created by iterm new and iterm split are tagged with the user.agsmithTerminal
iTerm2 variable, and every <session> lookup above matches that tag before falling back to the
session name — names get rewritten by whatever runs in the tab. See agsmith cc below.
agsmith cc
Claude Code session helpers.
agsmith cc rc <path> Open a new iTerm2 tab, cd to <path>, and launch
`claude --remote-control "<session>"`
-t, --terminal <name> iTerm2 terminal to reuse (no new tab). If no such
terminal exists, a new tab with this name is created
-s, --session <name> Remote-control session name (default: <dirname>-MMDD)
-p, --prompt <text> Initial prompt to send to Claude on startupWith no flags, a new tab is opened, named after the target directory, and the remote-control
session is <dirname>-MMDD.
-t never restarts a Claude that is already running. What it does depends on the terminal:
| Terminal state | What happens |
|---|---|
| Does not exist | New tab is created, then claude --remote-control is launched in it |
| Exists, running Claude | --prompt is typed into that Claude session; --session is ignored |
| Exists, running Claude, no --prompt | Nothing — reports that Claude is already running |
| Exists, plain shell | cds and launches claude --remote-control in it |
"Running Claude" is detected by looking for a claude process on the session's tty. The iTerm2
session.jobName variable is not usable for this: it reports the deepest child process, so a
Claude session that has spawned an LSP or caffeinate reports that child instead.
Terminals touched by cc rc, iterm new, and iterm split are tagged with the iTerm2 user
variable user.agsmithTerminal, which -t and all iterm lookups match on first. Session
names are rewritten by whatever runs in the tab — Claude Code retitles them to ✳ Claude
Code and then to a summary of the conversation — so the tag is what makes a terminal reliably
addressable. Untagged sessions, including any created by hand, still match on name.
