From bdd882bce82535e35f741b4dcef0b7b1b2f4fe1a Mon Sep 17 00:00:00 2001 From: Codeman maintainer Date: Wed, 11 Nov 2026 10:11:51 +0100 Subject: [PATCH 1/2] feat(skill): a local worker with the coding agent of your choice MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `spawn_worker local` started the one agent the model was installed with. The server now lists every agent the model can be run with, so the skill passes the choice on: - `local_model` adds `agents` (each with its status, whether it is the default, and the readiness mark the server measured) and `models` (what is on the Mac; only the active one runs). A new reason, `no-agent`: installed, but nothing that can run the model is there. - `spawn_worker local:` (`local:pi`, `local:deepseek`, `local:opencode`, `local:claude`, also as `name:local:pi` in `spawn_workers`) starts an agent whose status is `ready`. Anything else fails the spawn before a session exists and names the agents that are startable. No agent is ever installed from here: that stays a person's click in the window. A Codeman that lists no agents knows one, and only that one can be named. - The readiness mark comes from the server when it names one, else from what this skill measured (OpenCode `┃`, Pi `extended-keys`), and `LOCAL_READY_MARK` overrides both. - A Pi worker on the local model is started with `piConfig.approveProjectTrust: false` (--no-approve). On a custom model Pi keeps no home, so it asks to trust the folder in every session with Trust preselected, and the task prompt's Enter would answer that. Pi's mark is the last line of its startup, a warning about tmux's `extended-keys` option. Its composer rule is painted in the first frame, before Pi takes Enter: a prompt sent at the rule stayed unsent, 2 of 2, and one sent at the warning was submitted, 4 of 4 (pi 1.5.2, Linux). It is a side effect, not a contract, so a Pi that stops printing it fails the spawn and the worker is deleted, never handed back on a guess. The preamble changed, so CODEMAN_PREAMBLE moves to 1.51.0 in the preamble, the SKILL.md copy of it and every version check, and the plugin mirror is synced. Co-Authored-By: Claude Fable 5.2 --- docs/local-models.md | 8 +- plugins/codeman/skills/codeman/SKILL.md | 150 +++++++++---- plugins/codeman/skills/codeman/preamble.sh | 98 +++++++-- .../skills/codeman/reference/recipes.md | 2 +- .../codeman/skills/codeman/reference/verbs.md | 48 +++- skills/codeman/SKILL.md | 150 +++++++++---- skills/codeman/preamble.sh | 98 +++++++-- skills/codeman/reference/recipes.md | 2 +- skills/codeman/reference/verbs.md | 48 +++- test/agent-skill-local-model.test.ts | 207 +++++++++++++++++- 10 files changed, 652 insertions(+), 159 deletions(-) diff --git a/docs/local-models.md b/docs/local-models.md index e6f1f58d..b3814c5d 100644 --- a/docs/local-models.md +++ b/docs/local-models.md @@ -329,10 +329,12 @@ The codeman skill ([`skills/codeman`](../skills/codeman/SKILL.md#claude-in-the-c lets an agent in a Codeman session hand work to this model, so Claude in the cloud plans and reviews while the model on this Mac does the bulk: summaries, classification, first drafts, and files that should not leave the machine. `local_model` reads `GET /api/local-model` and says what -is installed or why nothing can run; `spawn_worker local` starts the runtime when it was -stopped (as Start does), then starts the default coding agent (`install.cli`) on `local-ollama` through the same +is installed or why nothing can run, with the coding agents the model can be run with and the +models on the Mac; `spawn_worker local` starts the runtime when it was +stopped (as Start does), then starts the default coding agent (`install.cli`), or with +`local:` (`local:pi`, `local:deepseek`) another one that is ready, on `local-ollama` through the same quick-start launch and the same checks as above; `sendmark` gives the worker a task and reads its -answer back from a file it writes in its own folder. The skill never installs a model and never +answer back from a file it writes in its own folder. The skill never installs a model or a coding agent and never answers a confirmation, the runtime still answers one request at a time, and while a worker it opened is live, Stop and Remove are refused until it is closed. diff --git a/plugins/codeman/skills/codeman/SKILL.md b/plugins/codeman/skills/codeman/SKILL.md index 3c4c9b67..3aebe247 100644 --- a/plugins/codeman/skills/codeman/SKILL.md +++ b/plugins/codeman/skills/codeman/SKILL.md @@ -107,7 +107,7 @@ later call opens with, and your first REAL call performs them anyway: ```bash . "${XDG_CACHE_HOME:-$HOME/.cache}/codeman-agent-$CODEMAN_SESSION_ID.sh" 2>/dev/null -[ "${CODEMAN_PREAMBLE:-}" = 1.50.0 ] || { echo "preamble missing or stale; run the full §0 block"; exit 1; } +[ "${CODEMAN_PREAMBLE:-}" = 1.51.0 ] || { echo "preamble missing or stale; run the full §0 block"; exit 1; } ``` ⚠️ **Never spend a Bash call on this check alone.** §1's block opens with this same @@ -135,8 +135,8 @@ PRE="${XDG_CACHE_HOME:-$HOME/.cache}/codeman-agent-$CODEMAN_SESSION_ID.sh" mkdir -p "$(dirname "$PRE")" # Rewrite unless the file already ends with THIS version's stamp, so a stale or a # half-written file self-heals here instead of costing you a round trip to rm it. -grep -qs '^CODEMAN_PREAMBLE=1.50.0$' "$PRE" || (umask 077; cat > "$PRE" <<'PREAMBLE' -# ---- Codeman agent preamble 1.50.0 (seeded by Codeman at session spawn; the SKILL.md §0 bootstrap rewrites it when missing or stale) ---- +grep -qs '^CODEMAN_PREAMBLE=1.51.0$' "$PRE" || (umask 077; cat > "$PRE" <<'PREAMBLE' +# ---- Codeman agent preamble 1.51.0 (seeded by Codeman at session spawn; the SKILL.md §0 bootstrap rewrites it when missing or stale) ---- # Positional parameters are always written with braces, ${1} and never a bare dollar-digit: Claude Code # replaces a bare one in a skill body with the matching word of the skill's arguments, and §0 is pasted # from that body (test/agent-skill.test.ts pins it). @@ -259,9 +259,18 @@ _accept_trust() { # -> 0 once it has answered the dialog, 1 if it could n # local_model -> ONE compact JSON object on stdout. rc 0 only when a `local` worker can be # spawned now (`running:true`) or once spawn_worker has started the runtime (`running:false`): # {"spawnable":true,"state":"installed","running":true,"cli":"opencode","cliLabel":"OpenCode", -# "endpointId":"local-ollama","modelId":"","modelLabel":"