aicodeman 1.18.3 → 1.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (150) hide show
  1. package/README.md +15 -0
  2. package/dist/attachment-registry.d.ts +25 -0
  3. package/dist/attachment-registry.d.ts.map +1 -1
  4. package/dist/attachment-registry.js +45 -1
  5. package/dist/attachment-registry.js.map +1 -1
  6. package/dist/hooks-config.d.ts +22 -16
  7. package/dist/hooks-config.d.ts.map +1 -1
  8. package/dist/hooks-config.js +20 -14
  9. package/dist/hooks-config.js.map +1 -1
  10. package/dist/types/tools.d.ts +1 -1
  11. package/dist/types/tools.d.ts.map +1 -1
  12. package/dist/web/ports/config-port.d.ts +2 -0
  13. package/dist/web/ports/config-port.d.ts.map +1 -1
  14. package/dist/web/public/admin-ui.js.gz +0 -0
  15. package/dist/web/public/api-client.c9b1cddc.js.gz +0 -0
  16. package/dist/web/public/app.a8b08065.js +34 -0
  17. package/dist/web/public/app.a8b08065.js.br +0 -0
  18. package/dist/web/public/app.a8b08065.js.gz +0 -0
  19. package/dist/web/public/approvals-ui.js +24 -15
  20. package/dist/web/public/approvals-ui.js.br +0 -0
  21. package/dist/web/public/approvals-ui.js.gz +0 -0
  22. package/dist/web/public/{constants.822a486d.js → constants.35c7f91c.js} +167 -20
  23. package/dist/web/public/constants.35c7f91c.js.br +0 -0
  24. package/dist/web/public/constants.35c7f91c.js.gz +0 -0
  25. package/dist/web/public/cron-ui.js.gz +0 -0
  26. package/dist/web/public/entrance-animations.js.gz +0 -0
  27. package/dist/web/public/home-sessions.js +79 -35
  28. package/dist/web/public/home-sessions.js.br +0 -0
  29. package/dist/web/public/home-sessions.js.gz +0 -0
  30. package/dist/web/public/i18n.bb8949de.js +1 -0
  31. package/dist/web/public/i18n.bb8949de.js.br +0 -0
  32. package/dist/web/public/i18n.bb8949de.js.gz +0 -0
  33. package/dist/web/public/image-input.ee16ad88.js.gz +0 -0
  34. package/dist/web/public/index.html +109 -18
  35. package/dist/web/public/index.html.br +0 -0
  36. package/dist/web/public/index.html.gz +0 -0
  37. package/dist/web/public/input-cjk.63794d0b.js.gz +0 -0
  38. package/dist/web/public/keyboard-accessory.0454a3e9.js.gz +0 -0
  39. package/dist/web/public/{mobile-handlers.bc45f976.js → mobile-handlers.05b89237.js} +16 -0
  40. package/dist/web/public/mobile-handlers.05b89237.js.br +0 -0
  41. package/dist/web/public/mobile-handlers.05b89237.js.gz +0 -0
  42. package/dist/web/public/mobile-overview.js +17 -15
  43. package/dist/web/public/mobile-overview.js.br +0 -0
  44. package/dist/web/public/mobile-overview.js.gz +0 -0
  45. package/dist/web/public/mobile.c40d28a6.css +1 -0
  46. package/dist/web/public/mobile.c40d28a6.css.br +0 -0
  47. package/dist/web/public/mobile.c40d28a6.css.gz +0 -0
  48. package/dist/web/public/notification-manager.5d229063.js.gz +0 -0
  49. package/dist/web/public/orchestrator-panel.js.gz +0 -0
  50. package/dist/web/public/{panels-ui.1e4cb4b2.js → panels-ui.c5a82b6e.js} +48 -46
  51. package/dist/web/public/panels-ui.c5a82b6e.js.br +0 -0
  52. package/dist/web/public/panels-ui.c5a82b6e.js.gz +0 -0
  53. package/dist/web/public/ralph-panel.6de2d0f8.js.gz +0 -0
  54. package/dist/web/public/ralph-wizard.13a1831e.js.gz +0 -0
  55. package/dist/web/public/readmymind-ui.js.gz +0 -0
  56. package/dist/web/public/respawn-ui.ff0dae4c.js.gz +0 -0
  57. package/dist/web/public/sanitize-html.bc7078d6.js.gz +0 -0
  58. package/dist/web/public/session-lineage.js +47 -2
  59. package/dist/web/public/session-lineage.js.br +0 -0
  60. package/dist/web/public/session-lineage.js.gz +0 -0
  61. package/dist/web/public/{session-ui.20be58fa.js → session-ui.71a0ce9a.js} +3 -3
  62. package/dist/web/public/session-ui.71a0ce9a.js.br +0 -0
  63. package/dist/web/public/session-ui.71a0ce9a.js.gz +0 -0
  64. package/dist/web/public/{settings-ui.eacb4f92.js → settings-ui.6cb12d4b.js} +2 -2
  65. package/dist/web/public/settings-ui.6cb12d4b.js.br +0 -0
  66. package/dist/web/public/settings-ui.6cb12d4b.js.gz +0 -0
  67. package/dist/web/public/styles.325eb4f3.css +1 -0
  68. package/dist/web/public/styles.325eb4f3.css.br +0 -0
  69. package/dist/web/public/styles.325eb4f3.css.gz +0 -0
  70. package/dist/web/public/{subagent-windows.2a192c3e.js → subagent-windows.e6ca799f.js} +24 -12
  71. package/dist/web/public/subagent-windows.e6ca799f.js.br +0 -0
  72. package/dist/web/public/subagent-windows.e6ca799f.js.gz +0 -0
  73. package/dist/web/public/sw.js.gz +0 -0
  74. package/dist/web/public/terminal-ui.02b89e2c.js +4 -0
  75. package/dist/web/public/terminal-ui.02b89e2c.js.br +0 -0
  76. package/dist/web/public/terminal-ui.02b89e2c.js.gz +0 -0
  77. package/dist/web/public/ultracode-panel.js.gz +0 -0
  78. package/dist/web/public/ultracode-windows.js +15 -19
  79. package/dist/web/public/ultracode-windows.js.br +0 -0
  80. package/dist/web/public/ultracode-windows.js.gz +0 -0
  81. package/dist/web/public/upload.html.gz +0 -0
  82. package/dist/web/public/vendor/dompurify.min.js.gz +0 -0
  83. package/dist/web/public/vendor/marked.min.js.gz +0 -0
  84. package/dist/web/public/vendor/xterm-addon-fit.min.js.gz +0 -0
  85. package/dist/web/public/vendor/xterm-addon-serialize.min.js.gz +0 -0
  86. package/dist/web/public/vendor/xterm-addon-unicode11.min.js.gz +0 -0
  87. package/dist/web/public/vendor/xterm-addon-webgl.min.js.gz +0 -0
  88. package/dist/web/public/vendor/xterm-predictive-echo.6cec0fba.js.gz +0 -0
  89. package/dist/web/public/vendor/xterm-zerolag-input.137ad9f0.js.gz +0 -0
  90. package/dist/web/public/vendor/xterm.css.gz +0 -0
  91. package/dist/web/public/vendor/xterm.min.js.gz +0 -0
  92. package/dist/web/public/voice-input.4ec670b0.js.gz +0 -0
  93. package/dist/web/public/voice-pcm-worklet.js.gz +0 -0
  94. package/dist/web/public/webview-tabs.js +3 -0
  95. package/dist/web/public/webview-tabs.js.br +0 -0
  96. package/dist/web/public/webview-tabs.js.gz +0 -0
  97. package/dist/web/routes/file-routes.d.ts.map +1 -1
  98. package/dist/web/routes/file-routes.js +48 -6
  99. package/dist/web/routes/file-routes.js.map +1 -1
  100. package/dist/web/routes/session-routes.d.ts.map +1 -1
  101. package/dist/web/routes/session-routes.js +50 -12
  102. package/dist/web/routes/session-routes.js.map +1 -1
  103. package/dist/web/schemas.d.ts +5 -0
  104. package/dist/web/schemas.d.ts.map +1 -1
  105. package/dist/web/schemas.js +13 -0
  106. package/dist/web/schemas.js.map +1 -1
  107. package/dist/web/sensitive-path.d.ts.map +1 -1
  108. package/dist/web/sensitive-path.js +17 -0
  109. package/dist/web/sensitive-path.js.map +1 -1
  110. package/dist/web/server.d.ts +20 -0
  111. package/dist/web/server.d.ts.map +1 -1
  112. package/dist/web/server.js +51 -0
  113. package/dist/web/server.js.map +1 -1
  114. package/dist/web/session-listener-wiring.d.ts.map +1 -1
  115. package/dist/web/session-listener-wiring.js +5 -0
  116. package/dist/web/session-listener-wiring.js.map +1 -1
  117. package/package.json +1 -1
  118. package/skills/codeman/SKILL.md +41 -26
  119. package/skills/codeman/preamble.sh +8 -8
  120. package/skills/codeman/reference/endpoints.md +28 -34
  121. package/skills/codeman/reference/messaging.md +8 -6
  122. package/skills/codeman/reference/recipes.md +3 -3
  123. package/skills/codeman/reference/verbs.md +39 -24
  124. package/dist/web/public/app.445f1584.js +0 -34
  125. package/dist/web/public/app.445f1584.js.br +0 -0
  126. package/dist/web/public/app.445f1584.js.gz +0 -0
  127. package/dist/web/public/constants.822a486d.js.br +0 -0
  128. package/dist/web/public/constants.822a486d.js.gz +0 -0
  129. package/dist/web/public/i18n.86757605.js +0 -1
  130. package/dist/web/public/i18n.86757605.js.br +0 -0
  131. package/dist/web/public/i18n.86757605.js.gz +0 -0
  132. package/dist/web/public/mobile-handlers.bc45f976.js.br +0 -0
  133. package/dist/web/public/mobile-handlers.bc45f976.js.gz +0 -0
  134. package/dist/web/public/mobile.1eef31fe.css +0 -1
  135. package/dist/web/public/mobile.1eef31fe.css.br +0 -0
  136. package/dist/web/public/mobile.1eef31fe.css.gz +0 -0
  137. package/dist/web/public/panels-ui.1e4cb4b2.js.br +0 -0
  138. package/dist/web/public/panels-ui.1e4cb4b2.js.gz +0 -0
  139. package/dist/web/public/session-ui.20be58fa.js.br +0 -0
  140. package/dist/web/public/session-ui.20be58fa.js.gz +0 -0
  141. package/dist/web/public/settings-ui.eacb4f92.js.br +0 -0
  142. package/dist/web/public/settings-ui.eacb4f92.js.gz +0 -0
  143. package/dist/web/public/styles.70bf9c2a.css +0 -1
  144. package/dist/web/public/styles.70bf9c2a.css.br +0 -0
  145. package/dist/web/public/styles.70bf9c2a.css.gz +0 -0
  146. package/dist/web/public/subagent-windows.2a192c3e.js.br +0 -0
  147. package/dist/web/public/subagent-windows.2a192c3e.js.gz +0 -0
  148. package/dist/web/public/terminal-ui.c182a68d.js +0 -4
  149. package/dist/web/public/terminal-ui.c182a68d.js.br +0 -0
  150. package/dist/web/public/terminal-ui.c182a68d.js.gz +0 -0
