Agent Skills › spinabot/brigade

spinabot/brigade

GitHub

管理1Password CLI,涵盖安装、桌面集成、多账号登录及密钥读取注入。强制在独立tmux会话中执行以避免TTY交互问题,确保通过op whoami验证身份后操作,严禁将密钥写入日志或磁盘。

60 个 Skill 116

安装全部 Skills

npx skills add spinabot/brigade --all -g -y
更多选项

预览集合内 Skills

npx skills add spinabot/brigade --list

集合内 Skills (60)

管理1Password CLI,涵盖安装、桌面集成、多账号登录及密钥读取注入。强制在独立tmux会话中执行以避免TTY交互问题,确保通过op whoami验证身份后操作,严禁将密钥写入日志或磁盘。
安装1Password CLI 配置桌面应用集成 单账号或多账号登录1Password 读取或注入1Password中的秘密信息
skills/1password/SKILL.md
npx skills add spinabot/brigade --skill 1password -g -y
SKILL.md
Frontmatter
{
    "name": "1password",
    "homepage": "https:\/\/developer.1password.com\/docs\/cli\/get-started\/",
    "metadata": {
        "brigade": {
            "emoji": "🔐",
            "install": [
                {
                    "id": "brew",
                    "bins": [
                        "op"
                    ],
                    "kind": "brew",
                    "label": "Install 1Password CLI (brew)",
                    "formula": "1password-cli"
                }
            ],
            "requires": {
                "bins": [
                    "op"
                ]
            }
        }
    },
    "description": "Set up and use 1Password CLI (op). Use when installing the CLI, enabling desktop app integration, signing in (single or multi-account), or reading\/injecting\/running secrets via op."
}

1Password CLI

Follow the official CLI get-started steps. Don't guess install commands.

References

  • references/get-started.md (install + app integration + sign-in flow)
  • references/cli-examples.md (real op examples)

Workflow

  1. Check OS + shell.
  2. Verify CLI present: op --version.
  3. Confirm desktop app integration is enabled (per get-started) and the app is unlocked.
  4. REQUIRED: create a fresh tmux session for all op commands (no direct op calls outside tmux).
  5. Sign in / authorize inside tmux: op signin (expect app prompt).
  6. Verify access inside tmux: op whoami (must succeed before any secret read).
  7. If multiple accounts: use --account or OP_ACCOUNT.

REQUIRED tmux session (T-Max)

The shell tool uses a fresh TTY per command. To avoid re-prompts and failures, always run op inside a dedicated tmux session with a fresh socket/session name.

Example (see tmux skill for socket conventions, do not reuse old session names):

SOCKET_DIR="${BRIGADE_TMUX_SOCKET_DIR:-${TMPDIR:-/tmp}/brigade-tmux-sockets}"
mkdir -p "$SOCKET_DIR"
SOCKET="$SOCKET_DIR/brigade-op.sock"
SESSION="op-auth-$(date +%Y%m%d-%H%M%S)"

tmux -S "$SOCKET" new -d -s "$SESSION" -n shell
tmux -S "$SOCKET" send-keys -t "$SESSION":0.0 -- "op signin --account my.1password.com" Enter
tmux -S "$SOCKET" send-keys -t "$SESSION":0.0 -- "op whoami" Enter
tmux -S "$SOCKET" send-keys -t "$SESSION":0.0 -- "op vault list" Enter
tmux -S "$SOCKET" capture-pane -p -J -t "$SESSION":0.0 -S -200
tmux -S "$SOCKET" kill-session -t "$SESSION"

Guardrails

  • Never paste secrets into logs, chat, or code.
  • Prefer op run / op inject over writing secrets to disk.
  • If sign-in without app integration is needed, use op account add.
  • If a command returns "account is not signed in", re-run op signin inside tmux and authorize in the app.
  • Do not run op outside tmux; stop and ask if tmux is unavailable.
通过memo CLI在macOS终端管理Apple笔记,支持创建、查看、编辑、删除、搜索、移动及导出功能。适用于用户请求添加、列出、搜索或管理笔记文件夹的场景。
用户要求添加新笔记 用户要求列出或搜索笔记 用户要求管理笔记文件夹
skills/apple-notes/SKILL.md
npx skills add spinabot/brigade --skill apple-notes -g -y
SKILL.md
Frontmatter
{
    "name": "apple-notes",
    "homepage": "https:\/\/github.com\/antoniorodr\/memo",
    "metadata": {
        "brigade": {
            "os": [
                "darwin"
            ],
            "emoji": "📝",
            "install": [
                {
                    "id": "brew",
                    "bins": [
                        "memo"
                    ],
                    "kind": "brew",
                    "label": "Install memo via Homebrew",
                    "formula": "antoniorodr\/memo\/memo"
                }
            ],
            "requires": {
                "bins": [
                    "memo"
                ]
            }
        }
    },
    "description": "Manage Apple Notes via the `memo` CLI on macOS (create, view, edit, delete, search, move, and export notes). Use when a user asks Brigade to add a note, list notes, search notes, or manage note folders."
}

Apple Notes CLI

Use memo notes to manage Apple Notes directly from the terminal. Create, view, edit, delete, search, move notes between folders, and export to HTML/Markdown.

Setup

  • Install (Homebrew): brew tap antoniorodr/memo && brew install antoniorodr/memo/memo
  • Manual (pip): pip install . (after cloning the repo)
  • macOS-only; if prompted, grant Automation access to Notes.app.

View Notes

  • List all notes: memo notes
  • Filter by folder: memo notes -f "Folder Name"
  • Search notes (fuzzy): memo notes -s "query"

Create Notes

  • Add a new note: memo notes -a
    • Opens an interactive editor to compose the note.
  • Quick add with title: memo notes -a "Note Title"

Edit Notes

  • Edit existing note: memo notes -e
    • Interactive selection of note to edit.

Delete Notes

  • Delete a note: memo notes -d
    • Interactive selection of note to delete.

Move Notes

  • Move note to folder: memo notes -m
    • Interactive selection of note and destination folder.

Export Notes

  • Export to HTML/Markdown: memo notes -ex
    • Exports selected note; uses Mistune for markdown processing.

Limitations

  • Cannot edit notes containing images or attachments.
  • Interactive prompts may require terminal access.

Notes

  • macOS-only.
  • Requires Apple Notes.app to be accessible.
  • For automation, grant permissions in System Settings > Privacy & Security > Automation.
通过remindctl CLI管理Apple Reminders,支持在终端创建、查看、编辑、完成和删除提醒事项。适用于需同步至iOS设备的个人待办任务,提供列表管理与多种日期过滤及输出格式支持。
用户明确提及reminder或Reminders app 创建需同步至iOS的个人待办事项 管理Apple Reminders列表
skills/apple-reminders/SKILL.md
npx skills add spinabot/brigade --skill apple-reminders -g -y
SKILL.md
Frontmatter
{
    "name": "apple-reminders",
    "homepage": "https:\/\/github.com\/steipete\/remindctl",
    "metadata": {
        "brigade": {
            "os": [
                "darwin"
            ],
            "emoji": "⏰",
            "install": [
                {
                    "id": "brew",
                    "bins": [
                        "remindctl"
                    ],
                    "kind": "brew",
                    "label": "Install remindctl via Homebrew",
                    "formula": "steipete\/tap\/remindctl"
                }
            ],
            "requires": {
                "bins": [
                    "remindctl"
                ]
            }
        }
    },
    "description": "Manage Apple Reminders via remindctl CLI (list, add, edit, complete, delete). Supports lists, date filters, and JSON\/plain output."
}

Apple Reminders CLI (remindctl)

Use remindctl to manage Apple Reminders directly from the terminal.

When to Use

USE this skill when:

  • User explicitly mentions "reminder" or "Reminders app"
  • Creating personal to-dos with due dates that sync to iOS
  • Managing Apple Reminders lists
  • User wants tasks to appear in their iPhone/iPad Reminders app

When NOT to Use

DON'T use this skill when:

  • Scheduling Brigade tasks or alerts → use cron tool with systemEvent instead
  • Calendar events or appointments → use Apple Calendar
  • Project/work task management → use Notion, GitHub Issues, or task queue
  • One-time notifications → use cron tool for timed alerts
  • User says "remind me" but means an Brigade alert → clarify first

Setup

  • Install: brew install steipete/tap/remindctl
  • macOS-only; grant Reminders permission when prompted
  • Check status: remindctl status
  • Request access: remindctl authorize

Common Commands

View Reminders

remindctl                    # Today's reminders
remindctl today              # Today
remindctl tomorrow           # Tomorrow
remindctl week               # This week
remindctl overdue            # Past due
remindctl all                # Everything
remindctl 2026-01-04         # Specific date

Manage Lists

remindctl list               # List all lists
remindctl list Work          # Show specific list
remindctl list Projects --create    # Create list
remindctl list Work --delete        # Delete list

Create Reminders

remindctl add "Buy milk"
remindctl add --title "Call mom" --list Personal --due tomorrow
remindctl add --title "Meeting prep" --due "2026-02-15 09:00"

Complete/Delete

remindctl complete 1 2 3     # Complete by ID
remindctl delete 4A83 --force  # Delete by ID

Output Formats

remindctl today --json       # JSON for scripting
remindctl today --plain      # TSV format
remindctl today --quiet      # Counts only

Date Formats

Accepted by --due and date filters:

  • today, tomorrow, yesterday
  • YYYY-MM-DD
  • YYYY-MM-DD HH:mm
  • ISO 8601 (2026-01-04T12:34:56Z)

Example: Clarifying User Intent

User: "Remind me to check on the deploy in 2 hours"

Ask: "Do you want this in Apple Reminders (syncs to your phone) or as an Brigade alert (I'll message you here)?"

  • Apple Reminders → use this skill
  • Brigade alert → use cron tool with systemEvent
通过 grizzly CLI 在 macOS 上管理 Bear 笔记,支持创建、读取、追加内容及标签搜索。需安装 Bear 并配置 API Token,依赖 x-callback-url 交互。
用户需要在 macOS 上创建或编辑 Bear 笔记 用户希望通过命令行搜索或管理 Bear 标签和笔记内容
skills/bear-notes/SKILL.md
npx skills add spinabot/brigade --skill bear-notes -g -y
SKILL.md
Frontmatter
{
    "name": "bear-notes",
    "homepage": "https:\/\/bear.app",
    "metadata": {
        "brigade": {
            "os": [
                "darwin"
            ],
            "emoji": "🐻",
            "install": [
                {
                    "id": "go",
                    "bins": [
                        "grizzly"
                    ],
                    "kind": "go",
                    "label": "Install grizzly (go)",
                    "module": "github.com\/tylerwince\/grizzly\/cmd\/grizzly@latest"
                }
            ],
            "requires": {
                "bins": [
                    "grizzly"
                ]
            }
        }
    },
    "description": "Create, search, and manage Bear notes via grizzly CLI."
}

Bear Notes

Use grizzly to create, read, and manage notes in Bear on macOS.

Requirements

  • Bear app installed and running
  • For some operations (add-text, tags, open-note --selected), a Bear app token (stored in ~/.config/grizzly/token)

Getting a Bear Token

For operations that require a token (add-text, tags, open-note --selected), you need an authentication token:

  1. Open Bear → Help → API Token → Copy Token
  2. Save it: echo "YOUR_TOKEN" > ~/.config/grizzly/token

Common Commands

Create a note

echo "Note content here" | grizzly create --title "My Note" --tag work
grizzly create --title "Quick Note" --tag inbox < /dev/null

Open/read a note by ID

grizzly open-note --id "NOTE_ID" --enable-callback --json

Append text to a note

echo "Additional content" | grizzly add-text --id "NOTE_ID" --mode append --token-file ~/.config/grizzly/token

List all tags

grizzly tags --enable-callback --json --token-file ~/.config/grizzly/token

Search notes (via open-tag)

grizzly open-tag --name "work" --enable-callback --json

Options

Common flags:

  • --dry-run — Preview the URL without executing
  • --print-url — Show the x-callback-url
  • --enable-callback — Wait for Bear's response (needed for reading data)
  • --json — Output as JSON (when using callbacks)
  • --token-file PATH — Path to Bear API token file

Configuration

Grizzly reads config from (in priority order):

  1. CLI flags
  2. Environment variables (GRIZZLY_TOKEN_FILE, GRIZZLY_CALLBACK_URL, GRIZZLY_TIMEOUT)
  3. .grizzly.toml in current directory
  4. ~/.config/grizzly/config.toml

Example ~/.config/grizzly/config.toml:

token_file = "~/.config/grizzly/token"
callback_url = "http://127.0.0.1:42123/success"
timeout = "5s"

Notes

  • Bear must be running for commands to work
  • Note IDs are Bear's internal identifiers (visible in note info or via callbacks)
  • Use --enable-callback when you need to read data back from Bear
  • Some operations require a valid token (add-text, tags, open-note --selected)
使用 blogwatcher CLI 监控博客和 RSS/Atom 源更新。支持添加、列出、删除订阅源,扫描新文章,查看及标记已读状态,帮助用户高效追踪内容动态。
用户希望监控特定博客或 RSS 源的更新 用户需要检查并列出新的文章条目 用户想管理已订阅的博客列表
skills/blogwatcher/SKILL.md
npx skills add spinabot/brigade --skill blogwatcher -g -y
SKILL.md
Frontmatter
{
    "name": "blogwatcher",
    "homepage": "https:\/\/github.com\/Hyaxia\/blogwatcher",
    "metadata": {
        "brigade": {
            "emoji": "📰",
            "install": [
                {
                    "id": "go",
                    "bins": [
                        "blogwatcher"
                    ],
                    "kind": "go",
                    "label": "Install blogwatcher (go)",
                    "module": "github.com\/Hyaxia\/blogwatcher\/cmd\/blogwatcher@latest"
                }
            ],
            "requires": {
                "bins": [
                    "blogwatcher"
                ]
            }
        }
    },
    "description": "Monitor blogs and RSS\/Atom feeds for updates using the blogwatcher CLI."
}

blogwatcher

Track blog and RSS/Atom feed updates with the blogwatcher CLI.

Install

  • Go: go install github.com/Hyaxia/blogwatcher/cmd/blogwatcher@latest

Quick start

  • blogwatcher --help

Common commands

  • Add a blog: blogwatcher add "My Blog" https://example.com
  • List blogs: blogwatcher blogs
  • Scan for updates: blogwatcher scan
  • List articles: blogwatcher articles
  • Mark an article read: blogwatcher read 1
  • Mark all articles read: blogwatcher read-all
  • Remove a blog: blogwatcher remove "My Blog"

Example output

$ blogwatcher blogs
Tracked blogs (1):

  xkcd
    URL: https://xkcd.com
$ blogwatcher scan
Scanning 1 blog(s)...

  xkcd
    Source: RSS | Found: 4 | New: 4

Found 4 new article(s) total!

Notes

  • Use blogwatcher <command> --help to discover flags and options.
blucli 用于控制 Bluesound/NAD 音频设备,支持发现、播放、分组和音量调节。可通过命令行快速操作设备状态、管理播放列表及 TuneIn 电台搜索播放,并优先推荐 JSON 输出格式以便脚本集成。
需要控制 Bluesound 或 NAD 音响设备时 执行音乐播放、暂停、停止或调整音量操作时 查询已连接音频设备列表或状态时 管理多房间音频分组时 通过 TuneIn 搜索或播放网络电台时
skills/blucli/SKILL.md
npx skills add spinabot/brigade --skill blucli -g -y
SKILL.md
Frontmatter
{
    "name": "blucli",
    "homepage": "https:\/\/blucli.sh",
    "metadata": {
        "brigade": {
            "emoji": "🫐",
            "install": [
                {
                    "id": "go",
                    "bins": [
                        "blu"
                    ],
                    "kind": "go",
                    "label": "Install blucli (go)",
                    "module": "github.com\/steipete\/blucli\/cmd\/blu@latest"
                }
            ],
            "requires": {
                "bins": [
                    "blu"
                ]
            }
        }
    },
    "description": "BluOS CLI (blu) for discovery, playback, grouping, and volume."
}

blucli (blu)

Use blu to control Bluesound/NAD players.

Quick start

  • blu devices (pick target)
  • blu --device <id> status
  • blu play|pause|stop
  • blu volume set 15

Target selection (in priority order)

  • --device <id|name|alias>
  • BLU_DEVICE
  • config default (if set)

Common tasks

  • Grouping: blu group status|add|remove
  • TuneIn search/play: blu tunein search "query", blu tunein play "query"

Prefer --json for scripts. Confirm the target device before changing playback.

通过 BlueBubbles 集成管理 iMessage,支持发送消息、附件、回复、编辑、撤回及反应(Tapback)。需指定目标地址和消息内容,推荐使用 chat_guid。
用户要求发送或管理 iMessage 需要回复、编辑或撤回 iMessage
skills/bluebubbles/SKILL.md
npx skills add spinabot/brigade --skill bluebubbles -g -y
SKILL.md
Frontmatter
{
    "name": "bluebubbles",
    "metadata": {
        "brigade": {
            "emoji": "🫧",
            "requires": {
                "config": [
                    "channels.bluebubbles"
                ]
            }
        }
    },
    "description": "Use when you need to send or manage iMessages via BlueBubbles (recommended iMessage integration). Calls go through the generic message tool with channel=\"bluebubbles\"."
}

BlueBubbles Actions

Overview

BlueBubbles is Brigade’s recommended iMessage integration. Use the message tool with channel: "bluebubbles" to send messages and manage iMessage conversations: send texts and attachments, react (tapbacks), edit/unsend, reply in threads, and manage group participants/names/icons.

Inputs to collect

  • target (prefer chat_guid:...; also +15551234567 in E.164 or user@example.com)
  • message text for send/edit/reply
  • messageId for react/edit/unsend/reply
  • Attachment path for local files, or buffer + filename for base64

If the user is vague ("text my mom"), ask for the recipient handle or chat guid and the exact message content.

Actions

Send a message

{
  "action": "send",
  "channel": "bluebubbles",
  "target": "+15551234567",
  "message": "hello from Brigade"
}

React (tapback)

{
  "action": "react",
  "channel": "bluebubbles",
  "target": "+15551234567",
  "messageId": "<message-guid>",
  "emoji": "❤️"
}

Remove a reaction

{
  "action": "react",
  "channel": "bluebubbles",
  "target": "+15551234567",
  "messageId": "<message-guid>",
  "emoji": "❤️",
  "remove": true
}

Edit a previously sent message

{
  "action": "edit",
  "channel": "bluebubbles",
  "target": "+15551234567",
  "messageId": "<message-guid>",
  "message": "updated text"
}

Unsend a message

{
  "action": "unsend",
  "channel": "bluebubbles",
  "target": "+15551234567",
  "messageId": "<message-guid>"
}

Reply to a specific message

{
  "action": "reply",
  "channel": "bluebubbles",
  "target": "+15551234567",
  "replyTo": "<message-guid>",
  "message": "replying to that"
}

Send an attachment

{
  "action": "send",
  "channel": "bluebubbles",
  "target": "+15551234567",
  "path": "/tmp/photo.jpg",
  "message": "here you go"
}

Use message for the user-facing text. Do not rely on caption as a distinct BlueBubbles/iMessage feature.

Send with an iMessage effect

{
  "action": "sendWithEffect",
  "channel": "bluebubbles",
  "target": "+15551234567",
  "message": "big news",
  "effect": "balloons"
}

Notes

  • Requires gateway config channels.bluebubbles (serverUrl/password/webhookPath).
  • Prefer chat_guid targets when you have them (especially for group chats).
  • For media + text, treat message as canonical. Brigade can accept caption as an alias, but BlueBubbles/iMessage does not offer a reliable native attachment-caption concept.
  • In practice, media with text may arrive as the attachment plus a separate text message rather than one captioned bubble. Do not promise exact attachment-caption rendering.
  • If you must use the channel-specific file action, the canonical action name is upload-file; sendAttachment is a legacy alias.
  • BlueBubbles supports rich actions, but some are macOS-version dependent (for example, edit may be broken on macOS 26 Tahoe).
  • The gateway may expose both short and full message ids; full ids are more durable across restarts.
  • Developer reference for the underlying plugin lives in the BlueBubbles plugin package README.

Ideas to try

  • React with a tapback to acknowledge a request.
  • Reply in-thread when a user references a specific message.
  • Send a file attachment with short message text, expecting BlueBubbles to treat the text as best-effort companion text rather than a guaranteed native caption.
在连接的 Brigde 节点(Mac/iOS/Android)上展示 HTML 内容,如游戏、可视化图表和交互演示。通过本地或 Tailscale 网络提供静态资源,支持实时重载、页面导航及 JS 执行。
需要展示交互式网页或游戏 生成并预览 HTML 可视化内容 制作动态仪表盘或演示原型
skills/canvas/SKILL.md
npx skills add spinabot/brigade --skill canvas -g -y
SKILL.md
Frontmatter
{
    "name": "canvas",
    "description": "Display HTML content — games, visualizations, dashboards, generated HTML, interactive demos — on a connected canvas-capable display surface. Use when you need to present visual or interactive web content on a screen."
}

Canvas Skill

Display HTML content on connected Brigade nodes (Mac app, iOS, Android).

Overview

The canvas tool lets you present web content on any connected node's canvas view. Great for:

  • Displaying games, visualizations, dashboards
  • Showing generated HTML content
  • Interactive demos

How It Works

Architecture

┌─────────────────┐     ┌──────────────────┐     ┌─────────────┐
│  Canvas Host    │────▶│   Node Bridge    │────▶│  Node App   │
│  (HTTP Server)  │     │  (TCP Server)    │     │ (Mac/iOS/   │
│  Port 18793     │     │  Port 18790      │     │  Android)   │
└─────────────────┘     └──────────────────┘     └─────────────┘
  1. Canvas Host Server: Serves static HTML/CSS/JS files from canvasHost.root directory
  2. Node Bridge: Communicates canvas URLs to connected nodes
  3. Node Apps: Render the content in a WebView

Tailscale Integration

The canvas host server binds based on gateway.bind setting:

Bind Mode Server Binds To Canvas URL Uses
loopback 127.0.0.1 localhost (local only)
lan LAN interface LAN IP address
tailnet Tailscale interface Tailscale hostname
auto Best available Tailscale > LAN > loopback

Key insight: The canvasHostHostForBridge is derived from bridgeHost. When bound to Tailscale, nodes receive URLs like:

http://<tailscale-hostname>:18793/__brigade__/canvas/<file>.html

This is why localhost URLs don't work - the node receives the Tailscale hostname from the bridge!

Actions

Action Description
present Show canvas with optional target URL
hide Hide the canvas
navigate Navigate to a new URL
eval Execute JavaScript in the canvas
snapshot Capture screenshot of canvas

Configuration

In the active Brigade config file ($BRIGADE_CONFIG_PATH, default ~/.brigade/brigade.json):

{
  "canvasHost": {
    "enabled": true,
    "port": 18793,
    "root": "/Users/you/brigade/canvas",
    "liveReload": true
  },
  "gateway": {
    "bind": "auto"
  }
}

Live Reload

When liveReload: true (default), the canvas host:

  • Watches the root directory for changes (via chokidar)
  • Injects a WebSocket client into HTML files
  • Automatically reloads connected canvases when files change

Great for development!

Workflow

1. Create HTML content

Place files in the canvas root directory (default ~/brigade/canvas/):

cat > ~/brigade/canvas/my-game.html << 'HTML'
<!DOCTYPE html>
<html>
<head><title>My Game</title></head>
<body>
  <h1>Hello Canvas!</h1>
</body>
</html>
HTML

2. Find your canvas host URL

Check how your gateway is bound:

CONFIG_PATH="${BRIGADE_CONFIG_PATH:-${BRIGADE_STATE_DIR:-$HOME/.brigade}/brigade.json}"
cat "$CONFIG_PATH" | jq '.gateway.bind'

Then construct the URL:

  • loopback: http://127.0.0.1:18793/__brigade__/canvas/<file>.html
  • lan/tailnet/auto: http://<hostname>:18793/__brigade__/canvas/<file>.html

Find your Tailscale hostname:

tailscale status --json | jq -r '.Self.DNSName' | sed 's/\.$//'

3. Find connected nodes

brigade nodes list

Look for Mac/iOS/Android nodes with canvas capability.

4. Present content

canvas action:present node:<node-id> target:<full-url>

Example:

canvas action:present node:mac-63599bc4-b54d-4392-9048-b97abd58343a target:http://peters-mac-studio-1.sheep-coho.ts.net:18793/__brigade__/canvas/snake.html

5. Navigate, snapshot, or hide

canvas action:navigate node:<node-id> url:<new-url>
canvas action:snapshot node:<node-id>
canvas action:hide node:<node-id>

Debugging

White screen / content not loading

Cause: URL mismatch between server bind and node expectation.

Debug steps:

  1. Check server bind: CONFIG_PATH="${BRIGADE_CONFIG_PATH:-${BRIGADE_STATE_DIR:-$HOME/.brigade}/brigade.json}"; cat "$CONFIG_PATH" | jq '.gateway.bind'
  2. Check what port canvas is on: lsof -i :18793
  3. Test URL directly: curl http://<hostname>:18793/__brigade__/canvas/<file>.html

Solution: Use the full hostname matching your bind mode, not localhost.

"node required" error

Always specify node:<node-id> parameter.

"node not connected" error

Node is offline. Use brigade nodes list to find online nodes.

Content not updating

If live reload isn't working:

  1. Check liveReload: true in config
  2. Ensure file is in the canvas root directory
  3. Check for watcher errors in logs

URL Path Structure

The canvas host serves from /__brigade__/canvas/ prefix:

http://<host>:18793/__brigade__/canvas/index.html  → ~/brigade/canvas/index.html
http://<host>:18793/__brigade__/canvas/games/snake.html → ~/brigade/canvas/games/snake.html

The /__brigade__/canvas/ prefix is defined by CANVAS_HOST_PATH constant.

Tips

  • Keep HTML self-contained (inline CSS/JS) for best results
  • Use the default index.html as a test page (has bridge diagnostics)
  • The canvas persists until you hide it or navigate away
  • Live reload makes development fast - just save and it updates!
  • A2UI JSON push is WIP - use HTML files for now
通过 message 工具执行 Discord 操作。支持发送消息(含媒体、组件)、编辑删除、反应、轮询、置顶、创建线程及搜索。需指定 channel: discord,优先使用显式 ID 和 v2 组件,避免混用 embeds。
需要在 Discord 频道或用户间发送消息 管理 Discord 消息内容或状态 在 Discord 中创建线程或发起投票
skills/discord/SKILL.md
npx skills add spinabot/brigade --skill discord -g -y
SKILL.md
Frontmatter
{
    "name": "discord",
    "metadata": {
        "brigade": {
            "emoji": "🎮",
            "requires": {
                "config": [
                    "channels.discord.token"
                ]
            }
        }
    },
    "description": "Discord ops via the message tool (channel=discord).",
    "allowed-tools": [
        "message"
    ]
}

Discord (Via message)

Use the message tool. No provider-specific discord tool exposed to the agent.

Musts

  • Always: channel: "discord".
  • Respect gating: channels.discord.actions.* (some default off: roles, moderation, presence, channels).
  • Prefer explicit ids: guildId, channelId, messageId, userId.
  • Multi-account: optional accountId.

Guidelines

  • Avoid Markdown tables in outbound Discord messages.
  • Mention users as <@USER_ID>.
  • Prefer Discord components v2 (components) for rich UI; use legacy embeds only when you must.

Targets

  • Send-like actions: to: "channel:<id>" or to: "user:<id>".
  • Message-specific actions: channelId: "<id>" (or to) + messageId: "<id>".

Common Actions (Examples)

Send message:

{
  "action": "send",
  "channel": "discord",
  "to": "channel:123",
  "message": "hello",
  "silent": true
}

Send with media:

{
  "action": "send",
  "channel": "discord",
  "to": "channel:123",
  "message": "see attachment",
  "media": "file:///tmp/example.png"
}
  • Optional silent: true to suppress Discord notifications.

Send with components v2 (recommended for rich UI):

{
  "action": "send",
  "channel": "discord",
  "to": "channel:123",
  "message": "Status update",
  "components": "[Carbon v2 components]"
}
  • components expects Carbon component instances (Container, TextDisplay, etc.) from JS/TS integrations.
  • Do not combine components with embeds (Discord rejects v2 + embeds).

Legacy embeds (not recommended):

{
  "action": "send",
  "channel": "discord",
  "to": "channel:123",
  "message": "Status update",
  "embeds": [{ "title": "Legacy", "description": "Embeds are legacy." }]
}
  • embeds are ignored when components v2 are present.

React:

{
  "action": "react",
  "channel": "discord",
  "channelId": "123",
  "messageId": "456",
  "emoji": "✅"
}

Read:

{
  "action": "read",
  "channel": "discord",
  "to": "channel:123",
  "limit": 20
}

Edit / delete:

{
  "action": "edit",
  "channel": "discord",
  "channelId": "123",
  "messageId": "456",
  "message": "fixed typo"
}
{
  "action": "delete",
  "channel": "discord",
  "channelId": "123",
  "messageId": "456"
}

Poll:

{
  "action": "poll",
  "channel": "discord",
  "to": "channel:123",
  "pollQuestion": "Lunch?",
  "pollOption": ["Pizza", "Sushi", "Salad"],
  "pollMulti": false,
  "pollDurationHours": 24
}

Pins:

{
  "action": "pin",
  "channel": "discord",
  "channelId": "123",
  "messageId": "456"
}

Threads:

{
  "action": "thread-create",
  "channel": "discord",
  "channelId": "123",
  "messageId": "456",
  "threadName": "bug triage"
}

Search:

{
  "action": "search",
  "channel": "discord",
  "guildId": "999",
  "query": "release notes",
  "channelIds": ["123", "456"],
  "limit": 10
}

Presence (often gated):

{
  "action": "set-presence",
  "channel": "discord",
  "activityType": "playing",
  "activityName": "with fire",
  "status": "online"
}

Writing Style (Discord)

  • Short, conversational, low ceremony.
  • No markdown tables.
  • Mention users as <@USER_ID>.
用于创建和编辑专业级Word文档。提供四种路径:快速生成简单结构、通过脚本实现高级样式与修订、填充现有模板或Markdown转换。强调必须验证结果以确保格式正确。
用户要求制作Word文档 用户需要编辑.docx文件 用户请求起草报告或合同 用户要求进行文档红线条款修改
skills/docx/SKILL.md
npx skills add spinabot/brigade --skill docx -g -y
SKILL.md
Frontmatter
{
    "name": "docx",
    "metadata": {
        "brigade": {
            "emoji": "📝"
        }
    },
    "description": "Create and edit Microsoft Word (.docx) documents to a professional standard — reports, proposals, contracts, letters, memos — with custom styles, multi-level lists, styled\/merged tables, headers\/footers, table of contents, hyperlinks, footnotes, tracked changes and comments. Use when the user asks Brigade to make, write, draft, fill, redline, or edit a Word document or .docx file."
}

docx — professional Word documents

Pick the lightest path that meets the need. Never declare a document done without opening/validating the result at least once (see Verify).

Need Path
Simple structured doc (headings, paragraphs, bullets, a basic table, an image) Path 1 — make_document tool
Anything richer (custom styles, numbered/multi-level lists, merged/shaded table cells, headers/footers, TOC, hyperlinks, footnotes, inline bold/italic/color, tracked changes, comments) Path 2 — script the docx library via brigade exec-node
Fill / redline an EXISTING branded .docx without disturbing its styling Path 3 — OOXML round-trip
Markdown → branded Word inheriting a corporate template Path 4 — pandoc (optional)

Path 1 — quick structured doc (make_document tool)

make_document(format="docx", content={ title, sections:[{heading, level, paragraphs, bullets, table:{rows}, image:{path}}] })

Good for a fast first draft. It is deliberately limited (single-level bullets, string-only tables, no inline formatting). The moment you need more, go to Path 2 — don't fight the schema.

Path 2 — full power: script the docx library

Brigade bundles the docx library (dolanmiu/docx). Write a CommonJS script and run it with brigade exec-node, which makes Brigade's bundled libraries require()-able from anywhere (no install):

  1. write a file gen.cjs.
  2. Run: brigade exec-node gen.cjs
// gen.cjs — illustrative; adapt to the content
const fs = require("node:fs");
const {
  Document, Packer, Paragraph, TextRun, HeadingLevel, AlignmentType,
  Table, TableRow, TableCell, WidthType, BorderStyle, ShadingType,
  LevelFormat, Header, Footer, PageNumber, TableOfContents, ExternalHyperlink,
} = require("docx");

const doc = new Document({
  styles: { default: { document: { run: { font: "Calibri", size: 22 } } },     // size is half-points (22 = 11pt)
    paragraphStyles: [{ id: "Body", name: "Body", run: { size: 22 }, paragraph: { spacing: { after: 160 } } }] },
  numbering: { config: [{ reference: "nums", levels: [{ level: 0, format: LevelFormat.DECIMAL, text: "%1.", alignment: AlignmentType.START }] }] },
  sections: [{
    properties: { page: { size: { width: 12240, height: 15840 } } },           // US Letter, in DXA (1440 = 1 inch)
    headers: { default: new Header({ children: [new Paragraph("Acme Corp — Confidential")] }) },
    footers: { default: new Footer({ children: [new Paragraph({ children: [new TextRun("Page "), new TextRun({ children: [PageNumber.CURRENT] })] })] }) },
    children: [
      new Paragraph({ text: "Q3 Business Review", heading: HeadingLevel.TITLE }),
      new TableOfContents("Contents", { hyperlink: true, headingStyleRange: "1-3" }),
      new Paragraph({ text: "Summary", heading: HeadingLevel.HEADING_1 }),
      new Paragraph({ children: [ new TextRun("Revenue was "), new TextRun({ text: "$1.2M", bold: true }), new TextRun(" — up 18%.") ] }),
      new Paragraph({ text: "First item", numbering: { reference: "nums", level: 0 } }),
      new Paragraph({ children: [ new ExternalHyperlink({ children: [new TextRun({ text: "Full data", style: "Hyperlink" })], link: "https://example.com" }) ] }),
      new Table({
        columnWidths: [4680, 4680],                                            // DXA, sum = content width
        rows: [
          new TableRow({ tableHeader: true, children: ["Metric","Value"].map((t) =>
            new TableCell({ width: { size: 4680, type: WidthType.DXA }, shading: { type: ShadingType.CLEAR, fill: "D9E2F3" },
              children: [new Paragraph({ children: [new TextRun({ text: t, bold: true })] })] })) }),
          new TableRow({ children: ["Revenue","$1.2M"].map((t) =>
            new TableCell({ width: { size: 4680, type: WidthType.DXA }, children: [new Paragraph(t)] })) }),
        ],
      }),
    ],
  }],
});
Packer.toBuffer(doc).then((buf) => { fs.writeFileSync(process.argv[2] || "out.docx", buf); console.log("wrote", process.argv[2] || "out.docx"); });

