diff --git a/CLAUDE.md b/CLAUDE.md index 96733e9d..103a1ec4 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -33,6 +33,9 @@ dispatch/ ├── e2e/ # Playwright E2E tests ├── bin/ # dispatch-dev, dispatch-server, install-dispatch, etc. ├── scripts/ # e2e-isolated.sh, generate-icon-colors.ts +├── plugins/ +│ └── dispatch/ # official Dispatch plugin (skills for Claude Code + Codex) +├── release-notes/ # release notes + assisted-update metadata ├── .dispatch/ # repo-level Dispatch config │ ├── config.json # repo-level settings (e.g. Linear integration) │ ├── job-state/ # persistent state files for recurring jobs diff --git a/apps/web/src/components/app/docs-sections/tools.tsx b/apps/web/src/components/app/docs-sections/tools.tsx index 8286df94..62d4c1ae 100644 --- a/apps/web/src/components/app/docs-sections/tools.tsx +++ b/apps/web/src/components/app/docs-sections/tools.tsx @@ -44,6 +44,15 @@ export function ToolsContent() { The tools above would be available to agents as repo_lint , repo_test, and repo_db_reset.

+

+ Every entry needs a name, a description, and + a non-empty command array. The command is executed + directly rather than through a shell, so pipes, globs, and{" "} + && don't work — put anything shell-shaped in a + script and point command at that. A malformed entry + aborts the whole manifest, so a typo in one tool makes every{" "} + repo_ tool disappear. +

@@ -78,7 +87,39 @@ export function ToolsContent() { When an agent calls repo_dev_up with{" "} {'{ cwd: "/path", live: true }'}, Dispatch runs{" "} ./bin/dev up --cwd /path --live. Parameters that are - omitted or false are skipped. + omitted, false, or an empty string are skipped. Every parameter is + optional to the agent, and name, type, and{" "} + flag are required in the definition — the{" "} + description is what the agent reads to decide what to + pass, so it's worth writing. +

+
+ +
+

What agents get back

+

+ A repo tool call doesn't fail on a non-zero exit. The command's stdout + comes back as the tool's text result, with the exit code, stdout, and + stderr in the structured payload, so the agent can read a failure and + react to it instead of just seeing an error. Write scripts that fail + loudly on stderr. +

+

+ Dispatch puts no time limit on a repo tool command (lifecycle hooks + below do get one), though the agent's own MCP client may give up on a + very long call. +

+
+ +
+

Picking up changes

+

+ .dispatch/tools.json is re-read from disk on every tool + listing — no server restart needed. The limit is on the agent's side: + a CLI fetches its tool list once when the session starts and holds it. + So an edited command runs the new version on the next call, but a + newly added tool usually isn't callable until the agent reconnects or + a new session starts.

diff --git a/apps/web/src/lib/tips/tips.ts b/apps/web/src/lib/tips/tips.ts index 0d2acc2b..19d386d8 100644 --- a/apps/web/src/lib/tips/tips.ts +++ b/apps/web/src/lib/tips/tips.ts @@ -260,6 +260,14 @@ export const tips: Tip[] = [ since: "0.33.6", surfaces: ["ambient"], }, + { + id: "repo-tools", + title: "Give Agents Your Repo's Commands", + body: "Add a .dispatch/tools.json to a repo and every entry becomes a real tool in each agent's tool list — so nobody has to paste the same build, reset, or dev-stack command into a prompt again.", + docsSection: "tools", + since: "0.11.12", + surfaces: ["ambient"], + }, { id: "shortcut-pins", title: "One-Click Agent Shortcuts", diff --git a/plugins/dispatch/skills/repo-tools/SKILL.md b/plugins/dispatch/skills/repo-tools/SKILL.md index 2455f9d8..31b4ec4a 100644 --- a/plugins/dispatch/skills/repo-tools/SKILL.md +++ b/plugins/dispatch/skills/repo-tools/SKILL.md @@ -54,13 +54,13 @@ it at the moment of use. | ------------- | -------- | --------------------------------------------------------------------------- | | `name` | yes | Exposed as `repo_`. Dots are stripped — MCP names cannot contain them | | `description` | yes | This is what makes the tool get used. See below | -| `command` | yes | Argv array, run from the repo root | +| `command` | yes | Argv array, run from the agent's checkout root | | `params` | no | Turned into CLI flags appended to `command` | | `scope` | no | Any of `agent`, `reviewer`, `job`. Omit to expose everywhere | -`repo_` prefixing is automatic, and a name that would collide with a built-in -Dispatch tool (`create_pr`, `get_pr_status`, `dispatch_event`, `dispatch_share`) -is rejected at load. +`repo_` prefixing is automatic and keeps repo tools in their own namespace, so a +repo tool can never shadow a built-in Dispatch tool like `create_pr` or +`dispatch_event`. **Write the description for an agent that has never seen this repo.** It is the only thing standing between the tool existing and the tool being used. Say what @@ -79,7 +79,10 @@ load. Give every param a `description` too — the agent picks values from it. ## Execution model -Commands run from the repo root with `DISPATCH_AGENT_ID` set in the environment. +Commands run from the root of the agent's checkout — the worktree root for +worktree agents, otherwise the repo root — with `DISPATCH_AGENT_ID` set in the +environment. There is no shell: the argv array is spawned directly, so pipes, +globs, and `&&` need a wrapper script. **Every exit code is returned to the agent** rather than throwing — stdout, stderr, and the exit code all come back, so the agent can read a failure instead of just seeing an error. Write scripts that fail loudly on stderr. @@ -89,7 +92,8 @@ of just seeing an error. Write scripts that fail loudly on stderr. `hooks.stop.command` runs when the agent stops. Use it for teardown that would otherwise leak: stopping a dev stack, removing a container, releasing a port. Keep it fast and idempotent — it may run when the thing it cleans up was never -started. +started, and it is killed after 15 seconds. A failing or timed-out hook is +logged and never blocks the stop. ## Working on it