@@ -42,18 +42,22 @@ hundred-odd lines at the top of every call (a half-re-pasted preamble used to be
42
42
  single most likely way to break a run).
43
43
 
44
44
  **Codeman seeds the preamble file for you** when it spawns a claude session (server
45
- 1.18.3+), so the bootstrap is usually just loading it — the same two lines every later
46
- call starts with:
45
+ 1.18.3+), so the bootstrap is usually nothing at all: these are the two lines every
46
+ later call opens with, and your first REAL call performs them anyway:
47
47
 
48
48
  ```bash
49
49
  . "${XDG_CACHE_HOME:-$HOME/.cache}/codeman-agent-$CODEMAN_SESSION_ID.sh" 2>/dev/null
50
- [ "${CODEMAN_PREAMBLE:-}" = 1.18.3 ] || { echo "preamble missing or stale; run the full §0 block"; exit 1; }
50
+ [ "${CODEMAN_PREAMBLE:-}" = 1.19.0 ] || { echo "preamble missing or stale; run the full §0 block"; exit 1; }
51
51
  ```
52
52
 
53
- If that passed, §0 is done: go straight to your job (§1's block opens with this same
54
- loader, so when §1 is the job you can simply start there). Only when it reports
55
- missing or stale, run the full block below once — and run it **verbatim**: paste it
56
- as-is, never re-type it, trim it, or "extract the parts you need". A hand-assembled
53
+ ⚠️ **Never spend a Bash call on this check alone.** §1's block opens with this same
54
+ loader, so when §1 is the job, start there: the check rides the spawn call for free,
55
+ and a standalone "preamble OK" call buys nothing while costing a full model turn
56
+ (measured live: a lone check plus the deliberation around it added ~6 s to a 28 s
57
+ two-worker run). §0 is done the moment any job call passes its opening check. Only
58
+ when a call reports missing or stale, run the full block below once — and run it
59
+ **verbatim**: paste it as-is, never re-type it, trim it, or "extract the parts you
60
+ need". A hand-assembled
57
61
  preamble is the documented failure mode of this skill: one live run rebuilt it