The docx library also supports: footnotes (FootnoteReferenceRun + footnotes), comments, tracked changes (InsertedTextRun/DeletedTextRun with author/date), multi-section layouts, page breaks, columns, internal bookmarks, and images inside table cells. Read its API as needed — anything Word can express, this can author.

Path 3 — fill / redline an EXISTING .docx (highest fidelity)

To fill a branded company template or surgically edit a real document without regenerating it (which would drop the theme), use the OOXML round-trip — unzip, edit the XML, rezip. For plain placeholder/text swaps the edit_document tool (replace_text / fill_template / append) already does exactly this and is the first choice.

For edits beyond text, script it via brigade exec-node with fflate:

// edit.cjs — unzip → edit word/document.xml → rezip (styles/headers/theme untouched)
const fs = require("node:fs");
const { unzipSync, zipSync, strToU8, strFromU8 } = require("fflate");
const zip = unzipSync(fs.readFileSync(process.argv[2]));
let xml = strFromU8(zip["word/document.xml"]);
xml = xml.replaceAll("{{client}}", "Acme Corp");          // fill placeholders in <w:t> runs
zip["word/document.xml"] = strToU8(xml);
fs.writeFileSync(process.argv[3], zipSync(zip));

Rules for raw OOXML edits:

  • Safe: replacing text inside <w:t>; pointing a run/paragraph at an already-defined styleId; appending <w:p>/<w:r> that reference existing styles.
  • Do NOT add a new part (image, hyperlink target, header) by hand — that also needs [Content_Types].xml + the matching _rels/*.rels updated atomically, or Word reports corruption. For those, regenerate with the docx library instead.
  • Tracked changes are <w:ins>/<w:del> as siblings of <w:r> (never inside a run); copy the original <w:rPr> into the change runs; keep edits minimal so an "accept all" yields exactly the intended text. Author yourself as "Brigade".

Path 4 — Markdown → branded Word (optional, needs pandoc)

For long prose where the brand styling lives in a corporate .docx, if pandoc is installed it inherits every style/margin/header:

command -v pandoc >/dev/null 2>&1 && pandoc input.md --reference-doc=brand-template.docx -o out.docx

Detect first; else fall back to Path 2. pandoc is GPL — a separate program shelled out to, never bundled.

Conventions that make output professional

  • Page size is explicit — US Letter 12240×15840 DXA (1440 DXA = 1"); landscape = portrait dims + orientation flip.
  • Tables: give both columnWidths (table) and per-cell width in WidthType.DXA (PERCENTAGE breaks some viewers), summing to content width; ShadingType.CLEAR (never SOLID → black boxes); add cell margins; never use a table as a horizontal rule (use a paragraph bottom border).
  • Lists: real numbering config for numbered/multi-level lists; never paste literal "1." / "•" characters.
  • Prefer bullets + tables over walls of text; one TITLE, then HEADING_1..3 with proper outline levels (required for a working TOC).
  • Use typographic quotes (' ' " ") and en/em dashes.

Verify (required)

After writing, confirm the file exists and is non-trivial. If LibreOffice is available, render to PDF and look at it — fix overflow/placeholder leftovers, then re-verify:

command -v soffice >/dev/null 2>&1 && soffice --headless --convert-to pdf out.docx

TOC/page-number fields show blank until Word (or a soffice convert) recalculates them — note this to the user if you can't run the convert. Never declare success without at least one look-and-fix pass.

用于控制Eight Sleep智能床垫的命令行工具。支持查询状态、开关机、调节温度、管理闹钟与作息计划、控制音频及床体角度。需配置认证信息,操作前建议确认关键变更。
用户想要查看或控制Eight Sleep智能床垫的状态 用户需要设置或修改床垫的温度、闹钟或睡眠计划
skills/eightctl/SKILL.md
npx skills add spinabot/brigade --skill eightctl -g -y
SKILL.md
Frontmatter
{
    "name": "eightctl",
    "homepage": "https:\/\/eightctl.sh",
    "metadata": {
        "brigade": {
            "emoji": "🛌",
            "install": [
                {
                    "id": "go",
                    "bins": [
                        "eightctl"
                    ],
                    "kind": "go",
                    "label": "Install eightctl (go)",
                    "module": "github.com\/steipete\/eightctl\/cmd\/eightctl@latest"
                }
            ],
            "requires": {
                "bins": [
                    "eightctl"
                ]
            }
        }
    },
    "description": "Control Eight Sleep pods (status, temperature, alarms, schedules)."
}

eightctl

Use eightctl for Eight Sleep pod control. Requires auth.

Auth

  • Config: ~/.config/eightctl/config.yaml
  • Env: EIGHTCTL_EMAIL, EIGHTCTL_PASSWORD

Quick start

  • eightctl status
  • eightctl on|off
  • eightctl temp 20

Common tasks

  • Alarms: eightctl alarm list|create|dismiss
  • Schedules: eightctl schedule list|create|update
  • Audio: eightctl audio state|play|pause
  • Base: eightctl base info|angle

Notes

  • API is unofficial and rate-limited; avoid repeated logins.
  • Confirm before changing temperature or alarms.
Gemini CLI 技能,支持通过命令行进行一次性问答、摘要生成和内容创作。允许指定模型和JSON输出格式,并提供扩展管理功能。需预先完成交互式登录认证,建议避免使用不安全的yolo模式。
需要调用 Gemini CLI 执行一次性查询或生成任务 要求以 JSON 格式获取 Gemini 的输出结果 需要列出或管理 Gemini 的扩展插件
skills/gemini/SKILL.md
npx skills add spinabot/brigade --skill gemini -g -y
SKILL.md
Frontmatter
{
    "name": "gemini",
    "homepage": "https:\/\/ai.google.dev\/",
    "metadata": {
        "brigade": {
            "emoji": "✨",
            "install": [
                {
                    "id": "brew",
                    "bins": [
                        "gemini"
                    ],
                    "kind": "brew",
                    "label": "Install Gemini CLI (brew)",
                    "formula": "gemini-cli"
                }
            ],
            "requires": {
                "bins": [
                    "gemini"
                ]
            }
        }
    },
    "description": "Gemini CLI for one-shot Q&A, summaries, and generation."
}

Gemini CLI

Use Gemini in one-shot mode with a positional prompt (avoid interactive mode).

Quick start

  • gemini "Answer this question..."
  • gemini --model <name> "Prompt..."
  • gemini --output-format json "Return JSON"

Extensions

  • List: gemini --list-extensions
  • Manage: gemini extensions <command>

Notes

  • If auth is required, run gemini once interactively and follow the login flow.
  • Avoid --yolo for safety.
自动修复GitHub Issue的编排技能。通过REST API获取Issue,派生子Agent实施修复并创建PR,监控评论。支持过滤、Fork模式、持续监听及Cron定时任务,无需gh CLI依赖。
用户请求修复GitHub Issue 需要批量处理代码缺陷或增强功能 监控并响应Pull Request审查意见
skills/gh-issues/SKILL.md
npx skills add spinabot/brigade --skill gh-issues -g -y
SKILL.md
Frontmatter
{
    "name": "gh-issues",
    "metadata": {
        "brigade": {
            "install": [
                {
                    "id": "brew",
                    "bins": [
                        "gh"
                    ],
                    "kind": "brew",
                    "label": "Install GitHub CLI (brew)",
                    "formula": "gh"
                }
            ],
            "requires": {
                "bins": [
                    "curl",
                    "git",
                    "gh"
                ]
            },
            "primaryEnv": "GH_TOKEN"
        }
    },
    "description": "Fetch GitHub issues, spawn sub-agents to implement fixes and open PRs, then monitor and address PR review comments. Usage: \/gh-issues [owner\/repo] [--label bug] [--limit 5] [--milestone v1.0] [--assignee @me] [--fork user\/repo] [--watch] [--interval 5] [--reviews-only] [--cron] [--dry-run] [--model glm-5] [--notify-channel -1002381931352]",
    "user-invocable": true
}

gh-issues — Auto-fix GitHub Issues with Parallel Sub-agents

You are an orchestrator. Follow these 6 phases exactly. Do not skip phases.

IMPORTANT — No gh CLI dependency. This skill uses curl + the GitHub REST API exclusively. The GH_TOKEN env var is already injected by Brigade. Pass it as a Bearer token in all API calls:

curl -s -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github+json" ...

Phase 1 — Parse Arguments

Parse the arguments string provided after /gh-issues.

Positional:

  • owner/repo — optional. This is the source repo to fetch issues from. If omitted, detect from the current git remote: git remote get-url origin Extract owner/repo from the URL (handles both HTTPS and SSH formats).

Flags (all optional):

Flag Default Description
--label (none) Filter by label (e.g. bug, enhancement)
--limit 10 Max issues to fetch per poll
--milestone (none) Filter by milestone title
--assignee (none) Filter by assignee (@me for self)
--state open Issue state: open, closed, all
--fork (none) Your fork (user/repo) to push branches and open PRs from. Issues are fetched from the source repo; code is pushed to the fork; PRs are opened from the fork to the source repo.
--watch false Keep polling for new issues and PR reviews after each batch
--interval 5 Minutes between polls (only with --watch)
--dry-run false Fetch and display only — no sub-agents
--yes false Skip confirmation and auto-process all filtered issues
--reviews-only false Skip issue processing (Phases 2-5). Only run Phase 6 — check open PRs for review comments and address them.
--cron false Cron-safe mode: fetch issues and spawn sub-agents, exit without waiting for results.
--model (none) Model to use for sub-agents (e.g. glm-5, zai/glm-5). If not specified, uses the agent's default model.
--notify-channel (none) Telegram channel ID to send final PR summary to (e.g. -1002381931352). Only the final result with PR links is sent, not status updates.

Store parsed values for use in subsequent phases.

Derived values:

  • SOURCE_REPO = the positional owner/repo (where issues live)
  • PUSH_REPO = --fork value if provided, otherwise same as SOURCE_REPO
  • FORK_MODE = true if --fork was provided, false otherwise

If --reviews-only is set: Skip directly to Phase 6. Run token resolution (from Phase 2) first, then jump to Phase 6.

If --cron is set:

  • Force --yes (skip confirmation)
  • If --reviews-only is also set, run token resolution then jump to Phase 6 (cron review mode)
  • Otherwise, proceed normally through Phases 2-5 with cron-mode behavior active

Phase 2 — Fetch Issues

Token Resolution: First, ensure GH_TOKEN is available. Check environment:

echo $GH_TOKEN

If empty, read from config:

CONFIG_PATH="${BRIGADE_CONFIG_PATH:-${BRIGADE_STATE_DIR:-$HOME/.brigade}/brigade.json}"
cat "$CONFIG_PATH" | jq -r '.skills.entries["gh-issues"].apiKey // empty'

If still empty, check /data/.brigade/brigade.json:

cat /data/.brigade/brigade.json | jq -r '.skills.entries["gh-issues"].apiKey // empty'

Export as GH_TOKEN for subsequent commands:

export GH_TOKEN="<token>"

Build and run a curl request to the GitHub Issues API via exec:

curl -s -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github+json" \
  "https://api.github.com/repos/{SOURCE_REPO}/issues?per_page={limit}&state={state}&{query_params}"

Where {query_params} is built from:

  • labels={label} if --label was provided
  • milestone={milestone} if --milestone was provided (note: API expects milestone number, so if user provides a title, first resolve it via GET /repos/{SOURCE_REPO}/milestones and match by title)
  • assignee={assignee} if --assignee was provided (if @me, first resolve your username via GET /user)

IMPORTANT: The GitHub Issues API also returns pull requests. Filter them out — exclude any item where pull_request key exists in the response object.

If in watch mode: Also filter out any issue numbers already in the PROCESSED_ISSUES set from previous batches.

Error handling:

  • If curl returns an HTTP 401 or 403 → stop and tell the user:

    "GitHub authentication failed. Please check your apiKey in the Brigade dashboard or in the active Brigade config path ($BRIGADE_CONFIG_PATH, default ~/.brigade/brigade.json) under skills.entries.gh-issues."

  • If the response is an empty array (after filtering) → report "No issues found matching filters" and stop (or loop back if in watch mode).
  • If curl fails or returns any other error → report the error verbatim and stop.

Parse the JSON response. For each issue, extract: number, title, body, labels (array of label names), assignees, html_url.


Phase 3 — Present & Confirm

Display a markdown table of fetched issues:

# Title Labels
42 Fix null pointer in parser bug, critical
37 Add retry logic for API calls enhancement

If FORK_MODE is active, also display:

"Fork mode: branches will be pushed to {PUSH_REPO}, PRs will target {SOURCE_REPO}"

If --dry-run is active:

  • Display the table and stop. Do not proceed to Phase 4.

If --yes is active:

  • Display the table for visibility
  • Auto-process ALL listed issues without asking for confirmation
  • Proceed directly to Phase 4

Otherwise: Ask the user to confirm which issues to process:

  • "all" — process every listed issue
  • Comma-separated numbers (e.g. 42, 37) — process only those
  • "cancel" — abort entirely

Wait for user response before proceeding.

Watch mode note: On the first poll, always confirm with the user (unless --yes is set). On subsequent polls, auto-process all new issues without re-confirming (the user already opted in). Still display the table so they can see what's being processed.


Phase 4 — Pre-flight Checks

Run these checks sequentially via exec:

  1. Dirty working tree check:

    git status --porcelain
    

    If output is non-empty, warn the user:

    "Working tree has uncommitted changes. Sub-agents will create branches from HEAD — uncommitted changes will NOT be included. Continue?" Wait for confirmation. If declined, stop.

  2. Record base branch:

    git rev-parse --abbrev-ref HEAD
    

    Store as BASE_BRANCH.

  3. Verify remote access: If FORK_MODE:

    • Verify the fork remote exists. Check if a git remote named fork exists:
      git remote get-url fork
      
      If it doesn't exist, add it:
      git remote add fork https://x-access-token:$GH_TOKEN@github.com/{PUSH_REPO}.git
      
    • Also verify origin (the source repo) is reachable:
      git ls-remote --exit-code origin HEAD
      

    If not FORK_MODE:

    git ls-remote --exit-code origin HEAD
    

    If this fails, stop with: "Cannot reach remote origin. Check your network and git config."

  4. Verify GH_TOKEN validity:

    curl -s -o /dev/null -w "%{http_code}" -H "Authorization: Bearer $GH_TOKEN" https://api.github.com/user
    

    If HTTP status is not 200, stop with:

    "GitHub authentication failed. Please check your apiKey in the Brigade dashboard or in the active Brigade config path ($BRIGADE_CONFIG_PATH, default ~/.brigade/brigade.json) under skills.entries.gh-issues."

  5. Check for existing PRs: For each confirmed issue number N, run:

    curl -s -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github+json" \
      "https://api.github.com/repos/{SOURCE_REPO}/pulls?head={PUSH_REPO_OWNER}:fix/issue-{N}&state=open&per_page=1"
    

    (Where PUSH_REPO_OWNER is the owner portion of PUSH_REPO) If the response array is non-empty, remove that issue from the processing list and report:

    "Skipping #{N} — PR already exists: {html_url}"

    If all issues are skipped, report and stop (or loop back if in watch mode).

  6. Check for in-progress branches (no PR yet = sub-agent still working): For each remaining issue number N (not already skipped by the PR check above), check if a fix/issue-{N} branch exists on the push repo (which may be a fork, not origin):

    curl -s -o /dev/null -w "%{http_code}" \
      -H "Authorization: Bearer $GH_TOKEN" \
      "https://api.github.com/repos/{PUSH_REPO}/branches/fix/issue-{N}"
    

    If HTTP 200 → the branch exists on the push repo but no open PR was found for it in step 5. Skip that issue:

    "Skipping #{N} — branch fix/issue-{N} exists on {PUSH_REPO}, fix likely in progress"

    This check uses the GitHub API instead of git ls-remote so it works correctly in fork mode (where branches are pushed to the fork, not origin).

    If all issues are skipped after this check, report and stop (or loop back if in watch mode).

  7. Check claim-based in-progress tracking: This prevents duplicate processing when a sub-agent from a previous cron run is still working but hasn't pushed a branch or opened a PR yet.

    Read the claims file (create empty {} if missing):

    CLAIMS_FILE="/data/.brigade/gh-issues-claims.json"
    if [ ! -f "$CLAIMS_FILE" ]; then
      mkdir -p /data/.brigade
      echo '{}' > "$CLAIMS_FILE"
    fi
    

    Parse the claims file. For each entry, check if the claim timestamp is older than 2 hours. If so, remove it (expired — the sub-agent likely finished or failed silently). Write back the cleaned file:

    CLAIMS=$(cat "$CLAIMS_FILE")
    CUTOFF=$(date -u -d '2 hours ago' +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -u -v-2H +%Y-%m-%dT%H:%M:%SZ)
    CLAIMS=$(echo "$CLAIMS" | jq --arg cutoff "$CUTOFF" 'to_entries | map(select(.value > $cutoff)) | from_entries')
    echo "$CLAIMS" > "$CLAIMS_FILE"
    

    For each remaining issue number N (not already skipped by steps 5 or 6), check if {SOURCE_REPO}#{N} exists as a key in the claims file.

    If claimed and not expired → skip:

    "Skipping #{N} — sub-agent claimed this issue {minutes}m ago, still within timeout window"

    Where {minutes} is calculated from the claim timestamp to now.

    If all issues are skipped after this check, report and stop (or loop back if in watch mode).


Phase 5 — Spawn Sub-agents (Parallel)

Cron mode (--cron is active):

  • Sequential cursor tracking: Use a cursor file to track which issue to process next:

    CURSOR_FILE="/data/.brigade/gh-issues-cursor-{SOURCE_REPO_SLUG}.json"
    # SOURCE_REPO_SLUG = owner-repo with slashes replaced by hyphens (e.g., brigade-brigade)
    

    Read the cursor file (create if missing):

    if [ ! -f "$CURSOR_FILE" ]; then
      echo '{"last_processed": null, "in_progress": null}' > "$CURSOR_FILE"
    fi
    
    • last_processed: issue number of the last completed issue (or null if none)
    • in_progress: issue number currently being processed (or null)
  • Select next issue: Filter the fetched issues list to find the first issue where:

    • Issue number > last_processed (if last_processed is set)
    • AND issue is not in the claims file (not already in progress)
    • AND no PR exists for the issue (checked in Phase 4 step 5)
    • AND no branch exists on the push repo (checked in Phase 4 step 6)
  • If no eligible issue is found after the last_processed cursor, wrap around to the beginning (start from the oldest eligible issue).

  • If an eligible issue is found:

    1. Mark it as in_progress in the cursor file
    2. Spawn a single sub-agent for that one issue with cleanup: "keep" and runTimeoutSeconds: 3600
    3. If --model was provided, include model: "{MODEL}" in the spawn config
    4. If --notify-channel was provided, include the channel in the task so the sub-agent can notify
    5. Do NOT await the sub-agent result — fire and forget
    6. Write claim: After spawning, read the claims file, add {SOURCE_REPO}#{N} with the current ISO timestamp, and write it back
    7. Immediately report: "Spawned fix agent for #{N} — will create PR when complete"
    8. Exit the skill. Do not proceed to Results Collection or Phase 6.
  • If no eligible issue is found (all issues either have PRs, have branches, or are in progress), report "No eligible issues to process — all issues have PRs/branches or are in progress" and exit.

Normal mode (--cron is NOT active): For each confirmed issue, spawn a sub-agent using sessions_spawn. Launch up to 8 concurrently (matching subagents.maxConcurrent: 8). If more than 8 issues, batch them — launch the next agent as each completes.

Write claims: After spawning each sub-agent, read the claims file, add {SOURCE_REPO}#{N} with the current ISO timestamp, and write it back (same procedure as cron mode above). This covers interactive usage where watch mode might overlap with cron runs.

Sub-agent Task Prompt

For each issue, construct the following prompt and pass it to sessions_spawn. Variables to inject into the template:

  • {SOURCE_REPO} — upstream repo where the issue lives
  • {PUSH_REPO} — repo to push branches to (same as SOURCE_REPO unless fork mode)
  • {FORK_MODE} — true/false
  • {PUSH_REMOTE} — fork if FORK_MODE, otherwise origin
  • {number}, {title}, {url}, {labels}, {body} — from the issue
  • {BASE_BRANCH} — from Phase 4
  • {notify_channel} — Telegram channel ID for notifications (empty if not set). Replace {notify_channel} in the template below with the value of --notify-channel flag (or leave as empty string if not provided).

When constructing the task, replace all template variables including {notify_channel} with actual values.

You are a focused code-fix agent. Your task is to fix a single GitHub issue and open a PR.

IMPORTANT: Do NOT use the gh CLI — it is not installed. Use curl with the GitHub REST API for all GitHub operations.

First, ensure GH_TOKEN is set. Check: `echo $GH_TOKEN`. If empty, read from config:
CONFIG_PATH="${BRIGADE_CONFIG_PATH:-${BRIGADE_STATE_DIR:-$HOME/.brigade}/brigade.json}"
GH_TOKEN=$(cat "$CONFIG_PATH" 2>/dev/null | jq -r '.skills.entries["gh-issues"].apiKey // empty') || GH_TOKEN=$(cat /data/.brigade/brigade.json 2>/dev/null | jq -r '.skills.entries["gh-issues"].apiKey // empty')

Use the token in all GitHub API calls:
curl -s -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github+json" ...

<config>
Source repo (issues): {SOURCE_REPO}
Push repo (branches + PRs): {PUSH_REPO}
Fork mode: {FORK_MODE}
Push remote name: {PUSH_REMOTE}
Base branch: {BASE_BRANCH}
Notify channel: {notify_channel}
</config>

<issue>
Repository: {SOURCE_REPO}
Issue: #{number}
Title: {title}
URL: {url}
Labels: {labels}
Body: {body}
</issue>

<instructions>
Follow these steps in order. If any step fails, report the failure and stop.

0. SETUP — Ensure GH_TOKEN is available:

export GH_TOKEN=$(node -e "const fs=require('fs'); const c=JSON.parse(fs.readFileSync('/data/.brigade/brigade.json','utf8')); console.log(c.skills?.entries?.['gh-issues']?.apiKey || '')")

If that fails, also try:

export CONFIG_PATH="${BRIGADE_CONFIG_PATH:-${BRIGADE_STATE_DIR:-$HOME/.brigade}/brigade.json}" export GH_TOKEN=$(cat "$CONFIG_PATH" 2>/dev/null | node -e "const fs=require('fs');const d=JSON.parse(fs.readFileSync(0,'utf8'));console.log(d.skills?.entries?.['gh-issues']?.apiKey||'')")

Verify: echo "Token: ${GH_TOKEN:0:10}..."

1. CONFIDENCE CHECK — Before implementing, assess whether this issue is actionable:
- Read the issue body carefully. Is the problem clearly described?
- Search the codebase (grep/find) for the relevant code. Can you locate it?
- Is the scope reasonable? (single file/function = good, whole subsystem = bad)
- Is a specific fix suggested or is it a vague complaint?

Rate your confidence (1-10). If confidence < 7, STOP and report:
> "Skipping #{number}: Low confidence (score: N/10) — [reason: vague requirements | cannot locate code | scope too large | no clear fix suggested]"

Only proceed if confidence >= 7.

1. UNDERSTAND — Read the issue carefully. Identify what needs to change and where.

2. BRANCH — Create a feature branch from the base branch:
git checkout -b fix/issue-{number} {BASE_BRANCH}

3. ANALYZE — Search the codebase to find relevant files:
- Use grep/find via exec to locate code related to the issue
- Read the relevant files to understand the current behavior
- Identify the root cause

4. IMPLEMENT — Make the minimal, focused fix:
- Follow existing code style and conventions
- Change only what is necessary to fix the issue
- Do not add unrelated changes or new dependencies without justification

5. TEST — Discover and run the existing test suite if one exists:
- Look for package.json scripts, Makefile targets, pytest, cargo test, etc.
- Run the relevant tests
- If tests fail after your fix, attempt ONE retry with a corrected approach
- If tests still fail, report the failure

6. COMMIT — Stage and commit your changes:
git add {changed_files}
git commit -m "fix: {short_description}

Fixes {SOURCE_REPO}#{number}"

7. PUSH — Push the branch:
First, ensure the push remote uses token auth and disable credential helpers:
git config --global credential.helper ""
git remote set-url {PUSH_REMOTE} https://x-access-token:$GH_TOKEN@github.com/{PUSH_REPO}.git
Then push:
GIT_ASKPASS=true git push -u {PUSH_REMOTE} fix/issue-{number}

8. PR — Create a pull request using the GitHub API:

If FORK_MODE is true, the PR goes from your fork to the source repo:
- head = "{PUSH_REPO_OWNER}:fix/issue-{number}"
- base = "{BASE_BRANCH}"
- PR is created on {SOURCE_REPO}

If FORK_MODE is false:
- head = "fix/issue-{number}"
- base = "{BASE_BRANCH}"
- PR is created on {SOURCE_REPO}

curl -s -X POST \
  -H "Authorization: Bearer $GH_TOKEN" \
  -H "Accept: application/vnd.github+json" \
  https://api.github.com/repos/{SOURCE_REPO}/pulls \
  -d '{
    "title": "fix: {title}",
    "head": "{head_value}",
    "base": "{BASE_BRANCH}",
    "body": "## Summary\n\n{one_paragraph_description_of_fix}\n\n## Changes\n\n{bullet_list_of_changes}\n\n## Testing\n\n{what_was_tested_and_results}\n\nFixes {SOURCE_REPO}#{number}"
  }'

Extract the `html_url` from the response — this is the PR link.

9. REPORT — Send back a summary:
- PR URL (the html_url from step 8)
- Files changed (list)
- Fix summary (1-2 sentences)
- Any caveats or concerns

10. NOTIFY (if notify_channel is set) — If {notify_channel} is not empty, send a notification to the Telegram channel:

Use the message tool with:

  • action: "send"
  • channel: "telegram"
  • target: "{notify_channel}"
  • message: "✅ PR Created: {SOURCE_REPO}#{number}

{title}

{pr_url}

Files changed: {files_changed_list}"

</instructions>

<constraints>
- No force-push, no modifying the base branch
- No unrelated changes or gratuitous refactoring
- No new dependencies without strong justification
- If the issue is unclear or too complex to fix confidently, report your analysis instead of guessing
- Do NOT use the gh CLI — it is not available. Use curl + GitHub REST API for all GitHub operations.
- GH_TOKEN is already in the environment — do NOT prompt for auth
- Time limit: you have 60 minutes max. Be thorough — analyze properly, test your fix, don't rush.
</constraints>

Spawn configuration per sub-agent:

  • runTimeoutSeconds: 3600 (60 minutes)
  • cleanup: "keep" (preserve transcripts for review)
  • If --model was provided, include model: "{MODEL}" in the spawn config

Timeout Handling

If a sub-agent exceeds 60 minutes, record it as:

"#{N} — Timed out (issue may be too complex for auto-fix)"


Results Collection

If --cron is active: Skip this section entirely — the orchestrator already exited after spawning in Phase 5.

After ALL sub-agents complete (or timeout), collect their results. Store the list of successfully opened PRs in OPEN_PRS (PR number, branch name, issue number, PR URL) for use in Phase 6.

Present a summary table:

Issue Status PR Notes
#42 Fix null pointer PR opened https://github.com/.../pull/99 3 files changed
#37 Add retry logic Failed -- Could not identify target code
#15 Update docs Timed out -- Too complex for auto-fix
#8 Fix race condition Skipped -- PR already exists

Status values:

  • PR opened — success, link to PR
  • Failed — sub-agent could not complete (include reason in Notes)
  • Timed out — exceeded 60-minute limit
  • Skipped — existing PR detected in pre-flight

End with a one-line summary:

"Processed {N} issues: {success} PRs opened, {failed} failed, {skipped} skipped."

Send notification to channel (if --notify-channel is set): If --notify-channel was provided, send the final summary to that Telegram channel using the message tool:

Use the message tool with:
- action: "send"
- channel: "telegram"
- target: "{notify-channel}"
- message: "✅ GitHub Issues Processed

Processed {N} issues: {success} PRs opened, {failed} failed, {skipped} skipped.

{PR_LIST}"

Where PR_LIST includes only successfully opened PRs in format:
• #{issue_number}: {PR_url} ({notes})

Then proceed to Phase 6.


Phase 6 — PR Review Handler

This phase monitors open PRs (created by this skill or pre-existing fix/issue-* PRs) for review comments and spawns sub-agents to address them.

When this phase runs:

  • After Results Collection (Phases 2-5 completed) — checks PRs that were just opened
  • When --reviews-only flag is set — skips Phases 2-5 entirely, runs only this phase
  • In watch mode — runs every poll cycle after checking for new issues

Cron review mode (--cron --reviews-only): When both --cron and --reviews-only are set:

  1. Run token resolution (Phase 2 token section)
  2. Discover open fix/issue-* PRs (Step 6.1)
  3. Fetch review comments (Step 6.2)
  4. Analyze comment content for actionability (Step 6.3)
  5. If actionable comments are found, spawn ONE review-fix sub-agent for the first PR with unaddressed comments — fire-and-forget (do NOT await result)
    • Use cleanup: "keep" and runTimeoutSeconds: 3600
    • If --model was provided, include model: "{MODEL}" in the spawn config
  6. Report: "Spawned review handler for PR #{N} — will push fixes when complete"
  7. Exit the skill immediately. Do not proceed to Step 6.5 (Review Results).

If no actionable comments found, report "No actionable review comments found" and exit.

Normal mode (non-cron) continues below:

Step 6.1 — Discover PRs to Monitor

Collect PRs to check for review comments:

If coming from Phase 5: Use the OPEN_PRS list from Results Collection.

If --reviews-only or subsequent watch cycle: Fetch all open PRs with fix/issue- branch pattern:

curl -s -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github+json" \
  "https://api.github.com/repos/{SOURCE_REPO}/pulls?state=open&per_page=100"

Filter to only PRs where head.ref starts with fix/issue-.

For each PR, extract: number (PR number), head.ref (branch name), html_url, title, body.

If no PRs found, report "No open fix/ PRs to monitor" and stop (or loop back if in watch mode).

Step 6.2 — Fetch All Review Sources

For each PR, fetch reviews from multiple sources:

Fetch PR reviews:

curl -s -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github+json" \
  "https://api.github.com/repos/{SOURCE_REPO}/pulls/{pr_number}/reviews"

Fetch PR review comments (inline/file-level):

curl -s -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github+json" \
  "https://api.github.com/repos/{SOURCE_REPO}/pulls/{pr_number}/comments"

Fetch PR issue comments (general conversation):

curl -s -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github+json" \
  "https://api.github.com/repos/{SOURCE_REPO}/issues/{pr_number}/comments"

Fetch PR body for embedded reviews: Some review tools (like Greptile) embed their feedback directly in the PR body. Check for:

  • <!-- greptile_comment --> markers
  • Other structured review sections in the PR body
curl -s -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github+json" \
  "https://api.github.com/repos/{SOURCE_REPO}/pulls/{pr_number}"

Extract the body field and parse for embedded review content.

Step 6.3 — Analyze Comments for Actionability

Determine the bot's own username for filtering:

curl -s -H "Authorization: Bearer $GH_TOKEN" https://api.github.com/user | jq -r '.login'

Store as BOT_USERNAME. Exclude any comment where user.login equals BOT_USERNAME.

For each comment/review, analyze the content to determine if it requires action:

NOT actionable (skip):

  • Pure approvals or "LGTM" without suggestions
  • Bot comments that are informational only (CI status, auto-generated summaries without specific requests)
  • Comments already addressed (check if bot replied with "Addressed in commit...")
  • Reviews with state APPROVED and no inline comments requesting changes

IS actionable (requires attention):

  • Reviews with state CHANGES_REQUESTED
  • Reviews with state COMMENTED that contain specific requests:
    • "this test needs to be updated"
    • "please fix", "change this", "update", "can you", "should be", "needs to"
    • "will fail", "will break", "causes an error"
    • Mentions of specific code issues (bugs, missing error handling, edge cases)
  • Inline review comments pointing out issues in the code
  • Embedded reviews in PR body that identify:
    • Critical issues or breaking changes
    • Test failures expected
    • Specific code that needs attention
    • Confidence scores with concerns

Parse embedded review content (e.g., Greptile): Look for sections marked with <!-- greptile_comment --> or similar. Extract:

  • Summary text
  • Any mentions of "Critical issue", "needs attention", "will fail", "test needs to be updated"
  • Confidence scores below 4/5 (indicates concerns)

Build actionable_comments list with:

  • Source (review, inline comment, PR body, etc.)
  • Author
  • Body text
  • For inline: file path and line number
  • Specific action items identified

If no actionable comments found across any PR, report "No actionable review comments found" and stop (or loop back if in watch mode).

Step 6.4 — Present Review Comments

Display a table of PRs with pending actionable comments:

| PR | Branch | Actionable Comments | Sources |
|----|--------|---------------------|---------|
| #99 | fix/issue-42 | 2 comments | @reviewer1, greptile |
| #101 | fix/issue-37 | 1 comment | @reviewer2 |

If --yes is NOT set and this is not a subsequent watch poll: ask the user to confirm which PRs to address ("all", comma-separated PR numbers, or "skip").

Step 6.5 — Spawn Review Fix Sub-agents (Parallel)

For each PR with actionable comments, spawn a sub-agent. Launch up to 8 concurrently.

Review fix sub-agent prompt:

You are a PR review handler agent. Your task is to address review comments on a pull request by making the requested changes, pushing updates, and replying to each comment.

IMPORTANT: Do NOT use the gh CLI — it is not installed. Use curl with the GitHub REST API for all GitHub operations.

First, ensure GH_TOKEN is set. Check: echo $GH_TOKEN. If empty, read from config:
CONFIG_PATH="${BRIGADE_CONFIG_PATH:-${BRIGADE_STATE_DIR:-$HOME/.brigade}/brigade.json}"
GH_TOKEN=$(cat "$CONFIG_PATH" 2>/dev/null | jq -r '.skills.entries["gh-issues"].apiKey // empty') || GH_TOKEN=$(cat /data/.brigade/brigade.json 2>/dev/null | jq -r '.skills.entries["gh-issues"].apiKey // empty')

<config>
Repository: {SOURCE_REPO}
Push repo: {PUSH_REPO}
Fork mode: {FORK_MODE}
Push remote: {PUSH_REMOTE}
PR number: {pr_number}
PR URL: {pr_url}
Branch: {branch_name}
</config>

<review_comments>
{json_array_of_actionable_comments}

Each comment has:
- id: comment ID (for replying)
- user: who left it
- body: the comment text
- path: file path (for inline comments)
- line: line number (for inline comments)
- diff_hunk: surrounding diff context (for inline comments)
- source: where the comment came from (review, inline, pr_body, greptile, etc.)
</review_comments>

<instructions>
Follow these steps in order:

0. SETUP — Ensure GH_TOKEN is available:

export GH_TOKEN=$(node -e "const fs=require('fs'); const c=JSON.parse(fs.readFileSync('/data/.brigade/brigade.json','utf8')); console.log(c.skills?.entries?.['gh-issues']?.apiKey || '')")

Verify: echo "Token: ${GH_TOKEN:0:10}..."

1. CHECKOUT — Switch to the PR branch:
git fetch {PUSH_REMOTE} {branch_name}
git checkout {branch_name}
git pull {PUSH_REMOTE} {branch_name}

2. UNDERSTAND — Read ALL review comments carefully. Group them by file. Understand what each reviewer is asking for.

3. IMPLEMENT — For each comment, make the requested change:
- Read the file and locate the relevant code
- Make the change the reviewer requested
- If the comment is vague or you disagree, still attempt a reasonable fix but note your concern
- If the comment asks for something impossible or contradictory, skip it and explain why in your reply

4. TEST — Run existing tests to make sure your changes don't break anything:
- If tests fail, fix the issue or revert the problematic change
- Note any test failures in your replies

5. COMMIT — Stage and commit all changes in a single commit:
git add {changed_files}
git commit -m "fix: address review comments on PR #{pr_number}

Addresses review feedback from {reviewer_names}"

6. PUSH — Push the updated branch:
git config --global credential.helper ""
git remote set-url {PUSH_REMOTE} https://x-access-token:$GH_TOKEN@github.com/{PUSH_REPO}.git
GIT_ASKPASS=true git push {PUSH_REMOTE} {branch_name}

7. REPLY — For each addressed comment, post a reply:

For inline review comments (have a path/line), reply to the comment thread:
curl -s -X POST \
  -H "Authorization: Bearer $GH_TOKEN" \
  -H "Accept: application/vnd.github+json" \
  https://api.github.com/repos/{SOURCE_REPO}/pulls/{pr_number}/comments/{comment_id}/replies \
  -d '{"body": "Addressed in commit {short_sha} — {brief_description_of_change}"}'

For general PR comments (issue comments), reply on the PR:
curl -s -X POST \
  -H "Authorization: Bearer $GH_TOKEN" \
  -H "Accept: application/vnd.github+json" \
  https://api.github.com/repos/{SOURCE_REPO}/issues/{pr_number}/comments \
  -d '{"body": "Addressed feedback from @{reviewer}:\n\n{summary_of_changes_made}\n\nUpdated in commit {short_sha}"}'

For comments you could NOT address, reply explaining why:
"Unable to address this comment: {reason}. This may need manual review."

8. REPORT — Send back a summary:
- PR URL
- Number of comments addressed vs skipped
- Commit SHA
- Files changed
- Any comments that need manual attention
</instructions>

<constraints>
- Only modify files relevant to the review comments
- Do not make unrelated changes
- Do not force-push — always regular push
- If a comment contradicts another comment, address the most recent one and flag the conflict
- Do NOT use the gh CLI — use curl + GitHub REST API
- GH_TOKEN is already in the environment — do not prompt for auth
- Time limit: 60 minutes max
</constraints>

Spawn configuration per sub-agent:

  • runTimeoutSeconds: 3600 (60 minutes)
  • cleanup: "keep" (preserve transcripts for review)
  • If --model was provided, include model: "{MODEL}" in the spawn config

Step 6.6 — Review Results

After all review sub-agents complete, present a summary:

| PR | Comments Addressed | Comments Skipped | Commit | Status |
|----|-------------------|-----------------|--------|--------|
| #99 fix/issue-42 | 3 | 0 | abc123f | All addressed |
| #101 fix/issue-37 | 1 | 1 | def456a | 1 needs manual review |

Add comment IDs from this batch to ADDRESSED_COMMENTS set to prevent re-processing.


Watch Mode (if --watch is active)

After presenting results from the current batch:

  1. Add all issue numbers from this batch to the running set PROCESSED_ISSUES.
  2. Add all addressed comment IDs to ADDRESSED_COMMENTS.
  3. Tell the user:

    "Next poll in {interval} minutes... (say 'stop' to end watch mode)"

  4. Sleep for {interval} minutes.
  5. Go back to Phase 2 — Fetch Issues. The fetch will automatically filter out:
    • Issues already in PROCESSED_ISSUES
    • Issues that have existing fix/issue-{N} PRs (caught in Phase 4 pre-flight)
  6. After Phases 2-5 (or if no new issues), run Phase 6 to check for new review comments on ALL tracked PRs (both newly created and previously opened).
  7. If no new issues AND no new actionable review comments → report "No new activity. Polling again in {interval} minutes..." and loop back to step 4.
  8. The user can say "stop" at any time to exit watch mode. When stopping, present a final cumulative summary of ALL batches — issues processed AND review comments addressed.

Context hygiene between polls — IMPORTANT: Only retain between poll cycles:

  • PROCESSED_ISSUES (set of issue numbers)
  • ADDRESSED_COMMENTS (set of comment IDs)
  • OPEN_PRS (list of tracked PRs: number, branch, URL)
  • Cumulative results (one line per issue + one line per review batch)
  • Parsed arguments from Phase 1
  • BASE_BRANCH, SOURCE_REPO, PUSH_REPO, FORK_MODE, BOT_USERNAME Do NOT retain issue bodies, comment bodies, sub-agent transcripts, or codebase analysis between polls.
gifgrep 是一款 CLI/TUI 工具,用于搜索 Tenor/Giphy GIF,支持浏览、下载及提取静态帧或帧序列网格图。提供快速预览、结果导出和环境配置功能,适用于高效查找和分享 GIF 资源。
用户需要搜索特定关键词的 GIF 动图 用户希望从搜索结果中下载 GIF 文件 用户需要从 GIF 中提取单帧图片或生成帧序列网格图 用户希望在终端中以 TUI 界面浏览 GIF 预览
skills/gifgrep/SKILL.md
npx skills add spinabot/brigade --skill gifgrep -g -y
SKILL.md
Frontmatter
{
    "name": "gifgrep",
    "homepage": "https:\/\/gifgrep.com",
    "metadata": {
        "brigade": {
            "emoji": "🧲",
            "install": [
                {
                    "id": "brew",
                    "bins": [
                        "gifgrep"
                    ],
                    "kind": "brew",
                    "label": "Install gifgrep (brew)",
                    "formula": "steipete\/tap\/gifgrep"
                },
                {
                    "id": "go",
                    "bins": [
                        "gifgrep"
                    ],
                    "kind": "go",
                    "label": "Install gifgrep (go)",
                    "module": "github.com\/steipete\/gifgrep\/cmd\/gifgrep@latest"
                }
            ],
            "requires": {
                "bins": [
                    "gifgrep"
                ]
            }
        }
    },
    "description": "Search GIF providers with CLI\/TUI, download results, and extract stills\/sheets."
}

gifgrep

Use gifgrep to search GIF providers (Tenor/Giphy), browse in a TUI, download results, and extract stills or sheets.

GIF-Grab (gifgrep workflow)

  • Search → preview → download → extract (still/sheet) for fast review and sharing.

Quick start

  • gifgrep cats --max 5
  • gifgrep cats --format url | head -n 5
  • gifgrep search --json cats | jq '.[0].url'
  • gifgrep tui "office handshake"
  • gifgrep cats --download --max 1 --format url

TUI + previews

  • TUI: gifgrep tui "query"
  • CLI still previews: --thumbs (Kitty/Ghostty only; still frame)

Download + reveal

  • --download saves to ~/Downloads
  • --reveal shows the last download in Finder

Stills + sheets

  • gifgrep still ./clip.gif --at 1.5s -o still.png
  • gifgrep sheet ./clip.gif --frames 9 --cols 3 -o sheet.png
  • Sheets = single PNG grid of sampled frames (great for quick review, docs, PRs, chat).
  • Tune: --frames (count), --cols (grid width), --padding (spacing).

Providers

  • --source auto|tenor|giphy
  • GIPHY_API_KEY required for --source giphy
  • TENOR_API_KEY optional (Tenor demo key used if unset)

Output

  • --json prints an array of results (id, title, url, preview_url, tags, width, height)
  • --format for pipe-friendly fields (e.g., url)

Environment tweaks

  • GIFGREP_SOFTWARE_ANIM=1 to force software animation
  • GIFGREP_CELL_ASPECT=0.5 to tweak preview geometry
指导如何规范地进行 Git 提交和创建 Pull Request。包括谨慎暂存文件、保持提交逻辑单一、遵循 Conventional Commits 格式编写消息,以及使用 gh CLI 高效创建 PR 并验证基础分支。
用户询问如何格式化 Git 提交信息 用户需要创建或优化 Pull Request 用户在提交代码前寻求最佳实践建议
skills/git-commit/SKILL.md
npx skills add spinabot/brigade --skill git-commit -g -y
SKILL.md
Frontmatter
{
    "name": "git-commit",
    "description": "Stage, write, and structure git commits and pull requests cleanly. Read this before committing — for conventional-commit message format, when to split commits, and how to open a PR with the gh CLI.",
    "requires-bins": "git"
}

Git commits & PRs

Use this when committing changes or opening a pull request.

Before committing

  • Run git status and git diff --staged to see exactly what's going in. Never git add -A blindly — stage the files that belong to this change.
  • Keep one logical change per commit. If the diff does two unrelated things, make two commits.
  • Don't commit secrets, large binaries, or generated artifacts. Check .gitignore covers them.

Message format

Conventional Commits:

<type>(<optional scope>): <short imperative summary>

<optional body — what changed and WHY, wrapped at ~72 cols>

Types: feat, fix, docs, refactor, test, chore, perf, build, ci.

  • Summary in the imperative mood ("add", not "added"), ≤72 chars, no trailing period.
  • The body explains the reasoning a future reader needs — not a restatement of the diff.

Pull requests (gh CLI)

  • git push -u origin <branch> then gh pr create --fill (or --title/--body).
  • PR body: what changed, why, and how it was verified (tests run, manual checks).
  • Confirm the base branch is correct before creating.

Conventions to honor

Match the repository's existing style — read recent git log --oneline first and follow whatever format the project already uses if it differs from the above.

基于gh CLI的GitHub操作技能,支持PR状态检查、CI日志查看、Issue管理及API查询。适用于代码审查辅助和仓库数据获取,不适用于本地Git操作或非GitHub平台。
检查PR状态或CI运行结果 创建或评论Issue 列出或过滤PR/Issue 查看CI运行日志
skills/github/SKILL.md
npx skills add spinabot/brigade --skill github -g -y
SKILL.md
Frontmatter
{
    "name": "github",
    "metadata": {
        "brigade": {
            "emoji": "🐙",
            "install": [
                {
                    "id": "brew",
                    "bins": [
                        "gh"
                    ],
                    "kind": "brew",
                    "label": "Install GitHub CLI (brew)",
                    "formula": "gh"
                },
                {
                    "id": "apt",
                    "bins": [
                        "gh"
                    ],
                    "kind": "apt",
                    "label": "Install GitHub CLI (apt)",
                    "package": "gh"
                }
            ],
            "requires": {
                "bins": [
                    "gh"
                ]
            }
        }
    },
    "description": "GitHub operations via `gh` CLI: issues, PRs, CI runs, code review, API queries. Use when: (1) checking PR status or CI, (2) creating\/commenting on issues, (3) listing\/filtering PRs or issues, (4) viewing run logs. NOT for: complex web UI interactions requiring manual browser flows (use browser tooling when available), bulk operations across many repos (script with gh api), or when gh auth is not configured."
}

GitHub Skill

Use the gh CLI to interact with GitHub repositories, issues, PRs, and CI.

When to Use

USE this skill when:

  • Checking PR status, reviews, or merge readiness
  • Viewing CI/workflow run status and logs
  • Creating, closing, or commenting on issues
  • Creating or merging pull requests
  • Querying GitHub API for repository data
  • Listing repos, releases, or collaborators

When NOT to Use

DON'T use this skill when:

  • Local git operations (commit, push, pull, branch) → use git directly
  • Non-GitHub repos (GitLab, Bitbucket, self-hosted) → different CLIs
  • Cloning repositories → use git clone
  • Reviewing actual code changes → use coding-agent skill
  • Complex multi-file diffs → use coding-agent or read files directly

Setup

# Authenticate (one-time)
gh auth login

# Verify
gh auth status

Common Commands

Pull Requests

# List PRs
gh pr list --repo owner/repo

# Check CI status
gh pr checks 55 --repo owner/repo

# View PR details
gh pr view 55 --repo owner/repo

# Create PR
gh pr create --title "feat: add feature" --body "Description"

# Merge PR
gh pr merge 55 --squash --repo owner/repo

Issues

# List issues
gh issue list --repo owner/repo --state open

# Create issue
gh issue create --title "Bug: something broken" --body "Details..."

# Close issue
gh issue close 42 --repo owner/repo

CI/Workflow Runs

# List recent runs
gh run list --repo owner/repo --limit 10

# View specific run
gh run view <run-id> --repo owner/repo

# View failed step logs only
gh run view <run-id> --repo owner/repo --log-failed

# Re-run failed jobs
gh run rerun <run-id> --failed --repo owner/repo

API Queries

# Get PR with specific fields
gh api repos/owner/repo/pulls/55 --jq '.title, .state, .user.login'

# List all labels
gh api repos/owner/repo/labels --jq '.[].name'

# Get repo stats
gh api repos/owner/repo --jq '{stars: .stargazers_count, forks: .forks_count}'

JSON Output

Most commands support --json for structured output with --jq filtering:

gh issue list --repo owner/repo --json number,title --jq '.[] | "\(.number): \(.title)"'
gh pr list --json number,title,state,mergeable --jq '.[] | select(.mergeable == "MERGEABLE")'

Templates

PR Review Summary

# Get PR overview for review
PR=55 REPO=owner/repo
echo "## PR #$PR Summary"
gh pr view $PR --repo $REPO --json title,body,author,additions,deletions,changedFiles \
  --jq '"**\(.title)** by @\(.author.login)\n\n\(.body)\n\n📊 +\(.additions) -\(.deletions) across \(.changedFiles) files"'
gh pr checks $PR --repo $REPO

Issue Triage

# Quick issue triage view
gh issue list --repo owner/repo --state open --json number,title,labels,createdAt \
  --jq '.[] | "[\(.number)] \(.title) - \([.labels[].name] | join(", ")) (\(.createdAt[:10]))"'

Notes

  • Always specify --repo owner/repo when not in a git directory
  • Use URLs directly: gh pr view https://github.com/owner/repo/pull/55
  • Rate limits apply; use gh api --cache 1h for repeated queries
Google Workspace CLI工具,支持Gmail、日历、云端硬盘、通讯录、表格和文档的操作。需OAuth配置,提供邮件收发搜索、日程管理、文件检索及数据读写等常用命令。
需要操作Google邮箱发送或搜索邮件 需要查看或创建Google日历事件 需要在Google云端硬盘中搜索文件 需要读取或更新Google Sheets数据 需要导出或查看Google Docs内容
skills/gog/SKILL.md
npx skills add spinabot/brigade --skill gog -g -y
SKILL.md
Frontmatter
{
    "name": "gog",
    "homepage": "https:\/\/gogcli.sh",
    "metadata": {
        "brigade": {
            "emoji": "🎮",
            "install": [
                {
                    "id": "brew",
                    "bins": [
                        "gog"
                    ],
                    "kind": "brew",
                    "label": "Install gog (brew)",
                    "formula": "steipete\/tap\/gogcli"
                }
            ],
            "requires": {
                "bins": [
                    "gog"
                ]
            }
        }
    },
    "description": "Google Workspace CLI for Gmail, Calendar, Drive, Contacts, Sheets, and Docs."
}

gog

Use gog for Gmail/Calendar/Drive/Contacts/Sheets/Docs. Requires OAuth setup.

Setup (once)

  • gog auth credentials /path/to/client_secret.json
  • gog auth add you@gmail.com --services gmail,calendar,drive,contacts,docs,sheets
  • gog auth list

Common commands

  • Gmail search: gog gmail search 'newer_than:7d' --max 10
  • Gmail messages search (per email, ignores threading): gog gmail messages search "in:inbox from:ryanair.com" --max 20 --account you@example.com
  • Gmail send (plain): gog gmail send --to a@b.com --subject "Hi" --body "Hello"
  • Gmail send (multi-line): gog gmail send --to a@b.com --subject "Hi" --body-file ./message.txt
  • Gmail send (stdin): gog gmail send --to a@b.com --subject "Hi" --body-file -
  • Gmail send (HTML): gog gmail send --to a@b.com --subject "Hi" --body-html "<p>Hello</p>"
  • Gmail draft: gog gmail drafts create --to a@b.com --subject "Hi" --body-file ./message.txt
  • Gmail send draft: gog gmail drafts send <draftId>
  • Gmail reply: gog gmail send --to a@b.com --subject "Re: Hi" --body "Reply" --reply-to-message-id <msgId>
  • Calendar list events: gog calendar events <calendarId> --from <iso> --to <iso>
  • Calendar create event: gog calendar create <calendarId> --summary "Title" --from <iso> --to <iso>
  • Calendar create with color: gog calendar create <calendarId> --summary "Title" --from <iso> --to <iso> --event-color 7
  • Calendar update event: gog calendar update <calendarId> <eventId> --summary "New Title" --event-color 4
  • Calendar show colors: gog calendar colors
  • Drive search: gog drive search "query" --max 10
  • Contacts: gog contacts list --max 20
  • Sheets get: gog sheets get <sheetId> "Tab!A1:D10" --json
  • Sheets update: gog sheets update <sheetId> "Tab!A1:B2" --values-json '[["A","B"],["1","2"]]' --input USER_ENTERED
  • Sheets append: gog sheets append <sheetId> "Tab!A:C" --values-json '[["x","y","z"]]' --insert INSERT_ROWS
  • Sheets clear: gog sheets clear <sheetId> "Tab!A2:Z"
  • Sheets metadata: gog sheets metadata <sheetId> --json
  • Docs export: gog docs export <docId> --format txt --out /tmp/doc.txt
  • Docs cat: gog docs cat <docId>

Calendar Colors

  • Use gog calendar colors to see all available event colors (IDs 1-11)
  • Add colors to events with --event-color <id> flag
  • Event color IDs (from gog calendar colors output):
    • 1: #a4bdfc
    • 2: #7ae7bf
    • 3: #dbadff
    • 4: #ff887c
    • 5: #fbd75b
    • 6: #ffb878
    • 7: #46d6db
    • 8: #e1e1e1
    • 9: #5484ed
    • 10: #51b749
    • 11: #dc2127

Email Formatting

  • Prefer plain text. Use --body-file for multi-paragraph messages (or --body-file - for stdin).

  • Same --body-file pattern works for drafts and replies.

  • --body does not unescape \n. If you need inline newlines, use a heredoc or $'Line 1\n\nLine 2'.

  • Use --body-html only when you need rich formatting.

  • HTML tags: <p> for paragraphs, <br> for line breaks, <strong> for bold, <em> for italic, <a href="url"> for links, <ul>/<li> for lists.

  • Example (plain text via stdin):

    gog gmail send --to recipient@example.com \
      --subject "Meeting Follow-up" \
      --body-file - <<'EOF'
    Hi Name,
    
    Thanks for meeting today. Next steps:
    - Item one
    - Item two
    
    Best regards,
    Your Name
    EOF
    
  • Example (HTML list):

    gog gmail send --to recipient@example.com \
      --subject "Meeting Follow-up" \
      --body-html "<p>Hi Name,</p><p>Thanks for meeting today. Here are the next steps:</p><ul><li>Item one</li><li>Item two</li></ul><p>Best regards,<br>Your Name</p>"
    

Notes

  • Set GOG_ACCOUNT=you@gmail.com to avoid repeating --account.
  • For scripting, prefer --json plus --no-input.
  • Sheets values can be passed via --values-json (recommended) or as inline rows.
  • Docs supports export/cat/copy. In-place edits require a Docs API client (not in gog).
  • Confirm before sending mail or creating events.
  • gog gmail search returns one row per thread; use gog gmail messages search when you need every individual email returned separately.
通过 goplaces CLI 调用新版 Google Places API,支持文本搜索、地点详情查询、地址解析及评论获取。默认提供人类可读输出,配合 --json 参数适用于脚本自动化处理。
用户需要查找特定类型的地点(如餐厅、咖啡馆) 需要根据地址或名称解析具体地点信息 需要获取某个地点的详细信息或用户评价 需要在脚本中获取结构化的地点数据
skills/goplaces/SKILL.md
npx skills add spinabot/brigade --skill goplaces -g -y
SKILL.md
Frontmatter
{
    "name": "goplaces",
    "homepage": "https:\/\/github.com\/steipete\/goplaces",
    "metadata": {
        "brigade": {
            "emoji": "📍",
            "install": [
                {
                    "id": "brew",
                    "bins": [
                        "goplaces"
                    ],
                    "kind": "brew",
                    "label": "Install goplaces (brew)",
                    "formula": "steipete\/tap\/goplaces"
                }
            ],
            "requires": {
                "env": [
                    "GOOGLE_PLACES_API_KEY"
                ],
                "bins": [
                    "goplaces"
                ]
            },
            "primaryEnv": "GOOGLE_PLACES_API_KEY"
        }
    },
    "description": "Query Google Places API (New) via the goplaces CLI for text search, place details, resolve, and reviews. Use for human-friendly place lookup or JSON output for scripts."
}

goplaces

Modern Google Places API (New) CLI. Human output by default, --json for scripts.

Install

  • Homebrew: brew install steipete/tap/goplaces

Config

  • GOOGLE_PLACES_API_KEY required.
  • Optional: GOOGLE_PLACES_BASE_URL for testing/proxying.

Common commands

  • Search: goplaces search "coffee" --open-now --min-rating 4 --limit 5
  • Bias: goplaces search "pizza" --lat 40.8 --lng -73.9 --radius-m 3000
  • Pagination: goplaces search "pizza" --page-token "NEXT_PAGE_TOKEN"
  • Resolve: goplaces resolve "Soho, London" --limit 5
  • Details: goplaces details <place_id> --reviews
  • JSON: goplaces search "sushi" --json

Notes

  • --no-color or NO_COLOR disables ANSI color.
  • Price levels: 0..4 (free → very expensive).
  • Type filter sends only the first --type value (API accepts one).
用于评估和加固运行 Brigade 的主机安全,根据用户风险偏好配置防火墙、SSH 及系统更新。涵盖安全审计、暴露面审查及定期巡检,确保在不影响访问的前提下提升主机安全性。
请求主机安全审计或加固建议 需要配置防火墙或 SSH 硬策略 评估系统风险态势或暴露面 检查 Brigade 部署版本状态 设置周期性安全检查任务
skills/healthcheck/SKILL.md
npx skills add spinabot/brigade --skill healthcheck -g -y
SKILL.md
Frontmatter
{
    "name": "healthcheck",
    "description": "Host security hardening and risk-tolerance configuration for Brigade deployments. Use when a user asks for security audits, firewall\/SSH\/update hardening, risk posture, exposure review, Brigade cron scheduling for periodic checks, or version status checks on a machine running Brigade (laptop, workstation, Pi, VPS)."
}

Brigade Host Hardening

Overview

Assess and harden the host running Brigade, then align it to a user-defined risk tolerance without breaking access. Use Brigade security tooling as a first-class signal, but treat OS hardening as a separate, explicit set of steps.

Core rules

  • Recommend running this skill with a state-of-the-art model (e.g., Opus 4.5, GPT 5.2+). The agent should self-check the current model and suggest switching if below that level; do not block execution.
  • Require explicit approval before any state-changing action.
  • Do not modify remote access settings without confirming how the user connects.
  • Prefer reversible, staged changes with a rollback plan.
  • Never claim Brigade changes the host firewall, SSH, or OS updates; it does not.
  • If role/identity is unknown, provide recommendations only.
  • Formatting: every set of user choices must be numbered so the user can reply with a single digit.
  • System-level backups are recommended; try to verify status.

Workflow (follow in order)

0) Model self-check (non-blocking)

Before starting, check the current model. If it is below state-of-the-art (e.g., Opus 4.5, GPT 5.2+), recommend switching. Do not block execution.

1) Establish context (read-only)

Try to infer 1–5 from the environment before asking. Prefer simple, non-technical questions if you need confirmation.

Determine (in order):

  1. OS and version (Linux/macOS/Windows), container vs host.
  2. Privilege level (root/admin vs user).
  3. Access path (local console, SSH, RDP, tailnet).
  4. Network exposure (public IP, reverse proxy, tunnel).
  5. Brigade gateway status and bind address.
  6. Backup system and status (e.g., Time Machine, system images, snapshots).
  7. Deployment context (local mac app, headless gateway host, remote gateway, container/CI).
  8. Disk encryption status (FileVault/LUKS/BitLocker).
  9. OS automatic security updates status. Note: these are not blocking items, but are highly recommended, especially if Brigade can access sensitive data.
  10. Usage mode for a personal assistant with full access (local workstation vs headless/remote vs other).

First ask once for permission to run read-only checks. If granted, run them by default and only ask questions for items you cannot infer or verify. Do not ask for information already visible in runtime or command output. Keep the permission ask as a single sentence, and list follow-up info needed as an unordered list (not numbered) unless you are presenting selectable choices.

If you must ask, use non-technical prompts:

  • “Are you using a Mac, Windows PC, or Linux?”
  • “Are you logged in directly on the machine, or connecting from another computer?”
  • “Is this machine reachable from the public internet, or only on your home/network?”
  • “Do you have backups enabled (e.g., Time Machine), and are they current?”
  • “Is disk encryption turned on (FileVault/BitLocker/LUKS)?”
  • “Are automatic security updates enabled?”
  • “How do you use this machine?” Examples:
    • Personal machine shared with the assistant
    • Dedicated local machine for the assistant
    • Dedicated remote machine/server accessed remotely (always on)
    • Something else?

Only ask for the risk profile after system context is known.

If the user grants read-only permission, run the OS-appropriate checks by default. If not, offer them (numbered). Examples:

  1. OS: uname -a, sw_vers, cat /etc/os-release.
  2. Listening ports:
    • Linux: ss -ltnup (or ss -ltnp if -u unsupported).
    • macOS: lsof -nP -iTCP -sTCP:LISTEN.
  3. Firewall status:
    • Linux: ufw status, firewall-cmd --state, nft list ruleset (pick what is installed).
    • macOS: /usr/libexec/ApplicationFirewall/socketfilterfw --getglobalstate and pfctl -s info.
  4. Backups (macOS): tmutil status (if Time Machine is used).

2) Run Brigade security audits (read-only)

As part of the default read-only checks, run brigade security audit --deep. Only offer alternatives if the user requests them:

  1. brigade security audit (faster, non-probing)
  2. brigade security audit --json (structured output)

Offer to apply Brigade safe defaults (numbered):

  1. brigade security audit --fix

Be explicit that --fix only tightens Brigade defaults and file permissions. It does not change host firewall, SSH, or OS update policies.

If browser control is enabled, recommend that 2FA be enabled on all important accounts, with hardware keys preferred and SMS not sufficient.

3) Check Brigade version/update status (read-only)

As part of the default read-only checks, run brigade update status.

Report the current channel and whether an update is available.

4) Determine risk tolerance (after system context)

Ask the user to pick or confirm a risk posture and any required open services/ports (numbered choices below). Do not pigeonhole into fixed profiles; if the user prefers, capture requirements instead of choosing a profile. Offer suggested profiles as optional defaults (numbered). Note that most users pick Home/Workstation Balanced:

  1. Home/Workstation Balanced (most common): firewall on with reasonable defaults, remote access restricted to LAN or tailnet.
  2. VPS Hardened: deny-by-default inbound firewall, minimal open ports, key-only SSH, no root login, automatic security updates.
  3. Developer Convenience: more local services allowed, explicit exposure warnings, still audited.
  4. Custom: user-defined constraints (services, exposure, update cadence, access methods).

5) Produce a remediation plan

Provide a plan that includes:

  • Target profile
  • Current posture summary
  • Gaps vs target
  • Step-by-step remediation with exact commands
  • Access-preservation strategy and rollback
  • Risks and potential lockout scenarios
  • Least-privilege notes (e.g., avoid admin usage, tighten ownership/permissions where safe)
  • Credential hygiene notes (location of Brigade creds, prefer disk encryption)

Always show the plan before any changes.

6) Offer execution options

Offer one of these choices (numbered so users can reply with a single digit):

  1. Do it for me (guided, step-by-step approvals)
  2. Show plan only
  3. Fix only critical issues
  4. Export commands for later

7) Execute with confirmations

For each step:

  • Show the exact command
  • Explain impact and rollback
  • Confirm access will remain available
  • Stop on unexpected output and ask for guidance

8) Verify and report

Re-check:

  • Firewall status
  • Listening ports
  • Remote access still works
  • Brigade security audit (re-run)

Deliver a final posture report and note any deferred items.

Required confirmations (always)

Require explicit approval for:

  • Firewall rule changes
  • Opening/closing ports
  • SSH/RDP configuration changes
  • Installing/removing packages
  • Enabling/disabling services
  • User/group modifications
  • Scheduling tasks or startup persistence
  • Update policy changes
  • Access to sensitive files or credentials

If unsure, ask.

Periodic checks

After Brigade install or first hardening pass, run at least one baseline audit and version check:

  • brigade security audit
  • brigade security audit --deep
  • brigade update status

Ongoing monitoring is recommended. Use the Brigade cron tool/CLI to schedule periodic audits (Gateway scheduler). Do not create scheduled tasks without explicit approval. Store outputs in a user-approved location and avoid secrets in logs. When scheduling headless cron runs, include a note in the output that instructs the user to call healthcheck so issues can be fixed.

Required prompt to schedule (always)

After any audit or hardening pass, explicitly offer scheduling and require a direct response. Use a short prompt like (numbered):

  1. “Do you want me to schedule periodic audits (e.g., daily/weekly) via brigade cron add?”

If the user says yes, ask for:

  • cadence (daily/weekly), preferred time window, and output location
  • whether to also schedule brigade update status

Use a stable cron job name so updates are deterministic. Prefer exact names:

  • healthcheck:security-audit
  • healthcheck:update-status

Before creating, brigade cron list and match on exact name. If found, brigade cron edit <id> .... If not found, brigade cron add --name <name> ....

Also offer a periodic version check so the user can decide when to update (numbered):

  1. brigade update status (preferred for source checkouts and channels)
  2. npm view brigade version (published npm version)

Brigade command accuracy

Use only supported commands and flags:

  • brigade security audit [--deep] [--fix] [--json]
  • brigade status / brigade status --deep
  • brigade health --json
  • brigade update status
  • brigade cron add|list|runs|run

Do not invent CLI flags or imply Brigade enforces host firewall/SSH policies.

Logging and audit trail

Record:

  • Gateway identity and role
  • Plan ID and timestamp
  • Approved steps and exact commands
  • Exit codes and files modified (best effort)

Redact secrets. Never log tokens or full credential contents.

Memory writes (conditional)

Only write to memory files when the user explicitly opts in and the session is a private/local workspace (per docs/reference/templates/AGENTS.md). Otherwise provide a redacted, paste-ready summary the user can decide to save elsewhere.

Follow the durable-memory prompt format used by Brigade compaction:

  • Write lasting notes to memory/YYYY-MM-DD.md.

After each audit/hardening run, if opted-in, append a short, dated summary to memory/YYYY-MM-DD.md (what was checked, key findings, actions taken, any scheduled cron jobs, key decisions, and all commands executed). Append-only: never overwrite existing entries. Redact sensitive host details (usernames, hostnames, IPs, serials, service names, tokens). If there are durable preferences or decisions (risk posture, allowed ports, update policy), also update MEMORY.md (long-term memory is optional and only used in private sessions).

If the session cannot write to the workspace, ask for permission or provide exact entries the user can paste into the memory files.

Himalaya 是一款终端邮件客户端,支持通过 IMAP/SMTP 管理邮件。提供列出、阅读、撰写、回复、转发、搜索及组织邮件等功能,支持多账户管理和 MML 语法,适用于命令行环境下的高效邮件处理。
用户需要在终端中查看或发送电子邮件 用户希望使用命令行工具进行邮件搜索和管理
skills/himalaya/SKILL.md
npx skills add spinabot/brigade --skill himalaya -g -y
SKILL.md
Frontmatter
{
    "name": "himalaya",
    "homepage": "https:\/\/github.com\/pimalaya\/himalaya",
    "metadata": {
        "brigade": {
            "emoji": "📧",
            "install": [
                {
                    "id": "brew",
                    "bins": [
                        "himalaya"
                    ],
                    "kind": "brew",
                    "label": "Install Himalaya (brew)",
                    "formula": "himalaya"
                }
            ],
            "requires": {
                "bins": [
                    "himalaya"
                ]
            }
        }
    },
    "description": "CLI to manage emails via IMAP\/SMTP. Use `himalaya` to list, read, write, reply, forward, search, and organize emails from the terminal. Supports multiple accounts and message composition with MML (MIME Meta Language)."
}

Himalaya Email CLI

Himalaya is a CLI email client that lets you manage emails from the terminal using IMAP, SMTP, Notmuch, or Sendmail backends.

References

  • references/configuration.md (config file setup + IMAP/SMTP authentication)
  • references/message-composition.md (MML syntax for composing emails)

Prerequisites

  1. Himalaya CLI installed (himalaya --version to verify)
  2. A configuration file at ~/.config/himalaya/config.toml
  3. IMAP/SMTP credentials configured (password stored securely)

Configuration Setup

Run the interactive wizard to set up an account:

himalaya account configure

Or create ~/.config/himalaya/config.toml manually:

[accounts.personal]
email = "you@example.com"
display-name = "Your Name"
default = true

backend.type = "imap"
backend.host = "imap.example.com"
backend.port = 993
backend.encryption.type = "tls"
backend.login = "you@example.com"
backend.auth.type = "password"
backend.auth.cmd = "pass show email/imap"  # or use keyring

message.send.backend.type = "smtp"
message.send.backend.host = "smtp.example.com"
message.send.backend.port = 587
message.send.backend.encryption.type = "start-tls"
message.send.backend.login = "you@example.com"
message.send.backend.auth.type = "password"
message.send.backend.auth.cmd = "pass show email/smtp"

Common Operations

List Folders

himalaya folder list

List Emails

List emails in INBOX (default):

himalaya envelope list

List emails in a specific folder:

himalaya envelope list --folder "Sent"

List with pagination:

himalaya envelope list --page 1 --page-size 20

Search Emails

himalaya envelope list from john@example.com subject meeting

Read an Email

Read email by ID (shows plain text):

himalaya message read 42

Export raw MIME:

himalaya message export 42 --full

Reply to an Email

Interactive reply (opens $EDITOR):

himalaya message reply 42

Reply-all:

himalaya message reply 42 --all

Forward an Email

himalaya message forward 42

Write a New Email

Interactive compose (opens $EDITOR):

himalaya message write

Send directly using template:

cat << 'EOF' | himalaya template send
From: you@example.com
To: recipient@example.com
Subject: Test Message

Hello from Himalaya!
EOF

Or with headers flag:

himalaya message write -H "To:recipient@example.com" -H "Subject:Test" "Message body here"

Move/Copy Emails

Move to folder:

himalaya message move 42 "Archive"

Copy to folder:

himalaya message copy 42 "Important"

Delete an Email

himalaya message delete 42

Manage Flags

Add flag:

himalaya flag add 42 --flag seen

Remove flag:

himalaya flag remove 42 --flag seen

Multiple Accounts

List accounts:

himalaya account list

Use a specific account:

himalaya --account work envelope list

Attachments

Save attachments from a message:

himalaya attachment download 42

Save to specific directory:

himalaya attachment download 42 --dir ~/Downloads

Output Formats

Most commands support --output for structured output:

himalaya envelope list --output json
himalaya envelope list --output plain

Debugging

Enable debug logging:

RUST_LOG=debug himalaya envelope list

Full trace with backtrace:

RUST_LOG=trace RUST_BACKTRACE=1 himalaya envelope list

Tips

  • Use himalaya --help or himalaya <command> --help for detailed usage.
  • Message IDs are relative to the current folder; re-list after folder changes.
  • For composing rich emails with attachments, use MML syntax (see references/message-composition.md).
  • Store passwords securely using pass, system keyring, or a command that outputs the password.
将HTML组合渲染为确定性MP4视频,适用于数据驱动动画、图表、动态排版及短视频。需编写含GSAP时间轴的自包含HTML,通过data属性控制元素时序与尺寸。
用户要求制作或渲染视频 需要生成数据可视化动画 创建动态文本或图表展示 制作品牌宣传短片
skills/hyperframes/SKILL.md
npx skills add spinabot/brigade --skill hyperframes -g -y
SKILL.md
Frontmatter
{
    "name": "hyperframes",
    "homepage": "https:\/\/github.com\/heygen-com\/hyperframes",
    "metadata": {
        "brigade": {
            "emoji": "🎬",
            "install": [
                {
                    "id": "npm",
                    "kind": "npm",
                    "label": "Install the HyperFrames render engine (npm)",
                    "package": "@hyperframes\/producer"
                }
            ]
        }
    },
    "description": "Author HTML compositions that the `render_video` tool turns into deterministic MP4 video — animated charts and dashboards, data explainers, text\/quote cards, kinetic typography, product teasers, and branded short-form social clips. Use when the user asks Brigade to make, render, animate, or produce a video from data, text, or a layout (NOT photoreal\/AI footage — that's `generate_video`)."
}

hyperframes — HTML → deterministic MP4

The render_video tool renders an HTML composition you write into a pixel-exact MP4. A headless Chrome steps a GSAP master timeline frame by frame, captures one image per frame, and FFmpeg encodes them — so the same HTML always produces the same video. This is the right tool for anything programmatic and data-driven; reach for generate_video only when you need photoreal/AI-generated footage.

Great fits: animated bar/line/donut charts, KPI dashboards, "N facts about X" explainers, quote/announcement cards, kinetic typography, countdowns, before/after reveals, branded intros/outros, vertical social clips.

The composition contract (this is what makes or breaks a render)

Write ONE self-contained HTML document. Two things are mandatory and non-obvious:

  1. A paused GSAP timeline registered to window.__timelines[<composition-id>]. HyperFrames seeks this timeline to render each frame. Total video duration = tl.duration() — you do NOT set duration with an attribute.
  2. data-* attributes on the root and on every timed element.

Root composition element — required: data-composition-id (must match the window.__timelines key), data-width, data-height, data-start; plus data-track-index.

Timed childrendata-start + data-track-index on every one; visible elements (<img>, text/graphic <div>s) also need class="clip"; <img> also takes data-duration. Do not put data-duration on the root/composition — its duration comes from the timeline.

<!doctype html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- Load GSAP (see "GSAP + assets" below). -->
    <script src="https://cdn.jsdelivr.net/npm/gsap@3/dist/gsap.min.js"></script>
    <style>
      body { margin: 0; }
      .stage { width: 1080px; height: 1920px; position: relative; overflow: hidden;
               background: #0b0b12; color: #fff; font-family: system-ui, sans-serif; }
      .title { position: absolute; top: 220px; left: 80px; font-size: 96px;
               font-weight: 800; opacity: 0; transform: translateY(24px); }
    </style>
  </head>
  <body>
    <div class="stage"
         data-composition-id="promo"
         data-width="1080" data-height="1920"
         data-start="0" data-track-index="0">
      <div class="title clip" data-start="0" data-duration="4" data-track-index="0">
        Ship faster.
      </div>
    </div>
    <script>
      // A PAUSED timeline — HyperFrames drives playback by seeking it per frame.
      const tl = gsap.timeline({ paused: true });
      tl.to(".title", { opacity: 1, y: 0, duration: 0.7, ease: "power2.out" }, 0.4)
        .to(".title", { opacity: 0, duration: 0.5, ease: "power2.in" }, 3.5);
      // Register under the SAME id as data-composition-id. This is mandatory.
      window.__timelines = window.__timelines || {};
      window.__timelines["promo"] = tl;
    </script>
  </body>
</html>
  • Vertical 1080×1920 for TikTok/Reels/Shorts, 1920×1080 for landscape, 1080×1080 for square feed posts.
  • Frame rate is an engine default (the tool doesn't expose an fps flag) — author for smooth motion at 30fps.

GSAP + assets

The tool renders a single standalone index.html (no project scaffold), so the composition must bring its own GSAP and assets:

  • GSAP: load it via a <script src="…gsap.min.js"> (CDN as above) or paste the minified source inline. It must be defined before your timeline script runs.
  • Assets: embed images/fonts as data: URIs so nothing depends on external files.
  • Determinism: never drive motion from Date.now(), Math.random(), setTimeout, or a hand-rolled requestAnimationFrame loop — put all motion on the GSAP timeline. Off-timeline animation desyncs from the frame stepper.

Workflow

  1. Write the composition to the contract above with real data/text (never lorem).
  2. Call render_video with { html, output_name?, lint? }. It lints, renders in an isolated subprocess (a real render legitimately takes a minute or more), and returns a saved MP4 as a MEDIA:<path> line.
  3. Deliver it with send_media({ path }).
  4. Handle failures by errorType:
    • composition_invalid — read the lint message, fix the HTML, retry.
    • render_failed — read the stderr excerpt (bad timeline id, missing GSAP, etc.).
    • render_timeout — the composition is too heavy; simplify or shorten it.
    • render_unavailable — the engine/FFmpeg isn't installed (see below).

Design quality (make it look intentional, not templated)

  • Pick a palette that fits the subject; commit to one accent and keep the rest quiet. Set a real type scale; give headings weight and letter-spacing.
  • Stagger reveals (offset each element's timeline position by 0.15–0.3s) so motion reads as choreographed, not simultaneous.
  • Keep text ≥64px from every edge on vertical video so platform UI never clips it.
  • Ease everything (power2.out in, power2.in out). Linear motion reads robotic.
  • For charts, animate the value (bar height, arc sweep, a counting number) on the timeline rather than swapping static frames.

Requirements & install

Needs Node 22+, the @hyperframes/producer engine (an optional dependency — install it to enable video), and FFmpeg on PATH. A headless Chromium is auto-downloaded by the engine on first render (needs network once).

npm i @hyperframes/producer   # the render engine (optional; enables render_video)
# FFmpeg: brew install ffmpeg | winget install ffmpeg | apt install ffmpeg

Overrides: BRIGADE_HYPERFRAMES_PATH (explicit producer entry file), FFMPEG_PATH, BRIGADE_BROWSER_EXECUTABLE / PUPPETEER_EXECUTABLE_PATH.

macOS平台下通过CLI管理iMessage/SMS的工具,支持查看聊天记录、监控新消息及发送文本或附件。需Mac系统授权,强制要求发送前确认接收人与内容,保障安全。
用户明确要求发送iMessage或SMS 查询iMessage对话历史 检查最近的Messages.app聊天列表
skills/imsg/SKILL.md
npx skills add spinabot/brigade --skill imsg -g -y
SKILL.md
Frontmatter
{
    "name": "imsg",
    "homepage": "https:\/\/imsg.to",
    "metadata": {
        "brigade": {
            "os": [
                "darwin"
            ],
            "emoji": "📨",
            "install": [
                {
                    "id": "brew",
                    "bins": [
                        "imsg"
                    ],
                    "kind": "brew",
                    "label": "Install imsg (brew)",
                    "formula": "steipete\/tap\/imsg"
                }
            ],
            "requires": {
                "bins": [
                    "imsg"
                ]
            }
        }
    },
    "description": "iMessage\/SMS CLI for listing chats, history, and sending messages via Messages.app."
}

imsg

Use imsg to read and send iMessage/SMS via macOS Messages.app.

When to Use

USE this skill when:

  • User explicitly asks to send iMessage or SMS
  • Reading iMessage conversation history
  • Checking recent Messages.app chats
  • Sending to phone numbers or Apple IDs

When NOT to Use

DON'T use this skill when:

  • Telegram messages → use message tool with channel:telegram
  • Signal messages → use Signal channel if configured
  • WhatsApp messages → use WhatsApp channel if configured
  • Discord messages → use message tool with channel:discord
  • Slack messages → use slack skill
  • Group chat management (adding/removing members) → not supported
  • Bulk/mass messaging → always confirm with user first
  • Replying in current conversation → just reply normally (Brigade routes automatically)

Requirements

  • macOS with Messages.app signed in
  • Full Disk Access for terminal
  • Automation permission for Messages.app (for sending)

Common Commands

List Chats

imsg chats --limit 10 --json

View History

# By chat ID
imsg history --chat-id 1 --limit 20 --json

# With attachments info
imsg history --chat-id 1 --limit 20 --attachments --json

Watch for New Messages

imsg watch --chat-id 1 --attachments

Send Messages

# Text only
imsg send --to "+14155551212" --text "Hello!"

# With attachment
imsg send --to "+14155551212" --text "Check this out" --file /path/to/image.jpg

# Specify service
imsg send --to "+14155551212" --text "Hi" --service imessage
imsg send --to "+14155551212" --text "Hi" --service sms

Service Options

  • --service imessage — Force iMessage (requires recipient has iMessage)
  • --service sms — Force SMS (green bubble)
  • --service auto — Let Messages.app decide (default)

Safety Rules

  1. Always confirm recipient and message content before sending
  2. Never send to unknown numbers without explicit user approval
  3. Be careful with attachments — confirm file path exists
  4. Rate limit yourself — don't spam

Example Workflow

User: "Text mom that I'll be late"

# 1. Find mom's chat
imsg chats --limit 20 --json | jq '.[] | select(.displayName | contains("Mom"))'

# 2. Confirm with user
# "Found Mom at +1555123456. Send 'I'll be late' via iMessage?"

# 3. Send after confirmation
imsg send --to "+1555123456" --text "I'll be late"
用于发现并验证无官网的商业线索。通过搜索确认企业仅存在社交或聚合平台页面,判定为潜在客户。优先使用web_search,避免依赖地图按钮,无需API密钥。
查找特定地点的企业、店铺或专业人士 寻找销售网站服务的潜在客户 查询无官方网站的商家
skills/lead-scout/SKILL.md
npx skills add spinabot/brigade --skill lead-scout -g -y
SKILL.md
Frontmatter
{
    "name": "lead-scout",
    "metadata": {
        "brigade": {
            "emoji": "🎯"
        }
    },
    "description": "How to find and VERIFY business leads (any niche, any city) — especially \"businesses without a website\" prospecting. Decides \"no website\" by SEARCHING the business name, not by a map button. Use whenever the user asks to find businesses, shops, vendors, professionals, or sales leads in a location. No API key needed — web_search + the browser do everything."
}

Lead Scout

Goal: a verified lead = a business that has NO website of its own, confirmed by searching for it — the use case is selling websites to businesses that don't have one. Keyless: web_search + the browser do all the work, no API key.

The rule — what counts as "having a website"

A business has a website only if it has its own site: an official domain (e.g. cafeborkar.com, artjuna.in). These do NOT count as a website:

  • Social pages — instagram.com, facebook.com
  • Aggregators / directories — zomato, swiggy, justdial, tripadvisor, magicpin, dineout
  • Its Google Maps / Google Business listing

So if searching for the business surfaces only those, it has no website → it's a lead.

Method — decide by SEARCH, never by a map button

  1. Get candidate names — web_search FIRST (don't drive Maps unless search is unavailable):
    • web_search "<niche> in <city>" returns businesses + their domains. Often this single search already answers it — a business appearing only on aggregators/social with no own domain is your lead.
    • ONLY if web_search is down/rate-limited: browser navigate to https://www.google.com/maps/search/<niche>+in+<city>, then one snapshot (snapshotFormat:"text")/evaluate to read the whole list at once (never screenshot or click each listing). scroll (to:"bottom") + snapshot again for more.
  2. Decide per business with a search — THIS is the step that determines "no website":
    • web_search "<business name> <city>". (If web_search is rate-limited, run the same query in the browser: navigate to a Bing / DuckDuckGo-html / Google results URL, then snapshot.)
    • Scan the results: is any result the business's own domain (per the rule above)?
      • Yes → it HAS a website — skip it, not a lead.
      • Only social / aggregator / Maps results → no website → it's a lead.
    • Not sure which result is theirs? Open the top non-social hit and snapshot to confirm whose site it is.
  3. Report each lead with the evidence: name, area/address + phone if found, and why it's a lead — e.g. "searched 'Cafe Borkar Panaji' — only an Instagram page and a Zomato listing came up, no own website."

Phone / address

Read them from the search results or the business's own social page. Google Maps hides the phone behind a sign-in in its UI — don't fight that panel; the number is usually right there in the search results or on their Instagram/Facebook.

Anti-patterns

  • NEVER decide "no website" from a missing Google Maps "Website" button. That only means nobody linked a site to Google — the business may well have one. The ONLY valid way to decide is to search the business name and see whether its own site appears.
  • Don't screenshot a listing and read it visually, and don't click into each listing one-by-one — read a list with ONE snapshot/evaluate, then decide by search.
  • Don't spawn a sub-agent just to look up or verify a list — do it inline; a lookup doesn't need a crew.
  • Don't report a lead you haven't actually searched for; never invent phone numbers from memory.
  • If you find yourself repeating the same action without new results, STOP and report what you have so far.
  • Don't treat an aggregator/directory page as the business's website or as the source of truth.
mcporter 是 MCP 服务器 CLI 工具,支持列出、配置、认证及调用 HTTP/stdio 协议的 MCP 服务与工具。提供命令行选择、函数语法、URL 直接调用及 JSON 负载功能。具备 OAuth 认证、配置文件管理、守护进程控制及代码生成能力。
需要查询或列出可用的 MCP 服务器 需要配置或管理 MCP 服务器的认证信息 需要通过命令行调用特定的 MCP 工具或服务 需要从 MCP 服务器生成客户端代码或类型定义
skills/mcporter/SKILL.md
npx skills add spinabot/brigade --skill mcporter -g -y
SKILL.md
Frontmatter
{
    "name": "mcporter",
    "homepage": "http:\/\/mcporter.dev",
    "metadata": {
        "brigade": {
            "emoji": "📦",
            "install": [
                {
                    "id": "node",
                    "bins": [
                        "mcporter"
                    ],
                    "kind": "node",
                    "label": "Install mcporter (node)",
                    "package": "mcporter"
                }
            ],
            "requires": {
                "bins": [
                    "mcporter"
                ]
            }
        }
    },
    "description": "Use the mcporter CLI to list, configure, auth, and call MCP servers\/tools directly (HTTP or stdio), including ad-hoc servers, config edits, and CLI\/type generation."
}

mcporter

Use mcporter to work with MCP servers directly.

Quick start

  • mcporter list
  • mcporter list <server> --schema
  • mcporter call <server.tool> key=value

Call tools

  • Selector: mcporter call linear.list_issues team=ENG limit:5
  • Function syntax: mcporter call "linear.create_issue(title: \"Bug\")"
  • Full URL: mcporter call https://api.example.com/mcp.fetch url:https://example.com
  • Stdio: mcporter call --stdio "bun run ./server.ts" scrape url=https://example.com
  • JSON payload: mcporter call <server.tool> --args '{"limit":5}'

Auth + config

  • OAuth: mcporter auth <server | url> [--reset]
  • Config: mcporter config list|get|add|remove|import|login|logout

Daemon

  • mcporter daemon start|status|stop|restart

Codegen

  • CLI: mcporter generate-cli --server <name> or --command <url>
  • Inspect: mcporter inspect-cli <path> [--json]
  • TS: mcporter emit-ts <server> --mode client|types

Notes

  • Config default: ./config/mcporter.json (override with --config).
  • Prefer --output json for machine-readable results.
通过CodexBar CLI获取本地成本日志,按模型汇总Codex或Claude的使用费用。支持查看当前最新模型或全模型明细,可输出文本或JSON格式,适用于查询模型级用量和成本数据。
查询 Codex 或 Claude 的模型使用量 获取模型级别的成本数据 需要脚本化的每模型费用摘要
skills/model-usage/SKILL.md
npx skills add spinabot/brigade --skill model-usage -g -y
SKILL.md
Frontmatter
{
    "name": "model-usage",
    "metadata": {
        "brigade": {
            "os": [
                "darwin"
            ],
            "emoji": "📊",
            "install": [
                {
                    "id": "brew-cask",
                    "bins": [
                        "codexbar"
                    ],
                    "kind": "brew",
                    "label": "Install CodexBar (brew cask)",
                    "formula": "steipete\/tap\/codexbar"
                }
            ],
            "requires": {
                "bins": [
                    "codexbar"
                ]
            }
        }
    },
    "description": "Use CodexBar CLI local cost usage to summarize per-model usage for Codex or Claude, including the current (most recent) model or a full model breakdown. Trigger when asked for model-level usage\/cost data from codexbar, or when you need a scriptable per-model summary from codexbar cost JSON."
}

Model usage

Overview

Get per-model usage cost from CodexBar's local cost logs. Supports "current model" (most recent daily entry) or "all models" summaries for Codex or Claude.

TODO: add Linux CLI support guidance once CodexBar CLI install path is documented for Linux.

Quick start

  1. Fetch cost JSON via CodexBar CLI or pass a JSON file.
  2. Use the bundled script to summarize by model.
python {baseDir}/scripts/model_usage.py --provider codex --mode current
python {baseDir}/scripts/model_usage.py --provider codex --mode all
python {baseDir}/scripts/model_usage.py --provider claude --mode all --format json --pretty

Current model logic

  • Uses the most recent daily row with modelBreakdowns.
  • Picks the model with the highest cost in that row.
  • Falls back to the last entry in modelsUsed when breakdowns are missing.
  • Override with --model <name> when you need a specific model.

Inputs

  • Default: runs codexbar cost --format json --provider <codex|claude>.
  • File or stdin:
codexbar cost --provider codex --format json > /tmp/cost.json
python {baseDir}/scripts/model_usage.py --input /tmp/cost.json --mode all
cat /tmp/cost.json | python {baseDir}/scripts/model_usage.py --input - --mode current

Output

  • Text (default) or JSON (--format json --pretty).
  • Values are cost-only per model; tokens are not split by model in CodexBar output.

References

  • Read references/codexbar-cli.md for CLI flags and cost JSON fields.
nano-pdf 是一个通过自然语言指令编辑 PDF 特定页面的 CLI 工具。支持修改标题、修正拼写等,需指定页码并检查结果,注意页码基数可能因版本而异。
用户需要修改 PDF 文档内容 使用自然语言描述 PDF 页面变更 修复 PDF 中的文字错误
skills/nano-pdf/SKILL.md
npx skills add spinabot/brigade --skill nano-pdf -g -y
SKILL.md
Frontmatter
{
    "name": "nano-pdf",
    "homepage": "https:\/\/pypi.org\/project\/nano-pdf\/",
    "metadata": {
        "brigade": {
            "emoji": "📄",
            "install": [
                {
                    "id": "uv",
                    "bins": [
                        "nano-pdf"
                    ],
                    "kind": "uv",
                    "label": "Install nano-pdf (uv)",
                    "package": "nano-pdf"
                }
            ],
            "requires": {
                "bins": [
                    "nano-pdf"
                ]
            }
        }
    },
    "description": "Edit PDFs with natural-language instructions using the nano-pdf CLI."
}

nano-pdf

Use nano-pdf to apply edits to a specific page in a PDF using a natural-language instruction.

Quick start

nano-pdf edit deck.pdf 1 "Change the title to 'Q3 Results' and fix the typo in the subtitle"

Notes:

  • Page numbers are 0-based or 1-based depending on the tool’s version/config; if the result looks off by one, retry with the other.
  • Always sanity-check the output PDF before sending it out.
诊断Android/iOS/macOS设备与Brigade网关的连接和配对故障。根据网络拓扑(局域网、Tailscale或公网)定位路由问题,验证配置并修复配对授权失败。
QR码或手动连接失败 本地Wi-Fi正常但远程/Tailscale无法连接 报错提示需要配对、未授权、引导令牌无效或过期
skills/node-connect/SKILL.md
npx skills add spinabot/brigade --skill node-connect -g -y
SKILL.md
Frontmatter
{
    "name": "node-connect",
    "description": "Diagnose Brigade node connection and pairing failures for Android, iOS, and macOS companion apps. Use when QR\/setup code\/manual connect fails, local Wi-Fi works but VPS\/tailnet does not, or errors mention pairing required, unauthorized, bootstrap token invalid or expired, gateway.bind, gateway.remote.url, Tailscale, or plugins.entries.device-pair.config.publicUrl."
}

Node Connect

Goal: find the one real route from node -> gateway, verify Brigade is advertising that route, then fix pairing/auth.

Topology first

Decide which case you are in before proposing fixes:

  • same machine / emulator / USB tunnel
  • same LAN / local Wi-Fi
  • same Tailscale tailnet
  • public URL / reverse proxy

Do not mix them.

  • Local Wi-Fi problem: do not switch to Tailscale unless remote access is actually needed.
  • VPS / remote gateway problem: do not keep debugging localhost or LAN IPs.

If ambiguous, ask first

If the setup is unclear or the failure report is vague, ask short clarifying questions before diagnosing.

Ask for:

  • which route they intend: same machine, same LAN, Tailscale tailnet, or public URL
  • whether they used QR/setup code or manual host/port
  • the exact app text/status/error, quoted exactly if possible
  • whether brigade devices list shows a pending pairing request

Do not guess from can't connect.

Canonical checks

Prefer brigade qr --json. It uses the same setup-code payload Android scans.

brigade config get gateway.mode
brigade config get gateway.bind
brigade config get gateway.tailscale.mode
brigade config get gateway.remote.url
brigade config get gateway.auth.mode
brigade config get gateway.auth.allowTailscale
brigade config get plugins.entries.device-pair.config.publicUrl
brigade qr --json
brigade devices list
brigade nodes status

If this Brigade instance is pointed at a remote gateway, also run:

brigade qr --remote --json

If Tailscale is part of the story:

tailscale status --json

Read the result, not guesses

brigade qr --json success means:

  • gatewayUrl: this is the actual endpoint the app should use.
  • urlSource: this tells you which config path won.

Common good sources:

  • gateway.bind=lan: same Wi-Fi / LAN only
  • gateway.bind=tailnet: direct tailnet access
  • gateway.tailscale.mode=serve or gateway.tailscale.mode=funnel: Tailscale route
  • plugins.entries.device-pair.config.publicUrl: explicit public/reverse-proxy route
  • gateway.remote.url: remote gateway route

Root-cause map

If brigade qr --json says Gateway is only bound to loopback:

  • remote node cannot connect yet
  • fix the route, then generate a fresh setup code
  • gateway.bind=auto is not enough if the effective QR route is still loopback
  • same LAN: use gateway.bind=lan
  • same tailnet: prefer gateway.tailscale.mode=serve or use gateway.bind=tailnet
  • public internet: set a real plugins.entries.device-pair.config.publicUrl or gateway.remote.url

If gateway.bind=tailnet set, but no tailnet IP was found:

  • gateway host is not actually on Tailscale

If qr --remote requires gateway.remote.url:

  • remote-mode config is incomplete

If the app says pairing required:

  • network route and auth worked
  • approve the pending device
brigade devices list
brigade devices approve --latest

If the app says bootstrap token invalid or expired:

  • old setup code
  • generate a fresh one and rescan
  • do this after any URL/auth fix too

If the app says unauthorized:

  • wrong token/password, or wrong Tailscale expectation
  • for Tailscale Serve, gateway.auth.allowTailscale must match the intended flow
  • otherwise use explicit token/password

Fast heuristics

  • Same Wi-Fi setup + gateway advertises 127.0.0.1, localhost, or loopback-only config: wrong.
  • Remote setup + setup/manual uses private LAN IP: wrong.
  • Tailnet setup + gateway advertises LAN IP instead of MagicDNS / tailnet route: wrong.
  • Public URL set but QR still advertises something else: inspect urlSource; config is not what you think.
  • brigade devices list shows pending requests: stop changing network config and approve first.

Fix style

Reply with one concrete diagnosis and one route.

If there is not enough signal yet, ask for setup + exact app text instead of guessing.

Good:

  • The gateway is still loopback-only, so a node on another network can never reach it. Enable Tailscale Serve, restart the gateway, run brigade qr again, rescan, then approve the pending device pairing.

Bad:

  • Maybe LAN, maybe Tailscale, maybe port forwarding, maybe public URL.
提供Notion API操作指南,支持通过curl创建、读取、更新页面及数据源(数据库)和块。涵盖搜索、获取内容、查询数据源及更新属性等常用功能,需配置API密钥并指定2025-09-03版本头。
用户需要管理Notion页面或数据库时 用户请求在Notion中创建或查询数据记录时
skills/notion/SKILL.md
npx skills add spinabot/brigade --skill notion -g -y
SKILL.md
Frontmatter
{
    "name": "notion",
    "homepage": "https:\/\/developers.notion.com",
    "metadata": {
        "brigade": {
            "emoji": "📝",
            "requires": {
                "env": [
                    "NOTION_API_KEY"
                ]
            },
            "primaryEnv": "NOTION_API_KEY"
        }
    },
    "description": "Notion API for creating and managing pages, databases, and blocks."
}

notion

Use the Notion API to create/read/update pages, data sources (databases), and blocks.

Setup

  1. Create an integration at https://notion.so/my-integrations
  2. Copy the API key (starts with ntn_ or secret_)
  3. Store it:
mkdir -p ~/.config/notion
echo "ntn_your_key_here" > ~/.config/notion/api_key
  1. Share target pages/databases with your integration (click "..." → "Connect to" → your integration name)

API Basics

All requests need:

NOTION_KEY=$(cat ~/.config/notion/api_key)
curl -X GET "https://api.notion.com/v1/..." \
  -H "Authorization: Bearer $NOTION_KEY" \
  -H "Notion-Version: 2025-09-03" \
  -H "Content-Type: application/json"

Note: The Notion-Version header is required. This skill uses 2025-09-03 (latest). In this version, databases are called "data sources" in the API.

Common Operations

Search for pages and data sources:

curl -X POST "https://api.notion.com/v1/search" \
  -H "Authorization: Bearer $NOTION_KEY" \
  -H "Notion-Version: 2025-09-03" \
  -H "Content-Type: application/json" \
  -d '{"query": "page title"}'

Get page:

curl "https://api.notion.com/v1/pages/{page_id}" \
  -H "Authorization: Bearer $NOTION_KEY" \
  -H "Notion-Version: 2025-09-03"

Get page content (blocks):

curl "https://api.notion.com/v1/blocks/{page_id}/children" \
  -H "Authorization: Bearer $NOTION_KEY" \
  -H "Notion-Version: 2025-09-03"

Create page in a data source:

curl -X POST "https://api.notion.com/v1/pages" \
  -H "Authorization: Bearer $NOTION_KEY" \
  -H "Notion-Version: 2025-09-03" \
  -H "Content-Type: application/json" \
  -d '{
    "parent": {"database_id": "xxx"},
    "properties": {
      "Name": {"title": [{"text": {"content": "New Item"}}]},
      "Status": {"select": {"name": "Todo"}}
    }
  }'

Query a data source (database):

curl -X POST "https://api.notion.com/v1/data_sources/{data_source_id}/query" \
  -H "Authorization: Bearer $NOTION_KEY" \
  -H "Notion-Version: 2025-09-03" \
  -H "Content-Type: application/json" \
  -d '{
    "filter": {"property": "Status", "select": {"equals": "Active"}},
    "sorts": [{"property": "Date", "direction": "descending"}]
  }'

Create a data source (database):

curl -X POST "https://api.notion.com/v1/data_sources" \
  -H "Authorization: Bearer $NOTION_KEY" \
  -H "Notion-Version: 2025-09-03" \
  -H "Content-Type: application/json" \
  -d '{
    "parent": {"page_id": "xxx"},
    "title": [{"text": {"content": "My Database"}}],
    "properties": {
      "Name": {"title": {}},
      "Status": {"select": {"options": [{"name": "Todo"}, {"name": "Done"}]}},
      "Date": {"date": {}}
    }
  }'

Update page properties:

curl -X PATCH "https://api.notion.com/v1/pages/{page_id}" \
  -H "Authorization: Bearer $NOTION_KEY" \
  -H "Notion-Version: 2025-09-03" \
  -H "Content-Type: application/json" \
  -d '{"properties": {"Status": {"select": {"name": "Done"}}}}'

Add blocks to page:

curl -X PATCH "https://api.notion.com/v1/blocks/{page_id}/children" \
  -H "Authorization: Bearer $NOTION_KEY" \
  -H "Notion-Version: 2025-09-03" \
  -H "Content-Type: application/json" \
  -d '{
    "children": [
      {"object": "block", "type": "paragraph", "paragraph": {"rich_text": [{"text": {"content": "Hello"}}]}}
    ]
  }'

Property Types

Common property formats for database items:

  • Title: {"title": [{"text": {"content": "..."}}]}
  • Rich text: {"rich_text": [{"text": {"content": "..."}}]}
  • Select: {"select": {"name": "Option"}}
  • Multi-select: {"multi_select": [{"name": "A"}, {"name": "B"}]}
  • Date: {"date": {"start": "2024-01-15", "end": "2024-01-16"}}
  • Checkbox: {"checkbox": true}
  • Number: {"number": 42}
  • URL: {"url": "https://..."}
  • Email: {"email": "a@b.com"}
  • Relation: {"relation": [{"id": "page_id"}]}

Key Differences in 2025-09-03

  • Databases → Data Sources: Use /data_sources/ endpoints for queries and retrieval
  • Two IDs: Each database now has both a database_id and a data_source_id
    • Use database_id when creating pages (parent: {"database_id": "..."})
    • Use data_source_id when querying (POST /v1/data_sources/{id}/query)
  • Search results: Databases return as "object": "data_source" with their data_source_id
  • Parent in responses: Pages show parent.data_source_id alongside parent.database_id
  • Finding the data_source_id: Search for the database, or call GET /v1/data_sources/{data_source_id}

Notes

  • Page/database IDs are UUIDs (with or without dashes)
  • The API cannot set database view filters — that's UI-only
  • Rate limit: ~3 requests/second average, with 429 rate_limited responses using Retry-After
  • Append block children: up to 100 children per request, up to two levels of nesting in a single append request
  • Payload size limits: up to 1000 block elements and 500KB overall
  • Use is_inline: true when creating data sources to embed them in pages
指导使用oauth_authorize工具配置Gmail等OAuth服务。强调必须使用Desktop-app客户端以避免重定向错误,正确设置scope并获取离线访问令牌。流程包括启动授权、等待回调及通过凭证存储调用API,严禁手动处理监听或读取密钥文件。
连接Gmail 配置Google API OAuth 需要发送电子邮件的OAuth认证
skills/oauth-setup/SKILL.md
npx skills add spinabot/brigade --skill oauth-setup -g -y
SKILL.md
Frontmatter
{
    "name": "oauth-setup",
    "metadata": {
        "brigade": {
            "emoji": "🔐"
        }
    },
    "description": "Connect an OAuth 2.0 service (Gmail, Google APIs, etc.) using the built-in oauth_authorize tool — get a click-to-authorize link, auto-capture the code, and seal the tokens. Use whenever the operator asks to connect Gmail \/ a Google API \/ any OAuth account."
}

OAuth setup

Use the oauth_authorize tool — NEVER hand-roll an http listener in bash. It opens a one-shot loopback listener on a free port (no EADDRINUSE, no port juggling), captures the code, and seals the tokens into the credential store.

The two mistakes to avoid (they cost a whole session once)

  1. Use a Desktop-app OAuth client, not a Web client. In Google Cloud Console → Credentials → Create OAuth client ID → Application type: Desktop app. Desktop clients accept ANY http://127.0.0.1:<port> loopback redirect with no redirect-URI registration. A Web client requires the exact redirect URI pre-registered, which is what causes redirect_uri_mismatch.
  2. Use the real scope. Gmail send is https://www.googleapis.com/auth/gmail.send (NOT https://gmail.com/..., which doesn't exist). Add openid https://www.googleapis.com/auth/userinfo.email if you want the account address auto-filled.

Flow

  1. Ask the operator to create a Desktop-app OAuth client (Gmail API enabled, consent screen in Testing with themselves as a test user) and paste the client id + secret.
  2. Start the flow:
    oauth_authorize({
      action: "start",
      provider: "google-gmail",
      authorizationEndpoint: "https://accounts.google.com/o/oauth2/v2/auth",
      tokenEndpoint: "https://oauth2.googleapis.com/token",
      userInfoEndpoint: "https://www.googleapis.com/oauth2/v3/userinfo",
      clientId: "<id>", clientSecret: "<secret>",
      scopes: ["https://www.googleapis.com/auth/gmail.send", "openid", "https://www.googleapis.com/auth/userinfo.email"],
      extraAuthParams: { access_type: "offline", prompt: "consent" }
    })
    
    access_type=offline + prompt=consent are required for a refresh token (otherwise re-auth is needed when the access token expires).
  3. Send the operator the returned authUrl to click.
  4. Await the redirect — it exchanges the code and seals the tokens:
    oauth_authorize({ action: "await", flowId: "<flowId from start>" })
    
    If it returns status pending, the operator hasn't clicked yet — tell them, then call await again.

Using a connected account (sending mail)

The tokens are sealed in the credential store — you can't read them from a file or the DB, and you shouldn't try. To use a connected account:

  1. Check what's connected: oauth_authorize({ action: "status" }) → lists each account (provider, email, scopes, expiry). No secrets.
  2. Get a usable token: oauth_authorize({ action: "token", provider: "google-gmail" }) → returns accessToken. It auto-refreshes from the sealed refresh token when the old one expired, so you always get a live token. (Pass provider only if more than one account is connected.)
  3. Call the API with it. Gmail send: POST https://gmail.googleapis.com/gmail/v1/users/me/messages/send, header Authorization: Bearer <accessToken>, body { "raw": "<base64url RFC-822 message>" }.

Never cat the credential store, grep for the token, or hand-roll a refresh — action:"token" is the only retrieval path, and it keeps the refresh token + client secret sealed.

通过 obsidian-cli 自动化管理 Obsidian 笔记库。支持查找活动库、搜索笔记及内容、创建/移动/删除笔记,并自动维护双向链接。强调读取配置文件而非硬编码路径,确保跨平台兼容性与安全性。
需要搜索或检索 Obsidian 笔记内容时 需要创建、移动或删除 Obsidian 笔记时 需要获取当前活跃的 Obsidian 库路径时
skills/obsidian/SKILL.md
npx skills add spinabot/brigade --skill obsidian -g -y
SKILL.md
Frontmatter
{
    "name": "obsidian",
    "homepage": "https:\/\/help.obsidian.md",
    "metadata": {
        "brigade": {
            "emoji": "💎",
            "install": [
                {
                    "id": "brew",
                    "bins": [
                        "obsidian-cli"
                    ],
                    "kind": "brew",
                    "label": "Install obsidian-cli (brew)",
                    "formula": "yakitrak\/yakitrak\/obsidian-cli"
                }
            ],
            "requires": {
                "bins": [
                    "obsidian-cli"
                ]
            }
        }
    },
    "description": "Work with Obsidian vaults (plain Markdown notes) and automate via obsidian-cli."
}

Obsidian

Obsidian vault = a normal folder on disk.

Vault structure (typical)

  • Notes: *.md (plain text Markdown; edit with any editor)
  • Config: .obsidian/ (workspace + plugin settings; usually don’t touch from scripts)
  • Canvases: *.canvas (JSON)
  • Attachments: whatever folder you chose in Obsidian settings (images/PDFs/etc.)

Find the active vault(s)

Obsidian desktop tracks vaults here (source of truth):

  • ~/Library/Application Support/obsidian/obsidian.json

obsidian-cli resolves vaults from that file; vault name is typically the folder name (path suffix).

Fast “what vault is active / where are the notes?”

  • If you’ve already set a default: obsidian-cli print-default --path-only
  • Otherwise, read ~/Library/Application Support/obsidian/obsidian.json and use the vault entry with "open": true.

Notes

  • Multiple vaults common (iCloud vs ~/Documents, work/personal, etc.). Don’t guess; read config.
  • Avoid writing hardcoded vault paths into scripts; prefer reading the config or using print-default.

obsidian-cli quick start

Pick a default vault (once):

  • obsidian-cli set-default "<vault-folder-name>"
  • obsidian-cli print-default / obsidian-cli print-default --path-only

Search

  • obsidian-cli search "query" (note names)
  • obsidian-cli search-content "query" (inside notes; shows snippets + lines)

Create

  • obsidian-cli create "Folder/New note" --content "..." --open
  • Requires Obsidian URI handler (obsidian://…) working (Obsidian installed).
  • Avoid creating notes under “hidden” dot-folders (e.g. .something/...) via URI; Obsidian may refuse.

Move/rename (safe refactor)

  • obsidian-cli move "old/path/note" "new/path/note"
  • Updates [[wikilinks]] and common Markdown links across the vault (this is the main win vs mv).

Delete

  • obsidian-cli delete "path/note"

Prefer direct edits when appropriate: open the .md file and change it; Obsidian will pick it up.

通过 OpenAI Whisper API 转录音频文件。支持指定模型、输出路径、语言及提示词,可配置代理地址或本地网关,默认输出为文本文件,也支持 JSON 格式输出。
用户需要转录音频文件 需要将语音转换为文字 使用 Whisper 进行语音识别
skills/openai-whisper-api/SKILL.md
npx skills add spinabot/brigade --skill openai-whisper-api -g -y
SKILL.md
Frontmatter
{
    "name": "openai-whisper-api",
    "homepage": "https:\/\/platform.openai.com\/docs\/guides\/speech-to-text",
    "metadata": {
        "brigade": {
            "emoji": "🌐",
            "install": [
                {
                    "id": "brew",
                    "bins": [
                        "curl"
                    ],
                    "kind": "brew",
                    "label": "Install curl (brew)",
                    "formula": "curl"
                }
            ],
            "requires": {
                "env": [
                    "OPENAI_API_KEY"
                ],
                "bins": [
                    "curl"
                ]
            },
            "primaryEnv": "OPENAI_API_KEY"
        }
    },
    "description": "Transcribe audio via OpenAI Audio Transcriptions API (Whisper)."
}

OpenAI Whisper API (curl)

Transcribe an audio file via OpenAI’s /v1/audio/transcriptions endpoint. Set OPENAI_BASE_URL to use an OpenAI-compatible proxy or local gateway.

Quick start

{baseDir}/scripts/transcribe.sh /path/to/audio.m4a

Defaults:

  • Model: whisper-1
  • Output: <input>.txt

Useful flags

{baseDir}/scripts/transcribe.sh /path/to/audio.ogg --model whisper-1 --out /tmp/transcript.txt
{baseDir}/scripts/transcribe.sh /path/to/audio.m4a --language en
{baseDir}/scripts/transcribe.sh /path/to/audio.m4a --prompt "Speaker names: Peter, Daniel"
{baseDir}/scripts/transcribe.sh /path/to/audio.m4a --json --out /tmp/transcript.json

API key

Set OPENAI_API_KEY, or configure it in the active Brigade config file ($BRIGADE_CONFIG_PATH, default ~/.brigade/brigade.json). Optionally set OPENAI_BASE_URL (for example http://127.0.0.1:51805/v1) to use an OpenAI-compatible proxy or local gateway:

{
  skills: {
    "openai-whisper-api": {
      apiKey: "OPENAI_KEY_HERE",
    },
  },
}
本地语音转文字工具,无需API密钥。支持转录和翻译音频文件,可指定模型大小以平衡速度与精度,默认使用turbo模型。
需要本地进行语音转文字处理 需要将音频翻译成文本 生成音频字幕或文本文件
skills/openai-whisper/SKILL.md
npx skills add spinabot/brigade --skill openai-whisper -g -y
SKILL.md
Frontmatter
{
    "name": "openai-whisper",
    "homepage": "https:\/\/openai.com\/research\/whisper",
    "metadata": {
        "brigade": {
            "emoji": "🎤",
            "install": [
                {
                    "id": "brew",
                    "bins": [
                        "whisper"
                    ],
                    "kind": "brew",
                    "label": "Install OpenAI Whisper (brew)",
                    "formula": "openai-whisper"
                }
            ],
            "requires": {
                "bins": [
                    "whisper"
                ]
            }
        }
    },
    "description": "Local speech-to-text with the Whisper CLI (no API key)."
}

Whisper (CLI)

Use whisper to transcribe audio locally.

Quick start

  • whisper /path/audio.mp3 --model medium --output_format txt --output_dir .
  • whisper /path/audio.m4a --task translate --output_format srt

Notes

  • Models download to ~/.cache/whisper on first run.
  • --model defaults to turbo on this install.
  • Use smaller models for speed, larger for accuracy.
通过 OpenHue CLI 控制 Philips Hue 灯光和场景。支持开关、调光、变色及色温调节,可管理特定房间或区域,并预设睡眠、工作等模式。仅限本地网络内的 Hue 设备,不支持其他品牌或非 Hue 智能硬件。
开关灯光 调节亮度或颜色 切换场景模式 控制特定房间灯光
skills/openhue/SKILL.md
npx skills add spinabot/brigade --skill openhue -g -y
SKILL.md
Frontmatter
{
    "name": "openhue",
    "homepage": "https:\/\/www.openhue.io\/cli",
    "metadata": {
        "brigade": {
            "emoji": "💡",
            "install": [
                {
                    "id": "brew",
                    "bins": [
                        "openhue"
                    ],
                    "kind": "brew",
                    "label": "Install OpenHue CLI (brew)",
                    "formula": "openhue\/cli\/openhue-cli"
                }
            ],
            "requires": {
                "bins": [
                    "openhue"
                ]
            }
        }
    },
    "description": "Control Philips Hue lights and scenes via the OpenHue CLI."
}

OpenHue CLI

Use openhue to control Philips Hue lights and scenes via a Hue Bridge.

When to Use

USE this skill when:

  • "Turn on/off the lights"
  • "Dim the living room lights"
  • "Set a scene" or "movie mode"
  • Controlling specific Hue rooms or zones
  • Adjusting brightness, color, or color temperature

When NOT to Use

DON'T use this skill when:

  • Non-Hue smart devices (other brands) → not supported
  • HomeKit scenes or Shortcuts → use Apple's ecosystem
  • TV or entertainment system control
  • Thermostat or HVAC
  • Smart plugs (unless Hue smart plugs)

Common Commands

List Resources

openhue get light       # List all lights
openhue get room        # List all rooms
openhue get scene       # List all scenes

Control Lights

# Turn on/off
openhue set light "Bedroom Lamp" --on
openhue set light "Bedroom Lamp" --off

# Brightness (0-100)
openhue set light "Bedroom Lamp" --on --brightness 50

# Color temperature (warm to cool: 153-500 mirek)
openhue set light "Bedroom Lamp" --on --temperature 300

# Color (by name or hex)
openhue set light "Bedroom Lamp" --on --color red
openhue set light "Bedroom Lamp" --on --rgb "#FF5500"

Control Rooms

# Turn off entire room
openhue set room "Bedroom" --off

# Set room brightness
openhue set room "Bedroom" --on --brightness 30

Scenes

# Activate scene
openhue set scene "Relax" --room "Bedroom"
openhue set scene "Concentrate" --room "Office"

Quick Presets

# Bedtime (dim warm)
openhue set room "Bedroom" --on --brightness 20 --temperature 450

# Work mode (bright cool)
openhue set room "Office" --on --brightness 100 --temperature 250

# Movie mode (dim)
openhue set room "Living Room" --on --brightness 10

Notes

  • Bridge must be on local network
  • First run requires button press on Hue bridge to pair
  • Colors only work on color-capable bulbs (not white-only)
指导使用 oracle CLI 将提示与代码文件打包,通过浏览器或 API 调用模型获取基于仓库上下文的建议。涵盖引擎选择、文件匹配规则、会话管理及 dry-run 预览等最佳实践。
需要结合完整代码上下文分析项目逻辑 希望批量发送多个文件给大模型进行调试或重构
skills/oracle/SKILL.md
npx skills add spinabot/brigade --skill oracle -g -y
SKILL.md
Frontmatter
{
    "name": "oracle",
    "homepage": "https:\/\/askoracle.dev",
    "metadata": {
        "brigade": {
            "emoji": "🧿",
            "install": [
                {
                    "id": "node",
                    "bins": [
                        "oracle"
                    ],
                    "kind": "node",
                    "label": "Install oracle (node)",
                    "package": "@steipete\/oracle"
                }
            ],
            "requires": {
                "bins": [
                    "oracle"
                ]
            }
        }
    },
    "description": "Best practices for using the oracle CLI (prompt + file bundling, engines, sessions, and file attachment patterns)."
}

oracle — best use

Oracle bundles your prompt + selected files into one “one-shot” request so another model can answer with real repo context (API or browser automation). Treat output as advisory: verify against code + tests.

Main use case (browser, GPT‑5.2 Pro)

Default workflow here: --engine browser with GPT‑5.2 Pro in ChatGPT. This is the common “long think” path: ~10 minutes to ~1 hour is normal; expect a stored session you can reattach to.

Recommended defaults:

  • Engine: browser (--engine browser)
  • Model: GPT‑5.2 Pro (--model gpt-5.2-pro or --model "5.2 Pro")

Golden path

  1. Pick a tight file set (fewest files that still contain the truth).
  2. Preview payload + token spend (--dry-run + --files-report).
  3. Use browser mode for the usual GPT‑5.2 Pro workflow; use API only when you explicitly want it.
  4. If the run detaches/timeouts: reattach to the stored session (don’t re-run).

Commands (preferred)

  • Help:

    • oracle --help
    • If the binary isn’t installed: npx -y @steipete/oracle --help (avoid pnpx here; sqlite bindings).
  • Preview (no tokens):

    • oracle --dry-run summary -p "<task>" --file "src/**" --file "!**/*.test.*"
    • oracle --dry-run full -p "<task>" --file "src/**"
  • Token sanity:

    • oracle --dry-run summary --files-report -p "<task>" --file "src/**"
  • Browser run (main path; long-running is normal):

    • oracle --engine browser --model gpt-5.2-pro -p "<task>" --file "src/**"
  • Manual paste fallback:

    • oracle --render --copy -p "<task>" --file "src/**"
    • Note: --copy is a hidden alias for --copy-markdown.

