diff --git a/CHANGELOG.md b/CHANGELOG.md index 3510068b9..4dfd85962 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,17 @@ parallel copies under `docs/` or `scripts/notes/`. At cut time: rename still clamps non-shell tools when set and does not cap a longer requested `run_shell`. +### Directors + +- **Skywalker may DIY tiny product writes (CL-6629).** Path tools + (`write_file` / `edit_file` / `delete_file`) remount on the primary + session. Tiny/single-file/one-route bounded edits are the exception; + spawn remains default for substantial/multi-file/parallel/specialist + work (hard cap 4 workers). Docs/design still spawn shakespeare / + bruckheimer / brand-reviewer except one-line fixes. Greybeard stays + write-free. Shell file-writes stay denied. Spawn is a judgment call, + not a tool ban. + ## [0.2.99] - 2026-08-21 Skywalker is the primary orchestrator over a closed director fleet: product write tools stay off the primary, and you cannot spawn Skywalker as a task leaf. Workers are not done until they return the four-heading report. First-party action skills ship as slashes; eval runners require an explicit provider/model pair; the style skill no longer refuses non-git folders. diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 1c950f0a9..a8b4b4a88 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -299,12 +299,12 @@ Data-only agent plugins (`src/plugins/data-only-agent.ts`) synthesize `agentPlug ### System Prompt (`src/agent/prompts.ts`) -The primary session identity is **Skywalker** (`buildChatRole` → `createSkywalkerSystemPrompt`). Product name remains Corbits Code; when asked its name, the primary answers Skywalker. Role: orchestrate-only — classify, dispatch closed directors via `task`, track the fleet, synthesize. Product mutation tools (`write_file` / `edit_file` / `delete_file`) are **not mounted** on the primary session (structural never-implement for path tools). Residual mutation surfaces: `run_shell` remains (gated; shell file-writes denied by auto-shell policy), MCP tools added after the strip are not re-filtered by `PRIMARY_DENIED_PRODUCT_TOOLS`, and leaf `writePaths` only apply to path-keyed product tools. A frontier model already knows how to code; the static prompt carries harness-specific facts and the closed-fleet orchestration policy. The base is three individually-exported sections: +The primary session identity is **Skywalker** (`buildChatRole` → `createSkywalkerSystemPrompt`). Product name remains Corbits Code; when asked its name, the primary answers Skywalker. Role: orchestrate — classify, DIY tiny/single-file/one-route product edits, dispatch closed directors via `task` for substantial work, track the fleet, synthesize. Product mutation tools (`write_file` / `edit_file` / `delete_file`) are mounted on the primary session (CORE and `SKYWALKER_TOOLS`) so Skywalker can DIY bounded edits; spawn remains the default for substantial, multi-file, parallel, or specialist work (hard cap 4 workers). Shell file-writes stay denied by auto-shell policy. MCP tools are not re-filtered by a product-write deny list (that list is gone). Leaf `writePaths` only apply to path-keyed product tools when a profile sets them. A frontier model already knows how to code; the static prompt carries harness-specific facts and the closed-fleet orchestration policy. The base is three individually-exported sections: -- `buildChatRole` — Skywalker primary identity (orchestrate; do not implement product work by default). -- `buildHarnessFacts` — the non-derivable rules: shell file-writes are blocked, primary product mutations are unmounted (spawn implement/docs directors), dependency installs and off-limits paths need approval, images are native multimodal input, only core tools are resident (load the rest via `tool_search`; use `search_agents` before dispatching specialists), workflows run only from slash-command steps, and session memory lives at `.corbits/MEMORY.md`. -- `buildGuidelines` — be concise, prefer `task` for product work, answer questions and diagnose visual/product feedback before editing, work autonomously for explicit coding tasks, use `lsp` for symbol work, and verify changes when practical. -- `buildPromptDisciplineBlock` — a shared, prohibition-form section appended exactly once to every built prompt (chat and sub-agent, every provider family). Primary vs leaf wording differs for product writes: leaves are told to use `read_file`/`edit_file`/`write_file`; Skywalker is told product writes are unmounted and durable edits go through directors. Shared rules: never `cat`/`sed`/heredoc/`echo` for file work, no setting or exporting environment variables (recurring needs belong in project settings), `web_fetch`/`web_search` instead of `curl`/`wget`/hand-rolled queries, one operation per `run_shell` call, turn semantics (a tool-less reply is the final answer, no repeat searches, stop and change approach after three failed attempts, batch independent reads in parallel), and TTY output rules (short bold headers, one-line bullets, backticks for paths/commands, no wide tables). +- `buildChatRole` — Skywalker primary identity (orchestrate; DIY tiny/bounded product edits; spawn for substantial work). +- `buildHarnessFacts` — the non-derivable rules: shell file-writes are blocked, path tools are the DIY surface on primary (spawn implement/docs directors for substantial work), dependency installs and off-limits paths need approval, images are native multimodal input, only core tools are resident (load the rest via `tool_search`; use `search_agents` before dispatching specialists), workflows run only from slash-command steps, and session memory lives at `.corbits/MEMORY.md`. +- `buildGuidelines` — be concise, prefer `task` for substantial product work, DIY tiny/bounded edits on the parent, answer questions and diagnose visual/product feedback before editing, work autonomously for explicit coding tasks, use `lsp` for symbol work, and verify changes when practical. +- `buildPromptDisciplineBlock` — a shared, prohibition-form section appended exactly once to every built prompt (chat and sub-agent, every provider family). Primary vs leaf wording differs for product writes: leaves are told to use `read_file`/`edit_file`/`write_file`; Skywalker is told to DIY tiny/bounded edits with those path tools and spawn directors for substantial work. Shared rules: never `cat`/`sed`/heredoc/`echo` for file work, no setting or exporting environment variables (recurring needs belong in project settings), `web_fetch`/`web_search` instead of `curl`/`wget`/hand-rolled queries, one operation per `run_shell` call, turn semantics (a tool-less reply is the final answer, no repeat searches, stop and change approach after three failed attempts, batch independent reads in parallel), and TTY output rules (short bold headers, one-line bullets, backticks for paths/commands, no wide tables). **Provider-conditional residuals.** Per-family additions layer on top of the shared block via the same `ModelFamilyPolicy` mechanism the directors use (`src/subagent/provider-family.ts`, `src/agent/model-family-policy.ts`) — additive lines, never prompt forks. **Grok** leaves get `buildGrokLeafAntiThrashNote` (gated by `shouldApplyGrokAntiThrash` / `applyGrokFinishBias`, withheld from orchestrators): a compact finish-bias reinforcement plus a one-line reminder to route file/web work through the dedicated tools rather than `run_shell`, motivated by observed tool-routing thrash on the same harness. **Kimi** intentionally has no residual yet — `detectModelFamily` already resolves the family so callers can branch on it, but the prompt seam is left unfilled pending eval characterization of Kimi's behavior, mirroring the provisional (permissive-default) policy in `model-family-policy.ts`. diff --git a/docs/IMPLEMENTATION.md b/docs/IMPLEMENTATION.md index 28b736eb1..d27c6a3ad 100644 --- a/docs/IMPLEMENTATION.md +++ b/docs/IMPLEMENTATION.md @@ -158,7 +158,7 @@ Sixteen packages under `src/agent/directors//` register in `DIRECTOR_REGISTR 2. `packageToProfile` maps envelope (`tools.allow`/`deny`) to `AgentProfile.capabilities`, `spawn.maySpawn` → `orchestrator`, and optional `writePaths`. System prompts are prefixed with a stable identity block (`formatDirectorSystemPrompt`: agent id, model role, optional skills). 3. Nested spawn: packages with `spawn.allowlist` forward that list into nested `task` (`spawnAllowlist` on nestedDispatch). Off-list `agent` is refused. `task(agent=skywalker)` is refused (primary is not a spawned worker). Primary omits the list so plugin profiles stay reachable. 4. `directorProfiles()` is the spawn catalog (`default-agents.ts`) — closed set minus skywalker; plugin agent profiles still load and can override by id. -5. Primary chat role is Skywalker: `buildChatRole()` → `createSkywalkerSystemPrompt()`. Product mutation tools are stripped from the primary toolset and from CORE/CATALOG ads (`PRIMARY_DENIED_PRODUCT_TOOLS`) — never-implement is structural for path tools. Residual: `run_shell` stays on primary; MCP tools loaded later are not re-stripped by that deny list; optional `writePaths` (when a profile sets it) only gate path-keyed product tools. +5. Primary chat role is Skywalker: `buildChatRole()` → `createSkywalkerSystemPrompt()`. Product mutation tools (`write_file` / `edit_file` / `delete_file`) live in CORE (and `SKYWALKER_TOOLS`) so they are advertised on the primary without a `tool_search` round-trip. DIY tiny/bounded edits on the parent; spawn implement/docs directors for substantial work — a prompt judgment call, not a toolset strip. `PRIMARY_DENIED_PRODUCT_TOOLS` is gone. Shell file-writes stay denied; MCP tools are not re-filtered by a product-write deny list. Optional `writePaths` (when a profile sets it) only gate path-keyed product tools. 6. Shipped directors omit `writePaths`. The optional field is still enforced in the permission gate via ALS identity (`identity-context.ts` + `write-path-policy.ts`) when a plugin/custom profile sets it. 7. Spawn effort: pin > package `modelRole` default (`defaultEffortForDirector`; intern=low; plan/review/orchestrator=high; implement/explore/docs/test=medium) > orchestrator/worker binary > parent inheritance. Optional skills are listed in the identity header for awareness; workers do not mount `use_skill` (guidance is baked into package system prompts). Primary mounts `use_skill` for its own skill list. diff --git a/docs/PRODUCT.md b/docs/PRODUCT.md index 73bbedc6e..b9030b850 100644 --- a/docs/PRODUCT.md +++ b/docs/PRODUCT.md @@ -144,7 +144,7 @@ Capabilities beyond the core toolset are opt-in plugins, enabled per workspace t ## Multi-agent (sub-agents) -The primary session is always **orchestrator** (single-agent mode is gone). Its identity is **Skywalker** (product name remains Corbits Code; when asked its name, answer Skywalker): classify work, dispatch a **closed fleet of 16 directors**, track the fleet, and synthesize. Product mutation tools (`write_file` / `edit_file` / `delete_file`) are not mounted on the primary session — implement and docs workers own durable writes. Residual mutation surfaces remain: `run_shell` stays on the primary (gated; shell file-writes are denied), MCP tools loaded after the primary strip are not re-denied by name. Shipped directors have no package `writePaths`; the optional field still constrains path-keyed product tools (not shell) when a profile sets it. Yolo / skip-permissions still bypasses the write-path gate when enabled. Operator slash recipes (`/implement`, `/plan`, `/refactor`, `/review`, `/pull-request-review`, `/create-issue`, `/scribe`, `/interview`, `/ast-grep`) tell Skywalker which directors to spawn; they do not run the work on the primary. +The primary session is always **orchestrator** (single-agent mode is gone). Its identity is **Skywalker** (product name remains Corbits Code; when asked its name, answer Skywalker): classify work, DIY tiny/single-file/one-route product edits, dispatch a **closed fleet of 16 directors** for substantial work, track the fleet, and synthesize. Product mutation tools (`write_file` / `edit_file` / `delete_file`) are mounted on the primary (CORE / `SKYWALKER_TOOLS`) — path tools are the DIY surface; spawn remains the default for substantial, multi-file, parallel, or specialist work (hard cap 4). Shell file-writes stay denied. MCP tools are not re-filtered by a product-write deny list (that list is gone). Shipped directors have no package `writePaths`; the optional field still constrains path-keyed product tools (not shell) when a profile sets it. Yolo / skip-permissions still bypasses the write-path gate when enabled. Operator slash recipes (`/implement`, `/plan`, `/refactor`, `/review`, `/pull-request-review`, `/create-issue`, `/scribe`, `/interview`, `/ast-grep`) tell Skywalker which directors to spawn for substantial work; tiny/bounded edits may run on the primary. | Lane | Directors | |---|---| diff --git a/plugins/corbits-skills/skills/create-issue/SKILL.md b/plugins/corbits-skills/skills/create-issue/SKILL.md index 7e213e106..889065ef1 100644 --- a/plugins/corbits-skills/skills/create-issue/SKILL.md +++ b/plugins/corbits-skills/skills/create-issue/SKILL.md @@ -21,7 +21,7 @@ Pick the tracker before drafting. Do not skip this. - GitLab - Linear (enable MCP) - Other - Then persist the choice: spawn `task(agent="implement")` with a tiny brief that appends `Preferred issue tracker: ` to `.corbits/MEMORY.md` only. Primary cannot write product files; shell writes are blocked. Do not ask implement to touch anything else. + Then persist the choice: DIY with write_file/edit_file — append `Preferred issue tracker: ` to `.corbits/MEMORY.md` only. Path tools are the DIY surface; shell writes stay denied. Do not touch anything else. 4. **GitHub** → create with `gh issue create` (title + body) via `run_shell`. If `gh` is missing, tell the operator to install GitHub CLI (`gh`) and stop. Do not invent an HTTP client. 5. **GitLab** → create with `glab issue create` (title + body) via `run_shell` similarly. If `glab` is missing, tell the operator and stop. 6. **Linear without MCP** → stop and tell the operator to enable Linear MCP. Do not invent a Linear REST client. diff --git a/plugins/corbits-skills/skills/dispatch/SKILL.md b/plugins/corbits-skills/skills/dispatch/SKILL.md index 0fb9683e3..c3127f5b1 100644 --- a/plugins/corbits-skills/skills/dispatch/SKILL.md +++ b/plugins/corbits-skills/skills/dispatch/SKILL.md @@ -2,12 +2,12 @@ name: dispatch user-invocable: false argument-hint: "[ | dispatch// | dispatch//dispatch.yaml | ]" -description: Multi-lane DAG orchestration. Skywalker recipe — use_skill("dispatch"). Spawns explore, intern, implement, plan, and critique. Never implements product code. +description: Multi-lane DAG orchestration. Skywalker recipe — use_skill("dispatch"). Spawns explore, intern, implement, plan, and critique. DAG product tasks go through implement; Skywalker may DIY tiny edits outside the DAG. --- # Dispatch -You are Skywalker. This skill is loadable with `use_skill("dispatch")`. Follow this recipe; do not implement product code; do not write `dispatch.yaml` or `plan.md` yourself. +You are Skywalker. This skill is loadable with `use_skill("dispatch")`. Follow this recipe. DAG product tasks go through implement workers. Do not write `dispatch.yaml` or `plan.md` yourself (intern cannot write; implement writes manifests). Tiny / single-file / one-route product edits outside this DAG may be DIY with write_file/edit_file/delete_file. Orchestrate parallel director runs across a dependency graph. Fan out work, fan in reports, critique, verify, re-dispatch fixes, and synthesize until done. @@ -39,7 +39,7 @@ If the spec is vague, incomplete, or contradictory: stop and report Blockers. Do | Architecture judgment before a large DAG | `task(agent="greybeard")` | | Independent suite / repro evidence | `task(agent="tester")` | -Skywalker classifies, spawns, tracks, and synthesizes. Product mutation tools are not mounted on this session. Durable files go through implement: orchestration artifacts (`dispatch.yaml`, `plan.md`, status) and product code. Implement is used for those artifacts because it has write tools; intern does not (`INTERN_TOOLS` = run_shell, read_file, list_dir). Do not spawn a blob agent to author the manifest. +Skywalker classifies, spawns, tracks, and synthesizes. Path tools (`write_file` / `edit_file` / `delete_file`) are mounted for DIY tiny/bounded product edits; spawn remains the default for DAG product work. Durable orchestration artifacts (`dispatch.yaml`, `plan.md`, status) still go through implement — intern does not have write tools (`INTERN_TOOLS` = run_shell, read_file, list_dir). Do not spawn a blob agent to author the manifest. Do not write those manifests on Skywalker. Prefer typed briefs: `intent`, `success_criteria`, `do_not`, `report_focus`, and `agent`. diff --git a/plugins/corbits-skills/skills/implement/SKILL.md b/plugins/corbits-skills/skills/implement/SKILL.md index 266e3a730..e9deef6ce 100644 --- a/plugins/corbits-skills/skills/implement/SKILL.md +++ b/plugins/corbits-skills/skills/implement/SKILL.md @@ -5,9 +5,11 @@ description: Disciplined per-commit workflow — Skywalker spawns greybeard, imp # Implement -You are Skywalker. This skill is a per-commit spawn recipe. You orchestrate specialists; you do not implement. +You are Skywalker. This skill is a per-commit spawn recipe for substantial landings. -Primary never writes product files. Spawn workers. Wait for reports. Decide the next spawn from those reports. +DIY is the exception: tiny / single-file / one-route / clear bounded product edits → use write_file/edit_file/delete_file on this session. Do not load this loop for that work. + +Spawn remains the default for substantial, multi-file, parallel, or specialist work (hard cap 4 workers). When this recipe runs, spawn workers. Wait for reports. Decide the next spawn from those reports. ## Prerequisites @@ -24,7 +26,7 @@ Track commit-sized units with `manage_tasks`. One item per unit that will become ## Per-commit spawn loop -For each unit, run these steps in order. Do not skip. Do not write, edit, or delete product files yourself. +For each unit, run these steps in order. Do not skip. When this loop is running, do not DIY the unit — spawn implement. ### 1. Review — greybeard @@ -72,8 +74,7 @@ When critique is clean (or remaining findings are acknowledged judgment calls), ## Hard rules -- Skywalker MUST NOT write/edit/delete product files. -- Do not do the coding yourself. +- Tiny / single-file / one-route / clear bounded edits: DIY with write_file/edit_file/delete_file. This recipe is for substantial units — when running it, spawn, do not DIY the coding. - Spawn with `task(agent="greybeard")`, `task(agent="implement")`, `task(agent="intern")` or `task(agent="tester")`, and `task(agent="critique")`. - Track only with `manage_tasks`. - Do not shortcut the loop. Skipping greybeard “because this is simple” or skipping critique “because the build passed” defeats the recipe. diff --git a/plugins/corbits-skills/skills/linear-issue-workflow/SKILL.md b/plugins/corbits-skills/skills/linear-issue-workflow/SKILL.md index 6a2469cf9..a5277e9ff 100644 --- a/plugins/corbits-skills/skills/linear-issue-workflow/SKILL.md +++ b/plugins/corbits-skills/skills/linear-issue-workflow/SKILL.md @@ -1,13 +1,13 @@ --- name: linear-issue-workflow user-invocable: false -description: Skywalker implements a Linear issue by fetching it via MCP then running the /implement spawn loop. Does not write product code. +description: Skywalker implements a Linear issue by fetching it via MCP then running the /implement spawn loop. DIY tiny/bounded issue edits; spawn implement for substantial landings. argument-hint: " [--reviewer ]" --- # Linear Issue Workflow -You are Skywalker. Host is Corbits Code. This skill is a spawn recipe. You do not write product code. You orchestrate: Linear MCP on the primary, then the `/implement` spawn loop. +You are Skywalker. Host is Corbits Code. This skill is a spawn recipe for substantial issue work. Tiny / single-file / one-route / clear bounded product edits: DIY with write_file/edit_file/delete_file. Substantial landings: Linear MCP on the primary, then the `/implement` spawn loop. If Linear MCP (`mcp__linear__*`) is missing, stop and tell the operator. Do not invent Claude-only tools. @@ -154,7 +154,7 @@ If the worktree directory was already deleted: `git worktree prune`. ## Hard rules -- Skywalker MUST NOT write/edit/delete product files. +- Tiny / single-file / one-route / clear bounded edits: DIY with write_file/edit_file/delete_file. Substantial issue landings: spawn implement (this recipe). - Spawn with `task(agent="greybeard")`, `task(agent="implement")`, `task(agent="intern")` or `task(agent="tester")`, and `task(agent="critique")`. - Clarifying questions use `ask_operator`. - Shell is `run_shell`, not a Bash tool. diff --git a/plugins/corbits-skills/skills/opsh/SKILL.md b/plugins/corbits-skills/skills/opsh/SKILL.md index 57943bde6..8ef14edc6 100644 --- a/plugins/corbits-skills/skills/opsh/SKILL.md +++ b/plugins/corbits-skills/skills/opsh/SKILL.md @@ -1,14 +1,14 @@ --- name: opsh user-invocable: false -description: Write scripts using opsh and its built-in libraries. Skywalker copies these rules into an implement brief; does not write the script. Load when writing, reviewing, or debugging opsh scripts. +description: Write scripts using opsh and its built-in libraries. Tiny scripts: DIY with these rules. Substantial script work: copy the rules into an implement brief. Load when writing, reviewing, or debugging opsh scripts. --- # opsh Scripting -You are Skywalker. Host is Corbits Code. This is a convention skill. You do not write the script. +You are Skywalker. Host is Corbits Code. This is a convention skill. Tiny / single-file scripts: DIY with write_file/edit_file using these rules. Substantial script work: spawn implement with this skill's rules copied into the brief (workers do not mount `use_skill`). -If the operator wants a script written, spawn `task(agent="implement")` with this skill's rules copied into the brief (workers do not mount `use_skill`). If the operator wants a review, spawn `task(agent="critique")` (or `task(agent="neckbeard")` for hygiene-only) with the same rules copied in. +If the operator wants a substantial script written, spawn `task(agent="implement")` with this skill's rules copied into the brief. If the operator wants a review, spawn `task(agent="critique")` (or `task(agent="neckbeard")` for hygiene-only) with the same rules copied in. Shell for agent commands is `run_shell` (there is no Bash tool). Bash-the-language in the examples below stays — opsh scripts are bash. diff --git a/plugins/corbits-skills/skills/pull-request-review/SKILL.md b/plugins/corbits-skills/skills/pull-request-review/SKILL.md index ea7cc6eaa..eb320ab0b 100644 --- a/plugins/corbits-skills/skills/pull-request-review/SKILL.md +++ b/plugins/corbits-skills/skills/pull-request-review/SKILL.md @@ -5,7 +5,7 @@ description: Review a pull request by branch name or URL. Intern checks out a wo # Pull Request Review -You are Skywalker. Host is Corbits Code. This skill is a spawn recipe. Do not implement fixes. Do not write product patches. Do not impersonate GitHub-Claude (or any other vendor) review comments. +You are Skywalker. Host is Corbits Code. This skill is a spawn recipe. Do not implement fixes as part of the review. Do not impersonate GitHub-Claude (or any other vendor) review comments. ## Input @@ -101,7 +101,7 @@ Or leave it and tell the operator it remains for further investigation. ## Hard rules -- Skywalker MUST NOT write/edit/delete product files. +- This recipe reviews; it does not land product patches. If the operator then asks for a tiny/bounded fix, DIY with write_file/edit_file/delete_file; spawn implement for substantial fixes. - Skywalker MUST NOT run the worktree git; intern does, via `run_shell`. -- Do not implement fixes. +- Do not implement fixes as part of the review. - Do not impersonate GitHub-Claude review comments. diff --git a/plugins/corbits-skills/skills/refactor/SKILL.md b/plugins/corbits-skills/skills/refactor/SKILL.md index d4dc55719..393840733 100644 --- a/plugins/corbits-skills/skills/refactor/SKILL.md +++ b/plugins/corbits-skills/skills/refactor/SKILL.md @@ -1,12 +1,12 @@ --- name: refactor argument-hint: -description: Skywalker maps a directory then plans improvements. Explore, then plan. No product writes. +description: Skywalker maps a directory then plans improvements. Explore, then plan. Does not ship product code. --- # Refactor -You are Skywalker. This skill is a spawn recipe. You do not write a design document. You do not write product files. `$ARGUMENTS` is the directory to analyze. +You are Skywalker. This skill is a spawn recipe. You do not write a design document. `$ARGUMENTS` is the directory to analyze. This recipe maps and plans; it does not ship. Tiny / single-file / one-route product edits outside this recipe may be DIY with write_file/edit_file/delete_file. ## Recipe @@ -35,7 +35,7 @@ Do not write the plan to disk yourself. Plan's report is the artifact. A later ` ## Hard rules -- Skywalker MUST NOT write/edit/delete product files, including design documents. +- Do not write the plan to disk or author design documents on this session — plan's report is the artifact. A later `/implement` or `use_skill("dispatch")` ships substantial work; DIY remains for tiny/bounded edits outside this recipe. - Do not skip explore "because you already know the directory." - Do not skip `ask_operator` when the operator has not chosen among alternatives. - Spawn with `task(agent="explore")` then `task(agent="plan")`. diff --git a/plugins/corbits-skills/skills/scribe/SKILL.md b/plugins/corbits-skills/skills/scribe/SKILL.md index 46668690a..53df56d22 100644 --- a/plugins/corbits-skills/skills/scribe/SKILL.md +++ b/plugins/corbits-skills/skills/scribe/SKILL.md @@ -5,10 +5,10 @@ description: Skywalker spawn recipe — shakespeare writes PRODUCT.md, ARCHITECT # Scribe -You are Skywalker. This skill is a spawn recipe. You do not write the docs. +You are Skywalker. This skill is a spawn recipe. Spawn `task(agent="shakespeare")` for PRODUCT.md, ARCHITECTURE.md, and IMPLEMENTATION.md unless the ask is a one-line fix (DIY with write_file/edit_file). Spawn `task(agent="shakespeare")` with the operator args / pasted material as the brief. Shakespeare owns PRODUCT.md, ARCHITECTURE.md, and IMPLEMENTATION.md. Use `ask_operator` if the doc target (P vs A vs I) is ambiguous. -Do not edit those docs yourself. DESIGN.md is brand-reviewer, not this skill. +Do not edit those docs yourself except a one-line fix. DESIGN.md is brand-reviewer, not this skill. diff --git a/src/agent/directors/registry.test.ts b/src/agent/directors/registry.test.ts index 0d6825968..47797cdb9 100644 --- a/src/agent/directors/registry.test.ts +++ b/src/agent/directors/registry.test.ts @@ -139,7 +139,6 @@ describe("director registry", () => { "testsmith", "tester", "gaasbot", - "skywalker", ] as const) { const allow = DIRECTOR_REGISTRY[id].tools?.allow ?? []; expect(allow).not.toContain("write_file"); @@ -169,13 +168,16 @@ describe("director registry", () => { } }); - test("skywalker primary stance: never implement, no product write tools", () => { + test("skywalker primary stance: DIY tiny writes, spawn for substantial work", () => { const s = DIRECTOR_REGISTRY.skywalker; - expect(s.systemPrompt).toContain("NEVER implement"); + expect(s.systemPrompt).toContain("write_file/edit_file/delete_file"); + expect(s.systemPrompt).toContain("DIY tiny/single-file/one-route"); expect(s.systemPrompt).toContain("You are Skywalker"); expect(s.systemPrompt).toMatch(/No catch-all worker/i); expect(s.tools?.allow).toContain("task"); - expect(s.tools?.allow).not.toContain("write_file"); + expect(s.tools?.allow).toContain("write_file"); + expect(s.tools?.allow).toContain("edit_file"); + expect(s.tools?.allow).toContain("delete_file"); expect(s.spawn.allowlist).toHaveLength(15); }); diff --git a/src/agent/directors/skywalker/package.test.ts b/src/agent/directors/skywalker/package.test.ts index 4545ed319..f38a973d5 100644 --- a/src/agent/directors/skywalker/package.test.ts +++ b/src/agent/directors/skywalker/package.test.ts @@ -12,7 +12,8 @@ describe("skywalkerPackage", () => { expect(skywalkerPackage.systemPrompt).toContain("You are Skywalker"); expect(skywalkerPackage.systemPrompt).toContain("When asked your name, answer: Skywalker"); expect(skywalkerPackage.systemPrompt).toContain("PRIMARY INTENT"); - expect(skywalkerPackage.systemPrompt).toContain("NEVER implement"); + expect(skywalkerPackage.systemPrompt).toContain("write_file/edit_file/delete_file"); + expect(skywalkerPackage.systemPrompt).toContain("DIY tiny/single-file/one-route"); }); test("createSkywalkerSystemPrompt returns package systemPrompt", () => { @@ -41,13 +42,13 @@ describe("skywalkerPackage", () => { ]); }); - test("tools.allow mounts orchestrator surface without product writes", () => { + test("tools.allow mounts orchestrator surface plus product writes for DIY", () => { const allow = skywalkerPackage.tools?.allow ?? []; expect(allow).toContain("task"); expect(allow).toContain("search_agents"); - expect(allow).not.toContain("write_file"); - expect(allow).not.toContain("edit_file"); - expect(allow).not.toContain("delete_file"); + expect(allow).toContain("write_file"); + expect(allow).toContain("edit_file"); + expect(allow).toContain("delete_file"); }); test("report required sections", () => { @@ -74,9 +75,9 @@ describe("skywalkerPackage", () => { test("primaryIntent and outOfLane", () => { expect(skywalkerPackage.primaryIntent).toBe( - "Orchestrate only — triage and dispatch; do not implement product code", + "Orchestrate; DIY tiny/bounded product edits; spawn for substantial work", ); - expect(skywalkerPackage.outOfLane).toContain("product edits"); + expect(skywalkerPackage.outOfLane).toContain("substantial multi-file product work without spawning"); expect(skywalkerPackage.outOfLane).toContain("catch-all worker"); expect(skywalkerPackage.outOfLane).toContain( "searching the repo yourself after a worker stops without finishing", @@ -118,9 +119,9 @@ describe("skywalkerPackage", () => { test("systemPrompt simple path skips explore+critique for tiny work", () => { const p = skywalkerPackage.systemPrompt; - expect(p).toContain("one implement worker"); - expect(p).toContain("skip explore and skip critique"); - expect(p).toContain("tests green"); + expect(p).toContain("DIY on the parent"); + expect(p).toContain("skip spawn, skip explore, skip critique"); + expect(p).toContain("write_file/edit_file"); expect(p).toContain("Do not always explore→implement→critique"); }); diff --git a/src/agent/directors/skywalker/package.ts b/src/agent/directors/skywalker/package.ts index b6fd451cb..911c23248 100644 --- a/src/agent/directors/skywalker/package.ts +++ b/src/agent/directors/skywalker/package.ts @@ -1,25 +1,25 @@ // Skywalker: primary orchestration director. Chains specialists into a workflow. import type { DirectorPackage } from "../types.js"; -import { ORCHESTRATOR_TOOLS } from "../tool-sets.js"; +import { SKYWALKER_TOOLS } from "../tool-sets.js"; const SKYWALKER_SYSTEM_PROMPT = `You are Skywalker — the primary orchestrator for Corbits Code. When asked your name, answer: Skywalker. Agent id: skywalker (primary session; not a spawned worker). Start specialists with task(agent="…"). -PRIMARY INTENT: run the workflow. Classify every request. Delegate. Chain specialists into a sequence of actions. Track who is running. Synthesize for the operator. Do not become the implementer, reviewer, or explorer by default. +PRIMARY INTENT: run the workflow. Classify every request. DIY tiny/single-file/one-route product edits. Delegate substantial work. Chain specialists into a sequence of actions. Track who is running. Synthesize for the operator. Do not become the reviewer or explorer by default. -You do not do the specialists' jobs. You start them, wait for their reports, and decide the next action from those reports. +You do not do the specialists' jobs by default. For tiny bounded product edits, use write_file/edit_file/delete_file yourself. For substantial work you start specialists, wait for their reports, and decide the next action from those reports. # Parent tools -Do not run long-blocking jobs on the parent (evals, full test suites, long installs). Dispatch intern (mechanical shell) or tester (suite / repro). +Do not run long-blocking jobs on the parent (evals, full test suites, long installs, long-running implementation). Dispatch intern (mechanical shell), tester (suite / repro), or implement (substantial code). Path tools (write_file/edit_file/delete_file) are the DIY surface; shell file-writes stay denied. task() still awaits the worker's full report. Enter mid-run delivers at the next parent tool.boundary — a long parent run_shell or awaiting task() holds those steers. Dispatching a worker does not make Enter a new turn until that parent tool returns. Example chains: -- tiny fix: implement +- tiny fix: DIY write_file/edit_file (do not spawn) - feature: explore → implement → critique - "why / how / is this stalled": answer yourself; at most one explore if a single unknown blocks you @@ -51,14 +51,14 @@ Parallelize independent lanes. manage_tasks for your checklist. ask_operator whe When the operator (or brief) gives an http(s) URL to read: - Call **web_fetch** yourself on that URL — it is already mounted. Do not tool_search for it, do not shell curl/wget/fetch, do not thrash run_shell to download pages. -- After you have the content, spawn implement only if a file must be written (e.g. write the extracted fact). For pure Q&A from a URL, answer directly. +- After you have the content, DIY a tiny file write yourself; spawn implement only if the write is substantial. For pure Q&A from a URL, answer directly. - Cap retries: if web_fetch fails once with a clear error, report the blocker — do not burn a long tool-only streak on shell workarounds. # Effort scaling (IMPLEMENTATION / ORCHESTRATION) Scale fan-out to the ask — do not spawn 10+ workers for a simple request: - Simple (answer, one-path lookup, tiny fix): 0–1 worker, few tools; often answer without fleet -- Tiny single-file / one-route asks: **one implement worker**; skip explore and skip critique when implement reports tests green and criteria mapped pass. Do not always explore→implement→critique for simple work — that burns wall clock. +- Tiny single-file / one-route asks: **DIY on the parent** with write_file/edit_file; skip spawn, skip explore, skip critique. Do not always explore→implement→critique for simple work — that burns wall clock. - Medium: 2–4 workers with distinct path/package ownership - Complex: more workers only with named lanes and clear non-overlap Hard cap: **at most 4 workers at once** unless the operator explicitly asks for a wider fan-out. Prefer synthesizing early returns over launching a second wave. @@ -94,7 +94,13 @@ Before responding, classify: 2. ORCHESTRATION — plan, coordinate, or manage work in progress 3. COMMUNICATION — answer a question, provide information, or clarify -## If IMPLEMENTATION → dispatch; NEVER implement directly +## If IMPLEMENTATION → DIY when tiny; spawn when substantial + +Tiny / single-file / one-route / clear bounded edit: write_file/edit_file/delete_file on this session. Do not spawn. + +Substantial / multi-file / parallel lanes / long-running: spawn implement (hard cap 4). Keep long-blocking jobs off the parent so Enter can steer. + +Docs/design (PRODUCT.md, ARCHITECTURE.md, docs/design/*, brand) still spawn shakespeare / bruckheimer / brand-reviewer unless the ask is a one-line fix. 1. If requirements are fuzzy or complex, load interview and discover first. 2. Use explore workers for scope when needed. @@ -103,8 +109,6 @@ Before responding, classify: 5. Present the plan when the change is large or ambiguous; then execute via task spawns. 6. Track progress with manage_tasks; synthesize results for the operator. -Forbidden: product Write/Edit, "just quickly" shipping code yourself, implementing to save time. - ## If ORCHESTRATION → coordinate Track with manage_tasks. Parallelize independent lanes. Escalate blockers with ask_operator. This is your core role. @@ -117,11 +121,10 @@ Do not reclassify COMMUNICATION as ORCHESTRATION just to justify parallel task s # Non-negotiables -- NEVER implement product features yourself (zero product Write/Edit). +- Tiny/single-file/one-route product edits: write_file/edit_file/delete_file yourself. Substantial, multi-file, parallel, or specialist work: spawn (implement for code; shakespeare / bruckheimer / brand-reviewer for docs/design unless a one-line fix). - Interview when requirements are fuzzy; consult greybeard on architecture/approach. - Use plan or dispatch skill for multi-lane eng plans; clarify before large dispatch. -- Product file mutation tools (write_file, edit_file, delete_file) are not mounted on this session. Track work with manage_tasks; spawn implement (code), shakespeare (P/A/I docs), or brand-reviewer (DESIGN.md) for durable artifacts. -- Before any product file op, self-check: "Am I implementing instead of orchestrating?" If yes, STOP and spawn implement. +- Path tools are the DIY surface; shell file-writes stay denied. Track fleet work with manage_tasks. - Optional skills when needed on the primary session: dispatch, style, philosophy, interview (use_skill is primary-mounted). @@ -154,9 +157,10 @@ export function createSkywalkerSystemPrompt(): string { export const skywalkerPackage: DirectorPackage = { id: "skywalker", - primaryIntent: "Orchestrate only — triage and dispatch; do not implement product code", + primaryIntent: "Orchestrate; DIY tiny/bounded product edits; spawn for substantial work", outOfLane: [ - "product edits", + "substantial multi-file product work without spawning", + "docs/design authorship (PRODUCT.md, ARCHITECTURE.md, docs/design/*, brand) except one-line fixes", "deep multi-path repo walks when a single explore worker or mounted tools suffice", "being the reviewer/implementer by default", "catch-all worker", @@ -166,7 +170,7 @@ export const skywalkerPackage: DirectorPackage = { description: "Primary orchestration director — chains specialists into a workflow", systemPrompt: SKYWALKER_SYSTEM_PROMPT, optionalSkills: ["dispatch", "style", "philosophy", "interview"], - tools: { allow: ORCHESTRATOR_TOOLS }, + tools: { allow: SKYWALKER_TOOLS }, spawn: { maySpawn: true, allowlist: [ diff --git a/src/agent/directors/tool-sets.test.ts b/src/agent/directors/tool-sets.test.ts index 92b268ae6..c5baf5b6c 100644 --- a/src/agent/directors/tool-sets.test.ts +++ b/src/agent/directors/tool-sets.test.ts @@ -2,7 +2,9 @@ import { describe, expect, test } from "bun:test"; import { DOCS_TOOLS, IMPLEMENT_TOOLS, + ORCHESTRATOR_TOOLS, READ_TOOLS, + SKYWALKER_TOOLS, } from "./tool-sets.js"; describe("DOCS_TOOLS", () => { @@ -34,3 +36,14 @@ describe("DOCS_TOOLS", () => { } }); }); + +describe("SKYWALKER_TOOLS / ORCHESTRATOR_TOOLS", () => { + test("Skywalker mounts product writes; greybeard orchestrator surface does not", () => { + for (const name of ["write_file", "edit_file", "delete_file"] as const) { + expect(SKYWALKER_TOOLS as readonly string[]).toContain(name); + expect(ORCHESTRATOR_TOOLS as readonly string[]).not.toContain(name); + } + expect(SKYWALKER_TOOLS).toContain("task"); + expect(ORCHESTRATOR_TOOLS).toContain("task"); + }); +}); diff --git a/src/agent/directors/tool-sets.ts b/src/agent/directors/tool-sets.ts index e750cc33d..1c73604da 100644 --- a/src/agent/directors/tool-sets.ts +++ b/src/agent/directors/tool-sets.ts @@ -51,3 +51,11 @@ export const ORCHESTRATOR_TOOLS = [ "search_agents", "task", ] as const; + +/** Skywalker primary: orchestrator surface plus product writes for DIY tiny work. */ +export const SKYWALKER_TOOLS = [ + ...ORCHESTRATOR_TOOLS, + "write_file", + "edit_file", + "delete_file", +] as const; diff --git a/src/agent/prompt-contract.ts b/src/agent/prompt-contract.ts index db259c99c..ca9d4f35b 100644 --- a/src/agent/prompt-contract.ts +++ b/src/agent/prompt-contract.ts @@ -9,7 +9,7 @@ export const CHAT_PROMPT_QUALITY_MARKERS = [ "Tool choice:", "Ask vs proceed:", "Scope and conventions:", - "Product write tools are not mounted on Skywalker", + "DIY tiny/single-file/one-route", "ask_operator only when permission blocks you", "Touch only code required for the task", "load the style and philosophy skills", diff --git a/src/agent/prompts.test.ts b/src/agent/prompts.test.ts index d72f08db3..73207a3d1 100644 --- a/src/agent/prompts.test.ts +++ b/src/agent/prompts.test.ts @@ -13,10 +13,6 @@ import { CORE_TOOL_NAMES, CATALOG_TOOL_NAMES } from "./tool-search.js"; const REGISTERED_TOOL_NAMES = new Set([ ...CORE_TOOL_NAMES, ...CATALOG_TOOL_NAMES, - // Product mutation tools mount on workers, not primary CORE/CATALOG ads. - "write_file", - "edit_file", - "delete_file", ]); const REFERENCED_TOOL_NAMES = [ diff --git a/src/agent/prompts.ts b/src/agent/prompts.ts index 4b17b3d47..d1a4b1020 100644 --- a/src/agent/prompts.ts +++ b/src/agent/prompts.ts @@ -64,8 +64,8 @@ export function buildHarnessFacts( "- Change files with write_file/edit_file and remove files with delete_file; shell file-writes and deletions are blocked.", ] : [ - "- Product file mutations (write_file, edit_file, delete_file) are not mounted on the primary Skywalker session — spawn implement (code), shakespeare (P/A/I), brand-reviewer (DESIGN.md), or bruckheimer (PRODUCT.md) for durable edits.", - "- Shell file-writes and deletions are blocked; never use echo/heredoc/sed/rm as a substitute for product tools.", + "- Change files with write_file/edit_file and remove files with delete_file for tiny/single-file/one-route bounded edits. Spawn implement for substantial/multi-file/parallel/specialist work (hard cap 4 workers). Docs/design still spawn shakespeare/bruckheimer/brand-reviewer except one-line fixes.", + "- Shell file-writes and deletions are blocked; never use echo/heredoc/sed/rm as a substitute for product tools. Path tools are the DIY surface.", ]), "- Use the provided tools for file reads/searches instead of shelling out as a substitute.", "- read_file accepts a filesystem path or a tool-output:///{callId} URI from a prior tool result when the harness exposes one; prefer the URI over re-reading huge blobs.", @@ -108,12 +108,12 @@ export function buildGuidelines(opts: { subAgent?: boolean; sessionMode?: Sessio ...(subAgent ? [] : [ - "- Prefer task(intent=…) / task(agent=…) for product implementation, exploration, review, and docs — that is the primary loop.", + "- Prefer task(intent=…) / task(agent=…) for substantial product implementation, exploration, review, and docs — spawn remains default for substantial work, not a tool ban.", ]), "- read_file for file contents; grep or search_files to locate code; lsp for symbols, types, references, or call flow before opening large files.", subAgent ? "- edit_file for targeted changes; write_file for new files or full rewrites; delete_file to remove files — never echo, heredoc, sed, or rm in the shell for those jobs." - : "- Product write tools are not mounted on Skywalker. Spawn implement (or a docs director) for durable file changes; never shell-write (echo/heredoc/sed/rm).", + : "- edit_file for targeted DIY tiny/single-file/one-route edits; write_file for new files or full rewrites; delete_file to remove files — never shell-write (echo/heredoc/sed/rm). Spawn implement (or a docs director) for substantial/multi-file/parallel/specialist work.", "- run_shell for builds, tests, git, and one-off commands — not for shell find, head-position rg, or recursive grep -r (OOM risk), cat, or messaging the user.", ...(subAgent ? [] @@ -162,7 +162,7 @@ export function buildPromptDisciplineBlock(opts: { subAgent?: boolean } = {}): s const subAgent = opts.subAgent ?? false; const toolsOverShell = subAgent ? "- Never use run_shell to read, edit, or write files — use read_file, edit_file, write_file; cat/head/tail, sed/awk/perl -i, and heredoc/echo redirection are prohibited substitutes." - : "- Never use run_shell to read, edit, or write files — use read_file for reads; durable product edits go through implement/docs directors (write tools are not mounted on Skywalker); cat/head/tail, sed/awk/perl -i, and heredoc/echo redirection are prohibited substitutes."; + : "- Never use run_shell to read, edit, or write files — use read_file, edit_file, write_file for tiny/bounded DIY; spawn implement/docs directors for substantial work; cat/head/tail, sed/awk/perl -i, and heredoc/echo redirection are prohibited substitutes."; return [ "Prompt discipline:", "", diff --git a/src/agent/tool-search.test.ts b/src/agent/tool-search.test.ts index 53d492dc3..b516eb50c 100644 --- a/src/agent/tool-search.test.ts +++ b/src/agent/tool-search.test.ts @@ -9,7 +9,6 @@ import { coreToolNamesForSessionMode, CORE_TOOL_NAMES, CATALOG_TOOL_NAMES, - PRIMARY_DENIED_PRODUCT_TOOLS, type ToolAvailability, } from "./tool-search.js"; @@ -83,12 +82,11 @@ describe("createToolIndex", () => { expect(coreToolNamesForSessionMode("orchestrator", FULL_AVAILABILITY)).not.toContain("present"); }); - test("primary CORE and CATALOG omit product mutation tools", () => { + test("primary CORE includes product mutation tools; CATALOG does not duplicate them", () => { for (const name of ["write_file", "edit_file", "delete_file"] as const) { - expect(CORE_TOOL_NAMES).not.toContain(name); + expect(CORE_TOOL_NAMES).toContain(name); expect(CATALOG_TOOL_NAMES).not.toContain(name); } - expect(PRIMARY_DENIED_PRODUCT_TOOLS).toEqual(["write_file", "edit_file", "delete_file"]); }); test("catalog advertises web_fetch and web_search so URL work needs no tool_search", () => { @@ -193,8 +191,8 @@ describe("advertisedTools", () => { const names = advertisedTools(registry).map((d) => d.name); expect(names).toContain("read_file"); expect(names).toContain("grep"); - // write_file is not in primary CATALOG — product mutations are leaf-only. - expect(names).not.toContain("write_file"); + // write_file is in CORE so the primary can DIY tiny/bounded edits. + expect(names).toContain("write_file"); expect(names).not.toContain("mcp__linear__create_issue"); }); diff --git a/src/agent/tool-search.ts b/src/agent/tool-search.ts index b25486811..5229bcb67 100644 --- a/src/agent/tool-search.ts +++ b/src/agent/tool-search.ts @@ -16,11 +16,15 @@ import { sessionModeEnablesSubAgents } from "../config/session-mode.js"; // dispatchable — the model finds it via tool_search when a session actually // needs it. // -// Product mutation tools (write_file / edit_file / delete_file) are intentionally -// absent from the primary Skywalker core/catalog sets — they mount only on leaf -// directors that need them (implement, shakespeare, …). See PRIMARY_DENIED_PRODUCT_TOOLS. +// Product mutation tools (write_file / edit_file / delete_file) sit in CORE so +// the primary Skywalker session can DIY tiny/bounded edits without a +// tool_search round-trip. Substantial work still spawns implement / docs +// directors — that is a prompt judgment call, not a toolset strip. export const CORE_TOOL_NAMES: readonly string[] = [ "read_file", + "write_file", + "edit_file", + "delete_file", "lsp", "run_shell", "ask_operator", @@ -35,13 +39,6 @@ export const CORE_TOOL_NAMES: readonly string[] = [ "task", ]; -/** Product mutation tools denied on the primary Skywalker session (structural). */ -export const PRIMARY_DENIED_PRODUCT_TOOLS: readonly string[] = [ - "write_file", - "edit_file", - "delete_file", -]; - const ORCHESTRATOR_ONLY_TOOL_NAMES: readonly string[] = ["search_agents", "task"]; // Session-start facts that gate a core tool's advertisement. Each must be @@ -77,8 +74,8 @@ export function advertisedToolNamesForSessionMode( // Built-in file/search/web tools advertised alongside the core set. They carry full // schemas on the wire so the model can call them directly; MCP tools are not // listed at all — they are discovered blind via tool_search. -// write_file is intentionally omitted: primary Skywalker does not mutate product -// files; implement/docs leaves mount write tools via their own toolsets. +// write_file / edit_file / delete_file live in CORE (not here) so they are +// advertised without a tool_search round-trip. // web_fetch / web_search are catalog (not deferred): URL reads and search are // first-class primary work; requiring tool_search before web_fetch caused // thrash on web-bait and contradicted the skywalker "already mounted" rule. diff --git a/src/agent/tools.ts b/src/agent/tools.ts index aa901b71c..4661e05c8 100644 --- a/src/agent/tools.ts +++ b/src/agent/tools.ts @@ -32,7 +32,7 @@ import { import type { ToolWatchdogConfig } from "../tui/tool-execution-watchdog.js"; import type { SessionMode } from "../config/session-mode.js"; import { sessionModeEnablesSubAgents } from "../config/session-mode.js"; -import { advertisedToolNamesForSessionMode, PRIMARY_DENIED_PRODUCT_TOOLS, type ToolAvailability } from "./tool-search.js"; +import { advertisedToolNamesForSessionMode, type ToolAvailability } from "./tool-search.js"; import type { ProviderCatalogEntry } from "../config/index.js"; import type { AgentProfile } from "./profiles.js"; import { @@ -341,13 +341,7 @@ export async function createAgentToolset(args: AgentToolsetArgs): Promise !primaryDenied.has(tool.definition.name)); - - const dynamicRunner = createDynamicToolRunner(primaryTools, toolWatchdog); + const dynamicRunner = createDynamicToolRunner(baseTools, toolWatchdog); runnerRef = dynamicRunner; const connectedClients: MCPClient[] = []; diff --git a/src/prompts.test.ts b/src/prompts.test.ts index 4864e0a8b..a58057349 100644 --- a/src/prompts.test.ts +++ b/src/prompts.test.ts @@ -47,8 +47,10 @@ test("agent identity is Skywalker orchestrator", () => { test("harness facts state only the non-derivable tool and safety rules", () => { const facts = buildHarnessFacts(); - expect(facts).toContain("write_file, edit_file, delete_file"); - expect(facts).toContain("not mounted on the primary Skywalker session"); + expect(facts).toContain("write_file/edit_file"); + expect(facts).toContain("tiny/single-file/one-route"); + expect(facts).toContain("Spawn implement"); + expect(facts).not.toContain("not mounted on the primary Skywalker session"); expect(facts).toContain("blocked"); expect(facts).toContain("15s timeout"); expect(facts).toContain("find, rg, and grep -r"); @@ -92,6 +94,9 @@ test("guidelines cover response style, tool choice, ask vs proceed, and scope", expect(guidelines).toContain("grep or search_files"); expect(guidelines).toContain("ask_operator only when permission blocks you"); expect(guidelines).toContain("load the style and philosophy skills"); + expect(guidelines).toContain("DIY tiny/single-file/one-route"); + expect(guidelines).toContain("never shell-write (echo/heredoc/sed/rm)"); + expect(guidelines).not.toContain("not mounted on Skywalker"); }); test("orchestrator guidelines teach the typed task spawn contract", () => { diff --git a/tests/unit/tui/agent-tools.test.ts b/tests/unit/tui/agent-tools.test.ts index 59d576a21..a8c94bde2 100644 --- a/tests/unit/tui/agent-tools.test.ts +++ b/tests/unit/tui/agent-tools.test.ts @@ -8,6 +8,8 @@ const mockPosixTools = { definitions: [ { name: "read_file", description: "Read a file", inputSchema: { type: "object", properties: {}, required: [] } }, { name: "write_file", description: "Write a file", inputSchema: { type: "object", properties: {}, required: [] } }, + { name: "edit_file", description: "Edit a file", inputSchema: { type: "object", properties: {}, required: [] } }, + { name: "delete_file", description: "Delete a file", inputSchema: { type: "object", properties: {}, required: [] } }, ] as ToolDefinition[], run: mock(async (_call: ToolCall, _signal: AbortSignal) => ({ callId: "test", @@ -191,10 +193,10 @@ test("dynamicRunner contains posix tool names plus ask_operator", async () => { const names = toolset.dynamicRunner.currentDefinitions().map((d) => d.name); expect(names).toContain("read_file"); expect(names).toContain("ask_operator"); - // Primary Skywalker never mounts product mutation tools. - expect(names).not.toContain("write_file"); - expect(names).not.toContain("edit_file"); - expect(names).not.toContain("delete_file"); + // Primary Skywalker mounts product mutation tools for DIY tiny/bounded edits. + expect(names).toContain("write_file"); + expect(names).toContain("edit_file"); + expect(names).toContain("delete_file"); }); test("onOperatorGate callback is invoked when the operator tool handler is called", async () => {