mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-08 08:29:42 +02:00
docs: publish docs/wiki as the user manual, with a sync workflow
30 pages covering install, concepts, the dashboard, the agent CLIs, unattended
runs, remote and Docker cases, security and the HTTP API, plus a sidebar and a
footer. The wiki repo has no CI and no review, so docs/wiki is the source of
truth and .github/workflows/wiki-sync.yml mirrors it on every push to master.
The workflow refuses to mirror when docs/wiki is missing or holds no pages,
because it deletes before it copies and would otherwise publish the deletion of
every page. The footer carries a {{VERSION}} placeholder stamped at publish
time rather than a hand-written version, which went stale on every release.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,188 @@
|
||||
# Driving Codeman From An Agent
|
||||
|
||||
Everything the dashboard does is HTTP, so an agent can do it too. This page is for the case
|
||||
that makes Codeman interesting: **Claude Code running inside a Codeman session, spawning and
|
||||
supervising other sessions.**
|
||||
|
||||
Two routes. Start with the skill.
|
||||
|
||||
## The agent skill
|
||||
|
||||
A Claude Code skill that teaches the agent the whole API, so you ask in plain English
|
||||
instead of pasting endpoint documentation into prompts.
|
||||
|
||||
### Install it
|
||||
|
||||
| How | Command | Scope |
|
||||
| ------------ | ----------------------------------------------------------- | ----------------------------------------------------------- |
|
||||
| Skills CLI | `npx skills add Ark0N/Codeman --skill codeman -g` | Global, any skills-aware agent. |
|
||||
| Bundled CLI | `codeman skill install` | Global, at `~/.claude/skills/codeman`. |
|
||||
| Bundled CLI | `codeman skill install --case <name>` | One case. |
|
||||
| Web UI | **App Settings → Agents & CLIs → Claude → Agent Skill** | Injects into each case when a Claude session is created. Off by default. |
|
||||
|
||||
`codeman skill uninstall [--case <name>]` reverses the CLI installs, and never touches a
|
||||
`skills/codeman` you wrote yourself.
|
||||
|
||||
### Then just ask
|
||||
|
||||
| You say | What happens |
|
||||
| --------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ |
|
||||
| "What sessions are running right now?" | Lists them with name, mode, and status. Read-only. |
|
||||
| "Start a shell worker on the `myapp` case, run the test suite, tell me if it passes." | Spawns, waits on a completion marker, reads the exit code, cleans up. |
|
||||
| "Spin up 3 workers for lint, typecheck and tests, run them in parallel, report failures." | One session per task, all started first, then gathered as each finishes. |
|
||||
| "Have a claude worker summarize `src/session.ts`, then close it." | Spawns, runs the readiness ladder, sends and waits, reads the answer, deletes the session. |
|
||||
| "Watch session w4 and tell me if it gets stuck on a permission prompt." | Blocks on the `blocked` signal and surfaces the question to **you**. |
|
||||
|
||||
Sessions the agent creates get deleted when it is done. You can watch the tabs appear and
|
||||
disappear in the dashboard while it works.
|
||||
|
||||
### What it will and will not do
|
||||
|
||||
- **It self-gates.** Outside a Codeman session it refuses to act and does not guess an API
|
||||
URL, so a global install costs an unrelated Claude Code session nothing.
|
||||
- **Unprompted, it may only** spawn sessions, prompt them, and delete ones **it created in
|
||||
that conversation, by exact id**, behind a guard that refuses to delete the agent's own
|
||||
session.
|
||||
- **It will not** answer another session's permission prompt on your behalf. It surfaces the
|
||||
question instead.
|
||||
- **Deleting a case** (which erases a real directory of your code), bulk kills, respawn,
|
||||
Ralph, cron, orchestrator, and settings writes all require you to ask, naming the target.
|
||||
|
||||
Turning the setting back off **does not remove already-injected copies**, because a
|
||||
create-time sweep would yank the skill out from under other live sessions sharing that
|
||||
directory. Remove them per case with `codeman skill uninstall --case <name>`.
|
||||
|
||||
The skill ships with the verb index always loaded, plus on-demand references for the verbs,
|
||||
worked multi-worker recipes, endpoint tables, and cross-session messaging.
|
||||
|
||||
## The manual path
|
||||
|
||||
The same operations as raw HTTP, for a CI bot, a shell script, or an agent without skill
|
||||
support.
|
||||
|
||||
### Detect that you are inside Codeman
|
||||
|
||||
These are set in every managed session. Read them rather than hardcoding anything:
|
||||
|
||||
| Variable | Meaning |
|
||||
| -------------------------- | ------------------------------------------------------------------------ |
|
||||
| `CODEMAN_MUX=1` | You are in a managed tmux session. Never `tmux kill-session`, `pkill claude`, or `pkill tmux`: you will kill yourself or a sibling. |
|
||||
| `CODEMAN_API_URL` | Base URL, with the correct scheme. |
|
||||
| `CODEMAN_SESSION_ID` | Your own session id. Use it to avoid acting on yourself. |
|
||||
| `CODEMAN_HOOK_SECRET_FILE` | Path to the hook secret. |
|
||||
|
||||
### Rules of the road
|
||||
|
||||
Read these before writing any code. Each one has cost somebody an afternoon.
|
||||
|
||||
1. **Input is single line and must end with `\r`.** Enter fires only when the payload
|
||||
contains a carriage return. Without it the text sits unsubmitted on the prompt, the
|
||||
request still succeeds, and a combined wait burns its full timeout on a turn that never
|
||||
started. Embedded newlines are stripped rather than rejected, so `"echo A\necho B\r"` runs
|
||||
the joined `echo Aecho B`. One line per call.
|
||||
2. **Make input idempotent.** Send a stable `clientId` and a monotonic per-session `seq`. The
|
||||
server deduplicates, so a retry after a dropped connection cannot double-deliver.
|
||||
3. **Auth.** With `CODEMAN_PASSWORD` set, use HTTP Basic or the session cookie. A missing
|
||||
`Origin` is allowed, so plain curl works. A `401` replies with the bare string
|
||||
`Unauthorized`, **not** the JSON envelope, so piping it into `jq` throws a parse error
|
||||
instead of showing the failure. Check the status before parsing.
|
||||
4. **Envelope.** Most endpoints return `{ "success": true, "data": ... }`. A few legacy GETs
|
||||
return bare bodies, so handle both: `body.data ?? body`.
|
||||
5. **Wait instead of polling, and a timeout is not an error.** The wait endpoints answer
|
||||
`200` with `wait.timedOut: true`. Loop over short waits rather than one long call, because
|
||||
tunnels cut idle connections.
|
||||
6. **Only `claude` sessions emit `stop` and `blocked`.** They come from Claude Code hooks.
|
||||
Shell and the external CLIs accept only `idle`, `working`, and `exit`; asking for `stop`
|
||||
explicitly there is a `400`, while omitting `until` is always safe. On a shell session
|
||||
`idle` fires **once at startup and never again**, so synchronize hook-less sessions with an
|
||||
output marker instead.
|
||||
7. **Nothing reports "ready", so wait for it explicitly.** A new session answers
|
||||
`{"signal":"exit","immediate":true}` until its PID exists, and that means *not started*,
|
||||
not *crashed*. A Claude worker in a fresh case then sits on the CLI's trust dialog; prompt
|
||||
it there and the wait resolves on idle in about two seconds looking exactly like a finished
|
||||
turn, while your text sits stuck in the dialog.
|
||||
|
||||
### Recipes
|
||||
|
||||
```bash
|
||||
API="${CODEMAN_API_URL:-http://localhost:3000}"
|
||||
# Add -u admin:"$CODEMAN_PASSWORD" if a password is set, and -k on an HTTPS install.
|
||||
|
||||
# What is running
|
||||
curl -s "$API/api/sessions" | jq '.data[] | {id, name, mode, status}'
|
||||
|
||||
# Spawn a worker in a case
|
||||
curl -s -X POST "$API/api/quick-start" \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"caseName":"myapp","mode":"shell"}' | jq
|
||||
|
||||
# Send a prompt (note the \r)
|
||||
curl -s -X POST "$API/api/sessions/$ID/input" \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"input":"run the tests\r","clientId":"my-agent","seq":1}' | jq
|
||||
|
||||
# Send and block until the turn finishes (registers the wait BEFORE writing)
|
||||
curl -s -X POST "$API/api/sessions/$ID/input" \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"input":"summarize src/session.ts\r","wait":["stop"],"waitTimeout":120000}' | jq
|
||||
|
||||
# Or wait for a marker in the output, which works on shell sessions too
|
||||
curl -s "$API/api/sessions/$ID/wait-output?contains=DONE_17909&from=buffer" | jq
|
||||
|
||||
# Read the terminal back
|
||||
curl -s "$API/api/sessions/$ID/terminal?tail=4000" | jq -r '.data.output'
|
||||
|
||||
# Clean up, by exact id
|
||||
curl -s -X DELETE "$API/api/sessions/$ID" | jq
|
||||
```
|
||||
|
||||
Use `POST /api/quick-start` rather than `POST /api/sessions` when a case might be remote:
|
||||
the plain create endpoint validates the working directory locally and has no case concept.
|
||||
|
||||
### The split-marker trick
|
||||
|
||||
For hook-less sessions, synchronize on a marker in the output. The catch: your own
|
||||
keystrokes echo into the output stream, so an unsplit marker matches **before the command
|
||||
has run**.
|
||||
|
||||
Split it so the typed line never contains the string you are waiting for:
|
||||
|
||||
```bash
|
||||
M=DONE; R=17909
|
||||
# typed: echo ${M}_${R} → output contains DONE_17909, the typed line does not
|
||||
```
|
||||
|
||||
Make it unique per call, because tmux repaints replay old screen text.
|
||||
|
||||
### Reading output
|
||||
|
||||
Use `terminal?tail=`, not `/output`. The latter's text field is empty for every tmux-backed
|
||||
session, which is every interactive session. `tail` counts **bytes**, and what comes back is
|
||||
terminal data with ANSI sequences included.
|
||||
|
||||
## Fan-out, and why it needs care
|
||||
|
||||
Wait signals are **edge triggered with no history**. A signal that fires with no waiter
|
||||
registered is unobservable afterwards.
|
||||
|
||||
So a fan-out must register its waits before or as it dispatches: use send-and-wait per
|
||||
worker, or latched output markers. Dispatching all the workers and then waiting on them one
|
||||
at a time loses the signals of everyone who finished early.
|
||||
|
||||
Send-and-wait registers the waiter **before** the write for the same reason. A separate POST
|
||||
followed by a wait races, and reports the previous turn's state.
|
||||
|
||||
## Lineage
|
||||
|
||||
A create request can name the session that spawned it, through a body field or a header, and
|
||||
the dashboard then draws a lineage arc from parent to child. The skill sets it automatically.
|
||||
|
||||
It is resolved rather than trusted: an unresolvable parent is dropped silently rather than
|
||||
failing the spawn, because a cosmetic field must never break a worker.
|
||||
|
||||
## Read next
|
||||
|
||||
- [HTTP API](HTTP-API) - the endpoint map and the envelope.
|
||||
- [Hooks And Integrations](Hooks-And-Integrations) - events flowing the other way.
|
||||
- [Watching Agents Work](Watching-Agents-Work) - seeing the fan-out in the UI.
|
||||
- [`skills/codeman/SKILL.md`](https://github.com/Ark0N/Codeman/blob/master/skills/codeman/SKILL.md) - the skill itself.
|
||||
Reference in New Issue
Block a user