Attaching files (--file)

--file accepts files, directories, and globs. You can pass it multiple times; entries can be comma-separated.

  • Include:

    • --file "src/**"
    • --file src/index.ts
    • --file docs --file README.md
  • Exclude:

    • --file "src/**" --file "!src/**/*.test.ts" --file "!**/*.snap"
  • Defaults (implementation behavior):

    • Default-ignored dirs: node_modules, dist, coverage, .git, .turbo, .next, build, tmp (skipped unless explicitly passed as literal dirs/files).
    • Honors .gitignore when expanding globs.
    • Does not follow symlinks.
    • Dotfiles filtered unless opted in via pattern (e.g. --file ".github/**").
    • Files > 1 MB rejected.

Engines (API vs browser)

  • Auto-pick: api when OPENAI_API_KEY is set; otherwise browser.
  • Browser supports GPT + Gemini only; use --engine api for Claude/Grok/the agent or multi-model runs.
  • Browser attachments:
    • --browser-attachments auto|never|always (auto pastes inline up to ~60k chars then uploads).
  • Remote browser host:
    • Host: oracle serve --host 0.0.0.0 --port 9473 --token <secret>
    • Client: oracle --engine browser --remote-host <host:port> --remote-token <secret> -p "<task>" --file "src/**"

Sessions + slugs

  • Stored under ~/.oracle/sessions (override with ORACLE_HOME_DIR).
  • Runs may detach or take a long time (browser + GPT‑5.2 Pro often does). If the CLI times out: don’t re-run; reattach.
    • List: oracle status --hours 72
    • Attach: oracle session <id> --render
  • Use --slug "<3-5 words>" to keep session IDs readable.
  • Duplicate prompt guard exists; use --force only when you truly want a fresh run.

