geekbot-cli
v0.2.4
Published
CLI tool for managing Geekbot standups, reports, and polls — designed for AI agents and humans
Maintainers
Readme
geekbot-cli
A cross-platform CLI tool for interacting with the Geekbot API, designed for AI agents and humans. Built with Bun and TypeScript.
Why geekbot-cli?
- Structured JSON output on every command -- pipe results into scripts, dashboards, or AI agents
- Machine-readable exit codes and actionable error messages -- no guessing what went wrong
- Secure credential storage via OS keychain -- no API keys in dotfiles or env scripts
- AI-agent ready -- ships as a Vercel Skill for Claude Code, Cursor, Windsurf, Copilot, and more
Table of Contents
- Installation
- Authentication
- Security
- Global Options
- Commands
- Output Format
- Exit Codes
- Error Handling
- Examples
- Development
- Contributing
- License
Installation
Prerequisites
- Bun runtime (v1.3.5 or later)
Install via npm
npm install -g geekbot-cliThis installs the geekbot binary globally. The postinstall script checks for Bun and prints a hint about skill registration.
Note:
npx geekbot-clialso requires Bun on PATH — the CLI uses a#!/usr/bin/env bunshebang, so it is not a Node.js fallback.
Verify the installation:
geekbot --versionRegister the AI agent skill
To register the Geekbot skill with your AI coding agents (Claude Code, Cursor, Windsurf, Copilot, etc.):
npx skills add geekbot-com/geekbot-cliThis detects which AI coding agents are installed on your machine and copies the Geekbot skill files into each agent's skill directory, so the agent can discover and use the CLI autonomously. It uses the Vercel Skills ecosystem under the hood.
If you prefer not to use npx skills, copy the skills/geekbot/ directory into your agent's skill directory:
# Claude Code
cp -r node_modules/geekbot-cli/skills/geekbot ~/.claude/skills/geekbot
# Universal (.agents/skills/ — works with Cursor, Codex, Gemini CLI, etc.)
mkdir -p .agents/skills
cp -r node_modules/geekbot-cli/skills/geekbot .agents/skills/geekbot
# Windsurf
cp -r node_modules/geekbot-cli/skills/geekbot ~/.codeium/windsurf/skills/geekbot
# Roo Code
cp -r node_modules/geekbot-cli/skills/geekbot ~/.roo/skills/geekbotThe skill directory must contain SKILL.md and its sibling reference files (cli-commands.md, manager-workflows.md, etc.) — they are loaded by relative path.
Install from source (for development)
git clone https://github.com/geekbot-com/geekbot-cli.git
cd geekbot-cli
bun install
bun linkPlatform support
| Platform | Status | |----------|--------| | macOS (x64, ARM64) | Supported | | Linux (x64, ARM64) | Supported | | Windows (x64) | Supported (requires Bun >= 1.3.5) | | Windows (ARM64) | Not supported (Bun does not yet ship ARM64 Windows binaries) |
Authentication
The CLI resolves API credentials using a three-level priority chain. The first source found wins:
--api-keyflag (highest priority) -- per-command override, useful for scripts and CIGEEKBOT_API_KEYenvironment variable -- session or shell-level credential- OS keychain (lowest priority) -- persistent, secure storage via
geekbot auth setup
OS Keychain Storage
The CLI uses @napi-rs/keyring to store your API key in the platform-native credential store:
- macOS: Keychain
- Windows: Credential Vault
- Linux: Secret Service (GNOME Keyring / KDE Wallet)
Store a key interactively:
geekbot auth setupOr non-interactively:
geekbot auth setup --api-key YOUR_API_KEYThe setup command validates the key against the Geekbot API before storing it.
Verify credentials
geekbot auth statusRemove stored key
geekbot auth removeSecurity
- API keys are never written to disk in plaintext. The CLI stores credentials in your OS keychain (macOS Keychain, Windows Credential Vault, or Linux Secret Service). No config files, no dotfiles.
- Keys passed via
--api-keyorGEEKBOT_API_KEYare not logged. Debug output (--debug) redacts credential values. - Validate before storing.
geekbot auth setupchecks that the key is valid against the Geekbot API before persisting it, preventing silent failures from typos or revoked keys. - Prefer the keychain over environment variables for workstations. Environment variables are visible to other processes and may leak into shell history. Use
GEEKBOT_API_KEYfor CI/CD and ephemeral environments where a keychain is unavailable.
Global Options
These options apply to all commands:
| Option | Description | Default |
|--------|-------------|---------|
| --api-key <key> | Geekbot API key (overrides GEEKBOT_API_KEY env var) | -- |
| --output <format> | Output format (currently json only; reserved for future formats) | json |
| --debug | Show debug output on stderr | false |
| -v, --version | Print version number | -- |
| --help | Show help text | -- |
Commands
The CLI follows a noun-verb pattern: geekbot <resource> <action> [options].
standup -- Manage standups
| Subcommand | Syntax | Description |
|------------|--------|-------------|
| list | geekbot standup list [options] | List standups you participate in |
| get | geekbot standup get <id> | Get a standup by ID |
| create | geekbot standup create --name <name> --channel <channel> [options] | Create a new standup |
| update | geekbot standup update <id> [options] | Partially update a standup (PATCH) |
| replace | geekbot standup replace <id> --name <name> --channel <channel> [options] | Fully replace a standup (PUT) |
| delete | geekbot standup delete <id> --yes | Delete a standup |
| duplicate | geekbot standup duplicate <id> --name <name> | Duplicate an existing standup |
| start | geekbot standup start <id> [--users <ids>] | Trigger a standup immediately |
standup list options
| Option | Default | Description |
|--------|---------|-------------|
| --admin | false | Include all team standups (admin only) |
| --brief | false | Return only id, name, channel, time, timezone, days |
| --name <name> | -- | Filter by name (case-insensitive substring match) |
| --channel <channel> | -- | Filter by channel (case-insensitive substring match) |
| --mine | false | Show only standups you are a member of |
standup create options
| Option | Required | Default | Description |
|--------|----------|---------|-------------|
| --name <name> | Yes | -- | Standup name |
| --channel <channel> | Yes | -- | Slack channel name |
| --time <time> | No | 10:00 | Time in HH:MM 24-hour format |
| --timezone <tz> | No | UTC | IANA timezone |
| --days <days> | No | Mon,Tue,Wed,Thu,Fri | Comma-separated days |
| --questions <json> | Yes | -- | Questions as JSON array |
| --users <ids> | No | -- | Comma-separated user IDs |
| --wait-time <minutes> | No | 0 | Minutes between users |
standup update options
| Option | Required | Description |
|--------|----------|-------------|
| --name <name> | No | New standup name |
| --channel <channel> | No | New channel |
| --time <time> | No | New time (HH:MM) |
| --timezone <tz> | No | New timezone |
| --days <days> | No | New days (comma-separated) |
| --wait-time <minutes> | No | New wait time in minutes |
standup replace options
Same options as create. --name and --channel are required; all other options have the same defaults as create.
standup delete options
| Option | Description |
|--------|-------------|
| --yes | Confirm deletion (required; deletion fails with an error if omitted) |
standup duplicate options
| Option | Required | Description |
|--------|----------|-------------|
| --name <name> | Yes | Name for the new standup |
The <id> argument is the ID of the standup to duplicate.
standup start options
| Option | Required | Description |
|--------|----------|-------------|
| --users <ids> | No | Comma-separated user IDs to trigger (omit to trigger all members) |
The <id> argument is the ID of the standup to trigger immediately.
report -- Manage reports
| Subcommand | Syntax | Description |
|------------|--------|-------------|
| list | geekbot report list [options] | List reports with optional filters |
| create | geekbot report create --standup-id <id> --answers <json> | Submit a report for a standup |
report list options
| Option | Description |
|--------|-------------|
| --standup-id <id> | Filter by standup ID |
| --user-id <id> | Filter by user ID |
| --before <date> | Reports before date (ISO 8601 or unix timestamp) |
| --after <date> | Reports after date (ISO 8601 or unix timestamp) |
| --limit <n> | Max number of reports to return |
report create options
| Option | Required | Description |
|--------|----------|-------------|
| --standup-id <id> | Yes | Standup ID to report on |
| --answers <json> | Yes | Answers as JSON object: {"question_id": "answer", ...} |
poll -- Manage polls (Slack teams only)
Polls are only available for Slack-connected teams. Non-Slack teams will receive a platform error.
| Subcommand | Syntax | Description |
|------------|--------|-------------|
| list | geekbot poll list | List all polls |
| get | geekbot poll get <id> | Get a poll by ID |
| create | geekbot poll create --name <name> --channel <channel> --question <text> --choices <json> | Create a new poll |
| votes | geekbot poll votes <id> [--after <date>] [--before <date>] | Get voting results for a poll |
poll create options
| Option | Required | Description |
|--------|----------|-------------|
| --name <name> | Yes | Poll name |
| --channel <channel> | Yes | Slack channel |
| --question <text> | Yes | Poll question text |
| --choices <json> | Yes | Choices as JSON array of strings |
poll votes options
| Option | Description |
|--------|-------------|
| --after <date> | Votes after date |
| --before <date> | Votes before date |
me -- View your profile and teams
| Subcommand | Syntax | Description |
|------------|--------|-------------|
| show | geekbot me show | Show your Geekbot profile |
| teams | geekbot me teams | List teams you belong to |
team -- View team information
| Subcommand | Syntax | Description |
|------------|--------|-------------|
| list | geekbot team list | List all teams with members |
auth -- Manage authentication
| Subcommand | Syntax | Description |
|------------|--------|-------------|
| setup | geekbot auth setup [--api-key <key>] | Interactively configure and store API key |
| status | geekbot auth status | Verify stored credentials work |
| remove | geekbot auth remove | Remove stored API key from OS keychain |
Output Format
All command output is written to stdout as a JSON envelope. Diagnostic messages (debug output, Commander.js help/errors) go to stderr.
JSON Envelope
Every response follows this structure:
interface OutputEnvelope<T> {
ok: boolean;
data: T | null;
error: ErrorObject | null;
metadata: MetadataObject;
}Success Response
{
"ok": true,
"data": {
"id": 123,
"name": "Daily Standup",
"channel": "#engineering",
"time": "10:00",
"timezone": "America/New_York",
"days": ["Mon", "Tue", "Wed", "Thu", "Fri"],
"questions": [
{ "id": 101, "text": "What did you do yesterday?" },
{ "id": 102, "text": "What will you do today?" }
]
},
"error": null,
"metadata": {
"timestamp": "2026-03-17T10:30:00.000Z"
}
}Error Response
{
"ok": false,
"data": null,
"error": {
"code": "standup_not_found",
"message": "Standup 999 not found",
"retryable": false,
"suggestion": "Available standups: 123 (Daily Standup), 456 (Weekly Sync). Run `geekbot standup list` to see all."
},
"metadata": {
"timestamp": "2026-03-17T10:30:00.000Z"
}
}Exit Codes
The CLI uses specific exit codes for programmatic error handling:
| Code | Name | Meaning |
|------|------|---------|
| 0 | SUCCESS | Operation completed successfully |
| 1 | GENERAL | Unexpected or unclassified error |
| 2 | USAGE | Invalid command syntax or missing required options |
| 3 | NOT_FOUND | Requested resource does not exist |
| 4 | AUTH | Authentication failed (missing or invalid API key) |
| 5 | FORBIDDEN | Insufficient permissions for the operation |
| 6 | VALIDATION | Input validation failed (bad format, invalid values) |
| 7 | NETWORK | Network error (DNS failure, timeout, connection refused) |
| 8 | CONFLICT | Resource conflict (duplicate name, concurrent modification) |
| 9 | API_ERROR | Geekbot API returned an unexpected error |
Error Handling
Errors include machine-readable fields designed for programmatic consumption:
interface ErrorObject {
code: string; // Machine-readable error code (e.g., "standup_not_found")
message: string; // Human-readable description
retryable: boolean; // Whether retrying may succeed (e.g., network errors)
suggestion: string | null; // Actionable next step
}Key behaviors:
- Not-found errors suggest alternatives: When a resource ID is not found, the CLI queries for valid IDs and includes them in the
suggestionfield. - Retryable flag: Network errors and rate limits are marked
retryable: true. Auth and validation errors are not. - Structured output on failure: Even errors produce a valid JSON envelope on stdout, so parsers never encounter unexpected output.
Examples
List standups
geekbot standup list --output json{
"ok": true,
"data": [
{ "id": 123, "name": "Daily Standup", "channel": "#engineering", "time": "10:00", "timezone": "UTC" },
{ "id": 456, "name": "Weekly Sync", "channel": "#team", "time": "09:00", "timezone": "America/New_York" }
],
"error": null,
"metadata": { "timestamp": "2026-03-17T10:00:00.000Z" }
}Create a standup
geekbot standup create \
--name "Sprint Retro" \
--channel "#engineering" \
--time "15:00" \
--timezone "America/Chicago" \
--days "Fri" \
--questions '[{"question": "What went well?"}, {"question": "What could improve?"}]'{
"ok": true,
"data": {
"id": 789,
"name": "Sprint Retro",
"channel": "#engineering",
"time": "15:00",
"timezone": "America/Chicago",
"days": ["Fri"]
},
"error": null,
"metadata": {
"timestamp": "2026-03-17T10:05:00.000Z",
"operation": "created",
"undo": "geekbot standup delete 789 --yes"
}
}Submit a report
geekbot report create \
--standup-id 123 \
--answers '{"101": "Finished auth module", "102": "Starting API tests"}'{
"ok": true,
"data": {
"id": 5001,
"standup_id": 123,
"questions": [
{ "id": 101, "text": "What did you do yesterday?", "answer": "Finished auth module" },
{ "id": 102, "text": "What will you do today?", "answer": "Starting API tests" }
]
},
"error": null,
"metadata": { "timestamp": "2026-03-17T10:10:00.000Z" }
}Handle a not-found error
geekbot standup get 999{
"ok": false,
"data": null,
"error": {
"code": "standup_not_found",
"message": "Standup 999 not found",
"retryable": false,
"suggestion": "Available standups: 123 (Daily Standup), 456 (Weekly Sync). Run `geekbot standup list` to see all."
},
"metadata": { "timestamp": "2026-03-17T10:15:00.000Z" }
}Exit code: 3 (NOT_FOUND)
Create a poll (Slack only)
geekbot poll create \
--name "Team Lunch" \
--channel "#general" \
--question "Where should we eat?" \
--choices '["Pizza", "Sushi", "Tacos"]'Use environment variable for auth
export GEEKBOT_API_KEY=your-api-key
geekbot standup listDevelopment
Run tests
bun testLint
bun run lintFormat
bun run formatLint and auto-fix
bun run checkIntegration tests
Integration tests run against the live Geekbot API and require a valid API key:
GEEKBOT_INTEGRATION_TEST_API_KEY=your-key bun test:integrationTests are automatically skipped when GEEKBOT_INTEGRATION_TEST_API_KEY is not set. Tests that require a Slack channel (#geekbot-skill-tests) will gracefully skip with a warning if the channel does not exist in the workspace.
Contributing
Contributions are welcome! Please open an issue or pull request on GitHub.
- Fork the repository
- Create a feature branch (
git checkout -b my-feature) - Make your changes and add tests
- Run
bun testandbun run lintto verify - Commit and push your branch
- Open a pull request against
main
