From b913092be39eaf4c1aff44ff394daf2e96745f74 Mon Sep 17 00:00:00 2001 From: Codeman maintainer Date: Sun, 8 Nov 2026 07:48:08 +0100 Subject: [PATCH 01/10] feat(skill): spawn and drive a worker on the local model The codeman skill's preamble learns the model Codeman installs on this Mac: - local_model asks GET /api/v1/local-model and prints one JSON line: the installed model, the coding agent that runs it and its endpoint, or the reason there is none (not a Mac, nothing installed, an unfinished install, a blocked install, admin only, a server without the routes). Exit 0 only when a worker can start now or after a runtime start. - spawn_worker local (and name:local in spawn_workers) starts a stopped runtime the way the window's Start button does, quick-starts the installed agent with customModel, and waits for its composer (OpenCode's bar, the claude path for a claude agent, LOCAL_READY_MARK otherwise). A confirmation question from quick-start fails the spawn with empty stdout; nothing is ever confirmed on the person's behalf. - sendmark drives any agent worker without an end-of-turn signal: the exactly-once input body, a split marker through wait-output, then the answer read back from a file the worker wrote in its folder (regular files only, 256 KB at most), with one exit code per outcome. The preamble stamp moves to 1.50.0 so every cached copy is rewritten. The tests run the real preamble against a stubbed server. Co-Authored-By: Claude Opus 5.5 --- plugins/codeman/skills/codeman/SKILL.md | 246 +++++++- plugins/codeman/skills/codeman/preamble.sh | 238 ++++++- .../skills/codeman/reference/recipes.md | 2 +- skills/codeman/SKILL.md | 246 +++++++- skills/codeman/preamble.sh | 238 ++++++- skills/codeman/reference/recipes.md | 2 +- test/agent-skill-local-model.test.ts | 596 ++++++++++++++++++ 7 files changed, 1494 insertions(+), 74 deletions(-) create mode 100644 test/agent-skill-local-model.test.ts diff --git a/plugins/codeman/skills/codeman/SKILL.md b/plugins/codeman/skills/codeman/SKILL.md index f8d67f51..f1416871 100644 --- a/plugins/codeman/skills/codeman/SKILL.md +++ b/plugins/codeman/skills/codeman/SKILL.md @@ -106,7 +106,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.44.0 ] || { echo "preamble missing or stale; run the full §0 block"; exit 1; } +[ "${CODEMAN_PREAMBLE:-}" = 1.50.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 @@ -134,8 +134,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.44.0$' "$PRE" || (umask 077; cat > "$PRE" <<'PREAMBLE' -# ---- Codeman agent preamble 1.44.0 (seeded by Codeman at session spawn; the SKILL.md §0 bootstrap rewrites it when missing or stale) ---- +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) ---- # 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). @@ -186,11 +186,15 @@ _composer_up() { # -> "true"/"false". `shift+tab` is the one --data-urlencode 'match=shift+tab' --data-urlencode 'from=buffer' \ --data-urlencode "timeout=${2}" | jq -r '.data.wait.matched // false' } +_mark_up() { # -> "true"/"false": a literal, space-free token + # in the pane's output, read from the buffer so a composer drawn before the wait counts. + "${CURL[@]}" -G "$API/api/v1/sessions/${1}/wait-output" \ + --data-urlencode "match=${2}" --data-urlencode 'from=buffer' \ + --data-urlencode "timeout=${3}" | jq -r '.data.wait.matched // false' +} _dsh_up() { # -> "true"/"false". The DeepSeek Harness TUI's # composer glyph. Override with DSH_READY_MARK for a profile that draws another one. - "${CURL[@]}" -G "$API/api/v1/sessions/${1}/wait-output" \ - --data-urlencode "match=${DSH_READY_MARK:-❯}" --data-urlencode 'from=buffer' \ - --data-urlencode "timeout=${2}" | jq -r '.data.wait.matched // false' + _mark_up "${1}" "${DSH_READY_MARK:-❯}" "${2}" } # ---- the workspace-trust dialog: READ the screen, never press Enter blind ---- # Claude Code 2.1.252 dropped the option numbers, REVERSED them, and highlights @@ -250,21 +254,123 @@ _accept_trust() { # -> 0 once it has answered the dialog, 1 if it could n done return 1 } +# ---- the local model: the model Codeman installed on this Mac, as a worker ---- +# 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":"