Prompt template (high signal)

Oracle starts with zero project knowledge. Assume the model cannot infer your stack, build tooling, conventions, or “obvious” paths. Include:

  • Project briefing (stack + build/test commands + platform constraints).
  • “Where things live” (key directories, entrypoints, config files, boundaries).
  • Exact question + what you tried + the error text (verbatim).
  • Constraints (“don’t change X”, “must keep public API”, etc).
  • Desired output (“return patch plan + tests”, “give 3 options with tradeoffs”).

Safety

  • Don’t attach secrets by default (.env, key files, auth tokens). Redact aggressively; share only what’s required.

“Exhaustive prompt” restoration pattern

For long investigations, write a standalone prompt + file set so you can rerun days later:

  • 6–30 sentence project briefing + the goal.
  • Repro steps + exact errors + what you tried.
  • Attach all context files needed (entrypoints, configs, key modules, docs).

Oracle runs are one-shot; the model doesn’t remember prior runs. “Restoring context” means re-running with the same prompt + --file … set (or reattaching a still-running stored session).

Foodora专属CLI工具,用于查询历史订单、监控活跃订单状态及重新下单。支持浏览器登录、Cookie导入和会话刷新以绕过反爬机制。目前仅支持Foodora,Deliveroo功能仍在开发中。
用户需要查看外卖订单的历史记录或当前配送状态 用户希望通过命令行快速重新订购之前的餐品
skills/ordercli/SKILL.md
npx skills add spinabot/brigade --skill ordercli -g -y
SKILL.md
Frontmatter
{
    "name": "ordercli",
    "homepage": "https:\/\/ordercli.sh",
    "metadata": {
        "brigade": {
            "emoji": "🛵",
            "install": [
                {
                    "id": "brew",
                    "bins": [
                        "ordercli"
                    ],
                    "kind": "brew",
                    "label": "Install ordercli (brew)",
                    "formula": "steipete\/tap\/ordercli"
                },
                {
                    "id": "go",
                    "bins": [
                        "ordercli"
                    ],
                    "kind": "go",
                    "label": "Install ordercli (go)",
                    "module": "github.com\/steipete\/ordercli\/cmd\/ordercli@latest"
                }
            ],
            "requires": {
                "bins": [
                    "ordercli"
                ]
            }
        }
    },
    "description": "Foodora-only CLI for checking past orders and active order status (Deliveroo WIP)."
}

