mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-11-14 10:39:32 +01:00
docs(skill): Claude in the cloud, a local model on this Mac
SKILL.md gets a subsection after the DeepSeek Harness workers: why to hand work to the model on this Mac (bulk work with no per-token bill, files that stay on the machine, a second opinion, a worker that needs no network), the copy-paste block (local_model, spawn_worker local, sendmark, delete_session), and the limits as the code has them: a person installs the model, one request at a time, slower than the cloud, markers not hooks, a spawn can start a runtime the person stopped, admin only and experimental. The description now names the local model, so "use the local model" reaches the skill. Section 2 gains a row, the hook-less row points at sendmark, and reference/verbs.md gets 5.16 with every local_model reason, the spawn steps and the sendmark exit codes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
b913092be3
commit
28e8029ce5
4 files changed
+328
-10
No files matched your search
@@ -7,7 +7,8 @@ description: >-
|
||||
CLI, send them prompts, block until they finish (wait / wait-output / send-and-wait),
|
||||
read their output, and clean up; where available, message claude workers directly
|
||||
(Claude Code cross-session messaging). Use when asked to orchestrate or parallelize
|
||||
work across Codeman sessions, get a second opinion from another CLI or model, watch
|
||||
work across Codeman sessions, get a second opinion from another CLI or model, hand
|
||||
work to the local model Codeman installed on this Mac ("use the local model"), watch
|
||||
another session, start and manage workers, or retrieve saved local Codex history
|
||||
by file/native ID or root and descendants with topic/date filters. Live API
|
||||
operations require a Codeman-managed session (CODEMAN_MUX=1). Offline history
|
||||
@@ -763,8 +764,8 @@ the time went into deliberation, not the API. The four things that actually cost
|
||||
polled pid for nothing, and shipped its workers without lineage.
|
||||
- **Verifying what is already checked for you.** Two verifications specifically are not
|
||||
worth a call here, because `spawn_worker` carries them: the hooks check (it refuses a
|
||||
name that resolved to a hook-less directory with one local grep, so a worker it hands
|
||||
back always has a working `stop` and `sendwait` is trustworthy), and the pid poll,
|
||||
name that resolved to a hook-less directory with one local grep, so a claude worker it
|
||||
hands back always has a working `stop` and `sendwait` is trustworthy), and the pid poll,
|
||||
which is dead weight because `wait-output` already blocks on the composer.
|
||||
|
||||
Four things this block leans on, each one link away, no detour needed to run it:
|
||||
@@ -824,6 +825,70 @@ Three things to know before you spawn one:
|
||||
worker whose pane clearly finished: that profile is one of them, so drive it with
|
||||
markers instead.
|
||||
|
||||
### Claude in the cloud, a local model on this Mac
|
||||
|
||||
When Codeman has installed a model on the Mac it runs on (the **Run a local model** button
|
||||
on its welcome screen), you can hand work to that model from here. You stay the planner and
|
||||
the reviewer in the cloud; the Mac does the bulk. `spawn_worker <name> local` starts a worker
|
||||
on the installed model, run by the coding agent it was installed with (OpenCode on the Mac
|
||||
it was tested on), and from then on it is the same verbs. It mixes into one fleet like any
|
||||
other mode: `spawn_workers plan review:deepseek summ:local`.
|
||||
|
||||
Reach for it when:
|
||||
|
||||
- **The work is bulk, not judgment**: summarizing a folder, classifying, first drafts of
|
||||
tests, boilerplate. A local turn has no per-token bill.
|
||||
- **The files should stay on this machine**: the worker reads them here, and only what you
|
||||
read back from it enters your own context.
|
||||
- **You want a second opinion from a different model**, with different blind spots.
|
||||
- **The worker should not depend on the network**: the model's runtime listens on this Mac
|
||||
only, started with its cloud features off. You, the lead, still need a connection.
|
||||
|
||||
```bash
|
||||
. "${XDG_CACHE_HOME:-$HOME/.cache}/codeman-agent-$CODEMAN_SESSION_ID.sh" 2>/dev/null # §0 loader
|
||||
[ "${CODEMAN_PREAMBLE:-}" = 1.50.0 ] || { echo "preamble missing or stale; run the full §0 block"; exit 1; }
|
||||
local_model || exit 1 # one JSON line: model, agent, endpoint; or the reason there is none
|
||||
SID=$(spawn_worker summ local) || exit 1 # starts a stopped runtime first; empty = failed
|
||||
WD=$("${CURL[@]}" "$API/api/v1/sessions/$SID" | jq -r .data.workingDir)
|
||||
cp ./docs/*.md "$WD/" # its input goes inside its own folder (§5.9)
|
||||
R=$(sendmark "$SID" 'Summarize each .md file in this folder in two lines' 900); RC=$?
|
||||
jq -r '.answer // "(no answer)"' <<<"$R"
|
||||
if [ "$RC" = 0 ]; then delete_session "$SID" >/dev/null
|
||||
else echo "kept $SID (sendmark rc $RC: 2 no marker yet, 3 ended, 4 no answer file): $R"; fi
|
||||
```
|
||||
|
||||
`sendmark` is the local worker's `sendwait`: it adds one sentence to your prompt asking for
|
||||
the answer in `codeman-answer-<token>.md` in the worker's folder and then a split marker,
|
||||
waits for the marker with `wait-output`, and reads the file back
|
||||
([§5.16](reference/verbs.md#516-a-worker-on-the-local-model)). Read its `answer`, not the
|
||||
pane: a full-screen agent's pane is a poor transcript. ⚠️ A marker proves that the model
|
||||
printed a token, not that the work is done or right. Review what comes back the way you
|
||||
would review any first draft.
|
||||
|
||||
The limits, before you promise the user anything:
|
||||
|
||||
- **A Mac with Apple silicon, and a model a person installed** in Codeman's local model
|
||||
window. This skill never installs one: that is a download of many gigabytes the person
|
||||
confirms there. `local_model` says what is missing (`not-mac`, `not-installed`,
|
||||
`install-unfinished`, `blocked`, `admin-only`, `unsupported`), so tell the user that
|
||||
instead of guessing.
|
||||
- **One model, one request at a time.** The runtime loads one model and answers one request
|
||||
at a time, so two local workers take turns rather than work side by side. Fan out to the
|
||||
cloud; give the Mac a queue.
|
||||
- **Slower than a cloud model.** The one measured pair, OpenCode with Qwen3.6 35B-A3B on an
|
||||
M5 Pro with 48 GB, ran at about 68 tokens a second, and the first request after a start
|
||||
loads the model first. A local worker run by claude spends minutes on its first answer
|
||||
(its fixed prompt is about 36,000 tokens).
|
||||
- **Markers, not hooks.** OpenCode reports no end of turn, so `sendwait` answers 400 for
|
||||
that worker; `sendmark` is the way through. If the installed agent is claude, the worker
|
||||
has hooks and `sendwait` works as for any claude worker.
|
||||
- **Spawning can change what the person set.** A stopped runtime is started, which also
|
||||
undoes the person's Stop: it then starts with Codeman again. And while a local worker is
|
||||
open, the window refuses Stop and Remove and names your workers, so delete them as soon
|
||||
as they are done.
|
||||
- **Admin only in multi-user mode, and experimental.** The local model routes answer 403
|
||||
for anyone else (`admin-only`), and they may change in any release.
|
||||
|
||||
## 2. What do you want to do?
|
||||
|
||||
One row per job. Acting on this table alone is correct; the §5 links are the detail.
|
||||
@@ -833,7 +898,8 @@ One row per job. Acting on this table alone is correct; the §5 links are the de
|
||||
| start a worker **where the work is** | `POST /api/v1/quick-start {"caseName":…}`, which **creates** `~/codeman-cases/<name>` unless the name is already a case. Any other path (a git worktree): `POST /api/v1/sessions {"workingDir":…}` then `POST /api/v1/sessions/:id/interactive`. Both install hooks by default, so expect full signals in either, and **verify** rather than assume. N workers means N worktrees | [§5.1](reference/verbs.md#51-where-to-spawn) |
|
||||
| know a new worker can accept a prompt | `GET .../wait-output?match=shift+tab&from=buffer` (urlencode the `+`); a `deepseek` worker draws `❯` instead, and its boot `stop` fires ~300 ms BEFORE that, so never read the signal as readiness | [§5.2](reference/verbs.md#52-readiness) |
|
||||
| deliver a task **and** know when it finished | `POST .../input` with `"input":"…\r"`, `clientId`, `seq`, `"wait":true`. Resolves on `stop`, so it is trustworthy where the signal is real: claude mode with hooks (installed by default, but the operator can disable it and remote sessions never get them) and `deepseek` mode through its status bridge. Costs the worker one billed turn | [§5.3](reference/verbs.md#53-send-a-task-and-wait) |
|
||||
| know a hook-less worker finished | it has no `stop`, and `wait:true` there resolves on flapping `idle` **without erroring**: make it print a split, unique marker and `wait-output` on that instead | [§5.5](reference/verbs.md#55-markers-for-hook-less-workers) |
|
||||
| know a hook-less worker finished | it has no `stop`, and `wait:true` there resolves on flapping `idle` **without erroring**: make it print a split, unique marker and `wait-output` on that instead. For an agent worker, `sendmark <sid> "<prompt>"` does all of it and reads the answer back | [§5.5](reference/verbs.md#55-markers-for-hook-less-workers) |
|
||||
| hand work to the local model on this Mac (cheap bulk work, files that stay here, a second opinion) | `local_model` says what is installed; `spawn_worker <name> local` (or `name:local` in `spawn_workers`) starts a worker on it; `sendmark` tasks it and returns the answer | [Local model](#claude-in-the-cloud-a-local-model-on-this-mac), [§5.16](reference/verbs.md#516-a-worker-on-the-local-model) |
|
||||
| read the answer | `GET .../last-response`, **polled** (claude, codex and deepseek write a transcript; empty for the other modes) | [§5.4](reference/verbs.md#54-read-the-answer) |
|
||||
| know if it is alive | `GET .../wait?until=exit&timeout=1000`: an immediate `signal:"exit"` means dead. `status` and `pid` both lie | [§5.6](reference/verbs.md#56-alive-and-stuck) |
|
||||
| know if it is stuck | `GET .../active-tools` and `GET .../run-summary` are structured and free; two `terminal?tail=` samples are the crude fallback | [§5.6](reference/verbs.md#56-alive-and-stuck) |
|
||||
@@ -949,6 +1015,7 @@ these**; open the one row you actually hit.
|
||||
| [5.13 Messaging claude workers](reference/verbs.md#513-messaging-claude-workers) | `ListAgents` / `SendMessage` instead of the HTTP path |
|
||||
| [5.14 Clean up](reference/verbs.md#514-clean-up) | what deleting a session does **not** remove, and how to list the case dirs you left |
|
||||
| [5.15 Agent inbox](reference/verbs.md#515-agent-inbox) | messages that wait for the receiver instead of being typed: post, read until ack, who counts as the receiver |
|
||||
| [5.16 A worker on the local model](reference/verbs.md#516-a-worker-on-the-local-model) | what `local_model` answers, what `spawn_worker <name> local` does step by step, and every `sendmark` outcome |
|
||||
|
||||
## 6. Setup and auth
|
||||
|
||||
|
||||
@@ -484,7 +484,10 @@ snippet carries the exit code back to you.
|
||||
|
||||
For a **claude** worker with no hooks, ask for the marker in halves in the prompt
|
||||
itself ("print the word WORKDONE immediately followed by `_<token>`") for the same
|
||||
reason, and match the joined token. ⚠️ Against a TUI, match a single space-free token:
|
||||
reason, and match the joined token. For any **agent** worker without `stop` (a hook-less
|
||||
claude, opencode, gemini, pi, grok, omp, copilot, a worker on the local model), the
|
||||
preamble's `sendmark <sid> "<prompt>"` is exactly that in one call, and also asks for the
|
||||
answer in a file it reads back: [§5.16](#516-a-worker-on-the-local-model). ⚠️ Against a TUI, match a single space-free token:
|
||||
a full-screen TUI positions text with cursor movements rather than literal spaces, so
|
||||
the stripped stream can read `Yes,Itrustthisfolder`, and whether a phrase keeps its
|
||||
spaces depends on how the TUI happened to draw it (observed live: some match, some
|
||||
@@ -814,6 +817,95 @@ Rules that matter:
|
||||
- **Lifetime.** An inbox goes with its session on delete. A server restart empties all
|
||||
inboxes unless the user turned on *Keep agent inbox across restarts* in Settings.
|
||||
|
||||
### 5.16 A worker on the local model
|
||||
|
||||
The detail behind [Claude in the cloud, a local model on this
|
||||
Mac](../SKILL.md#claude-in-the-cloud-a-local-model-on-this-mac). Three preamble functions,
|
||||
all read from source and pinned by `test/agent-skill-local-model.test.ts` against a fake
|
||||
server; the local model routes themselves are experimental.
|
||||
|
||||
**`local_model`** is one `GET /api/v1/local-model` (the `/api/v1` alias reaches the
|
||||
experimental route like any other) and prints one JSON line. Exit 0 means a `local` worker
|
||||
can be spawned now (`running: true`) or once `spawn_worker` has started the runtime
|
||||
(`running: false`):
|
||||
|
||||
```json
|
||||
{"spawnable":true,"state":"installed","running":true,"cli":"opencode","cliLabel":"OpenCode",
|
||||
"endpointId":"local-ollama","modelId":"qwen3.6:35b-a3b","modelLabel":"Qwen3.6 35B-A3B"}
|
||||
```
|
||||
|
||||
Exit 1 carries `reason` and `message` (the server's own sentence where it has one):
|
||||
|
||||
| `reason` | What it means | What to tell the user |
|
||||
|----------|---------------|-----------------------|
|
||||
| `not-mac` | the server is not on macOS | local models need a Mac with Apple silicon |
|
||||
| `not-installed` | nothing installed; on a Mac where no model fits, `message` says why | install one with **Run a local model** on the welcome screen |
|
||||
| `install-unfinished` | a job is installing, paused or failed (`state`, `step`, `errorCode`) | finish it in that window; nothing here continues it |
|
||||
| `blocked` | installed, but the server refuses a session now (`errorCode`: `model-changed`, `port-in-use`, `runtime-start-failed`, `runtime-stopped`) | the window shows the one button that helps |
|
||||
| `admin-only` | multi-user mode, and you are not an admin (403) | ask an admin |
|
||||
| `unsupported` | the server has no local model routes (404) | it predates the feature |
|
||||
| `unauthorized`, `unreachable`, `error` | the request itself failed | §6 for a 401 |
|
||||
|
||||
It never downloads, installs or starts anything. Installing is many gigabytes the person
|
||||
confirms in the window, and no route this skill calls can do it.
|
||||
|
||||
**`spawn_worker <name> local`** (also `name:local` in `spawn_workers`), in order:
|
||||
|
||||
1. `local_model`; anything but exit 0 fails the spawn with that reason.
|
||||
2. Runtime stopped: `POST /api/v1/local-model/runtime {"action":"start"}`, what the
|
||||
window's Start button sends, then `local_model` again until it reports `running`
|
||||
(about 30 s at most). This undoes a Stop the person made. Concurrent spawns may both
|
||||
send it; the server runs one runtime verb at a time and keeps a runtime that runs.
|
||||
3. `POST /api/v1/quick-start {"caseName":…,"mode":"<cli>","parentSessionId":…,
|
||||
"customModel":{"endpointId":…,"modelId":…}}`: the launch of the window's "Start a
|
||||
session", with the installed agent as the mode. No advisor, which is a cloud model.
|
||||
The server checks again that its own runtime runs and that the model on disk is the one
|
||||
it installed, and refuses otherwise (`OPERATION_FAILED`, with the reason).
|
||||
4. An answer of `requiresContextWarning` or `requiresConfirmation` is a question for a
|
||||
person (a context below the agent's safe floor, a model swap that would evict another
|
||||
session's model). It fails the spawn with the answer on stderr and nothing on stdout,
|
||||
and no `confirmed*` flag is ever sent.
|
||||
5. Readiness: OpenCode's composer bar `┃` (its registry `promptGlyph`) through
|
||||
`wait-output`, 45 s, and the session deleted on a miss. A claude agent takes the claude
|
||||
path instead (hooks vetted, `shift+tab`, the trust dialog). Any other agent needs
|
||||
`LOCAL_READY_MARK` set to a space-free token its composer draws, or the spawn is refused
|
||||
before a session exists. Not measured here: an OpenCode first-run screen that hides the
|
||||
composer would read as a miss.
|
||||
|
||||
**`sendmark <sid> <prompt> [timeoutSec]`** drives it, and any other agent worker without
|
||||
an end-of-turn signal ([§5.5](#55-markers-for-hook-less-workers)):
|
||||
|
||||
- It joins the prompt onto one line and adds one sentence: write the complete answer to
|
||||
`codeman-answer-<token>.md` in the working directory, and only then end the reply with
|
||||
the word DONE immediately followed by `_<token>`. The typed text never holds the joined
|
||||
marker, so its echo cannot match.
|
||||
- It sends once with the exactly-once body (`$CID-<sid>`, an epoch-second `seq`, one `\r`)
|
||||
and a one-second `exit` wait, so `delivered` is the pane's answer, not a guess.
|
||||
- It waits on `wait-output?match=DONE_<token>&from=buffer` in rounds of at most 60 s until
|
||||
the timeout (default 600 s), then reads the answer file from this machine's disk: only a
|
||||
regular file (a symlink is refused), at most 256 KB.
|
||||
|
||||
| exit | JSON | Meaning |
|
||||
|------|------|---------|
|
||||
| 0 | `answer` holds the file | the marker printed and the file was read |
|
||||
| 2 | `matched: false` | no marker before the timeout. The worker may still be working: re-check the pane, never resend blindly |
|
||||
| 3 | `ended: true` or `delivered: false` | the worker's pane ended |
|
||||
| 4 | `matched: true, answer: null` | the marker printed, but no file within `SENDMARK_FILE_WAIT` s (default 120): the model ignored the instruction, or a reasoning model restated the marker while planning and is still at work |
|
||||
| 1 | `detail` | the send or a wait failed |
|
||||
|
||||
What a marker proves, and what it does not: the model printed the token. Not that the
|
||||
work is done, not that it is right, and with a reasoning model not even that the turn is
|
||||
over, which is why the file is the second gate. What a small local model writes is a
|
||||
first draft; review it before you build on it. The answer files stay in the worker's
|
||||
folder, a scratch case that [§5.14](#514-clean-up) lists for cleanup.
|
||||
`SENDMARK_ANSWER_FILE=0` asks for the marker alone, for a worker that cannot write where
|
||||
it runs (codex in a folder it runs read-only, an agent that asks before it writes a file,
|
||||
a worker on another host): exit 0 then means only that the marker printed. A worker that
|
||||
stops on such a question shows up here as exit 2, not as an error.
|
||||
|
||||
Close every local worker you opened: while one is open the window refuses Stop and
|
||||
Remove, naming it.
|
||||
|
||||
## Experimental bounded inspection and guarded submission
|
||||
|
||||
These three commands and their HTTP routes are labeled experimental, so they may
|
||||
|
||||
Reference in new issue
Block a user