mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-09-30 12:39:42 +02:00
ea5323d9908fd9ffe5651841076684627435c34a
5
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
4830e662f9 |
refactor(cli-registry): make CLI backends data instead of per-mode branching
Every run mode is now a `CliEntry` in `src/config/cli-registry/` — discovery (search dirs, version + identity probes), the launch argv template, env handling, the `capabilities` flags that replace per-CLI branching, and the `overlays` that back the remote/docker pane commands. Code that used to ask "which CLI is this?" reads the entry instead. Behaviour is unchanged. `test/cli-registry-spawn-golden.test.ts` pins every spawn command as a literal string, captured from the hand-written builders before they were deleted, and `test/location-overlay-commands.test.ts` does the same for all 20 remote and in-container pane commands. Config can never contain shell text: an entry declares typed argv tokens, literals are validated against a safe-word pattern at LOAD time (a bad literal rejects the whole entry — a silently dropped `--no-approve` is not cosmetic), and values resolve through patterns NAMED in code, so a user `clis.json` cannot widen its own validation. `~/.codeman/clis.json` overrides any entry, read-only in this release. OMP is included as a registry entry rather than a tenth hand-written builder, so `buildOmpCommand()`, the omp availability pre-flight, the omp arm of `buildPathExport()` and the omp entries in the truecolor/NO_COLOR, alt-screen and doctor ladders all drop out. Guard rails: - `test/cli-registry-no-id-branching.test.ts` fails the build if per-CLI-id branching reappears outside `stock.ts`, in any of its four shapes (`===`, `!==`, `switch`/`case`, `includes`) — an `===`-only version would miss the negated forms, which is how 36 of them survived an earlier pass. Every allowlisted branch carries its reason. - `external`, `hooks` and `altScreen` stay three INDEPENDENT capabilities; deriving one from another shipped the `until=stop`-hangs-on-shell bug. - `param` is two namespaces. `launch.params` keys, `configSetenv.fromParam` and `privilegedParams[].param` all name a LAUNCH param; the legacy `<Mode>Config` wire field is separate, bridged only by `legacyConfigAliases`. Getting `privilegedParams[].param` wrong is SILENT — it is the multi-user bypass clamp's only handle on a CLI's privilege switch, and a wrong name clamps nothing with no error and no failing test — so `schema.ts` rejects an entry naming a param it never declared. - Registry data resolves AT CALL TIME (`sessionModeSchema()`, `allowedEnvPrefixes()`, `dependencyRegistry()`, the resolvers' `searchDirs` thunks). A module-level const freezes at first import, so a CLI enabled while the server ran moved the run menu but not that surface. - Six fields are annotated DECLARED-FOR-LATER and read by nothing (`shortBadge`, `accent`, `capabilities.echo`/`wheelForward`/ `keyboardAccessory`/`maxFrameBytes`): all frontend behaviour, transcribed rather than measured. A test pins the list so it cannot quietly grow. Three user-visible changes, all deliberate and named: - `probeDockerCliVersion()` derives the in-container binary from the registry rather than assuming it equals the mode name (`antigravity` runs `agy`). - The remote CLI version probe now covers grok and deepseek, which the hardcoded map it replaces omitted while its own comment said the rule was "every mode except shell". - `codeman doctor`'s CLI rows are generated from the entries, so Claude's install hint is the install command rather than a docs URL, five CLIs gain hints they never had, and the row order follows the catalog. Also hardened along the way: `sessionModeSchema()` is bounded at 24 chars (matching the `cliId` pattern) before its failure message quotes the value back, and `deepMerge` skips `__proto__`/`constructor`/`prototype` when reading the hand-editable `clis.json`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WQkoi1cNegqVwZHgzx5SbJ |
||
|
|
6261b6f655 |
feat(skill): spawn and drive DeepSeek Harness workers
The agent skill could spawn a worker in any mode, but it could only
DRIVE a claude one: every other CLI has neither a real end-of-turn
signal nor an answer to read, so the recipes route them through output
markers.
dsh has both halves now -- its harness reports idle/working/blocked to
Codeman, and the previous commit reads its transcript -- so it joins
claude as a mode the four verbs work on unchanged. `spawn_workers alpha
beta:deepseek` is a mixed fleet in one call, and `sendwait` / `last_text`
/ `delete_session` need no per-mode variant.
Preamble 1.20.0 (SKILL.md's §0 heredoc regenerated from it):
- `spawn_worker` grows a deepseek branch that gates on the harness
composer. ⚠️ Readiness there is NOT the stop signal: the harness
reports idle at BOOT ~300 ms before its composer paints (measured
2.26 s vs 2.56 s after spawn), so a send-and-wait fired straight after
quick-start resolves on the boot edge, reports a turn that never ran,
and strands the prompt in a pane not yet taking input. Waiting for the
composer also spends that edge, since signals are edge-triggered.
- `spawn_workers` takes `name[:mode]`, so a mixed fleet stays one
concurrent call. Case names still have to be unique -- the mode never
disambiguates two workers that would share a directory.
- `sendwait` asks for `wait:"stop,exit"` instead of the `wait:true`
default set. That set also carries `idle`, which for an external CLI is
inferred from output stabilization: on a dsh worker whose TUI repaints
rarely, the re-wait resolved in 0 ms with `signal:"idle"` on a turn
with three minutes left to run. It also makes a wrong mode loud -- the
modes that cannot deliver `stop` answer 400 before writing anything,
instead of resolving on a flap.
- The self-heal resend carries `delivered:true` forward. The resend is a
tagged duplicate, so the server truthfully reports `delivered:false`
about a write it skipped, and §1's cleanup then read a completed turn
as an undelivered one and kept a finished worker forever.
- dsh workers spawn with the permission posture the Run button sends,
because the harness default still asks and a worker parked on an
approval row cannot finish a fan-out. The multi-user clamp still
applies.
Docs: a worked dsh flow in recipes.md, readiness and the signal rules in
verbs.md, and the corrections this makes necessary -- `stop`/`blocked`
are no longer claude-only, and `last-response` is no longer permanently
empty for deepseek. The integration guide gains a section on reading a
session back and driving one as a worker; its web-UI section was also
stale (that server moved out of a shell session).
The static guard that keeps those lists from naming some external CLIs but
not others is extended rather than exempted: it now knows the three real
classes inside that family (no transcript, no hook signals, and the
positive twin -- the modes whose answers can be read), with the hook class
derived from `hooksAvailableForMode()` so the predicate and the prose
cannot drift apart. Any other partial list still fails, and a new backend
belongs to none of the classes until someone says so.
|
||
|
|
bbc960a8ff |
fix(skill): harden the fast path against the review findings
Fifteen review findings on the fast-path rewrite plus one caught live, all verified against a real 1.18.1 server before landing: - sendwait picks a fresh seq (the epoch second) instead of a fixed 2, so a second prompt to the same worker is typed instead of silently swallowed as an already-applied duplicate; explicit seq remains for deliberate resends - sendwait self-heals stranded delivery: an Ink repaint occasionally eats the Enter (observed live), so a timed-out short first wait sends one bare \r and re-waits by resending the identical frame as a tagged duplicate - spawn_worker verifies the resolved casePath carries Codeman hooks (the same /api/hook-event marker the server checks), refusing names that resolve to linked or pre-existing hook-less directories instead of running the job in what may be the user's real repo - spawn_worker probes the trust dialog after a short 5s composer wait, not the full 45s, restoring the ladder staging verbs.md documents; on a readiness miss it deletes the half-spawned session and returns 1 with empty stdout, so a prompt can never be typed blind into a trust dialog - spawn_workers refuses duplicate case names and empty argument lists, and keys result files by index - section 1 is bash 3.2 compatible (indexed arrays, no declare -A), prints the full delivered/timedOut/signal tuple per worker with an explicit line for a missing result, deletes only workers whose turn really ended (a timeout means still working), cleans up spawned siblings when any spawn fails, and guards its mktemp - last_text takes the previous answer as an optional second argument for consecutive-turn reads (the transcript briefly serves the prior answer after a stop, observed live) - the stale duplicate bullets in section 1's closing list are gone - reference/verbs.md joins the mode-list drift guard's file list - README's skill inventory covers verbs.md and the new SKILL.md shape - the changeset is minor so the shipped release matches the 1.19.0 stamp Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
86234db1ef |
docs(skill): document the per-CLI availability probes, and guard the family
`GET /api/pi/status` shipped undocumented in the agent skill, and only a human reading the doc noticed. Turns out none of its five siblings were documented either, so this adds the whole family in one place: spawning with a mode whose CLI is absent fails with OPERATION_FAILED rather than falling back, which is exactly what an agent picking a backend it did not choose needs to know. Pi's extra `.data.version` is called out, since a false `available:false` there means an unrelated `pi` is in front on PATH. On whether the endpoint scanner should also check registered-to-documented: measured, and NO for the general case. The skill documents 34 of 217 registered endpoints deliberately (it is an agent guide, not an API reference), so a blanket reverse check needs a 183-entry allowlist that would fail CI on unrelated route work and get appended to mechanically, which is worse than the gap it closes. Grouping by path shape does not save it either: the families that yields are things like `DELETE /api/<any>/:id`, lumping cases, webviews and docker hosts together, and it would not have caught this gap anyway (the family had zero documented members). What IS cheap is a family the schema can enumerate with no allowlist: the new assertion derives the agent modes from the Zod enum and requires each one's `/api/<mode>/status` to be documented, so a seventh backend fails here until it is. The sibling scanner still proves the other direction, that nothing documented is a 404. Both mutation-checked: dropping pi's probe fails the new guard, and documenting a nonexistent probe fails the old one. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
86c78fece3 |
fix(pi): align the doctor with the pi resolver, correct the strip rationale, update the skill
Second review pass on #282, the three items left open after
|