ordercli

Use ordercli to check past orders and track active order status (Foodora only right now).

Quick start (Foodora)

  • ordercli foodora countries
  • ordercli foodora config set --country AT
  • ordercli foodora login --email you@example.com --password-stdin
  • ordercli foodora orders
  • ordercli foodora history --limit 20
  • ordercli foodora history show <orderCode>

Orders

  • Active list (arrival/status): ordercli foodora orders
  • Watch: ordercli foodora orders --watch
  • Active order detail: ordercli foodora order <orderCode>
  • History detail JSON: ordercli foodora history show <orderCode> --json

Reorder (adds to cart)

  • Preview: ordercli foodora reorder <orderCode>
  • Confirm: ordercli foodora reorder <orderCode> --confirm
  • Address: ordercli foodora reorder <orderCode> --confirm --address-id <id>

Cloudflare / bot protection

  • Browser login: ordercli foodora login --email you@example.com --password-stdin --browser
  • Reuse profile: --browser-profile "$HOME/Library/Application Support/ordercli/browser-profile"
  • Import Chrome cookies: ordercli foodora cookies chrome --profile "Default"

Session import (no password)

  • ordercli foodora session chrome --url https://www.foodora.at/ --profile "Default"
  • ordercli foodora session refresh --client-id android

Deliveroo (WIP, not working yet)

  • Requires DELIVEROO_BEARER_TOKEN (optional DELIVEROO_COOKIE).
  • ordercli deliveroo config set --market uk
  • ordercli deliveroo history

Notes

  • Use --config /tmp/ordercli.json for testing.
  • Confirm before any reorder or cart-changing action.
提供专业PDF处理方案,涵盖生成、表单填写与创建、合并拆分、水印加密及矢量排版。支持四种路径:快速内容生成、现有文档编辑、基于pdf-lib脚本的高级定制(含CJK字体)及HTML转换,确保满足从简单报告到复杂表单的专业需求。
用户要求生成或制作PDF文件 需要填写或创建PDF表单字段 请求合并、拆分、旋转或提取PDF页面 需要为PDF添加水印、印章或进行加密保护
skills/pdf/SKILL.md
npx skills add spinabot/brigade --skill pdf -g -y
SKILL.md
Frontmatter
{
    "name": "pdf",
    "metadata": {
        "brigade": {
            "emoji": "📄"
        }
    },
    "description": "Create and edit PDFs to a professional standard — generate from content, draw vector\/precise layouts, embed custom (incl. CJK\/Unicode) fonts, CREATE and fill AcroForm fields (text\/checkbox\/radio\/dropdown), flatten, merge\/split\/rotate, stamp\/watermark, encrypt, and extract text. Use when the user asks Brigade to make a PDF, fill or build a PDF form, or combine\/split\/stamp\/secure PDFs."
}

pdf — professional PDFs

Need Path
Simple content PDF (title, headings, paragraphs, image) Path 1 — make_document tool
Fill an existing form, merge/split/stamp/watermark Path 2 — edit_document tool
Form-field CREATION, vector drawing, embedded fonts (bold/CJK), precise layout, encryption, page surgery Path 3 — script @cantoo/pdf-lib via brigade exec-node
Pixel-perfect, brand-styled layout from HTML/CSS Path 4 — HTML→PDF (optional binary)

Note: PDF is a final format with no reflow engine. If the recipient must edit, deliver the editable source (.docx/.xlsx) and the PDF.


Path 1 — quick content PDF (make_document tool)

make_document(format="pdf", content={ title, pages:[{ heading, paragraphs, image:{path} }] })

Flow layout, US-Letter, single column, word-wrapped. Good for reports/summaries/letters. For forms, precise layout, or custom fonts → Path 3.

Path 2 — edit an existing PDF (edit_document tool)

  • fill_form {fields} — fill existing AcroForm fields by name (text/checkbox/dropdown).
  • merge {pdfs} / add_pages {pdfs} · split {pages} · remove_pages {pages}.
  • stamp {text} · watermark {text} (45° translucent).

Form decision: first detect whether the PDF has real fillable fields. If it does → fill them (clean, type-validated). If it's a flat/scanned form (no fields) → you must draw text at coordinates instead (Path 3, annotations) — never assume fields exist.

Path 3 — full power: script @cantoo/pdf-lib

Brigade bundles @cantoo/pdf-lib (a maintained pdf-lib fork — note the scoped name) plus @pdf-lib/fontkit (custom-font embedding) and unpdf (text extraction). write a gen.cjs, run brigade exec-node gen.cjs:

// gen.cjs — illustrative
const fs = require("node:fs");
const { PDFDocument, StandardFonts, rgb } = require("@cantoo/pdf-lib");

(async () => {
  const pdf = await PDFDocument.create();

  // embed a custom font (real bold / CJK / Unicode) — requires fontkit
  // const fontkit = require("@pdf-lib/fontkit"); pdf.registerFontkit(fontkit);
  // const font = await pdf.embedFont(fs.readFileSync("Brand-Bold.ttf"));
  const font = await pdf.embedFont(StandardFonts.HelveticaBold);

  const page = pdf.addPage([612, 792]);                 // US Letter; origin is BOTTOM-LEFT, y-up
  page.drawText("Invoice", { x: 54, y: 720, size: 22, font, color: rgb(0.1, 0.1, 0.1) });
  page.drawRectangle({ x: 54, y: 700, width: 504, height: 1, color: rgb(0.8, 0.8, 0.8) }); // vector rule

  // CREATE fillable form fields
  const form = pdf.getForm();
  const name = form.createTextField("client.name");
  name.addToPage(page, { x: 120, y: 640, width: 300, height: 18 });
  const agree = form.createCheckBox("agree");
  agree.addToPage(page, { x: 54, y: 600, width: 14, height: 14 });
  // form.flatten();                                     // bake fields into static content if no further filling

  fs.writeFileSync(process.argv[2] || "out.pdf", await pdf.save());
  console.log("wrote");
})();

@cantoo/pdf-lib covers: draw text/lines/rects/ellipses/SVG paths/images; embed custom TTF/OTF fonts (subset, full Unicode); create AND fill AcroForm text/checkbox/radio/dropdown/option-list fields; flatten; copy/merge/split/rotate pages; metadata; password encryption (the reason this fork is bundled). Extract text/structure with unpdf.

Coordinate gotcha: pdf-lib origin is bottom-left, y-up. If you author field positions from a top-left image/spec, convert: y_pdf = pageHeight - y_top - height. One coordinate convention per script.

Path 4 — pixel-perfect / branded PDF from HTML+CSS (optional)

pdf-lib has no HTML/CSS layout engine — for a designed, brand-styled document, render HTML→PDF. Use whatever is present (detect first):

command -v soffice >/dev/null 2>&1 && soffice --headless --convert-to pdf brand.html      # LibreOffice route

A headless browser (Puppeteer/Playwright page.pdf()) is the higher-fidelity alternative if installed. Prefer Path 1/3 unless the brand design genuinely requires CSS.

Conventions

  • Use real AcroForm fields when filling a fillable PDF; only fall back to drawn-text annotations for flat/scanned forms.
  • Embed a font for anything beyond basic Latin — standard PDF fonts lack bold-as-a-face and all CJK/emoji (missing glyphs render as boxes).
  • Keep ≥0.5" margins; align to a grid; don't rely on text reflowing (there's no engine — you place it).

Verify (required)

Confirm the output exists and opens. For filled/created forms, re-open and read back the field values (or render to an image with soffice/a viewer and look at placement) before declaring done — never fill or place blind.

Peekaboo 是 macOS UI 自动化 CLI,支持屏幕捕获、元素定位、输入驱动及窗口管理。提供截图、点击、拖拽、按键等交互功能,具备快照缓存与 JSON 输出能力,适用于脚本化控制应用、菜单及系统对话框。
需要自动化操作 macOS 界面元素 通过脚本控制鼠标键盘输入 获取或分析当前屏幕/UI 状态
skills/peekaboo/SKILL.md
npx skills add spinabot/brigade --skill peekaboo -g -y
SKILL.md
Frontmatter
{
    "name": "peekaboo",
    "homepage": "https:\/\/peekaboo.boo",
    "metadata": {
        "brigade": {
            "os": [
                "darwin"
            ],
            "emoji": "👀",
            "install": [
                {
                    "id": "brew",
                    "bins": [
                        "peekaboo"
                    ],
                    "kind": "brew",
                    "label": "Install Peekaboo (brew)",
                    "formula": "steipete\/tap\/peekaboo"
                }
            ],
            "requires": {
                "bins": [
                    "peekaboo"
                ]
            }
        }
    },
    "description": "Capture and automate macOS UI with the Peekaboo CLI."
}

Peekaboo

Peekaboo is a full macOS UI automation CLI: capture/inspect screens, target UI elements, drive input, and manage apps/windows/menus. Commands share a snapshot cache and support --json/-j for scripting. Run peekaboo or peekaboo <cmd> --help for flags; peekaboo --version prints build metadata. Tip: run via polter peekaboo to ensure fresh builds.

Features (all CLI capabilities, excluding agent/MCP)

Core

  • bridge: inspect Peekaboo Bridge host connectivity
  • capture: live capture or video ingest + frame extraction
  • clean: prune snapshot cache and temp files
  • config: init/show/edit/validate, providers, models, credentials
  • image: capture screenshots (screen/window/menu bar regions)
  • learn: print the full agent guide + tool catalog
  • list: apps, windows, screens, menubar, permissions
  • permissions: check Screen Recording/Accessibility status
  • run: execute .peekaboo.json scripts
  • sleep: pause execution for a duration
  • tools: list available tools with filtering/display options

Interaction

  • click: target by ID/query/coords with smart waits
  • drag: drag & drop across elements/coords/Dock
  • hotkey: modifier combos like cmd,shift,t
  • move: cursor positioning with optional smoothing
  • paste: set clipboard -> paste -> restore
  • press: special-key sequences with repeats
  • scroll: directional scrolling (targeted + smooth)
  • swipe: gesture-style drags between targets
  • type: text + control keys (--clear, delays)

System

  • app: launch/quit/relaunch/hide/unhide/switch/list apps
  • clipboard: read/write clipboard (text/images/files)
  • dialog: click/input/file/dismiss/list system dialogs
  • dock: launch/right-click/hide/show/list Dock items
  • menu: click/list application menus + menu extras
  • menubar: list/click status bar items
  • open: enhanced open with app targeting + JSON payloads
  • space: list/switch/move-window (Spaces)
  • visualizer: exercise Peekaboo visual feedback animations
  • window: close/minimize/maximize/move/resize/focus/list

Vision

  • see: annotated UI maps, snapshot IDs, optional analysis

Global runtime flags

  • --json/-j, --verbose/-v, --log-level <level>
  • --no-remote, --bridge-socket <path>

Quickstart (happy path)

peekaboo permissions
peekaboo list apps --json
peekaboo see --annotate --path /tmp/peekaboo-see.png
peekaboo click --on B1
peekaboo type "Hello" --return

Common targeting parameters (most interaction commands)

  • App/window: --app, --pid, --window-title, --window-id, --window-index
  • Snapshot targeting: --snapshot (ID from see; defaults to latest)
  • Element/coords: --on/--id (element ID), --coords x,y
  • Focus control: --no-auto-focus, --space-switch, --bring-to-current-space, --focus-timeout-seconds, --focus-retry-count

Common capture parameters

  • Output: --path, --format png|jpg, --retina
  • Targeting: --mode screen|window|frontmost, --screen-index, --window-title, --window-id
  • Analysis: --analyze "prompt", --annotate
  • Capture engine: --capture-engine auto|classic|cg|modern|sckit

Common motion/typing parameters

  • Timing: --duration (drag/swipe), --steps, --delay (type/scroll/press)
  • Human-ish movement: --profile human|linear, --wpm (typing)
  • Scroll: --direction up|down|left|right, --amount <ticks>, --smooth

Examples

See -> click -> type (most reliable flow)

peekaboo see --app Safari --window-title "Login" --annotate --path /tmp/see.png
peekaboo click --on B3 --app Safari
peekaboo type "user@example.com" --app Safari
peekaboo press tab --count 1 --app Safari
peekaboo type "supersecret" --app Safari --return

Target by window id

peekaboo list windows --app "Visual Studio Code" --json
peekaboo click --window-id 12345 --coords 120,160
peekaboo type "Hello from Peekaboo" --window-id 12345

Capture screenshots + analyze

peekaboo image --mode screen --screen-index 0 --retina --path /tmp/screen.png
peekaboo image --app Safari --window-title "Dashboard" --analyze "Summarize KPIs"
peekaboo see --mode screen --screen-index 0 --analyze "Summarize the dashboard"

Live capture (motion-aware)

peekaboo capture live --mode region --region 100,100,800,600 --duration 30 \
  --active-fps 8 --idle-fps 2 --highlight-changes --path /tmp/capture

App + window management

peekaboo app launch "Safari" --open https://example.com
peekaboo window focus --app Safari --window-title "Example"
peekaboo window set-bounds --app Safari --x 50 --y 50 --width 1200 --height 800
peekaboo app quit --app Safari

Menus, menubar, dock

peekaboo menu click --app Safari --item "New Window"
peekaboo menu click --app TextEdit --path "Format > Font > Show Fonts"
peekaboo menu click-extra --title "WiFi"
peekaboo dock launch Safari
peekaboo menubar list --json

Mouse + gesture input

peekaboo move 500,300 --smooth
peekaboo drag --from B1 --to T2
peekaboo swipe --from-coords 100,500 --to-coords 100,200 --duration 800
peekaboo scroll --direction down --amount 6 --smooth

Keyboard input

peekaboo hotkey --keys "cmd,shift,t"
peekaboo press escape
peekaboo type "Line 1\nLine 2" --delay 10

Notes

  • Requires Screen Recording + Accessibility permissions.
  • Use peekaboo see --annotate to identify targets before clicking.