58
62
  "minimally" and lost the `X-Codeman-Parent-Session` header (every worker spawned with
59
63
  no lineage arc in the web UI) and the fast-path functions (the spawn fell back to a
@@ -71,8 +75,8 @@ PRE="${XDG_CACHE_HOME:-$HOME/.cache}/codeman-agent-$CODEMAN_SESSION_ID.sh"
71
75
  mkdir -p "$(dirname "$PRE")"
72
76
  # Rewrite unless the file already ends with THIS version's stamp, so a stale or a
73
77
  # half-written file self-heals here instead of costing you a round trip to rm it.
74
- grep -qs '^CODEMAN_PREAMBLE=1.18.3$' "$PRE" || (umask 077; cat > "$PRE" <<'PREAMBLE'
75
- # ---- Codeman agent preamble 1.18.3 (seeded by Codeman at session spawn; the SKILL.md §0 bootstrap rewrites it when missing or stale) ----
78
+ grep -qs '^CODEMAN_PREAMBLE=1.19.0$' "$PRE" || (umask 077; cat > "$PRE" <<'PREAMBLE'
79
+ # ---- Codeman agent preamble 1.19.0 (seeded by Codeman at session spawn; the SKILL.md §0 bootstrap rewrites it when missing or stale) ----
76
80
  API="${CODEMAN_API_URL:?CODEMAN_API_URL not set; refusing to guess}"
77
81
  SELF="${CODEMAN_SESSION_ID:?CODEMAN_SESSION_ID not set}"
78
82
  # Credentials, cheapest first. Your session has usually INHERITED the server's
@@ -134,14 +138,14 @@ spawn_worker() {
134
138
  # NOT retryable in a loop: every quick-start failure code is terminal (§5.1).
135
139
  [ -n "$sid" ] || { jq -c '{error,errorCode}' <<<"$q" >&2; return 1; }
136
140
  [ "$mode" = claude ] || { printf '%s\n' "$sid"; return 0; } # only claude draws a composer
137
- # quick-start RESOLVES the name before creating: a linked case or an existing dir
138
- # wins over a fresh scratch case, so "created => hooks" is only true after this one
139
- # local grep (the same marker the server itself checks for). No marker means sendwait
140
- # would false-resolve on flapping idle, possibly inside the user's REAL repo: refuse
141
- # rather than run the job there.
141
+ # The server installs hooks into every claude workspace now, so this grep normally
142
+ # passes; it stays because the install is gated on a setting the operator can turn
143
+ # off, remote sessions never get hooks, and a session created by an older server
144
+ # still has none. No marker means sendwait would false-resolve on flapping idle,
145
+ # possibly inside the user's REAL repo: refuse rather than run the job there.
142
146
  cp=$(jq -r '.data.casePath // empty' <<<"$q")
143
147
  grep -qs '/api/hook-event' "$cp/.claude/settings.local.json" || {
144
- echo "case '$name' resolved to '$cp', which has no Codeman hooks (linked or pre-existing?): pick an unused name, or work §5.1+§5.5 by hand" >&2
148
+ echo "case '$name' resolved to '$cp', which has no Codeman hooks (workspaceHooksEnabled off, remote, or an older server?): turn the setting on, or work §5.1+§5.5 by hand with markers" >&2
145
149
  delete_session "$sid" >/dev/null; return 1; }
146
150
  # Short composer wait FIRST, then the trust-dialog probe: a case still showing the
147
151
  # dialog can never pass the composer wait, so probing early keeps a cold case from
@@ -229,10 +233,10 @@ last_text() {
229
233
  # The stamp is the LAST line on purpose (a truncated write leaves it unset) and is kept
230
234
  # bare on purpose: the write condition above anchors on it with $, so an inline comment
231
235
  # here would fail that match and rewrite this file on every single bootstrap.
232
- CODEMAN_PREAMBLE=1.18.3
236
+ CODEMAN_PREAMBLE=1.19.0
233
237
  PREAMBLE
234
238
  )
235
- . "$PRE"; [ "${CODEMAN_PREAMBLE:-}" = 1.18.3 ] || { echo "preamble at $PRE is stale or truncated: rm it and re-run this block"; exit 1; }
239
+ . "$PRE"; [ "${CODEMAN_PREAMBLE:-}" = 1.19.0 ] || { echo "preamble at $PRE is stale or truncated: rm it and re-run this block"; exit 1; }
236
240
  ```
237
241
 
238
242
  Every later Bash call that touches the API starts with the same two loader lines from
@@ -273,14 +277,18 @@ plain-text 401: see §6 and [the symptom gallery](reference/endpoints.md#symptom
273
277
  block is the whole thing. Run it, report, and stop reading. §2 onward is for jobs this
274
278
  does not cover; you are not being careless by not reading them.**
275
279
 
276
- Fill in the case names and the prompts. Everything below is `spawn_workers` /
277
- `sendwait` / `last_text` / `delete_session` from the §0 preamble, so there is nothing
278
- to assemble and no per-call body to hand-build.
280
+ Fill in the case names and the prompts, then run it as your FIRST Bash call: no
281
+ standalone preamble check before it (line one below IS that check), and no
282
+ reconnaissance. `ls ~/codeman-cases` answers nothing this block needs: invented
283
+ fresh names need no lookup, and `spawn_worker` refuses a name that already exists
284
+ rather than silently reusing it. Everything below is `spawn_workers` / `sendwait` /
285
+ `last_text` / `delete_session` from the §0 preamble, so there is nothing to assemble
286
+ and no per-call body to hand-build.
279
287
 
280
288
  ```bash
281
289
  . "${XDG_CACHE_HOME:-$HOME/.cache}/codeman-agent-$CODEMAN_SESSION_ID.sh" 2>/dev/null # §0 loader
282
- [ "${CODEMAN_PREAMBLE:-}" = 1.18.3 ] || { echo "preamble missing or stale; run the full §0 block"; exit 1; }
283
- N=(alpha beta) # one FRESH case name per worker
290
+ [ "${CODEMAN_PREAMBLE:-}" = 1.19.0 ] || { echo "preamble missing or stale; run the full §0 block"; exit 1; }
291
+ N=(alpha beta) # INVENT one fresh case name per worker; never list cases first
284
292
  T=('reply with one line: the absolute path of your working directory'
285
293
  'reply with one line: your model name') # tasks, same order as N
286
294
 
@@ -307,10 +315,16 @@ done; rm -rf "$D"
307
315
 
308
316
  Measured against a live 1.18.0 server: two cold workers spawned and ready in **6.3 s**,
309
317
  both turns dispatched and both answers read in **4.0 s** more. If your run takes minutes,
310
- the time went into deliberation, not the API. The three things that actually cost time:
318
+ the time went into deliberation, not the API. The four things that actually cost time:
311
319
 
312
320
  - **Spawning serially.** One worker per Bash call is one model turn per worker. `&` plus
313
321
  `wait`, as above, makes N workers cost about what one costs.
322
+ - **Reconnaissance turns before the spawn.** A standalone preamble check, an
323
+ `ls ~/codeman-cases`, a `list_sessions` "to see what is there": each is a whole
324
+ model turn spent learning something this block already handles (line one performs
325
+ the preamble check, invented names need no listing, and `spawn_worker` refuses
326
+ collisions). A live two-worker run spent ~12 s of its 28 s total on exactly two
327
+ such turns; the API work in between was under 10 s.
314
328
  - **Re-deriving the happy path** from §5.1 + §5.2 + §5.3 + §5.10. That is what the
315
329
  preamble functions exist to end. Compose them; do not rebuild them. The tells that
316
330
  you are rebuilding anyway: a `for` loop around `quick-start`, a poll on `.data.pid`,
@@ -329,7 +343,8 @@ Four things this block leans on, each one link away, no detour needed to run it:
329
343
  `~/codeman-cases/<name>`, not your repo. A name that already means something (a
330
344
  linked case, a pre-existing directory) is refused by `spawn_worker` rather than
331
345
  silently reused. Spawning where the work actually is (a linked case, a git worktree)
332
- is a different call with **no hooks**, and the costliest mistake in this skill: §5.1.
346
+ is a different call, and picking the wrong one is the costliest mistake in this
347
+ skill: §5.1. Those workspaces do get hooks now, unless the operator disabled it.
333
348
  - `sendwait` supplies the `\r`, picks a fresh `seq`, and self-heals a stranded Enter.
334
349
  A prompt without the `\r` is never submitted (§3), a reused `seq` is silently
335
350
  swallowed as an already-applied duplicate, and an Enter eaten by an Ink repaint
@@ -344,9 +359,9 @@ One row per job. Acting on this table alone is correct; the §5 links are the de
344
359
 
345
360
  | I want to | Call | Detail |
346
361
  |-----------|------|--------|
347
- | 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: full signals there. Any other path (a git worktree): `POST /api/v1/sessions {"workingDir":…}` then `POST /api/v1/sessions/:id/interactive`, and expect **no hooks**. N workers means N worktrees | [§5.1](reference/verbs.md#51-where-to-spawn) |
362
+ | 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) |
348
363
  | know a new worker can accept a prompt | `GET .../wait-output?match=shift+tab&from=buffer` (urlencode the `+`) | [§5.2](reference/verbs.md#52-readiness) |
349
- | deliver a task **and** know when it finished | `POST .../input` with `"input":"…\r"`, `clientId`, `seq`, `"wait":true`. Resolves on `stop`, so it is only trustworthy in a **case Codeman created** (claude mode + hooks present). Costs the worker one billed turn | [§5.3](reference/verbs.md#53-send-a-task-and-wait) |
364
+ | deliver a task **and** know when it finished | `POST .../input` with `"input":"…\r"`, `clientId`, `seq`, `"wait":true`. Resolves on `stop`, so it is trustworthy only where the workspace **has hooks** (claude mode; installed by default, but the operator can disable it and remote sessions never get them). Costs the worker one billed turn | [§5.3](reference/verbs.md#53-send-a-task-and-wait) |
350
365
  | 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) |
351
366
  | read the answer | `GET .../last-response`, **polled** (claude/codex only; empty for the other modes) | [§5.4](reference/verbs.md#54-read-the-answer) |
352
367
  | 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) |
@@ -1,4 +1,4 @@
1
- # ---- Codeman agent preamble 1.18.3 (seeded by Codeman at session spawn; the SKILL.md §0 bootstrap rewrites it when missing or stale) ----
1
+ # ---- Codeman agent preamble 1.19.0 (seeded by Codeman at session spawn; the SKILL.md §0 bootstrap rewrites it when missing or stale) ----
2
2
  API="${CODEMAN_API_URL:?CODEMAN_API_URL not set; refusing to guess}"
3
3
  SELF="${CODEMAN_SESSION_ID:?CODEMAN_SESSION_ID not set}"
4
4
  # Credentials, cheapest first. Your session has usually INHERITED the server's
@@ -60,14 +60,14 @@ spawn_worker() {
60
60
  # NOT retryable in a loop: every quick-start failure code is terminal (§5.1).
61
61
  [ -n "$sid" ] || { jq -c '{error,errorCode}' <<<"$q" >&2; return 1; }
62
62
  [ "$mode" = claude ] || { printf '%s\n' "$sid"; return 0; } # only claude draws a composer
63
- # quick-start RESOLVES the name before creating: a linked case or an existing dir
64
- # wins over a fresh scratch case, so "created => hooks" is only true after this one
65
- # local grep (the same marker the server itself checks for). No marker means sendwait
66
- # would false-resolve on flapping idle, possibly inside the user's REAL repo: refuse
67
- # rather than run the job there.
63
+ # The server installs hooks into every claude workspace now, so this grep normally
64
+ # passes; it stays because the install is gated on a setting the operator can turn
65
+ # off, remote sessions never get hooks, and a session created by an older server
66
+ # still has none. No marker means sendwait would false-resolve on flapping idle,
67
+ # possibly inside the user's REAL repo: refuse rather than run the job there.
68
68
  cp=$(jq -r '.data.casePath // empty' <<<"$q")
69
69
  grep -qs '/api/hook-event' "$cp/.claude/settings.local.json" || {
70
- echo "case '$name' resolved to '$cp', which has no Codeman hooks (linked or pre-existing?): pick an unused name, or work §5.1+§5.5 by hand" >&2
70
+ echo "case '$name' resolved to '$cp', which has no Codeman hooks (workspaceHooksEnabled off, remote, or an older server?): turn the setting on, or work §5.1+§5.5 by hand with markers" >&2
71
71
  delete_session "$sid" >/dev/null; return 1; }
72
72
  # Short composer wait FIRST, then the trust-dialog probe: a case still showing the
73
73
  # dialog can never pass the composer wait, so probing early keeps a cold case from
@@ -155,4 +155,4 @@ last_text() {
155
155
  # The stamp is the LAST line on purpose (a truncated write leaves it unset) and is kept
156
156
  # bare on purpose: the write condition above anchors on it with $, so an inline comment
157
157
  # here would fail that match and rewrite this file on every single bootstrap.
158
- CODEMAN_PREAMBLE=1.18.3
158
+ CODEMAN_PREAMBLE=1.19.0
@@ -251,10 +251,12 @@ that is expected, not a failure: read `terminal?tail=` and strip ANSI instead.
251
251
  **It means** that session has no Codeman hooks, so `stop` can never fire and the wait
252
252
  silently degraded to `idle`, which flaps mid-turn. Nothing rejected your request:
253
253
  `wait:true` (and even an explicit `until=stop`) is accepted because the 400 is about
254
- session **mode**, and the mode really is `claude`. Hooks are written only when Codeman
255
- **creates** the directory; a linked case or a raw `workingDir` gets none (an existing
256
- case that Codeman created earlier keeps the block it was given), see the table under
257
- [Signals by mode](#signals-by-mode). Measured: on a
254
+ session **mode**, and the mode really is `claude`. Hooks are installed into every
255
+ claude workspace at session create (synced `workspaceHooksEnabled`, default ON) and
256
+ swept across recovered sessions at boot, so a linked case or a raw `workingDir` gets
257
+ them too; with the setting off, on a remote session, or on a session from an older
258
+ server, they are absent, see the table under
259
+ [Signals by mode](#signals-by-mode). Measured before that changed: on a
258
260
  linked case whose `.claude/settings.local.json` carries env/model/permissions/statusLine
259
261
  and no `hooks` block, a `wait?until=stop,exit` parked for twelve consecutive 60 s rounds
260
262
  never resolved although the worker finished its turn.
@@ -360,10 +362,10 @@ loop.
360
362
  ⚠️ `caseName` resolves through the linked-cases registry first, so a name that happens
361
363
  to match a case the user linked in lands in that **real repo**, not a fresh scratch
362
364
  directory. Pick distinctive scratch names, and use a linked name deliberately when you
363
- do want a worker in an existing checkout. ⚠️ It also decides whether you get hooks:
364
- Codeman writes them only when it **creates** the directory, so a linked case or a raw
365
- path gives you a worker with no `stop` signal, while a scratch case Codeman created
366
- earlier keeps working signals ([Signals by mode](#signals-by-mode)).
365
+ do want a worker in an existing checkout. It no longer decides whether you get hooks:
366
+ every claude create path installs them, so a linked case and a raw path both get a
367
+ `stop` signal unless the operator turned `workspaceHooksEnabled` off
368
+ ([Signals by mode](#signals-by-mode)).
367
369
 
368
370
  **The two-step alternative, `POST /api/v1/sessions`.** Use it when you need a session in
369
371
  a directory that is not a case (body takes `workingDir`, `mode`, `name`, `effort`,
@@ -614,35 +616,27 @@ Three bounded long-polls. Shared semantics:
614
616
  | `exit` | PTY exited or session deleted | every mode |
615
617
 
616
618
  ⚠️ **`claude` mode is necessary for `stop`/`blocked`, not sufficient. The real
617
- precondition is that the session's working directory has a Codeman hooks block**, and
618
- whether it does depends on who created the directory:
619
+ precondition is that the session's working directory has a Codeman hooks block**, which
620
+ is now installed by default rather than depending on who created the directory:
619
621
 
620
622
  | The worker's directory | Hooks | `stop` / `blocked` | Synchronize with |
621
623
  |------------------------|-------|--------------------|------------------|
622
- | Codeman created it (`quick-start` with a NEW `caseName`, `POST /api/cases`, clone, docker quickcreate) | written at create | fire | send-and-wait on `stop` |
623
- | Codeman never created it (a linked case pointing at your own checkout, a raw `workingDir`) | none written | never fire | `wait-output` markers only |
624
-
625
- ⚠️ **Docker cases are the one exception.** For a docker case, quick-start writes hooks
626
- whenever `.claude/settings.local.json` is *missing* (`session-routes.ts:2836-2845`:
627
- absent means write, present means refresh), regardless of who created that host
628
- directory. There the discriminator really is "does the settings file exist". No
629
- downstream advice changes, since docker quickcreate is already on the create side.
630
-
631
- ⚠️ For every non-docker case the discriminator is **who created the directory, not
632
- whether it exists now**. A
633
- scratch case Codeman created last week still has its hooks block on disk, so
634
- `quick-start` against that existing name gets working `stop` signals. Only a directory
635
- Codeman never created lacks them. When in doubt, test it rather than reason about it:
636
- grep for `/api/hook-event` in `<casePath>/.claude/settings.local.json`.
637
-
638
- `writeHooksConfig()` runs only on the create paths (`case-routes.ts:341`, `:520`,
639
- `:869`, `ralph-routes.ts:318`, `session-routes.ts:2799` inside
640
- `if (!existsSync(resolvedCasePath))`, `:2841` for docker). Quick-start against a
641
- directory that already exists takes the else-if branch and calls
642
- `refreshStaleCodemanHooks()`, which returns immediately when there is no
643
- `settings.local.json` and again when the hooks it finds are not ours
644
- (`hooks-config.ts:706-731`); it never *adds* a hooks block. `POST /api/cases/link` is
645
- not on that list at all: it only records a name-to-path entry. See
624
+ | any claude workspace, with `workspaceHooksEnabled` ON (the default) | installed at session create, add-only merge | fire | send-and-wait on `stop` |
625
+ | the same, with the setting OFF and no block already on disk | none added | never fire | `wait-output` markers only |
626
+ | a remote SSH session, a docker case that opted out, a workspace Codeman cannot write | none | never fire | `wait-output` markers only |
627
+ | a session created by a pre-1.19.0 server and never restarted since | whatever it had | only if present | check, then choose |
628
+
629
+ The install is an add-only merge, so a user's own hook entries survive and a malformed
630
+ settings file is left untouched. Sessions recovered at server boot get the same sweep,
631
+ which is what heals sessions created before this behavior existed. When in doubt, test
632
+ it rather than reason about it: grep for `/api/hook-event` in
633
+ `<casePath>/.claude/settings.local.json`.
634
+
635
+ Before 1.19.0, `writeHooksConfig()` ran only on the create paths and `quick-start`
636
+ against an existing directory called `refreshStaleCodemanHooks()`, which never *adds* a
637
+ block, so a linked case or a raw `workingDir` had no hooks at all. `POST
638
+ /api/cases/link` still only records a name-to-path entry; what changed is that the
639
+ session-create path installs hooks regardless of how the directory got there. See
646
640
  [symptom 8](#8-send-and-wait-resolves-instantly-with-signalidle-and-the-answer-is-last-turns).
647
641
 
648
642
  Default `until` set: `stop,idle,exit`. On non-claude modes the server silently drops
@@ -196,12 +196,14 @@ idle:
196
196
  The contract an orchestrator follows for any fleet of two or more messaging workers.
197
197
  Every topology in the next section is this protocol plus a wiring diagram.
198
198
 
199
- 1. **Spawn with a name, and with hooks.** Use `quick-start` with `sessionName` (the
200
- `--name` gate above), and let it **CREATE** the case. ⚠️ Linking does NOT install
201
- hooks (`POST /api/cases/link` writes only the name-to-path entry), and neither does a
202
- bare `POST /api/sessions`; a worker in a directory Codeman did not create has no
203
- `stop`/`blocked` signals at all and every synchronization below degrades to output
204
- markers. The discriminator is who created the directory, not whether it exists now.
199
+ 1. **Spawn with a name, and confirm hooks.** Use `quick-start` with `sessionName` (the
200
+ `--name` gate above). Session create installs the hooks block into the workspace
201
+ whatever kind it is, so a linked case and a raw `POST /api/sessions` path both get
202
+ `stop`/`blocked` by default. ⚠️ Not unconditionally: the operator can turn
203
+ `workspaceHooksEnabled` off, remote SSH sessions never get hooks, and a session from
204
+ an older server may have none, and without them every synchronization below degrades
205
+ to output markers. Grep `<casePath>/.claude/settings.local.json` for
206
+ `/api/hook-event` at spawn rather than inferring it from how the directory got there.
205
207
  2. **Readiness before addressing.** Flow 1's ladder per worker, then the availability
206
208
  probe. A worker that fails the probe is an HTTP worker for the rest of the run; that
207
209
  is a routing decision, not an error.
@@ -492,9 +492,9 @@ What breaks if you use send-and-wait anyway: `wait:true` is accepted (the 400 is
492
492
  *mode*, not about hooks, and these are claude-mode sessions), so the call falls back to
493
493
  the default set's `idle`, which is a heuristic that flaps mid-turn. You get a "finished"
494
494
  answer for a turn still running, and `last-response` then hands you the *previous*
495
- turn's text. The contrast is the lesson: a worker in a case Codeman created (Flow 1) has
496
- the hooks, so `stop` there is definitive and free. In a worktree you pay one marker per
497
- worker instead.
495
+ turn's text. The contrast is the lesson: a worker whose workspace carries the hooks
496
+ block (Flow 1, and by default any other workspace too) has a `stop` that is definitive
497
+ and free. Where the block is absent you pay one marker per worker instead.
498
498
 
499
499
  ```bash
500
500
  declare -A TOK
@@ -30,28 +30,40 @@ wrong directory.** `quick-start` with a new `caseName` does not find your repo:
30
30
  | Where the work is | Call | Hooks, and therefore signals |
31
31
  |-------------------|------|------------------------------|
32
32
  | a fresh scratch dir (throwaway experiments) | `POST /api/v1/quick-start {"caseName":"scratch-1","mode":"claude"}` with a **new** case name | Codeman creates the directory and **writes hooks**: `stop` and `blocked` fire, send-and-wait is trustworthy |
33
- | a linked case (a real repo in the linked-cases registry) | same call with the linked name | **no hooks**, unless that repo already carries a Codeman hooks block from some earlier path. Check before relying on `stop` |
34
- | any other absolute path, e.g. a git worktree you made | `POST /api/v1/sessions {"workingDir":"/abs/path","mode":"claude"}` then `POST /api/v1/sessions/:id/interactive` | **no hooks**: no `stop`, no `blocked`, synchronize with markers ([§5.5](#55-markers-for-hook-less-workers)) |
33
+ | a linked case (a real repo in the linked-cases registry) | same call with the linked name | **hooks installed at session create**, so `stop` fires here too. Not guaranteed: the operator can turn it off. Check |
34
+ | any other absolute path, e.g. a git worktree you made | `POST /api/v1/sessions {"workingDir":"/abs/path","mode":"claude"}` then `POST /api/v1/sessions/:id/interactive` | same: **hooks installed at session create**, subject to the same setting. Check |
35
35
 
36
36
  Read `.data.casePath` back from the `quick-start` response and check it is where you
37
37
  meant. `caseName` accepts letters, digits, `-` and `_` only, and it resolves through
38
38
  the linked-cases registry **first**, so a name that collides with something the user
39
39
  linked in lands in that real repo rather than a scratch dir.
40
40
 
41
- **The rule is who created the directory.** Codeman writes hooks only where it created
42
- the workspace itself: `quick-start` on a NEW case name, `POST /api/cases`, the repo
43
- clone, the docker quick-create. Those hooks persist, so a scratch case created last
44
- week still has them today. A directory that already existed when Codeman first pointed
45
- at it never gets them: `POST /api/cases/link` writes only the name-to-path entry in
46
- `linked-cases.json`, and quick-start into an existing path runs
47
- `refreshStaleCodemanHooks()`, which by design returns immediately when there is no
48
- Codeman hooks block to refresh. Source-verified by exhaustive call-site grep, and
49
- measured: a worker in a linked case never resolved a parked `wait?until=stop,exit`
50
- across twelve consecutive 60 s rounds, although it had finished its turn.
51
-
52
- **Check, do not assume.** Read `<casePath>/.claude/settings.local.json` with your own
53
- file tools and look for `/api/hook-event`. Present means `stop`/`blocked` will fire;
54
- absent means they never will.
41
+ **The rule is a setting, not who created the directory.** Every claude create path
42
+ (`POST /api/sessions`, `POST /api/quick-start`, and quick-start's docker branch) now
43
+ installs the hooks block into the workspace, and the server sweeps the workspaces of
44
+ sessions it recovers at boot. So a linked case, a cloned repo and a hand-made git
45
+ worktree all get `stop`/`blocked`, not just a scratch case Codeman scaffolded. The
46
+ install is an **add-only merge**: a user's own hook entries and every other settings
47
+ key survive, and a malformed settings file is left alone.
48
+
49
+ The gate is the synced **`workspaceHooksEnabled`** setting, **default ON** (an absent
50
+ key counts as ON). Turned OFF, the old behavior returns exactly: an existing Codeman
51
+ block is still refreshed when stale, but one is never added, and the boot sweep is
52
+ skipped. Three cases stay hook-less regardless: **remote SSH sessions** (their
53
+ `workingDir` is a path on another host), **docker cases that opted out**, and any
54
+ workspace Codeman cannot write to.
55
+
56
+ Until this landed, hooks existed only where Codeman created the directory, and the
57
+ gap was invisible: a worker in a linked case never resolved a parked
58
+ `wait?until=stop,exit` across twelve consecutive 60 s rounds, although it had finished
59
+ its turn. If you are driving an older server, assume that older rule.
60
+
61
+ **Check, do not assume.** This is now the load-bearing habit, because you cannot tell
62
+ from the call which way the setting is set, and an old session created before the fix
63
+ on a server that has not restarted still has nothing. Read
64
+ `<casePath>/.claude/settings.local.json` with your own file tools and look for
65
+ `/api/hook-event`. Present means `stop`/`blocked` will fire; absent means they never
66
+ will, whatever kind of workspace it is.
55
67
 
56
68
  ⚠️ **The hook-less failure is silent, and it is the worst one in this skill.**
57
69
  `"wait":true` is still **accepted** on a hook-less claude session: the 400 you may be
@@ -59,9 +71,10 @@ expecting is about session *mode*, not about hooks. With no `stop` to resolve on
59
71
  default signal set falls back to the heuristic `idle`, which flaps mid-turn, so
60
72
  send-and-wait returns "finished" while the worker is still working, and the
61
73
  `last-response` you read next hands you the **previous** turn's text. No error is
62
- raised anywhere. In any workspace Codeman did not create, use markers
63
- ([§5.5](#55-markers-for-hook-less-workers)) and treat send-and-wait's answer as
64
- unreliable.
74
+ raised anywhere. Hooks are installed by default now, so this is rarer than it was, but
75
+ the failure is unchanged when it happens: in any workspace whose settings file has no
76
+ `/api/hook-event`, use markers ([§5.5](#55-markers-for-hook-less-workers)) and treat
77
+ send-and-wait's answer as unreliable.
65
78
 
66
79
  Spawning at a raw path:
67
80
 
@@ -238,11 +251,13 @@ fi
238
251
 
239
252
  ### 5.3 Send a task and wait
240
253
 
241
- ⚠️ **Precondition: this is the call to prefer only for a claude worker in a workspace
242
- Codeman created**, because it is trustworthy only when the `stop` hook exists. On a
243
- linked case or a raw path it is accepted, resolves on flapping `idle`, and reports a
244
- turn as finished while it is still running, with no error anywhere. Check hooks first
245
- ([§5.1](#51-where-to-spawn)); where they are absent, use markers
254
+ ⚠️ **Precondition: a claude worker whose workspace has the hooks block**, because
255
+ this is trustworthy only when the `stop` hook exists. Every claude create path installs
256
+ it by default now, so that is the normal case, but where it is absent (the setting off,
257
+ a remote session, an older server) the call is still accepted, resolves on flapping
258
+ `idle`, and reports a turn as finished while it is still running, with no error
259
+ anywhere. Check hooks first ([§5.1](#51-where-to-spawn)); where they are absent, use
260
+ markers
246
261
  ([§5.5](#55-markers-for-hook-less-workers)).
247
262
 
248
263
  It registers the waiter *before* typing,