--- name: agentctl-setup description: "Install and set up the optional agentctl mod (task scope, refusal before execution, evidence that goes stale, model-free hand-over) — checks the environment, installs after approval, builds the first task line, optional deny rules, uninstall" allowed-tools: Read, Write, AskUserQuestion, Bash(node:*), Bash(claude:*) model: sonnet --- # agentctl Setup ## Trigger - Keywords: agentctl, agent control plane, install the mod, set up agentctl, agentctl task, uninstall agentctl ## When NOT to Use - Reviewing or gating a change (use `/codex-review-fast`, `/precommit`) — the mod is a control and a display, never a gate - Installing sd0x-dev-flow's own rules, hooks or scripts (use `/install-rules`, `/install-hooks`, `/install-scripts`) ## What the mod is — say this first, in the user's language Hand a task to Claude and leave; when you come back, three questions: | Question | What the mod does | |---|---| | Did it stay in scope? | You declare the task once (what may be edited, which test command may run). A call outside it — `git push`, `kubectl rollout`, an edit outside the allowed directories — is refused **before it runs**, with the rule named. Only a line you type changes the scope | | Did Claude ask for more than it needs? | Claude drafts the scope from the ticket; the mod previews it and binds it only when **you** type `/agentctl accept`. Recognized direct `git push`, `gh pr merge` and production writes are refused even with no task; anything the mod cannot classify goes to Claude Code's own permission prompt or auto mode | | Is "tests pass" true? | A test run is recorded with a Git-derived fingerprint of the tree; a later change to a tracked or untracked file reads **stale** within 30 s, not "passed" (ignored files, submodule contents and the environment are outside it) | | Where is it, do I need to step in? | A one-line band above the prompt; `/agentctl` for detail; `/agentctl handoff` writes a hand-over from records with no model call | It is **not** a security boundary: production stays protected by credentials. It ships in this repository under `mods/agentctl/` and is **never** installed by installing sd0x-dev-flow — this skill is the opt-in. ## Modes | Invocation | Does | |---|---| | `/agentctl-setup` | Steps 1 → 5, in order | | `/agentctl-setup --task` | Step 4b (a proposal) only, then stop | | `/agentctl-setup --status` | Step 2's `doctor` report only — no install, enable or task question — then stop | | `/agentctl-setup --uninstall` | Step 6 only, then stop | Dispatch on the flag **before** step 1: each flagged mode runs only its row and returns. `SETUP` below is `node "${CLAUDE_PLUGIN_ROOT}/skills/agentctl-setup/scripts/agentctl-setup.js"`. Every subcommand prints one JSON document; read `ok`, `problems` / `errors` from it, never guess. ## Workflow ### 1. Explain and ask Show the table above in a few lines, then one AskUserQuestion: **Install** · **Not now**. On `Not now`, stop. ### 2. Check Run `SETUP doctor`. Report `claudeVersion`, `installed`, and every entry of `problems` in plain words: | Field | Meaning | Response | |---|---|---| | `problems` non-empty | Mods cannot run here | Say why (old Claude Code, no git, `disableAllHooks`), how to fix it, and stop | | `installed.enabled` is `true` | Installed and on | Skip to step 4 | | `installed.enabled` is `false` | Installed but disabled — `/agentctl` does not exist until it is enabled | Say so; one AskUserQuestion naming `claude plugin enable `; run it after approval, then step 4. It takes effect in the **next** session | | `installed: false` | Not installed | Step 3 | | `installed: null` | `claude plugin list` could not be read | Say so; step 3 is still safe — installing an installed plugin changes nothing | ### 3. Install (after approval) One AskUserQuestion naming the exact command, then run it: ```bash claude plugin install agentctl@sd0xdev-marketplace ``` On failure (for example sd0x-dev-flow was loaded with `--plugin-dir` and the marketplace was never added), report the error and offer the per-session alternative, which changes no setting: `claude --plugin-dir "${CLAUDE_PLUGIN_ROOT}/mods/agentctl"`. The mod loads in the **next** Claude Code session; say so. ### 4. First task Ask with AskUserQuestion (free text through "Other" where noted): | Question | Becomes | |---|---| | What is the task? (free text) | `--goal` | | Which directories may be edited? `src,tests` · none (read-only) · other | `--edit` | | Which test or check command may run? `npm test` · none · other | `--check` | | Anything else to forbid? none · other | `--forbid` | The answers are free text, so they **never go on a command line** — a shell would expand `$(…)`, backticks or `$VAR` inside them before the helper could refuse them. Instead: 1. `SETUP alloc` → prints `input`, a fresh file in a private temporary directory 2. Write the answers to that path with the **Write** tool, as one JSON object of strings: `{"goal": "…", "edit": "src,tests", "check": "npm test", "forbid": "terraform apply"}` (omit a key the user answered "none") 3. `SETUP task-line --input ` — it reads the file once and deletes it On `ok: false`, show `errors` and ask again for that answer — `task-line` runs each command through the mod's own classifier, so a form the mod would refuse (an inline `VAR=value`, a wildcard, shell operators) is caught here, not after the user has left. On `ok: true`, show `line` in a fenced block and say: > Send this line yourself in the session where the mod runs. The mod accepts a task only from a > line **you** type — a command run by Claude is refused, so nothing Claude reads can widen the scope. Do not send the line yourself, and do not paraphrase it: the user copies it as printed. ### 4b. Proposal instead of a typed line (`--task`, and the workflow skills) When the mod is installed, Claude drafts the scope and the user only accepts it. Follow `references/workflow-integration.md` § Drafting a proposal: the same `alloc` + Write answers file, then `SETUP propose --input `, which validates with the mod's own rules and writes one private file under `~/.claude/agentctl/proposals/`. While a task is bound, the Write to the answers file is refused by agentctl; use the reference's `--stdin` form. Show `preview`, then say the mod shows its own preview, with the digest, at the end of this turn and that **they** type `/agentctl accept ` (or `/agentctl discard`); `digest` is `null` unless a `base` was given. Accept binds exactly the previewed scope; a later change to the file needs a new preview. ### 5. Second layer (optional) The mod refuses only while it runs. Offer native `permissions.deny` rules as a second layer: AskUserQuestion, multiSelect, over the rules `SETUP deny-rules --settings ` lists as `missing`, plus where to write: **this project, personal** (`.claude/settings.local.json`) · **every project** (`~/.claude/settings.json`) · **Skip**. After the choice, run `SETUP deny-rules --settings --rules "" --write` and report `written`. `git push` is not offered: `/push-ci` runs it, and a native deny would block that workflow. ### 6. Uninstall (`--uninstall`) AskUserQuestion naming `claude plugin uninstall agentctl@sd0xdev-marketplace`; run it after approval. Then say the mod's own data stays in `~/.claude/plugins/store/agentctl_*.json`, and drafted proposals in `~/.claude/agentctl/proposals/`, until deleted, and that deny rules added in step 5 stay in the settings file they were written to. ## Prohibited - Putting any free-text answer on a command line — answers go through `alloc` + Write + `--input` - Installing, uninstalling or writing settings without that step's AskUserQuestion approval - Sending `/agentctl task set` or `/agentctl accept` on the user's behalf, or editing the printed line - Describing the mod as a security boundary, a gate, or an approval - Offering `Bash(git push:*)` as a deny rule ## Verification - [ ] The flag chose the steps: a flagged mode ran only its own row - [ ] Default mode: the user saw what the mod does before any install question - [ ] `doctor` ran (default and `--status`), and every `problems` entry was reported - [ ] Install, deny-rule writes and uninstall each had their own approval - [ ] The task line came from `task-line --input` (or the proposal from `propose --input`) with `ok: true`, and the user was told to send or accept it themselves ## Examples ``` Input: /agentctl-setup Action: explain → doctor (ok, not installed) → approve → claude plugin install → four questions → task-line → "send this line yourself" → deny rules for this project → written ``` ``` Input: /agentctl-setup --task Action: draft from the ticket → propose → preview shown → "type /agentctl accept " yourself ```