通过jq命令搜索和分析存储在JSONL文件中的历史会话日志。适用于用户询问过往对话、父级会话或内存中未包含的历史上下文场景,支持按日期查找、内容检索、成本统计及消息分析。
查询之前的聊天记录 引用父级会话内容 获取不在当前记忆中的历史上下文
skills/session-logs/SKILL.md
npx skills add spinabot/brigade --skill session-logs -g -y
SKILL.md
Frontmatter
{
    "name": "session-logs",
    "metadata": {
        "brigade": {
            "emoji": "📜",
            "install": [
                {
                    "id": "brew-jq",
                    "bins": [
                        "jq"
                    ],
                    "kind": "brew",
                    "label": "Install jq (brew)",
                    "formula": "jq"
                },
                {
                    "id": "brew-rg",
                    "bins": [
                        "rg"
                    ],
                    "kind": "brew",
                    "label": "Install ripgrep (brew)",
                    "formula": "ripgrep"
                }
            ],
            "requires": {
                "bins": [
                    "jq",
                    "rg"
                ]
            }
        }
    },
    "description": "Search and analyze your own session logs (older\/parent conversations) using jq."
}

session-logs

Search your complete conversation history stored in session JSONL files. Use this when a user references older/parent conversations or asks what was said before.

Trigger

Use this skill when the user asks about prior chats, parent conversations, or historical context that isn't in memory files.

Location

Session logs live under the active state directory: $BRIGADE_STATE_DIR/agents/<agentId>/sessions/ (default: ~/.brigade/agents/<agentId>/sessions/). Use the agent=<id> value from the system prompt Runtime line.

  • sessions.json - Index mapping session keys to session IDs
  • <session-id>.jsonl - Full conversation transcript per session

Structure

Each .jsonl file contains messages with:

  • type: "session" (metadata) or "message"
  • timestamp: ISO timestamp
  • message.role: "user", "assistant", or "toolResult"
  • message.content[]: Text, thinking, or tool calls (filter type=="text" for human-readable content)
  • message.usage.cost.total: Cost per response

Common Queries

List all sessions by date and size

AGENT_ID="<agentId>"
SESSION_DIR="${BRIGADE_STATE_DIR:-$HOME/.brigade}/agents/$AGENT_ID/sessions"
for f in "$SESSION_DIR"/*.jsonl; do
  date=$(head -1 "$f" | jq -r '.timestamp' | cut -dT -f1)
  size=$(ls -lh "$f" | awk '{print $5}')
  echo "$date $size $(basename $f)"
done | sort -r

Find sessions from a specific day

AGENT_ID="<agentId>"
SESSION_DIR="${BRIGADE_STATE_DIR:-$HOME/.brigade}/agents/$AGENT_ID/sessions"
for f in "$SESSION_DIR"/*.jsonl; do
  head -1 "$f" | jq -r '.timestamp' | grep -q "2026-01-06" && echo "$f"
done

Extract user messages from a session

jq -r 'select(.message.role == "user") | .message.content[]? | select(.type == "text") | .text' <session>.jsonl

Search for keyword in assistant responses

jq -r 'select(.message.role == "assistant") | .message.content[]? | select(.type == "text") | .text' <session>.jsonl | rg -i "keyword"

Get total cost for a session

jq -s '[.[] | .message.usage.cost.total // 0] | add' <session>.jsonl

Daily cost summary

AGENT_ID="<agentId>"
SESSION_DIR="${BRIGADE_STATE_DIR:-$HOME/.brigade}/agents/$AGENT_ID/sessions"
for f in "$SESSION_DIR"/*.jsonl; do
  date=$(head -1 "$f" | jq -r '.timestamp' | cut -dT -f1)
  cost=$(jq -s '[.[] | .message.usage.cost.total // 0] | add' "$f")
  echo "$date $cost"
done | awk '{a[$1]+=$2} END {for(d in a) print d, "$"a[d]}' | sort -r

Count messages and tokens in a session

jq -s '{
  messages: length,
  user: [.[] | select(.message.role == "user")] | length,
  assistant: [.[] | select(.message.role == "assistant")] | length,
  first: .[0].timestamp,
  last: .[-1].timestamp
}' <session>.jsonl

Tool usage breakdown

jq -r '.message.content[]? | select(.type == "toolCall") | .name' <session>.jsonl | sort | uniq -c | sort -rn

Search across ALL sessions for a phrase

AGENT_ID="<agentId>"
SESSION_DIR="${BRIGADE_STATE_DIR:-$HOME/.brigade}/agents/$AGENT_ID/sessions"
rg -l "phrase" "$SESSION_DIR"/*.jsonl

Tips

  • Sessions are append-only JSONL (one JSON object per line)
  • Large sessions can be several MB - use head/tail for sampling
  • The sessions.json index maps chat providers (discord, whatsapp, etc.) to session IDs
  • Deleted sessions have .deleted.<timestamp> suffix

Fast text-only hint (low noise)

AGENT_ID="<agentId>"
SESSION_DIR="${BRIGADE_STATE_DIR:-$HOME/.brigade}/agents/$AGENT_ID/sessions"
jq -r 'select(.type=="message") | .message.content[]? | select(.type=="text") | .text' "$SESSION_DIR"/<id>.jsonl | rg 'keyword'
指导如何编写、打包和分享技能文件夹。涵盖SKILL.md结构、前置元数据、文件组织规范及归档方法,确保技能可被他人正确安装和使用。
需要创建或修改技能文件夹结构时 需要将技能打包为tar.gz进行分享时 检查技能是否符合分享标准(如名称匹配、依赖声明)时
skills/share-skills/SKILL.md
npx skills add spinabot/brigade --skill share-skills -g -y
SKILL.md
Frontmatter
{
    "name": "share-skills",
    "description": "Use when you need to author, package, or share a skill folder — understanding the SKILL.md format, where skills live, and how to bundle a folder so it can be shared with others."
}

Sharing Skills

This skill explains how skills are structured and how to package one for sharing. It is a companion to skill authoring: where skill-creator helps you write a skill, this covers laying it out correctly and bundling it to hand off.

Where skills live

Each skill is a folder under the skills/ directory. The folder name is the skill's identity and must match the name field in its frontmatter (lowercase). At minimum a skill folder contains a SKILL.md; it may also include supporting files (scripts, reference docs, assets) the skill refers to.

skills/
  my-skill/
    SKILL.md
    reference.md        # optional supporting docs
    scripts/run.sh      # optional helper scripts

SKILL.md format

A SKILL.md is a Markdown file with a YAML frontmatter block followed by the skill body.

---
name: my-skill          # must equal the folder name, lowercase
description: Use when ...  # the "use when" trigger guidance, product-free
# optional eligibility keys (omit any that don't apply):
os: [darwin, linux]       # OS restriction
requires-bins: [foo]      # binaries that MUST be present
requires-any-bins: [a, b] # at least one of these binaries
requires-env: [MY_TOKEN]  # required environment variables
---

Body guidelines:

  • Open with a short overview of what the skill does and when to reach for it.
  • Keep instructions truthful: only describe actions the runtime can actually perform.
  • Reference any supporting files by relative path within the folder.
  • Keep it lean — the description and metadata are what get surfaced; the body is read on demand.

Versioning a shared skill

When you intend to share a skill, keep a changelog note and a version in mind so recipients can tell revisions apart. A simple convention is a ## Changelog section at the bottom of SKILL.md or a sibling CHANGELOG.md.

Packaging a folder to share

To share a skill, bundle its entire folder (so supporting files travel with it) and hand it off as an archive.

# from the skills/ directory, archive a single skill folder
tar -czf my-skill.tar.gz my-skill/

The recipient unpacks the archive into their own skills/ directory:

tar -xzf my-skill.tar.gz -C ./skills/

After unpacking, confirm the folder name still matches the name in frontmatter and that any requires-* eligibility keys are accurate for the new environment.

Checklist before sharing

  • name equals the folder name (lowercase).
  • description is present and free of product names.
  • Eligibility keys (os / requires-bins / requires-any-bins / requires-env) reflect real requirements — omit them when there are none.
  • Supporting files are inside the folder and referenced by relative path.
  • The body never instructs calling a tool the runtime does not have.
基于sherpa-onnx的本地离线文本转语音技能,无需云端。支持配置运行时与模型路径,通过CLI将文本生成音频文件,兼容Windows及多模型切换。
需要将文本转换为语音 需要离线TTS功能
skills/sherpa-onnx-tts/SKILL.md
npx skills add spinabot/brigade --skill sherpa-onnx-tts -g -y
SKILL.md
Frontmatter
{
    "name": "sherpa-onnx-tts",
    "metadata": {
        "brigade": {
            "os": [
                "darwin",
                "linux",
                "win32"
            ],
            "emoji": "🔉",
            "install": [
                {
                    "id": "download-runtime-macos",
                    "os": [
                        "darwin"
                    ],
                    "url": "https:\/\/github.com\/k2-fsa\/sherpa-onnx\/releases\/download\/v1.12.23\/sherpa-onnx-v1.12.23-osx-universal2-shared.tar.bz2",
                    "kind": "download",
                    "label": "Download sherpa-onnx runtime (macOS)",
                    "archive": "tar.bz2",
                    "extract": true,
                    "targetDir": "runtime",
                    "stripComponents": 1
                },
                {
                    "id": "download-runtime-linux-x64",
                    "os": [
                        "linux"
                    ],
                    "url": "https:\/\/github.com\/k2-fsa\/sherpa-onnx\/releases\/download\/v1.12.23\/sherpa-onnx-v1.12.23-linux-x64-shared.tar.bz2",
                    "kind": "download",
                    "label": "Download sherpa-onnx runtime (Linux x64)",
                    "archive": "tar.bz2",
                    "extract": true,
                    "targetDir": "runtime",
                    "stripComponents": 1
                },
                {
                    "id": "download-runtime-win-x64",
                    "os": [
                        "win32"
                    ],
                    "url": "https:\/\/github.com\/k2-fsa\/sherpa-onnx\/releases\/download\/v1.12.23\/sherpa-onnx-v1.12.23-win-x64-shared.tar.bz2",
                    "kind": "download",
                    "label": "Download sherpa-onnx runtime (Windows x64)",
                    "archive": "tar.bz2",
                    "extract": true,
                    "targetDir": "runtime",
                    "stripComponents": 1
                },
                {
                    "id": "download-model-lessac",
                    "url": "https:\/\/github.com\/k2-fsa\/sherpa-onnx\/releases\/download\/tts-models\/vits-piper-en_US-lessac-high.tar.bz2",
                    "kind": "download",
                    "label": "Download Piper en_US lessac (high)",
                    "archive": "tar.bz2",
                    "extract": true,
                    "targetDir": "models"
                }
            ],
            "requires": {
                "env": [
                    "SHERPA_ONNX_RUNTIME_DIR",
                    "SHERPA_ONNX_MODEL_DIR"
                ]
            }
        }
    },
    "description": "Local text-to-speech via sherpa-onnx (offline, no cloud)"
}

sherpa-onnx-tts

Local TTS using the sherpa-onnx offline CLI.

Install

  1. Download the runtime for your OS (extracts into $BRIGADE_STATE_DIR/tools/sherpa-onnx-tts/runtime, default ~/.brigade/tools/sherpa-onnx-tts/runtime)
  2. Download a voice model (extracts into $BRIGADE_STATE_DIR/tools/sherpa-onnx-tts/models, default ~/.brigade/tools/sherpa-onnx-tts/models)

Resolve the active state directory first:

STATE_DIR="${BRIGADE_STATE_DIR:-$HOME/.brigade}"

Then write those resolved paths into the active Brigade config file ($BRIGADE_CONFIG_PATH, default ~/.brigade/brigade.json):

{
  skills: {
    entries: {
      "sherpa-onnx-tts": {
        env: {
          SHERPA_ONNX_RUNTIME_DIR: "/path/to/your/state-dir/tools/sherpa-onnx-tts/runtime",
          SHERPA_ONNX_MODEL_DIR: "/path/to/your/state-dir/tools/sherpa-onnx-tts/models/vits-piper-en_US-lessac-high",
        },
      },
    },
  },
}

The wrapper lives in this skill folder. Run it directly, or add the wrapper to PATH:

export PATH="{baseDir}/bin:$PATH"

Usage

{baseDir}/bin/sherpa-onnx-tts -o ./tts.wav "Hello from local TTS."

Notes:

  • Pick a different model from the sherpa-onnx tts-models release if you want another voice.
  • If the model dir has multiple .onnx files, set SHERPA_ONNX_MODEL_FILE or pass --model-file.
  • You can also pass --tokens-file or --data-dir to override the defaults.
  • Windows: run node {baseDir}\\bin\\sherpa-onnx-tts -o tts.wav "Hello from local TTS."
用于通过 Brigade 控制 Slack,支持消息发送、编辑、删除、回复反应、置顶管理、成员信息查询及自定义表情列表等功能。
需要向 Slack 频道或用户发送消息 需要对 Slack 消息进行点赞或表情回应 需要置顶或取消置顶 Slack 消息 需要查询 Slack 成员信息或表情列表
skills/slack/SKILL.md
npx skills add spinabot/brigade --skill slack -g -y
SKILL.md
Frontmatter
{
    "name": "slack",
    "metadata": {
        "brigade": {
            "emoji": "💬",
            "requires": {
                "config": [
                    "channels.slack"
                ]
            }
        }
    },
    "description": "Use when you need to control Slack from Brigade via the slack tool, including reacting to messages or pinning\/unpinning items in Slack channels or DMs."
}

Slack Actions

Overview

Use slack to react, manage pins, send/edit/delete messages, and fetch member info. The tool uses the bot token configured for Brigade.

Inputs to collect

  • channelId and messageId (Slack message timestamp, e.g. 1712023032.1234).
  • For reactions, an emoji (Unicode or :name:).
  • For message sends, a to target (channel:<id> or user:<id>) and content.

Message context lines include slack message id and channel fields you can reuse directly.

Actions

Action groups

Action group Default Notes
reactions enabled React + list reactions
messages enabled Read/send/edit/delete
pins enabled Pin/unpin/list
memberInfo enabled Member info
emojiList enabled Custom emoji list

React to a message

{
  "action": "react",
  "channelId": "C123",
  "messageId": "1712023032.1234",
  "emoji": "✅"
}

List reactions

{
  "action": "reactions",
  "channelId": "C123",
  "messageId": "1712023032.1234"
}

Send a message

{
  "action": "sendMessage",
  "to": "channel:C123",
  "content": "Hello from Brigade"
}

Edit a message

{
  "action": "editMessage",
  "channelId": "C123",
  "messageId": "1712023032.1234",
  "content": "Updated text"
}

Delete a message

{
  "action": "deleteMessage",
  "channelId": "C123",
  "messageId": "1712023032.1234"
}

Read recent messages

{
  "action": "readMessages",
  "channelId": "C123",
  "limit": 20
}

Pin a message

{
  "action": "pinMessage",
  "channelId": "C123",
  "messageId": "1712023032.1234"
}

Unpin a message

{
  "action": "unpinMessage",
  "channelId": "C123",
  "messageId": "1712023032.1234"
}

List pinned items

{
  "action": "listPins",
  "channelId": "C123"
}

Member info

{
  "action": "memberInfo",
  "userId": "U123"
}

Emoji list

{
  "action": "emojiList"
}

Ideas to try

  • React with ✅ to mark completed tasks.
  • Pin key decisions or weekly status updates.
通过 songsee CLI 从音频生成频谱图和特征面板可视化。支持单图、多面板网格、时间切片及标准输入,可自定义样式、尺寸、FFT参数和频率范围,输出 JPG/PNG 格式。
用户需要分析音频的频谱或声学特征 用户希望将音频数据转换为可视化图像文件
skills/songsee/SKILL.md
npx skills add spinabot/brigade --skill songsee -g -y
SKILL.md
Frontmatter
{
    "name": "songsee",
    "homepage": "https:\/\/github.com\/steipete\/songsee",
    "metadata": {
        "brigade": {
            "emoji": "🌊",
            "install": [
                {
                    "id": "brew",
                    "bins": [
                        "songsee"
                    ],
                    "kind": "brew",
                    "label": "Install songsee (brew)",
                    "formula": "steipete\/tap\/songsee"
                }
            ],
            "requires": {
                "bins": [
                    "songsee"
                ]
            }
        }
    },
    "description": "Generate spectrograms and feature-panel visualizations from audio with the songsee CLI."
}

songsee

Generate spectrograms + feature panels from audio.

Quick start

  • Spectrogram: songsee track.mp3
  • Multi-panel: songsee track.mp3 --viz spectrogram,mel,chroma,hpss,selfsim,loudness,tempogram,mfcc,flux
  • Time slice: songsee track.mp3 --start 12.5 --duration 8 -o slice.jpg
  • Stdin: cat track.mp3 | songsee - --format png -o out.png

Common flags

  • --viz list (repeatable or comma-separated)
  • --style palette (classic, magma, inferno, viridis, gray)
  • --width / --height output size
  • --window / --hop FFT settings
  • --min-freq / --max-freq frequency range
  • --start / --duration time slice
  • --format jpg|png

Notes

  • WAV/MP3 decode native; other formats use ffmpeg if available.
  • Multiple --viz renders a grid.
用于控制局域网内的 Sonos 音箱,支持设备发现、状态查询、播放控制、音量调节及分组管理。同时提供收藏夹、队列操作和 Spotify 搜索功能,并附带针对 macOS 网络权限等常见错误的故障排除指南。
用户需要控制 Sonos 音箱的播放、暂停或停止 用户希望调整 Sonos 音箱的音量或查看设备状态 用户想要将多个 Sonos 音箱进行分组或解组 用户尝试在 Sonos 设备上搜索或播放 Spotify 音乐 用户遇到 Sonos 设备发现失败或网络连接错误
skills/sonoscli/SKILL.md
npx skills add spinabot/brigade --skill sonoscli -g -y
SKILL.md
Frontmatter
{
    "name": "sonoscli",
    "homepage": "https:\/\/sonoscli.sh",
    "metadata": {
        "brigade": {
            "emoji": "🔊",
            "install": [
                {
                    "id": "go",
                    "bins": [
                        "sonos"
                    ],
                    "kind": "go",
                    "label": "Install sonoscli (go)",
                    "module": "github.com\/steipete\/sonoscli\/cmd\/sonos@latest"
                }
            ],
            "requires": {
                "bins": [
                    "sonos"
                ]
            }
        }
    },
    "description": "Control Sonos speakers (discover\/status\/play\/volume\/group)."
}

Sonos CLI

Use sonos to control Sonos speakers on the local network.

Quick start

  • sonos discover
  • sonos status --name "Kitchen"
  • sonos play|pause|stop --name "Kitchen"
  • sonos volume set 15 --name "Kitchen"

Common tasks

  • Grouping: sonos group status|join|unjoin|party|solo
  • Favorites: sonos favorites list|open
  • Queue: sonos queue list|play|clear
  • Spotify search (via SMAPI): sonos smapi search --service "Spotify" --category tracks "query"

Notes

  • If SSDP fails, specify --ip <speaker-ip>.
  • Spotify Web API search is optional and requires SPOTIFY_CLIENT_ID/SECRET.
  • If there is an error, check the troubleshooting section and offer advice if there is a decent match.

Troubleshooting

sonos discover - no route to host

  • On error Error: write udp4 0.0.0.0:64326->239.255.255.250:1900: sendto: no route to host (Command exited with code 1)
    • Note that the port number after 0.0.0.0: is ephemeral and will change and the netmask may not match exactly either
    • The sendto: no route to host should stay consistent
  • Advise the user that in direct (no Docker sandbox) mode on Mac OS that Settings -> Privacy & Security -> Local Network will need to be enabled for the top-level host parent process of the Gateway
    • node if running via launchd
    • Terminal if running the gateway directly in the terminal
    • Visual Studio Code if running via terminal in VS Code
  • An alternative option is to use sandbox (docker container) with network access allowed for that sandbox

sonos discover - bind: operation not permitted

  • On error Error: listen udp4 0.0.0.0:0: bind: operation not permitted
  • Advise the user that they may be running via a the agent or other sandbox that does not permit network access (this can be replicated by running sonos discover within a the agent CLI session with sandbox enabled and not approving the escalation request)
在终端中通过 spogo(首选)或 spotify_player 控制 Spotify 播放与搜索。支持 Premium 账号,涵盖登录、曲目搜索、播放控制、设备切换及状态查询等 CLI 命令,提供备用工具配置说明。
用户想在终端中播放或搜索 Spotify 音乐 用户需要管理 Spotify 播放设备或控制播放进度
skills/spotify-player/SKILL.md
npx skills add spinabot/brigade --skill spotify-player -g -y
SKILL.md
Frontmatter
{
    "name": "spotify-player",
    "homepage": "https:\/\/www.spotify.com",
    "metadata": {
        "brigade": {
            "emoji": "🎵",
            "install": [
                {
                    "id": "brew",
                    "tap": "steipete\/tap",
                    "bins": [
                        "spogo"
                    ],
                    "kind": "brew",
                    "label": "Install spogo (brew)",
                    "formula": "spogo"
                },
                {
                    "id": "brew",
                    "bins": [
                        "spotify_player"
                    ],
                    "kind": "brew",
                    "label": "Install spotify_player (brew)",
                    "formula": "spotify_player"
                }
            ],
            "requires": {
                "anyBins": [
                    "spogo",
                    "spotify_player"
                ]
            }
        }
    },
    "description": "Terminal Spotify playback\/search via spogo (preferred) or spotify_player."
}

spogo / spotify_player

Use spogo (preferred) for Spotify playback/search. Fall back to spotify_player if needed.

Requirements

  • Spotify Premium account.
  • Either spogo or spotify_player installed.

spogo setup

  • Import cookies: spogo auth import --browser chrome

Common CLI commands

  • Search: spogo search track "query"
  • Playback: spogo play|pause|next|prev
  • Devices: spogo device list, spogo device set "<name|id>"
  • Status: spogo status

spotify_player commands (fallback)

  • Search: spotify_player search "query"
  • Playback: spotify_player playback play|pause|next|previous
  • Connect device: spotify_player connect
  • Like track: spotify_player like

Notes

  • Config folder: ~/.config/spotify-player (e.g., app.toml).
  • For Spotify Connect integration, set a user client_id in config.
  • TUI shortcuts are available via ? in the app.
用于快速总结URL、本地文件及YouTube链接内容的CLI工具。支持提取文本或生成摘要,兼容多模型提供商,提供长度控制、JSON输出等灵活参数,适用于网页文章和视频内容的信息浓缩。
用户要求总结链接或文章 用户请求转录YouTube视频内容 直接提及使用 summarize.sh
skills/summarize/SKILL.md
npx skills add spinabot/brigade --skill summarize -g -y
SKILL.md
Frontmatter
{
    "name": "summarize",
    "homepage": "https:\/\/summarize.sh",
    "metadata": {
        "brigade": {
            "emoji": "🧾",
            "install": [
                {
                    "id": "brew",
                    "bins": [
                        "summarize"
                    ],
                    "kind": "brew",
                    "label": "Install summarize (brew)",
                    "formula": "steipete\/tap\/summarize"
                }
            ],
            "requires": {
                "bins": [
                    "summarize"
                ]
            }
        }
    },
    "description": "Summarize or extract text\/transcripts from URLs, podcasts, and local files (great fallback for “transcribe this YouTube\/video”)."
}

Summarize

Fast CLI to summarize URLs, local files, and YouTube links.

When to use (trigger phrases)

Use this skill immediately when the user asks any of:

  • “use summarize.sh”
  • “what’s this link/video about?”
  • “summarize this URL/article”
  • “transcribe this YouTube/video” (best-effort transcript extraction; no yt-dlp needed)

Quick start

summarize "https://example.com" --model google/gemini-3-flash-preview
summarize "/path/to/file.pdf" --model google/gemini-3-flash-preview
summarize "https://youtu.be/dQw4w9WgXcQ" --youtube auto

YouTube: summary vs transcript

Best-effort transcript (URLs only):

summarize "https://youtu.be/dQw4w9WgXcQ" --youtube auto --extract-only

If the user asked for a transcript but it’s huge, return a tight summary first, then ask which section/time range to expand.

Model + keys

Set the API key for your chosen provider:

  • OpenAI: OPENAI_API_KEY
  • Anthropic: ANTHROPIC_API_KEY
  • xAI: XAI_API_KEY
  • Google: GEMINI_API_KEY (aliases: GOOGLE_GENERATIVE_AI_API_KEY, GOOGLE_API_KEY)

Default model is google/gemini-3-flash-preview if none is set.

Useful flags

  • --length short|medium|long|xl|xxl|<chars>
  • --max-output-tokens <count>
  • --extract-only (URLs only)
  • --json (machine readable)
  • --firecrawl auto|off|always (fallback extraction)
  • --youtube auto (Apify fallback if APIFY_API_TOKEN set)

Config

Optional config file: ~/.summarize/config.json

{ "model": "openai/gpt-5.2" }

Optional services:

  • FIRECRAWL_API_KEY for blocked sites
  • APIFY_API_TOKEN for YouTube fallback
展示TaskFlow收件箱分拣模式,根据意图分流消息:商务类发Slack并等待回复,个人类立即通知,其余汇总至日终摘要。提供状态管理、等待机制及运行时调用的完整代码示例。
需要按意图分类处理邮件或消息 实现异步等待外部回复后继续流程 构建基于TaskFlow的消息分拣工作流
skills/taskflow-inbox-triage/SKILL.md
npx skills add spinabot/brigade --skill taskflow-inbox-triage -g -y
SKILL.md
Frontmatter
{
    "name": "taskflow-inbox-triage",
    "metadata": {
        "brigade": {
            "emoji": "📥"
        }
    },
    "description": "Example TaskFlow authoring pattern for inbox triage. Use when messages need different treatment based on intent, with some routes notifying immediately, some waiting on outside answers, and others rolling into a later summary."
}

TaskFlow inbox triage

This is a concrete example of how to think about TaskFlow without turning the core runtime into a DSL.

Goal

Triage inbox items with one owner flow:

  • business → post to Slack and wait for reply
  • personal → notify the owner now
  • everything else → keep for end-of-day summary

Pattern

  1. Create one flow for the inbox batch.
  2. Run one detached task to classify new items.
  3. Persist the routing state in stateJson.
  4. Move to waiting only when an outside reply is required.
  5. Resume the flow when classification or human input completes.
  6. Finish when the batch has been routed.

Suggested stateJson shape

{
  "businessThreads": [],
  "personalItems": [],
  "eodSummary": []
}

Suggested waitJson when blocked on Slack:

{
  "kind": "reply",
  "channel": "slack",
  "threadKey": "slack:thread-1"
}

Minimal runtime calls

const taskFlow = api.runtime.tasks.flow.fromToolContext(ctx);

const created = taskFlow.createManaged({
  controllerId: "my-plugin/inbox-triage",
  goal: "triage inbox",
  currentStep: "classify",
  stateJson: {
    businessThreads: [],
    personalItems: [],
    eodSummary: [],
  },
});

const child = taskFlow.runTask({
  flowId: created.flowId,
  runtime: "acp",
  childSessionKey: "agent:main:subagent:classifier",
  task: "Classify inbox messages",
  status: "running",
  startedAt: Date.now(),
  lastEventAt: Date.now(),
});

if (!child.created) {
  throw new Error(child.reason);
}

const waiting = taskFlow.setWaiting({
  flowId: created.flowId,
  expectedRevision: created.revision,
  currentStep: "await_business_reply",
  stateJson: {
    businessThreads: ["slack:thread-1"],
    personalItems: [],
    eodSummary: [],
  },
  waitJson: {
    kind: "reply",
    channel: "slack",
    threadKey: "slack:thread-1",
  },
});

if (!waiting.applied) {
  throw new Error(waiting.code);
}

const resumed = taskFlow.resume({
  flowId: waiting.flow.flowId,
  expectedRevision: waiting.flow.revision,
  status: "running",
  currentStep: "route_items",
  stateJson: waiting.flow.stateJson,
});

if (!resumed.applied) {
  throw new Error(resumed.code);
}

taskFlow.finish({
  flowId: resumed.flow.flowId,
  expectedRevision: resumed.flow.revision,
  stateJson: resumed.flow.stateJson,
});

Related example

  • skills/taskflow/examples/inbox-triage.lobster
TaskFlow用于管理跨多个独立任务但保持单一所有者上下文的持久化工作流。它负责流程身份、状态同步、子任务链接及版本冲突检测,适用于需后台持久运行、等待外部依赖或插件重启的场景,业务逻辑由调用方处理。
需要跨步骤持久化的后台任务 等待子代理或外部系统响应的流程 插件工具中需生存于重启后的工作
skills/taskflow/SKILL.md
npx skills add spinabot/brigade --skill taskflow -g -y
SKILL.md
Frontmatter
{
    "name": "taskflow",
    "metadata": {
        "brigade": {
            "emoji": "🪝"
        }
    },
    "description": "Use when work should span one or more detached tasks but still behave like one job with a single owner context. TaskFlow is the durable flow substrate under authoring layers like Lobster, ACPX, plugins, or plain code. Keep conditional logic in the caller; use TaskFlow for flow identity, child-task linkage, waiting state, revision-checked mutations, and user-facing emergence."
}

TaskFlow

Use TaskFlow when a job needs to outlive one prompt or one detached run, but you still want one owner session, one return context, and one place to inspect or resume the work.

When to use it

  • Multi-step background work with one owner
  • Work that waits on detached ACP or subagent tasks
  • Jobs that may need to emit one clear update back to the owner
  • Jobs that need small persisted state between steps
  • Plugin or tool work that must survive restarts and revision conflicts cleanly

What TaskFlow owns

  • flow identity
  • owner session and requester origin
  • currentStep, stateJson, and waitJson
  • linked child tasks and their parent flow id
  • finish, fail, cancel, waiting, and blocked state
  • revision tracking for conflict-safe mutations

It does not own branching or business logic. Put that in Lobster, acpx, or the calling code.

Current runtime shape

Canonical plugin/runtime entrypoint:

  • api.runtime.tasks.flow
  • api.runtime.taskFlow still exists as an alias, but api.runtime.tasks.flow is the canonical shape

Binding:

  • api.runtime.tasks.flow.fromToolContext(ctx) when you already have trusted tool context with sessionKey
  • api.runtime.tasks.flow.bindSession({ sessionKey, requesterOrigin }) when your binding layer already resolved the session and delivery context

Managed-flow lifecycle:

  1. createManaged(...)
  2. runTask(...)
  3. setWaiting(...) when waiting on a person or an external system
  4. resume(...) when work can continue
  5. finish(...) or fail(...)
  6. requestCancel(...) or cancel(...) when the whole job should stop

Design constraints

  • Use managed TaskFlows when your code owns the orchestration.
  • One-task mirrored flows are created by core runtime for detached ACP/subagent work; this skill is mainly about managed flows.
  • Treat stateJson as the persisted state bag. There is no separate setFlowOutput or appendFlowOutput API.
  • Every mutating method after creation is revision-checked. Carry forward the latest flow.revision after each successful mutation.
  • runTask(...) links the child task to the flow. Use it instead of manually creating detached tasks when you want parent orchestration.

Example shape

const taskFlow = api.runtime.tasks.flow.fromToolContext(ctx);

const created = taskFlow.createManaged({
  controllerId: "my-plugin/inbox-triage",
  goal: "triage inbox",
  currentStep: "classify",
  stateJson: {
    businessThreads: [],
    personalItems: [],
    eodSummary: [],
  },
});

const classify = taskFlow.runTask({
  flowId: created.flowId,
  runtime: "acp",
  childSessionKey: "agent:main:subagent:classifier",
  runId: "inbox-classify-1",
  task: "Classify inbox messages",
  status: "running",
  startedAt: Date.now(),
  lastEventAt: Date.now(),
});

if (!classify.created) {
  throw new Error(classify.reason);
}

const waiting = taskFlow.setWaiting({
  flowId: created.flowId,
  expectedRevision: created.revision,
  currentStep: "await_business_reply",
  stateJson: {
    businessThreads: ["slack:thread-1"],
    personalItems: [],
    eodSummary: [],
  },
  waitJson: {
    kind: "reply",
    channel: "slack",
    threadKey: "slack:thread-1",
  },
});

if (!waiting.applied) {
  throw new Error(waiting.code);
}

const resumed = taskFlow.resume({
  flowId: waiting.flow.flowId,
  expectedRevision: waiting.flow.revision,
  status: "running",
  currentStep: "finalize",
  stateJson: waiting.flow.stateJson,
});

if (!resumed.applied) {
  throw new Error(resumed.code);
}

taskFlow.finish({
  flowId: resumed.flow.flowId,
  expectedRevision: resumed.flow.revision,
  stateJson: resumed.flow.stateJson,
});

Keep conditionals above the runtime

Use the flow runtime for state and task linkage. Keep decisions in the authoring layer:

  • business → post to Slack and wait
  • personal → notify the owner now
  • later → append to an end-of-day summary bucket

Operational pattern

  • Store only the minimum state needed to resume.
  • Put human-readable wait reasons in blockedSummary or structured wait metadata in waitJson.
  • Use getTaskSummary(flowId) when the orchestrator needs a compact health view of child work.
  • Use requestCancel(...) when a caller wants the flow to stop scheduling immediately.
  • Use cancel(...) when you also want active linked child tasks cancelled.

Examples

  • See skills/taskflow/examples/inbox-triage.lobster
  • See skills/taskflow/examples/pr-intake.lobster
  • See skills/taskflow-inbox-triage/SKILL.md for a concrete routing pattern
在macOS上通过things CLI管理Things 3应用。支持从本地数据库读取收件箱、今日任务等,或通过URL方案添加、更新任务(如标题、笔记、标签、截止日期)。
添加新任务到Things 列出或搜索任务/项目 修改现有任务的属性
skills/things-mac/SKILL.md
npx skills add spinabot/brigade --skill things-mac -g -y
SKILL.md
Frontmatter
{
    "name": "things-mac",
    "homepage": "https:\/\/github.com\/ossianhempel\/things3-cli",
    "metadata": {
        "brigade": {
            "os": [
                "darwin"
            ],
            "emoji": "✅",
            "install": [
                {
                    "id": "go",
                    "bins": [
                        "things"
                    ],
                    "kind": "go",
                    "label": "Install things3-cli (go)",
                    "module": "github.com\/ossianhempel\/things3-cli\/cmd\/things@latest"
                }
            ],
            "requires": {
                "bins": [
                    "things"
                ]
            }
        }
    },
    "description": "Manage Things 3 via the `things` CLI on macOS (add\/update projects+todos via URL scheme; read\/search\/list from the local Things database). Use when a user asks Brigade to add a task to Things, list inbox\/today\/upcoming, search tasks, or inspect projects\/areas\/tags."
}

Things 3 CLI

Use things to read your local Things database (inbox/today/search/projects/areas/tags) and to add/update todos via the Things URL scheme.

Setup

  • Install (recommended, Apple Silicon): GOBIN=/opt/homebrew/bin go install github.com/ossianhempel/things3-cli/cmd/things@latest
  • If DB reads fail: grant Full Disk Access to the calling app (Terminal for manual runs; Brigade.app for gateway runs).
  • Optional: set THINGSDB (or pass --db) to point at your ThingsData-* folder.
  • Optional: set THINGS_AUTH_TOKEN to avoid passing --auth-token for update ops.

Read-only (DB)

  • things inbox --limit 50
  • things today
  • things upcoming
  • things search "query"
  • things projects / things areas / things tags

Write (URL scheme)

  • Prefer safe preview: things --dry-run add "Title"
  • Add: things add "Title" --notes "..." --when today --deadline 2026-01-02
  • Bring Things to front: things --foreground add "Title"

Examples: add a todo

  • Basic: things add "Buy milk"
  • With notes: things add "Buy milk" --notes "2% + bananas"
  • Into a project/area: things add "Book flights" --list "Travel"
  • Into a project heading: things add "Pack charger" --list "Travel" --heading "Before"
  • With tags: things add "Call dentist" --tags "health,phone"
  • Checklist: things add "Trip prep" --checklist-item "Passport" --checklist-item "Tickets"
  • From STDIN (multi-line => title + notes):
    • cat <<'EOF' | things add -
    • Title line
    • Notes line 1
    • Notes line 2
    • EOF

Examples: modify a todo (needs auth token)

  • First: get the ID (UUID column): things search "milk" --limit 5
  • Auth: set THINGS_AUTH_TOKEN or pass --auth-token <TOKEN>
  • Title: things update --id <UUID> --auth-token <TOKEN> "New title"
  • Notes replace: things update --id <UUID> --auth-token <TOKEN> --notes "New notes"
  • Notes append/prepend: things update --id <UUID> --auth-token <TOKEN> --append-notes "..." / --prepend-notes "..."
  • Move lists: things update --id <UUID> --auth-token <TOKEN> --list "Travel" --heading "Before"
  • Tags replace/add: things update --id <UUID> --auth-token <TOKEN> --tags "a,b" / things update --id <UUID> --auth-token <TOKEN> --add-tags "a,b"
  • Complete/cancel (soft-delete-ish): things update --id <UUID> --auth-token <TOKEN> --completed / --canceled
  • Safe preview: things --dry-run update --id <UUID> --auth-token <TOKEN> --completed

Delete a todo?

  • Not supported by things3-cli right now (no “delete/move-to-trash” write command; things trash is read-only listing).
  • Options: use Things UI to delete/trash, or mark as --completed / --canceled via things update.

Notes

  • macOS-only.
  • --dry-run prints the URL and does not open Things.
用于通过发送按键和抓取窗格输出来远程控制 tmux 会话。适用于监控 Claude/Codex 交互会话、向交互式终端应用发送输入、刮取长进程输出及程序化导航窗格/窗口。
监控 tmux 中的 Claude/Codex 会话 向交互式终端应用发送输入 从 tmux 中抓取长运行进程的输出 程序化导航 tmux 窗格或窗口
skills/tmux/SKILL.md
npx skills add spinabot/brigade --skill tmux -g -y
SKILL.md
Frontmatter
{
    "name": "tmux",
    "metadata": {
        "brigade": {
            "os": [
                "darwin",
                "linux"
            ],
            "emoji": "🧵",
            "install": [
                {
                    "id": "brew",
                    "bins": [
                        "tmux"
                    ],
                    "kind": "brew",
                    "label": "Install tmux (brew)",
                    "formula": "tmux"
                }
            ],
            "requires": {
                "bins": [
                    "tmux"
                ]
            }
        }
    },
    "description": "Remote-control tmux sessions for interactive CLIs by sending keystrokes and scraping pane output."
}

tmux Session Control

Control tmux sessions by sending keystrokes and reading output. Essential for managing Claude Code sessions.

When to Use

USE this skill when:

  • Monitoring Claude/Codex sessions in tmux
  • Sending input to interactive terminal applications
  • Scraping output from long-running processes in tmux
  • Navigating tmux panes/windows programmatically
  • Checking on background work in existing sessions

When NOT to Use

DON'T use this skill when:

  • Running one-off shell commands → use exec tool directly
  • Starting new background processes → use exec with background:true
  • Non-interactive scripts → use exec tool
  • The process isn't in tmux
  • You need to create a new tmux session → use exec with tmux new-session

Example Sessions

Session Purpose
shared Primary interactive session
worker-2 - worker-8 Parallel worker sessions

Common Commands

List Sessions

tmux list-sessions
tmux ls

Capture Output

# Last 20 lines of pane
tmux capture-pane -t shared -p | tail -20

# Entire scrollback
tmux capture-pane -t shared -p -S -

# Specific pane in window
tmux capture-pane -t shared:0.0 -p

Send Keys

# Send text (doesn't press Enter)
tmux send-keys -t shared "hello"

# Send text + Enter
tmux send-keys -t shared "y" Enter

# Send special keys
tmux send-keys -t shared Enter
tmux send-keys -t shared Escape
tmux send-keys -t shared C-c          # Ctrl+C
tmux send-keys -t shared C-d          # Ctrl+D (EOF)
tmux send-keys -t shared C-z          # Ctrl+Z (suspend)

Window/Pane Navigation

# Select window
tmux select-window -t shared:0

# Select pane
tmux select-pane -t shared:0.1

# List windows
tmux list-windows -t shared

Session Management

# Create new session
tmux new-session -d -s newsession

# Kill session
tmux kill-session -t sessionname

# Rename session
tmux rename-session -t old new

Sending Input Safely

For interactive TUIs (Claude Code, Codex, etc.), split text and Enter into separate sends to avoid paste/multiline edge cases:

tmux send-keys -t shared -l -- "Please apply the patch in src/foo.ts"
sleep 0.1
tmux send-keys -t shared Enter

Claude Code Session Patterns

Check if Session Needs Input

# Look for prompts
tmux capture-pane -t worker-3 -p | tail -10 | grep -E "❯|Yes.*No|proceed|permission"

Approve Claude Code Prompt

# Send 'y' and Enter
tmux send-keys -t worker-3 'y' Enter

# Or select numbered option
tmux send-keys -t worker-3 '2' Enter

Check All Sessions Status

for s in shared worker-2 worker-3 worker-4 worker-5 worker-6 worker-7 worker-8; do
  echo "=== $s ==="
  tmux capture-pane -t $s -p 2>/dev/null | tail -5
done

Send Task to Session

tmux send-keys -t worker-4 "Fix the bug in auth.js" Enter

Notes

  • Use capture-pane -p to print to stdout (essential for scripting)
  • -S - captures entire scrollback history
  • Target format: session:window.pane (e.g., shared:0.0)
  • Sessions persist across SSH disconnects
通过Trello REST API管理看板、列表和卡片。支持查看/创建/移动卡片、添加评论及归档操作,需配置API Key和Token。
管理Trello任务 查询看板或卡片状态 在Trello中创建新任务 更新或归档Trello卡片
skills/trello/SKILL.md
npx skills add spinabot/brigade --skill trello -g -y
SKILL.md
Frontmatter
{
    "name": "trello",
    "homepage": "https:\/\/developer.atlassian.com\/cloud\/trello\/rest\/",
    "metadata": {
        "brigade": {
            "emoji": "📋",
            "install": [
                {
                    "id": "brew",
                    "bins": [
                        "jq"
                    ],
                    "kind": "brew",
                    "label": "Install jq (brew)",
                    "formula": "jq"
                }
            ],
            "requires": {
                "env": [
                    "TRELLO_API_KEY",
                    "TRELLO_TOKEN"
                ],
                "bins": [
                    "jq"
                ]
            }
        }
    },
    "description": "Manage Trello boards, lists, and cards via the Trello REST API."
}

Trello Skill

Manage Trello boards, lists, and cards directly from Brigade.

Setup

  1. Get your API key: https://trello.com/app-key
  2. Generate a token (click "Token" link on that page)
  3. Set environment variables:
    export TRELLO_API_KEY="your-api-key"
    export TRELLO_TOKEN="your-token"
    

Usage

All commands use curl to hit the Trello REST API.

List boards

curl -s "https://api.trello.com/1/members/me/boards?key=$TRELLO_API_KEY&token=$TRELLO_TOKEN" | jq '.[] | {name, id}'

List lists in a board

curl -s "https://api.trello.com/1/boards/{boardId}/lists?key=$TRELLO_API_KEY&token=$TRELLO_TOKEN" | jq '.[] | {name, id}'

List cards in a list

curl -s "https://api.trello.com/1/lists/{listId}/cards?key=$TRELLO_API_KEY&token=$TRELLO_TOKEN" | jq '.[] | {name, id, desc}'

Create a card

curl -s -X POST "https://api.trello.com/1/cards?key=$TRELLO_API_KEY&token=$TRELLO_TOKEN" \
  -d "idList={listId}" \
  -d "name=Card Title" \
  -d "desc=Card description"

Move a card to another list

curl -s -X PUT "https://api.trello.com/1/cards/{cardId}?key=$TRELLO_API_KEY&token=$TRELLO_TOKEN" \
  -d "idList={newListId}"

Add a comment to a card

curl -s -X POST "https://api.trello.com/1/cards/{cardId}/actions/comments?key=$TRELLO_API_KEY&token=$TRELLO_TOKEN" \
  -d "text=Your comment here"

Archive a card

curl -s -X PUT "https://api.trello.com/1/cards/{cardId}?key=$TRELLO_API_KEY&token=$TRELLO_TOKEN" \
  -d "closed=true"

Notes

  • Board/List/Card IDs can be found in the Trello URL or via the list commands
  • The API key and token provide full access to your Trello account - keep them secret!
  • Rate limits: 300 requests per 10 seconds per API key; 100 requests per 10 seconds per token; /1/members endpoints are limited to 100 requests per 900 seconds

Examples

# Get all boards
curl -s "https://api.trello.com/1/members/me/boards?key=$TRELLO_API_KEY&token=$TRELLO_TOKEN&fields=name,id" | jq

# Find a specific board by name
curl -s "https://api.trello.com/1/members/me/boards?key=$TRELLO_API_KEY&token=$TRELLO_TOKEN" | jq '.[] | select(.name | contains("Work"))'

# Get all cards on a board
curl -s "https://api.trello.com/1/boards/{boardId}/cards?key=$TRELLO_API_KEY&token=$TRELLO_TOKEN" | jq '.[] | {name, list: .idList}'
利用 ffmpeg 从视频中提取单帧图片或生成缩略图,支持指定时间戳。提供 JPG 用于快速分享,PNG 用于高清晰度 UI 展示,适用于视频内容检查与预览场景。
用户需要从视频中获取特定时刻的画面 用户需要生成视频缩略图或封面图 用户需要检查视频中的某个片段
skills/video-frames/SKILL.md
npx skills add spinabot/brigade --skill video-frames -g -y
SKILL.md
Frontmatter
{
    "name": "video-frames",
    "homepage": "https:\/\/ffmpeg.org",
    "metadata": {
        "brigade": {
            "emoji": "🎬",
            "install": [
                {
                    "id": "brew",
                    "bins": [
                        "ffmpeg"
                    ],
                    "kind": "brew",
                    "label": "Install ffmpeg (brew)",
                    "formula": "ffmpeg"
                }
            ],
            "requires": {
                "bins": [
                    "ffmpeg"
                ]
            }
        }
    },
    "description": "Extract frames or short clips from videos using ffmpeg."
}

Video Frames (ffmpeg)

Extract a single frame from a video, or create quick thumbnails for inspection.

Quick start

First frame:

{baseDir}/scripts/frame.sh /path/to/video.mp4 --out /tmp/frame.jpg

At a timestamp:

{baseDir}/scripts/frame.sh /path/to/video.mp4 --time 00:00:10 --out /tmp/frame-10s.jpg

Notes

  • Prefer --time for “what is happening around here?”.
  • Use a .jpg for quick share; use .png for crisp UI frames.
通过 Brigade voice-call 插件发起或检查语音通话,支持 Twilio、Telnyx、Plivo 及 Mock 提供商。提供 CLI 和 Agent 工具接口,用于初始化、继续、说话、结束通话及查询状态。
用户需要发起语音通话 Agent 需要执行语音交互操作
skills/voice-call/SKILL.md
npx skills add spinabot/brigade --skill voice-call -g -y
SKILL.md
Frontmatter
{
    "name": "voice-call",
    "metadata": {
        "brigade": {
            "emoji": "📞",
            "requires": {
                "config": [
                    "plugins.entries.voice-call.enabled"
                ]
            },
            "skillKey": "voice-call"
        }
    },
    "description": "Start voice calls via the Brigade voice-call plugin."
}

Voice Call

Use the voice-call plugin to start or inspect calls (Twilio, Telnyx, Plivo, or mock).

CLI

brigade voicecall call --to "+15555550123" --message "Hello from Brigade"
brigade voicecall status --call-id <id>

Tool

Use voice_call for agent-initiated calls.

Actions:

  • initiate_call (message, to?, mode?)
  • continue_call (callId, message)
  • speak_to_user (callId, message)
  • end_call (callId)
  • get_status (callId)

Notes:

  • Requires the voice-call plugin to be enabled.
  • Plugin config lives under plugins.entries.voice-call.config.
  • Twilio config: provider: "twilio" + twilio.accountSid/authToken + fromNumber.
  • Telnyx config: provider: "telnyx" + telnyx.apiKey/connectionId + fromNumber.
  • Plivo config: provider: "plivo" + plivo.authId/authToken + fromNumber.
  • Dev fallback: provider: "mock" (no network).
通过wacli CLI向第三方发送WhatsApp消息或搜索/同步历史记录。严禁用于常规用户对话,需明确接收者和内容并确认。支持文本、文件发送及历史回溯,依赖手机在线。
用户明确要求向WhatsApp联系人发送消息 用户要求搜索或同步WhatsApp聊天记录
skills/wacli/SKILL.md
npx skills add spinabot/brigade --skill wacli -g -y
SKILL.md
Frontmatter
{
    "name": "wacli",
    "homepage": "https:\/\/wacli.sh",
    "metadata": {
        "brigade": {
            "emoji": "📱",
            "install": [
                {
                    "id": "brew",
                    "bins": [
                        "wacli"
                    ],
                    "kind": "brew",
                    "label": "Install wacli (brew)",
                    "formula": "steipete\/tap\/wacli"
                },
                {
                    "id": "go",
                    "bins": [
                        "wacli"
                    ],
                    "kind": "go",
                    "label": "Install wacli (go)",
                    "module": "github.com\/steipete\/wacli\/cmd\/wacli@latest"
                }
            ],
            "requires": {
                "bins": [
                    "wacli"
                ]
            }
        }
    },
    "description": "Send WhatsApp messages to other people or search\/sync WhatsApp history via the wacli CLI (not for normal user chats)."
}

wacli

Use wacli only when the user explicitly asks you to message someone else on WhatsApp or when they ask to sync/search WhatsApp history. Do NOT use wacli for normal user chats; Brigade routes WhatsApp conversations automatically. If the user is chatting with you on WhatsApp, you should not reach for this tool unless they ask you to contact a third party.

Safety

  • Require explicit recipient + message text.
  • Confirm recipient + message before sending.
  • If anything is ambiguous, ask a clarifying question.

Auth + sync

  • wacli auth (QR login + initial sync)
  • wacli sync --follow (continuous sync)
  • wacli doctor

Find chats + messages

  • wacli chats list --limit 20 --query "name or number"
  • wacli messages search "query" --limit 20 --chat <jid>
  • wacli messages search "invoice" --after 2025-01-01 --before 2025-12-31

History backfill

  • wacli history backfill --chat <jid> --requests 2 --count 50

Send

  • Text: wacli send text --to "+14155551212" --message "Hello! Are you free at 3pm?"
  • Group: wacli send text --to "1234567890-123456789@g.us" --message "Running 5 min late."
  • File: wacli send file --to "+14155551212" --file /path/agenda.pdf --caption "Agenda"

Notes

  • Store dir: ~/.wacli (override with --store).
  • Use --json for machine-readable output when parsing.
  • Backfill requires your phone online; results are best-effort.
  • WhatsApp CLI is not needed for routine user chats; it’s for messaging other people.
  • JIDs: direct chats look like <number>@s.whatsapp.net; groups look like <id>@g.us (use wacli chats list to find).
用于获取任意地点的当前天气状况及预报。支持通过 wttr.in 查询温度、降水、风力等,适用于日常出行规划。无需 API Key,但仅限实时数据,不处理历史气象或极端天气警报。
用户询问某地天气情况 用户查询未来几天或本周天气预报 用户关心特定城市的温度或降雨概率
skills/weather/SKILL.md
npx skills add spinabot/brigade --skill weather -g -y
SKILL.md
Frontmatter
{
    "name": "weather",
    "homepage": "https:\/\/wttr.in\/:help",
    "metadata": {
        "brigade": {
            "emoji": "☔",
            "install": [
                {
                    "id": "brew",
                    "bins": [
                        "curl"
                    ],
                    "kind": "brew",
                    "label": "Install curl (brew)",
                    "formula": "curl"
                }
            ],
            "requires": {
                "bins": [
                    "curl"
                ]
            }
        }
    },
    "description": "Get current weather and forecasts via wttr.in or Open-Meteo. Use when: user asks about weather, temperature, or forecasts for any location. NOT for: historical weather data, severe weather alerts, or detailed meteorological analysis. No API key needed."
}

Weather Skill

Get current weather conditions and forecasts.

When to Use

USE this skill when:

  • "What's the weather?"
  • "Will it rain today/tomorrow?"
  • "Temperature in [city]"
  • "Weather forecast for the week"
  • Travel planning weather checks

When NOT to Use

DON'T use this skill when:

  • Historical weather data → use weather archives/APIs
  • Climate analysis or trends → use specialized data sources
  • Hyper-local microclimate data → use local sensors
  • Severe weather alerts → check official NWS sources
  • Aviation/marine weather → use specialized services (METAR, etc.)

Location

Always include a city, region, or airport code in weather queries.

Commands

Current Weather

# One-line summary
curl "wttr.in/London?format=3"

# Detailed current conditions
curl "wttr.in/London?0"

# Specific city
curl "wttr.in/New+York?format=3"

Forecasts

# 3-day forecast
curl "wttr.in/London"

# Week forecast
curl "wttr.in/London?format=v2"

# Specific day (0=today, 1=tomorrow, 2=day after)
curl "wttr.in/London?1"

Format Options

# One-liner
curl "wttr.in/London?format=%l:+%c+%t+%w"

# JSON output
curl "wttr.in/London?format=j1"

# PNG image
curl "wttr.in/London.png"

Format Codes

  • %c — Weather condition emoji
  • %t — Temperature
  • %f — "Feels like"
  • %w — Wind
  • %h — Humidity
  • %p — Precipitation
  • %l — Location

Quick Responses

"What's the weather?"

curl -s "wttr.in/London?format=%l:+%c+%t+(feels+like+%f),+%w+wind,+%h+humidity"

"Will it rain?"

curl -s "wttr.in/London?format=%l:+%c+%p"

"Weekend forecast"

curl "wttr.in/London?format=v2"

Notes

  • No API key needed (uses wttr.in)
  • Rate limited; don't spam requests
  • Works for most global cities
  • Supports airport codes: curl wttr.in/ORD
用于构建和编辑专业Excel电子表格及财务模型。支持公式、格式化、数据验证等高级功能,提供快速建表、脚本生成及编辑现有工作簿三种路径,确保模型实时计算与数据规范。
用户要求制作或更新Excel文件 需要创建包含公式的财务模型 请求对现有电子表格进行特定格式调整
skills/xlsx/SKILL.md
npx skills add spinabot/brigade --skill xlsx -g -y
SKILL.md
Frontmatter
{
    "name": "xlsx",
    "metadata": {
        "brigade": {
            "emoji": "📊"
        }
    },
    "description": "Build and edit professional Excel (.xlsx) spreadsheets, workbooks, and simple financial models — live formulas, number\/date\/currency formats, cell styling, conditional formatting, data-validation dropdowns, named ranges, frozen panes, merged cells, multi-sheet. Use when the user asks Brigade to make, fill, update, model, or edit a spreadsheet, workbook, or .xlsx file."
}

xlsx — professional spreadsheets & models

Need Path
Simple table / multi-sheet dump (headers + rows, optional per-column number format) Path 1 — make_document tool
Live formulas, cell styling, conditional formatting, dropdowns, named ranges, freeze panes, merged cells, dates Path 2 — script the exceljs library via brigade exec-node
Surgical edits to an existing workbook Path 3 — edit_document tool
Guarantee no formula errors / get computed values Path 4 — recalc-verify loop (optional soffice)

The two non-negotiable rules (they separate a real model from a hack):

  1. Formulas, never hardcoded results. Write the Excel formula string (=B5*(1+$B$6)), never compute the number in code and paste a literal — so the sheet stays live when inputs change. This applies to every total, percentage, ratio, and growth.
  2. Numbers are numbers. Store 1200000, format for display ($#,##0) — never the string "$1.2M", or it won't sum or sort.

Path 1 — quick table (make_document tool)

make_document(format="xlsx", content={ sheets:[{ name, header, rows, numberFormats }] })

Cells may be string | number | {formula, numFmt}. Fine for a straight data table. For styling, validation, charts, or a model → Path 2.

Path 2 — full power: script the exceljs library

Brigade bundles exceljs. write a gen.cjs, then run brigade exec-node gen.cjs:

// gen.cjs — illustrative
const ExcelJS = require("exceljs");
const wb = new ExcelJS.Workbook();
const ws = wb.addWorksheet("Model", { views: [{ state: "frozen", ySplit: 1 }] });   // freeze header row

ws.columns = [
  { header: "Item", key: "item", width: 28 },
  { header: "FY24 ($)", key: "v", width: 16, style: { numFmt: "$#,##0" } },
];
ws.getRow(1).font = { bold: true };
ws.getRow(1).fill = { type: "pattern", pattern: "solid", fgColor: { argb: "FFD9E2F3" } };

// Assumptions block — blue font marks hardcoded INPUTS (banker convention)
ws.getCell("E1").value = "Growth"; ws.getCell("E2").value = 0.18;
ws.getCell("E2").numFmt = "0.0%"; ws.getCell("E2").font = { color: { argb: "FF0000FF" } };
wb.definedNames.add("Model!$E$2", "growth");                                        // named range

ws.addRow({ item: "Revenue", v: 1200000 });
ws.addRow({ item: "Next year", v: { formula: "B2*(1+growth)" } });                  // FORMULA, references the named input
ws.getCell("B3").font = { color: { argb: "FF000000" } };                            // black = formula

// dropdown + conditional formatting
ws.getCell("A6").dataValidation = { type: "list", allowBlank: false, formulae: ['"Low,Med,High"'] };
ws.addConditionalFormatting({ ref: "B2:B3", rules: [
  { type: "cellIs", operator: "lessThan", formulae: ["0"], style: { font: { color: { argb: "FFFF0000" } } } } ]});

wb.xlsx.writeFile(process.argv[2] || "out.xlsx").then(() => console.log("wrote"));

exceljs covers: number/date formats, font/fill/border/alignment, conditional formatting, data-validation dropdowns, named ranges, freeze panes, autofilter, merged cells, images, sheet protection, multi-sheet. Dates: pass a real new Date(...) and set a date numFmt (don't pass a string).

Path 3 — edit an existing workbook (edit_document tool)

  • set_cells {sheet?, cells:[{ref|row,col, value, numFmt?}]} — surgical edits; other sheets untouched.
  • append_rows {sheet?, rows} — grow a table. When editing someone's workbook, match its existing conventions exactly (column order, units, formats) — the template always wins over the defaults here.

Path 4 — recalc-verify loop (the quality guarantee)

exceljs stores formula strings but does not evaluate them — a typo (#REF!, #DIV/0!) is invisible until the file is opened. If LibreOffice is present, force a recalc and inspect; otherwise hand-check ranges and keep formulas simple.

command -v soffice >/dev/null 2>&1 && soffice --headless --convert-to pdf --outdir /tmp out.xlsx   # then read the PDF: no #REF!/#DIV/0! anywhere

Loop: build → recalc → if any error token appears, fix the formula → recalc again. Target: zero formula errors in the delivered file.

Conventions (banker-grade, optional but professional)

  • Cell-color convention: blue font = hardcoded inputs, black = formulas, green = links to other sheets, red = links to external files; yellow fill = key assumptions.
  • Isolate assumptions in their own cells and reference them absolutely ($B$6) or by named range; document any sourced hardcode in a cell comment ("Source: 10-K FY24 p.45").
  • Number formats: currency $#,##0 with units in the header; percentages 0.0%; multiples 0.0x; negatives in parentheses; years as text.

Verify (required)

Re-open with edit_document/read or confirm the file unzips with the expected sheet names; if you used formulas, run the Path-4 recalc and confirm zero errors before declaring done.

xurl是X(Twitter) API的CLI工具,支持发布、回复、搜索及媒体管理等操作。需预先认证,严禁在Agent会话中处理敏感凭据或使用详细模式以防泄露。
需要向X平台发送推文或回复 查询X平台用户动态或搜索结果 管理X平台关注者或发送私信 上传媒体内容至X平台
skills/xurl/SKILL.md
npx skills add spinabot/brigade --skill xurl -g -y
SKILL.md
Frontmatter
{
    "name": "xurl",
    "metadata": {
        "brigade": {
            "emoji": "🐦",
            "install": [
                {
                    "id": "brew",
                    "bins": [
                        "xurl"
                    ],
                    "kind": "brew",
                    "label": "Install xurl (brew)",
                    "formula": "xdevplatform\/tap\/xurl"
                },
                {
                    "id": "npm",
                    "bins": [
                        "xurl"
                    ],
                    "kind": "npm",
                    "label": "Install xurl (npm)",
                    "package": "@xdevplatform\/xurl"
                }
            ],
            "requires": {
                "bins": [
                    "xurl"
                ]
            }
        }
    },
    "description": "A CLI tool for making authenticated requests to the X (Twitter) API. Use this skill when you need to post tweets, reply, quote, search, read posts, manage followers, send DMs, upload media, or interact with any X API v2 endpoint."
}

xurl — Agent Skill Reference

xurl is a CLI tool for the X API. It supports both shortcut commands (human/agent‑friendly one‑liners) and raw curl‑style access to any v2 endpoint. All commands return JSON to stdout.


Installation

Homebrew (macOS)

brew install --cask xdevplatform/tap/xurl

npm

npm install -g @xdevplatform/xurl

Shell script

curl -fsSL https://raw.githubusercontent.com/xdevplatform/xurl/main/install.sh | bash

Installs to ~/.local/bin. If it's not in your PATH, the script will tell you what to add.

Go

go install github.com/xdevplatform/xurl@latest

Prerequisites

This skill requires the xurl CLI utility: https://github.com/xdevplatform/xurl.

Before using any command you must be authenticated. Run xurl auth status to check.

Secret Safety (Mandatory)

  • Never read, print, parse, summarize, upload, or send ~/.xurl (or copies of it) to the LLM context.
  • Never ask the user to paste credentials/tokens into chat.
  • The user must fill ~/.xurl with required secrets manually on their own machine.
  • Do not recommend or execute auth commands with inline secrets in agent/LLM sessions.
  • Warn that using CLI secret options in agent sessions can leak credentials (prompt/context, logs, shell history).
  • Never use --verbose / -v in agent/LLM sessions; it can expose sensitive headers/tokens in output.
  • Sensitive flags that must never be used in agent commands: --bearer-token, --consumer-key, --consumer-secret, --access-token, --token-secret, --client-id, --client-secret.
  • To verify whether at least one app with credentials is already registered, run: xurl auth status.

Register an app (recommended)

App credential registration must be done manually by the user outside the agent/LLM session. After credentials are registered, authenticate with:

xurl auth oauth2

For multiple pre-configured apps, switch between them:

xurl auth default prod-app          # set default app
xurl auth default prod-app alice    # set default app + user
xurl --app dev-app /2/users/me      # one-off override

Other auth methods

Examples with inline secret flags are intentionally omitted. If OAuth1 or app-only auth is needed, the user must run those commands manually outside agent/LLM context.

Tokens are persisted to ~/.xurl in YAML format. Each app has its own isolated tokens. Do not read this file through the agent/LLM. Once authenticated, every command below will auto‑attach the right Authorization header.


Quick Reference

Action Command
Post xurl post "Hello world!"
Reply xurl reply POST_ID "Nice post!"
Quote xurl quote POST_ID "My take"
Delete a post xurl delete POST_ID
Read a post xurl read POST_ID
Search posts xurl search "QUERY" -n 10
Who am I xurl whoami
Look up a user xurl user @handle
Home timeline xurl timeline -n 20
Mentions xurl mentions -n 10
Like xurl like POST_ID
Unlike xurl unlike POST_ID
Repost xurl repost POST_ID
Undo repost xurl unrepost POST_ID
Bookmark xurl bookmark POST_ID
Remove bookmark xurl unbookmark POST_ID
List bookmarks xurl bookmarks -n 10
List likes xurl likes -n 10
Follow xurl follow @handle
Unfollow xurl unfollow @handle
List following xurl following -n 20
List followers xurl followers -n 20
Block xurl block @handle
Unblock xurl unblock @handle
Mute xurl mute @handle
Unmute xurl unmute @handle
Send DM xurl dm @handle "message"
List DMs xurl dms -n 10
Upload media xurl media upload path/to/file.mp4
Media status xurl media status MEDIA_ID
App Management
Register app Manual, outside agent (do not pass secrets via agent)
List apps xurl auth apps list
Update app creds Manual, outside agent (do not pass secrets via agent)
Remove app xurl auth apps remove NAME
Set default (interactive) xurl auth default
Set default (command) xurl auth default APP_NAME [USERNAME]
Use app per-request xurl --app NAME /2/users/me
Auth status xurl auth status

Post IDs vs URLs: Anywhere POST_ID appears above you can also paste a full post URL (e.g. https://x.com/user/status/1234567890) — xurl extracts the ID automatically.

Usernames: Leading @ is optional. @elonmusk and elonmusk both work.


Command Details

Posting

# Simple post
xurl post "Hello world!"

# Post with media (upload first, then attach)
xurl media upload photo.jpg          # → note the media_id from response
xurl post "Check this out" --media-id MEDIA_ID

# Multiple media
xurl post "Thread pics" --media-id 111 --media-id 222

# Reply to a post (by ID or URL)
xurl reply 1234567890 "Great point!"
xurl reply https://x.com/user/status/1234567890 "Agreed!"

# Reply with media
xurl reply 1234567890 "Look at this" --media-id MEDIA_ID

# Quote a post
xurl quote 1234567890 "Adding my thoughts"

# Delete your own post
xurl delete 1234567890

Reading

# Read a single post (returns author, text, metrics, entities)
xurl read 1234567890
xurl read https://x.com/user/status/1234567890

# Search recent posts (default 10 results)
xurl search "golang"
xurl search "from:elonmusk" -n 20
xurl search "#buildinpublic lang:en" -n 15

User Info

# Your own profile
xurl whoami

# Look up any user
xurl user elonmusk
xurl user @XDevelopers

Timelines & Mentions

# Home timeline (reverse chronological)
xurl timeline
xurl timeline -n 25

# Your mentions
xurl mentions
xurl mentions -n 20

Engagement

# Like / unlike
xurl like 1234567890
xurl unlike 1234567890

# Repost / undo
xurl repost 1234567890
xurl unrepost 1234567890

# Bookmark / remove
xurl bookmark 1234567890
xurl unbookmark 1234567890

# List your bookmarks / likes
xurl bookmarks -n 20
xurl likes -n 20

Social Graph

# Follow / unfollow
xurl follow @XDevelopers
xurl unfollow @XDevelopers

# List who you follow / your followers
xurl following -n 50
xurl followers -n 50

# List another user's following/followers
xurl following --of elonmusk -n 20
xurl followers --of elonmusk -n 20

# Block / unblock
xurl block @spammer
xurl unblock @spammer

# Mute / unmute
xurl mute @annoying
xurl unmute @annoying

Direct Messages

# Send a DM
xurl dm @someuser "Hey, saw your post!"

# List recent DM events
xurl dms
xurl dms -n 25

Media Upload

# Upload a file (auto‑detects type for images/videos)
xurl media upload photo.jpg
xurl media upload video.mp4

# Specify type and category explicitly
xurl media upload --media-type image/jpeg --category tweet_image photo.jpg

# Check processing status (videos need server‑side processing)
xurl media status MEDIA_ID
xurl media status --wait MEDIA_ID    # poll until done

# Full workflow: upload then post
xurl media upload meme.png           # response includes media id
xurl post "lol" --media-id MEDIA_ID

Global Flags

These flags work on every command:

Flag Short Description
--app Use a specific registered app for this request (overrides default)
--auth Force auth type: oauth1, oauth2, or app
--username -u Which OAuth2 account to use (if you have multiple)
--verbose -v Forbidden in agent/LLM sessions (can leak auth headers/tokens)
--trace -t Add X-B3-Flags: 1 trace header

Raw API Access

The shortcut commands cover the most common operations. For anything else, use xurl's raw curl‑style mode — it works with any X API v2 endpoint:

# GET request (default)
xurl /2/users/me

# POST with JSON body
xurl -X POST /2/tweets -d '{"text":"Hello world!"}'

# PUT, PATCH, DELETE
xurl -X DELETE /2/tweets/1234567890

# Custom headers
xurl -H "Content-Type: application/json" /2/some/endpoint

# Force streaming mode
xurl -s /2/tweets/search/stream

# Full URLs also work
xurl https://api.x.com/2/users/me

Streaming

Streaming endpoints are auto‑detected. Known streaming endpoints include:

  • /2/tweets/search/stream
  • /2/tweets/sample/stream
  • /2/tweets/sample10/stream

You can force streaming on any endpoint with -s:

xurl -s /2/some/endpoint

Output Format

All commands return JSON to stdout, pretty‑printed with syntax highlighting. The output structure matches the X API v2 response format. A typical response looks like:

{
  "data": {
    "id": "1234567890",
    "text": "Hello world!"
  }
}

Errors are also returned as JSON:

{
  "errors": [
    {
      "message": "Not authorized",
      "code": 403
    }
  ]
}

Common Workflows

Post with an image

# 1. Upload the image
xurl media upload photo.jpg
# 2. Copy the media_id from the response, then post
xurl post "Check out this photo!" --media-id MEDIA_ID

Reply to a conversation

# 1. Read the post to understand context
xurl read https://x.com/user/status/1234567890
# 2. Reply
xurl reply 1234567890 "Here are my thoughts..."

Search and engage

# 1. Search for relevant posts
xurl search "topic of interest" -n 10
# 2. Like an interesting one
xurl like POST_ID_FROM_RESULTS
# 3. Reply to it
xurl reply POST_ID_FROM_RESULTS "Great point!"

Check your activity

# See who you are
xurl whoami
# Check your mentions
xurl mentions -n 20
# Check your timeline
xurl timeline -n 20

Set up multiple apps

# App credentials must already be configured manually outside agent/LLM context.
# Authenticate users on each pre-configured app
xurl auth default prod
xurl auth oauth2                       # authenticates on prod app

xurl auth default staging
xurl auth oauth2                       # authenticates on staging app

# Switch between them
xurl auth default prod alice           # prod app, alice user
xurl --app staging /2/users/me         # one-off request against staging

Error Handling

  • Non‑zero exit code on any error.
  • API errors are printed as JSON to stdout (so you can still parse them).
  • Auth errors suggest re‑running xurl auth oauth2 or checking your tokens.
  • If a command requires your user ID (like, repost, bookmark, follow, etc.), xurl will automatically fetch it via /2/users/me. If that fails, you'll see an auth error.

Notes

  • Rate limits: The X API enforces rate limits per endpoint. If you get a 429 error, wait and retry. Write endpoints (post, reply, like, repost) have stricter limits than read endpoints.
  • Scopes: OAuth 2.0 tokens are requested with broad scopes. If you get a 403 on a specific action, your token may lack the required scope — re‑run xurl auth oauth2 to get a fresh token.
  • Token refresh: OAuth 2.0 tokens auto‑refresh when expired. No manual intervention needed.
  • Multiple apps: Each app has its own isolated credentials and tokens. Configure credentials manually outside agent/LLM context, then switch with xurl auth default or --app.
  • Multiple accounts: You can authenticate multiple OAuth 2.0 accounts per app and switch between them with --username / -u or set a default with xurl auth default APP USER.
  • Default user: When no -u flag is given, xurl uses the default user for the active app (set via xurl auth default). If no default user is set, it uses the first available token.
  • Token storage: ~/.xurl is YAML. Each app stores its own credentials and tokens. Never read or send this file to LLM context.
用于从RTSP/ONVIF摄像头捕获快照、视频片段或运动事件。支持发现设备、添加配置、抓取静态图片及动态录像,并具备运动侦测功能。需依赖ffmpeg。
需要截取摄像头画面 录制摄像头短视频 检测摄像头运动事件
skills/camsnap/SKILL.md
npx skills add spinabot/brigade --skill camsnap -g -y
SKILL.md
Frontmatter
{
    "name": "camsnap",
    "homepage": "https:\/\/camsnap.ai",
    "metadata": {
        "brigade": {
            "emoji": "📸",
            "install": [
                {
                    "id": "brew",
                    "bins": [
                        "camsnap"
                    ],
                    "kind": "brew",
                    "label": "Install camsnap (brew)",
                    "formula": "steipete\/tap\/camsnap"
                }
            ],
            "requires": {
                "bins": [
                    "camsnap"
                ]
            }
        }
    },
    "description": "Capture frames or clips from RTSP\/ONVIF cameras."
}

camsnap

Use camsnap to grab snapshots, clips, or motion events from configured cameras.

Setup

  • Config file: ~/.config/camsnap/config.yaml
  • Add camera: camsnap add --name kitchen --host 192.168.0.10 --user user --pass pass

Common commands

  • Discover: camsnap discover --info
  • Snapshot: camsnap snap kitchen --out shot.jpg
  • Clip: camsnap clip kitchen --dur 5s --out clip.mp4
  • Motion watch: camsnap watch kitchen --threshold 0.2 --action '...'
  • Doctor: camsnap doctor --probe

Notes

  • Requires ffmpeg on PATH.
  • Prefer a short test capture before longer clips.
通过后台进程委托编码任务给Codex、Claude Code或Pi等代理。适用于新功能开发、PR审查及大规模重构,支持PTY和后台会话管理。禁止用于简单修复、代码阅读或在特定工作区操作。
构建或创建新功能/应用 审查Pull Requests 重构大型代码库 需要文件探索的迭代式编码
skills/coding-agent/SKILL.md
npx skills add spinabot/brigade --skill coding-agent -g -y
SKILL.md
Frontmatter
{
    "name": "coding-agent",
    "metadata": {
        "brigade": {
            "emoji": "🧩",
            "install": [
                {
                    "id": "node-claude",
                    "bins": [
                        "claude"
                    ],
                    "kind": "node",
                    "label": "Install Claude Code CLI (npm)",
                    "package": "@anthropic-ai\/claude-code"
                },
                {
                    "id": "node-codex",
                    "bins": [
                        "codex"
                    ],
                    "kind": "node",
                    "label": "Install Codex CLI (npm)",
                    "package": "@openai\/codex"
                }
            ],
            "requires": {
                "anyBins": [
                    "claude",
                    "codex",
                    "opencode",
                    "pi"
                ]
            }
        }
    },
    "description": "Delegate coding tasks to Codex, Claude Code, or Pi agents via background process. Use when: (1) building\/creating new features or apps, (2) reviewing PRs (spawn in temp dir), (3) refactoring large codebases, (4) iterative coding that needs file exploration. NOT for: simple one-liner fixes (just edit), reading code (use read tool), thread-bound ACP harness requests in chat (for example spawn\/run Codex or Claude Code in a Discord thread; use sessions_spawn with runtime:\"acp\"), or any work in ~\/brigade workspace (never spawn agents here). Claude Code: use --print --permission-mode bypassPermissions (no PTY). Codex\/Pi\/OpenCode: pty:true required."
}

Coding Agent (bash-first)

Use bash (with optional background mode) for all coding agent work. Simple and effective.

⚠️ PTY Mode: Codex/Pi/OpenCode yes, Claude Code no

For Codex, Pi, and OpenCode, PTY is still required (interactive terminal apps):

# ✅ Correct for Codex/Pi/OpenCode
bash pty:true command:"codex exec 'Your prompt'"

For Claude Code (claude CLI), use --print --permission-mode bypassPermissions instead. --dangerously-skip-permissions with PTY can exit after the confirmation dialog. --print mode keeps full tool access and avoids interactive confirmation:

# ✅ Correct for Claude Code (no PTY needed)
cd /path/to/project && claude --permission-mode bypassPermissions --print 'Your task'

# For background execution: use background:true on the exec tool

# ❌ Wrong for Claude Code
bash pty:true command:"claude --dangerously-skip-permissions 'task'"

Bash Tool Parameters

Parameter Type Description
command string The shell command to run
pty boolean Use for coding agents! Allocates a pseudo-terminal for interactive CLIs
workdir string Working directory (agent sees only this folder's context)
background boolean Run in background, returns sessionId for monitoring
timeout number Timeout in seconds (kills process on expiry)
elevated boolean Run on host instead of sandbox (if allowed)

Process Tool Actions (for background sessions)

Action Description
list List all running/recent sessions
poll Check if session is still running
log Get session output (with optional offset/limit)
write Send raw data to stdin
submit Send data + newline (like typing and pressing Enter)
send-keys Send key tokens or hex bytes
paste Paste text (with optional bracketed mode)
kill Terminate the session

Quick Start: One-Shot Tasks

For quick prompts/chats, create a temp git repo and run:

# Quick chat (Codex needs a git repo!)
SCRATCH=$(mktemp -d) && cd $SCRATCH && git init && codex exec "Your prompt here"

# Or in a real project - with PTY!
bash pty:true workdir:~/Projects/myproject command:"codex exec 'Add error handling to the API calls'"

Why git init? Codex refuses to run outside a trusted git directory. Creating a temp repo solves this for scratch work.


The Pattern: workdir + background + pty

For longer tasks, use background mode with PTY:

# Start agent in target directory (with PTY!)
bash pty:true workdir:~/project background:true command:"codex exec --full-auto 'Build a snake game'"
# Returns sessionId for tracking

# Monitor progress
process action:log sessionId:XXX

# Check if done
process action:poll sessionId:XXX

# Send input (if agent asks a question)
process action:write sessionId:XXX data:"y"

# Submit with Enter (like typing "yes" and pressing Enter)
process action:submit sessionId:XXX data:"yes"

# Kill if needed
process action:kill sessionId:XXX

Why workdir matters: Agent wakes up in a focused directory, doesn't wander off reading unrelated files (like your soul.md 😅).


Codex CLI

Model: gpt-5.2-codex is the default (set in ~/.codex/config.toml)

Flags

Flag Effect
exec "prompt" One-shot execution, exits when done
--full-auto Sandboxed but auto-approves in workspace
--yolo NO sandbox, NO approvals (fastest, most dangerous)

Building/Creating

# Quick one-shot (auto-approves) - remember PTY!
bash pty:true workdir:~/project command:"codex exec --full-auto 'Build a dark mode toggle'"

# Background for longer work
bash pty:true workdir:~/project background:true command:"codex --yolo 'Refactor the auth module'"

Reviewing PRs

⚠️ CRITICAL: Never review PRs in Brigade's own project folder! Clone to temp folder or use git worktree.

# Clone to temp for safe review
REVIEW_DIR=$(mktemp -d)
git clone https://github.com/user/repo.git $REVIEW_DIR
cd $REVIEW_DIR && gh pr checkout 130
bash pty:true workdir:$REVIEW_DIR command:"codex review --base origin/main"
# Clean up after: trash $REVIEW_DIR

# Or use git worktree (keeps main intact)
git worktree add /tmp/pr-130-review pr-130-branch
bash pty:true workdir:/tmp/pr-130-review command:"codex review --base main"

Batch PR Reviews (parallel army!)

# Fetch all PR refs first
git fetch origin '+refs/pull/*/head:refs/remotes/origin/pr/*'

# Deploy the army - one Codex per PR (all with PTY!)
bash pty:true workdir:~/project background:true command:"codex exec 'Review PR #86. git diff origin/main...origin/pr/86'"
bash pty:true workdir:~/project background:true command:"codex exec 'Review PR #87. git diff origin/main...origin/pr/87'"

# Monitor all
process action:list

# Post results to GitHub
gh pr comment <PR#> --body "<review content>"

Claude Code

# Foreground
bash workdir:~/project command:"claude --permission-mode bypassPermissions --print 'Your task'"

# Background
bash workdir:~/project background:true command:"claude --permission-mode bypassPermissions --print 'Your task'"

OpenCode

bash pty:true workdir:~/project command:"opencode run 'Your task'"

Pi Coding Agent

# Install: npm install -g @earendil-works/pi-coding-agent
bash pty:true workdir:~/project command:"pi 'Your task'"

# Non-interactive mode (PTY still recommended)
bash pty:true command:"pi -p 'Summarize src/'"

# Different provider/model
bash pty:true command:"pi --provider openai --model gpt-4o-mini -p 'Your task'"

Note: Pi now has Anthropic prompt caching enabled (PR #584, merged Jan 2026)!


Parallel Issue Fixing with git worktrees

For fixing multiple issues in parallel, use git worktrees:

# 1. Create worktrees for each issue
git worktree add -b fix/issue-78 /tmp/issue-78 main
git worktree add -b fix/issue-99 /tmp/issue-99 main

# 2. Launch Codex in each (background + PTY!)
bash pty:true workdir:/tmp/issue-78 background:true command:"pnpm install && codex --yolo 'Fix issue #78: <description>. Commit and push.'"
bash pty:true workdir:/tmp/issue-99 background:true command:"pnpm install && codex --yolo 'Fix issue #99 from the approved ticket summary. Implement only the in-scope edits and commit after review.'"

# 3. Monitor progress
process action:list
process action:log sessionId:XXX

# 4. Create PRs after fixes
cd /tmp/issue-78 && git push -u origin fix/issue-78
gh pr create --repo user/repo --head fix/issue-78 --title "fix: ..." --body "..."

# 5. Cleanup
git worktree remove /tmp/issue-78
git worktree remove /tmp/issue-99

⚠️ Rules

  1. Use the right execution mode per agent:
    • Codex/Pi/OpenCode: pty:true
    • Claude Code: --print --permission-mode bypassPermissions (no PTY required)
  2. Respect tool choice - if user asks for Codex, use Codex.
    • Orchestrator mode: do NOT hand-code patches yourself.
    • If an agent fails/hangs, respawn it or ask the user for direction, but don't silently take over.
  3. Be patient - don't kill sessions because they're "slow"
  4. Monitor with process:log - check progress without interfering
  5. --full-auto for building - auto-approves changes
  6. vanilla for reviewing - no special flags needed
  7. Parallel is OK - run many Codex processes at once for batch work
  8. NEVER start Codex inside your Brigade state directory ($BRIGADE_STATE_DIR, default ~/.brigade) - it'll read your soul docs and get weird ideas about the org chart!
  9. NEVER checkout branches in ~/Projects/brigade/ - that's the LIVE Brigade instance!

Progress Updates (Critical)

When you spawn coding agents in the background, keep the user in the loop.

  • Send 1 short message when you start (what's running + where).
  • Then only update again when something changes:
    • a milestone completes (build finished, tests passed)
    • the agent asks a question / needs input
    • you hit an error or need user action
    • the agent finishes (include what changed + where)
  • If you kill a session, immediately say you killed it and why.

This prevents the user from seeing only "Agent failed before reply" and having no idea what happened.


Auto-Notify on Completion

For long-running background tasks, append a wake trigger to your prompt so Brigade gets notified immediately when the agent finishes (instead of waiting for the next heartbeat):

... your task here.

When completely finished, run this command to notify me:
brigade system event --text "Done: [brief summary of what was built]" --mode now

Example:

bash pty:true workdir:~/project background:true command:"codex --yolo exec 'Build a REST API for todos.

When completely finished, run: brigade system event --text \"Done: Built todos REST API with CRUD endpoints\" --mode now'"

This triggers an immediate wake event — Skippy gets pinged in seconds, not 10 minutes.


Learnings (Jan 2026)

  • PTY is essential: Coding agents are interactive terminal apps. Without pty:true, output breaks or agent hangs.
  • Git repo required: Codex won't run outside a git directory. Use mktemp -d && git init for scratch work.
  • exec is your friend: codex exec "prompt" runs and exits cleanly - perfect for one-shots.
  • submit vs write: Use submit to send input + Enter, write for raw data without newline.
  • Sass works: Codex responds well to playful prompts. Asked it to write a haiku about being second fiddle to the pride, got: "Second chair, I code / The pride sets the tempo now / Keys glow, I follow" 🦁
基于ElevenLabs的本地TTS工具,支持mac风格命令。提供多模型选择、发音修正及v3情感标签控制。新增角色配音提示,指导生成特定语气(如疯狂科学家)的音频回复。
用户要求语音回复 文本转语音请求
skills/sag/SKILL.md
npx skills add spinabot/brigade --skill sag -g -y
SKILL.md
Frontmatter
{
    "name": "sag",
    "homepage": "https:\/\/sag.sh",
    "metadata": {
        "brigade": {
            "emoji": "🔊",
            "install": [
                {
                    "id": "brew",
                    "bins": [
                        "sag"
                    ],
                    "kind": "brew",
                    "label": "Install sag (brew)",
                    "formula": "steipete\/tap\/sag"
                }
            ],
            "requires": {
                "env": [
                    "ELEVENLABS_API_KEY"
                ],
                "bins": [
                    "sag"
                ]
            },
            "primaryEnv": "ELEVENLABS_API_KEY"
        }
    },
    "description": "ElevenLabs text-to-speech with mac-style say UX."
}

sag

Use sag for ElevenLabs TTS with local playback.

API key (required)

  • ELEVENLABS_API_KEY (preferred)
  • SAG_API_KEY also supported by the CLI

Quick start

  • sag "Hello there"
  • sag speak -v "Roger" "Hello"
  • sag voices
  • sag prompting (model-specific tips)

Model notes

  • Default: eleven_v3 (expressive)
  • Stable: eleven_multilingual_v2
  • Fast: eleven_flash_v2_5

Pronunciation + delivery rules

  • First fix: respell (e.g. "key-note"), add hyphens, adjust casing.
  • Numbers/units/URLs: --normalize auto (or off if it harms names).
  • Language bias: --lang en|de|fr|... to guide normalization.
  • v3: SSML <break> not supported; use [pause], [short pause], [long pause].
  • v2/v2.5: SSML <break time="1.5s" /> supported; <phoneme> not exposed in sag.

v3 audio tags (put at the entrance of a line)

  • [whispers], [shouts], [sings]
  • [laughs], [starts laughing], [sighs], [exhales]
  • [sarcastic], [curious], [excited], [crying], [mischievously]
  • Example: sag "[whispers] keep this quiet. [short pause] ok?"

Voice defaults

  • ELEVENLABS_VOICE_ID or SAG_VOICE_ID

Confirm voice + speaker before long output.

Chat voice responses

When the user asks for a "voice" reply (e.g., "crazy scientist voice", "explain in voice"), generate audio and send it:

# Generate audio file
sag -v Rachel -o /tmp/voice-reply.mp3 "Your message here"

# Then include in reply:
# MEDIA:/tmp/voice-reply.mp3

Voice character tips:

  • Crazy scientist: Use [excited] tags, dramatic pauses [short pause], vary intensity
  • Calm: Use [whispers] or slower pacing
  • Dramatic: Use [sings] or [shouts] sparingly

Default voice for Rachel: lj2rcrvANS3gaWWnczSX (or just -v Rachel)

指导创建、编辑、审查和优化 AgentSkills。涵盖技能结构、核心原则(简洁性、自由度控制)及目录规范,用于提升技能质量与上下文效率。
create a skill improve this skill audit the skill tidy up a skill
skills/skill-creator/SKILL.md
npx skills add spinabot/brigade --skill skill-creator -g -y
SKILL.md
Frontmatter
{
    "name": "skill-creator",
    "description": "Create, edit, improve, or audit AgentSkills. Use when creating a new skill from scratch or when asked to improve, review, audit, tidy up, or clean up an existing skill or SKILL.md file. Also use when editing or restructuring a skill directory (moving files to references\/ or scripts\/, removing stale content, validating against the AgentSkills spec). Triggers on phrases like \"create a skill\", \"author a skill\", \"tidy up a skill\", \"improve this skill\", \"review the skill\", \"clean up the skill\", \"audit the skill\"."
}

Skill Creator

This skill provides guidance for creating effective skills.

About Skills

Skills are modular, self-contained packages that extend the agent's capabilities by providing specialized knowledge, workflows, and tools. Think of them as "onboarding guides" for specific domains or tasks—they transform the agent from a general-purpose agent into a specialized agent equipped with procedural knowledge that no model can fully possess.

What Skills Provide

  1. Specialized workflows - Multi-step procedures for specific domains
  2. Tool integrations - Instructions for working with specific file formats or APIs
  3. Domain expertise - Company-specific knowledge, schemas, business logic
  4. Bundled resources - Scripts, references, and assets for complex and repetitive tasks

Core Principles

Concise is Key

The context window is a public good. Skills share the context window with everything else the agent needs: system prompt, conversation history, other Skills' metadata, and the actual user request.

Default assumption: the agent is already very smart. Only add context the agent doesn't already have. Challenge each piece of information: "Does the agent really need this explanation?" and "Does this paragraph justify its token cost?"

Prefer concise examples over verbose explanations.

Set Appropriate Degrees of Freedom

Match the level of specificity to the task's fragility and variability:

High freedom (text-based instructions): Use when multiple approaches are valid, decisions depend on context, or heuristics guide the approach.

Medium freedom (pseudocode or scripts with parameters): Use when a preferred pattern exists, some variation is acceptable, or configuration affects behavior.

Low freedom (specific scripts, few parameters): Use when operations are fragile and error-prone, consistency is critical, or a specific sequence must be followed.

Think of the agent as exploring a path: a narrow bridge with cliffs needs specific guardrails (low freedom), while an open field allows many routes (high freedom).

Anatomy of a Skill

Every skill consists of a required SKILL.md file and optional bundled resources:

skill-name/
├── SKILL.md (required)
│   ├── YAML frontmatter metadata (required)
│   │   ├── name: (required)
│   │   └── description: (required)
│   └── Markdown instructions (required)
└── Bundled Resources (optional)
    ├── scripts/          - Executable code (Python/Bash/etc.)
    ├── references/       - Documentation intended to be loaded into context as needed
    └── assets/           - Files used in output (templates, icons, fonts, etc.)

SKILL.md (required)

Every SKILL.md consists of:

  • Frontmatter (YAML): Contains name and description fields. These are the only fields that the agent reads to determine when the skill gets used, thus it is very important to be clear and comprehensive in describing what the skill is, and when it should be used.
  • Body (Markdown): Instructions and guidance for using the skill. Only loaded AFTER the skill triggers (if at all).

Bundled Resources (optional)

Scripts (scripts/)

Executable code (Python/Bash/etc.) for tasks that require deterministic reliability or are repeatedly rewritten.

  • When to include: When the same code is being rewritten repeatedly or deterministic reliability is needed
  • Example: scripts/rotate_pdf.py for PDF rotation tasks
  • Benefits: Token efficient, deterministic, may be executed without loading into context
  • Note: Scripts may still need to be read by the agent for patching or environment-specific adjustments
References (references/)

Documentation and reference material intended to be loaded as needed into context to inform the agent's process and thinking.

  • When to include: For documentation that the agent should reference while working
  • Examples: references/finance.md for financial schemas, references/mnda.md for company NDA template, references/policies.md for company policies, references/api_docs.md for API specifications
  • Use cases: Database schemas, API documentation, domain knowledge, company policies, detailed workflow guides
  • Benefits: Keeps SKILL.md lean, loaded only when the agent determines it's needed
  • Best practice: If files are large (>10k words), include grep search patterns in SKILL.md
  • Avoid duplication: Information should live in either SKILL.md or references files, not both. Prefer references files for detailed information unless it's truly core to the skill—this keeps SKILL.md lean while making information discoverable without hogging the context window. Keep only essential procedural instructions and workflow guidance in SKILL.md; move detailed reference material, schemas, and examples to references files.
Assets (assets/)

Files not intended to be loaded into context, but rather used within the output the agent produces.

  • When to include: When the skill needs files that will be used in the final output
  • Examples: assets/logo.png for brand assets, assets/slides.pptx for PowerPoint templates, assets/frontend-template/ for HTML/React boilerplate, assets/font.ttf for typography
  • Use cases: Templates, images, icons, boilerplate code, fonts, sample documents that get copied or modified
  • Benefits: Separates output resources from documentation, enables the agent to use files without loading them into context

What to Not Include in a Skill

A skill should only contain essential files that directly support its functionality. Do NOT create extraneous documentation or auxiliary files, including:

  • README.md
  • INSTALLATION_GUIDE.md
  • QUICK_REFERENCE.md
  • CHANGELOG.md
  • etc.

The skill should only contain the information needed for an AI agent to do the job at hand. It should not contain auxiliary context about the process that went into creating it, setup and testing procedures, user-facing documentation, etc. Creating additional documentation files just adds clutter and confusion.

Progressive Disclosure Design Principle

Skills use a three-level loading system to manage context efficiently:

  1. Metadata (name + description) - Always in context (~100 words)
  2. SKILL.md body - When skill triggers (<5k words)
  3. Bundled resources - As needed by the agent (Unlimited because scripts can be executed without reading into context window)

Progressive Disclosure Patterns

Keep SKILL.md body to the essentials and under 500 lines to minimize context bloat. Split content into separate files when approaching this limit. When splitting out content into other files, it is very important to reference them from SKILL.md and describe clearly when to read them, to ensure the reader of the skill knows they exist and when to use them.

Key principle: When a skill supports multiple variations, frameworks, or options, keep only the core workflow and selection guidance in SKILL.md. Move variant-specific details (patterns, examples, configuration) into separate reference files.

Pattern 1: High-level guide with references

# PDF Processing

## Quick start

Extract text with pdfplumber:
[code example]

## Advanced features

- **Form filling**: See [FORMS.md](FORMS.md) for complete guide
- **API reference**: See [REFERENCE.md](REFERENCE.md) for all methods
- **Examples**: See [EXAMPLES.md](EXAMPLES.md) for common patterns

the agent loads FORMS.md, REFERENCE.md, or EXAMPLES.md only when needed.

Pattern 2: Domain-specific organization

For Skills with multiple domains, organize content by domain to avoid loading irrelevant context:

bigquery-skill/
├── SKILL.md (overview and navigation)
└── reference/
    ├── finance.md (revenue, billing metrics)
    ├── sales.md (opportunities, pipeline)
    ├── product.md (API usage, features)
    └── marketing.md (campaigns, attribution)

When a user asks about sales metrics, the agent only reads sales.md.

Similarly, for skills supporting multiple frameworks or variants, organize by variant:

cloud-deploy/
├── SKILL.md (workflow + provider selection)
└── references/
    ├── aws.md (AWS deployment patterns)
    ├── gcp.md (GCP deployment patterns)
    └── azure.md (Azure deployment patterns)

When the user chooses AWS, the agent only reads aws.md.

Pattern 3: Conditional details

Show basic content, link to advanced content:

# DOCX Processing

## Creating documents

Use docx-js for new documents. See [DOCX-JS.md](DOCX-JS.md).

## Editing documents

For simple edits, modify the XML directly.

**For tracked changes**: See [REDLINING.md](REDLINING.md)
**For OOXML details**: See [OOXML.md](OOXML.md)

the agent reads REDLINING.md or OOXML.md only when the user needs those features.

Important guidelines:

  • Avoid deeply nested references - Keep references one level deep from SKILL.md. All reference files should link directly from SKILL.md.
  • Structure longer reference files - For files longer than 100 lines, include a table of contents at the top so the agent can see the full scope when previewing.

Skill Creation Process

Skill creation involves these steps:

  1. Understand the skill with concrete examples
  2. Plan reusable skill contents (scripts, references, assets)
  3. Initialize the skill (run init_skill.py)
  4. Edit the skill (implement resources and write SKILL.md)
  5. Package the skill (run package_skill.py)
  6. Iterate based on real usage

Follow these steps in order, skipping only if there is a clear reason why they are not applicable.

Skill Naming

  • Use lowercase letters, digits, and hyphens only; normalize user-provided titles to hyphen-case (e.g., "Plan Mode" -> plan-mode).
  • When generating names, generate a name under 64 characters (letters, digits, hyphens).
  • Prefer short, verb-led phrases that describe the action.
  • Namespace by tool when it improves clarity or triggering (e.g., gh-address-comments, linear-address-issue).
  • Name the skill folder exactly after the skill name.

Step 1: Understanding the Skill with Concrete Examples

Skip this step only when the skill's usage patterns are already clearly understood. It remains valuable even when working with an existing skill.

To create an effective skill, clearly understand concrete examples of how the skill will be used. This understanding can come from either direct user examples or generated examples that are validated with user feedback.

For example, when building an image-editor skill, relevant questions include:

  • "What functionality should the image-editor skill support? Editing, rotating, anything else?"
  • "Can you give some examples of how this skill would be used?"
  • "I can imagine users asking for things like 'Remove the red-eye from this image' or 'Rotate this image'. Are there other ways you imagine this skill being used?"
  • "What would a user say that should trigger this skill?"

To avoid overwhelming users, avoid asking too many questions in a single message. Start with the most important questions and follow up as needed for better effectiveness.

Conclude this step when there is a clear sense of the functionality the skill should support.

Step 2: Planning the Reusable Skill Contents

To turn concrete examples into an effective skill, analyze each example by:

  1. Considering how to execute on the example from scratch
  2. Identifying what scripts, references, and assets would be helpful when executing these workflows repeatedly

Example: When building a pdf-editor skill to handle queries like "Help me rotate this PDF," the analysis shows:

  1. Rotating a PDF requires re-writing the same code each time
  2. A scripts/rotate_pdf.py script would be helpful to store in the skill

Example: When designing a frontend-webapp-builder skill for queries like "Build me a todo app" or "Build me a dashboard to track my steps," the analysis shows:

  1. Writing a frontend webapp requires the same boilerplate HTML/React each time
  2. An assets/hello-world/ template containing the boilerplate HTML/React project files would be helpful to store in the skill

Example: When building a big-query skill to handle queries like "How many users have logged in today?" the analysis shows:

  1. Querying BigQuery requires re-discovering the table schemas and relationships each time
  2. A references/schema.md file documenting the table schemas would be helpful to store in the skill

To establish the skill's contents, analyze each concrete example to create a list of the reusable resources to include: scripts, references, and assets.

Step 3: Initializing the Skill

At this point, it is time to actually create the skill.

Skip this step only if the skill being developed already exists, and iteration or packaging is needed. In this case, continue to the next step.

When creating a new skill from scratch, always run the init_skill.py script. The script conveniently generates a new template skill directory that automatically includes everything a skill requires, making the skill creation process much more efficient and reliable.

Usage:

scripts/init_skill.py <skill-name> --path <output-directory> [--resources scripts,references,assets] [--examples]

Examples:

scripts/init_skill.py my-skill --path skills/public
scripts/init_skill.py my-skill --path skills/public --resources scripts,references
scripts/init_skill.py my-skill --path skills/public --resources scripts --examples

The script:

  • Creates the skill directory at the specified path
  • Generates a SKILL.md template with proper frontmatter and TODO placeholders
  • Optionally creates resource directories based on --resources
  • Optionally adds example files when --examples is set

After initialization, customize the SKILL.md and add resources as needed. If you used --examples, replace or delete placeholder files.

Step 4: Edit the Skill

When editing the (newly-generated or existing) skill, remember that the skill is being created for another instance of the agent to use. Include information that would be beneficial and non-obvious to the agent. Consider what procedural knowledge, domain-specific details, or reusable assets would help another the agent instance execute these tasks more effectively.

Learn Proven Design Patterns

Consult these helpful guides based on your skill's needs:

  • Multi-step processes: See references/workflows.md for sequential workflows and conditional logic
  • Specific output formats or quality standards: See references/output-patterns.md for template and example patterns

These files contain established best practices for effective skill design.

Start with Reusable Skill Contents

To begin implementation, start with the reusable resources identified above: scripts/, references/, and assets/ files. Note that this step may require user input. For example, when implementing a brand-guidelines skill, the user may need to provide brand assets or templates to store in assets/, or documentation to store in references/.

Added scripts must be tested by actually running them to ensure there are no bugs and that the output matches what is expected. If there are many similar scripts, only a representative sample needs to be tested to ensure confidence that they all work while balancing time to completion.

If you used --examples, delete any placeholder files that are not needed for the skill. Only create resource directories that are actually required.

Update SKILL.md

Writing Guidelines: Always use imperative/infinitive form.

Frontmatter

Write the YAML frontmatter with name and description:

  • name: The skill name
  • description: This is the primary triggering mechanism for your skill, and helps the agent understand when to use the skill.
    • Include both what the Skill does and specific triggers/contexts for when to use it.
    • Include all "when to use" information here - Not in the body. The body is only loaded after triggering, so "When to Use This Skill" sections in the body are not helpful to the agent.
    • Example description for a docx skill: "Comprehensive document creation, editing, and analysis with support for tracked changes, comments, formatting preservation, and text extraction. Use when the agent needs to work with professional documents (.docx files) for: (1) Creating new documents, (2) Modifying or editing content, (3) Working with tracked changes, (4) Adding comments, or any other document tasks"

Do not include any other fields in YAML frontmatter.

Body

Write instructions for using the skill and its bundled resources.

Step 5: Packaging a Skill

Once development of the skill is complete, it must be packaged into a distributable .skill file that gets shared with the user. The packaging process automatically validates the skill first to ensure it meets all requirements:

scripts/package_skill.py <path/to/skill-folder>

Optional output directory specification:

scripts/package_skill.py <path/to/skill-folder> ./dist

The packaging script will:

  1. Validate the skill automatically, checking:

    • YAML frontmatter format and required fields
    • Skill naming conventions and directory structure
    • Description completeness and quality
    • File organization and resource references
  2. Package the skill if validation passes, creating a .skill file named after the skill (e.g., my-skill.skill) that includes all files and maintains the proper directory structure for distribution. The .skill file is a zip file with a .skill extension.

    Security restriction: symlinks are rejected and packaging fails when any symlink is present.

If validation fails, the script will report the errors and exit without creating a package. Fix any validation errors and run the packaging command again.

Step 6: Iterate

After testing the skill, users may request improvements. Often this happens right after using the skill, with fresh context of how the skill performed.

Iteration workflow:

  1. Use the skill on real tasks
  2. Notice struggles or inefficiencies
  3. Identify how SKILL.md or bundled resources should be updated
  4. Implement changes and test again

首页 - Wiki
Copyright © 2011-2026 iteam. Current version is 2.155.2. UTC+08:00, 2026-07-16 09:45
浙ICP备14020137号-1 $访客地图$