crewforth 3.2.0-rc.1 → 3.2.0-rc.10

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 (74) hide show
  1. package/CHANGELOG.md +190 -5
  2. package/README.md +2 -0
  3. package/VERSION +1 -1
  4. package/adopt.sh +55 -6
  5. package/bin/cli.js +3 -2
  6. package/kit/CLAUDE.md +6 -6
  7. package/kit/agents/crew-commit-agent.md +6 -4
  8. package/kit/agents/crew-review-agent.md +2 -0
  9. package/kit/eval/doctor.sh +37 -3
  10. package/kit/eval/gate-report.sh +2 -0
  11. package/kit/eval/lib/crew-map/narrative.mjs +42 -8
  12. package/kit/eval/lib/crew-map/project.mjs +3 -2
  13. package/kit/eval/lib/crew-map/rules/tech.json +35 -0
  14. package/kit/eval/lib/crew-map/scan.mjs +56 -4
  15. package/kit/eval/lib/crew-map/stacks.mjs +114 -8
  16. package/kit/eval/lib/crew-map/surface.mjs +81 -3
  17. package/kit/eval/lib/gate-log.sh +52 -0
  18. package/kit/eval/lib/json-string.awk +27 -0
  19. package/kit/eval/lib/preset.sh +109 -0
  20. package/kit/eval/lib/presets/default.json +7 -0
  21. package/kit/eval/lib/presets/solo.json +7 -0
  22. package/kit/eval/lib/presets/strict.json +11 -0
  23. package/kit/eval/lib/preview.sh +150 -0
  24. package/kit/eval/lib/review-bench/bench.mjs +273 -0
  25. package/kit/eval/lib/review-prepass/prepass.mjs +203 -0
  26. package/kit/eval/lib/review-prepass/prepass.sh +23 -0
  27. package/kit/eval/lib/settings-default.sh +36 -0
  28. package/kit/eval/lib/settings-json.awk +14 -4
  29. package/kit/eval/lib/statusline.sh +4 -19
  30. package/kit/eval/lib/verify-cmd.sh +17 -0
  31. package/kit/eval/lib/version.sh +41 -0
  32. package/kit/eval/review-bench.sh +26 -0
  33. package/kit/eval/smoke-test.sh +424 -26
  34. package/kit/hooks/agent-outcome.sh +38 -9
  35. package/kit/hooks/board-sync.sh +2 -23
  36. package/kit/hooks/guard-agent-model.sh +163 -20
  37. package/kit/hooks/guard-bash.sh +69 -36
  38. package/kit/hooks/guard-commit-scan.sh +10 -2
  39. package/kit/hooks/guard-write.sh +3 -7
  40. package/kit/hooks/precompact-snapshot.sh +75 -0
  41. package/kit/hooks/prompt-approval.sh +74 -5
  42. package/kit/hooks/session-rehydrate.sh +25 -2
  43. package/kit/hooks/session-update-check.sh +9 -8
  44. package/kit/hooks/trace-blocklist.txt +9 -0
  45. package/kit/settings.json +18 -0
  46. package/kit/skills/crew-code-review/SKILL.md +16 -0
  47. package/kit/skills/crew-map/SKILL.md +16 -5
  48. package/kit/skills/crew-review/SKILL.md +2 -1
  49. package/kit/skills/crew-ship/SKILL.md +3 -1
  50. package/kit/studio/README.md +21 -10
  51. package/kit/studio/server/index.js +85 -22
  52. package/kit/studio/server/lib/code-stamp.js +35 -0
  53. package/kit/studio/server/lib/graph.js +9 -1
  54. package/kit/studio/server/lib/kit-telemetry.js +30 -5
  55. package/kit/studio/server/lib/lan.js +87 -0
  56. package/kit/studio/server/lib/owned-stream.js +56 -0
  57. package/kit/studio/server/lib/projects.js +11 -4
  58. package/kit/studio/server/lib/redact.js +60 -0
  59. package/kit/studio/server/lib/remote.js +104 -18
  60. package/kit/studio/server/lib/session.js +1 -1
  61. package/kit/studio/server/lib/terminal.js +51 -13
  62. package/kit/studio/server/lib/transcript.js +32 -0
  63. package/kit/studio/web/app.js +79 -11
  64. package/kit/studio/web/canvas.js +14 -2
  65. package/kit/studio/web/chat.js +114 -24
  66. package/kit/studio/web/graph-plan.js +3 -2
  67. package/kit/studio/web/liveness.js +14 -0
  68. package/kit/studio/web/map-plan.js +72 -3
  69. package/kit/studio/web/map.js +103 -10
  70. package/kit/studio/web/qr.js +200 -0
  71. package/kit/studio/web/style.css +40 -0
  72. package/kit/studio/web/tokens.css +6 -0
  73. package/package.json +1 -1
  74. package/start.sh +14 -1
package/CHANGELOG.md CHANGED
@@ -9,8 +9,85 @@ versioning follows [SemVer](https://semver.org/).
9
9
 
10
10
  ### Added
11
11
 
12
- - **In `auto`, approve a commit or a push with one click.** The refusal now also offers a fixed question, asked with
13
- AskUserQuestion: Commit only, Commit + push, Push only, No. A new gate, `guard-question.sh`, answers `ask` on it,
12
+ - **Watch Studio from a phone on the same Wi-Fi.** A viewer is a second token that only reads: the Machines tab makes
13
+ one, lists each with its last request, and revokes it. **Add viewer** shows its link as a QR code; a phone on
14
+ the same network scans it and the panel opens there, with no ssh and no tunnel. While a viewer is live the server
15
+ also listens on the machine's private Wi-Fi address, for viewer tokens only (the panel's own token gets `401` from
16
+ the network); with none, nothing listens beyond `127.0.0.1`. A viewer lasts eight hours, then the phone shows
17
+ Session expired; revoking it closes its stream at once, and the last one closes the Wi-Fi listener. Transcripts
18
+ are hidden by default: the server sends such a link no transcript text at all (an allow-list of fields, applied to
19
+ every answer and to the event stream), and shows them only for a link added with them ticked. Every viewer
20
+ connection and refusal is logged. Measured with an Android phone; the firewall asks once (macOS: allow `node`;
21
+ Windows: Private networks).
22
+
23
+ - **The advisor, measured.** `review-bench --advisor <model>` runs the agent arm with an advisor and reports each
24
+ run's cost by model and its advisor calls; a case of `kind: "task"` is an implementation graded by a check the
25
+ session never saw. Against Opus alone on five review cases and a task (24 runs), Sonnet with an Opus advisor
26
+ never called it: 6 of 6 defects caught against 5 of 6, for $1.06 against $2.23. No rule is added; the runs and
27
+ their numbers are in `packaging/advisor-bench/`.
28
+
29
+ - **An organisation preset.** `--preset <name|file>` on the installers (and `preset.sh --apply` later) writes an
30
+ organisation's critical paths, model floors, verify commands and language as one marked block at the top of
31
+ `.claude/crew-model-rules`; the project's own lines stay below it and win where the rules let them. Three ship:
32
+ `default`, `strict`, `solo`. An invalid preset is refused before anything is installed; `doctor` names the one
33
+ in force. It covers the surfaces the gates read today; which agents a project gets is not part of it.
34
+
35
+ - **Continue here says what it will do before it is pressed.** Studio continues a session it did not start as
36
+ that same session when nothing else holds it open, and starts a copy when something does; the read-only foot now
37
+ says which it will be before the press, from the check it acts on, and offers the command that opens the session
38
+ in the desktop app again (`claude --desktop --resume <id>`) to copy. Measured with a real session: its id stayed
39
+ the same from a terminal, through Studio, and back to a terminal, in one transcript.
40
+ On Windows the desktop command is a PowerShell line, and the terminal Studio opens runs `claude --resume <id>`
41
+ without single quotes, which cmd.exe handed to the program as part of the id. An id that is not a session id
42
+ builds no command at all.
43
+ The terminal Studio opens runs `claude --resume <id>` and nothing else: the route no longer takes a `command`
44
+ from the request, which it ran unchecked and the panel never sent.
45
+
46
+ - **A stopped command says what it would have touched.** When the shell gate stops `git reset --hard`,
47
+ `git clean -f`, `git push --force` or `rm -r`, or asks about a commit or a push, one line says what it would
48
+ have done: the uncommitted changes and commits it discards, the paths a dry run lists, the remote commits it
49
+ overwrites, the files under a target inside the project, what a commit takes and a push sends. Read only, after
50
+ the decision and in a subshell of its own, so a failing preview never changes the decision. The push question
51
+ became cheaper too: the branch is read from HEAD without a process.
52
+
53
+ - **What a compaction keeps is written down first.** A PreCompact hook writes the state of the work from the files
54
+ and the gates (branch, uncommitted files, the last gate refusal, the findings on record, a handover if there is
55
+ one) inside the repository's git directory, and SessionStart gives it back after the compaction, to the same
56
+ session only. It never blocks the compaction. Studio's session card adds how much of the context since the last
57
+ compaction is tool output, and how many times the session was compacted.
58
+
59
+ - **The Map reads like a project, not a graph.** Studio's Map draws modules in the layers `/crew-map` wrote (UI,
60
+ API, Service, Data, Infrastructure, Library, Tools), each card naming the module in full with its domain, up to
61
+ three of what it is built with and the outside services it calls folded into one line under it. A search dims
62
+ what it does not name; the tour `/crew-map` wrote steps through the map over the bottom of the pane; the
63
+ inspector shows the layer, the domain and what is inside a module (its entry point, its folders and their file
64
+ counts). A map with no layers is drawn in the bands the scan gives, as before.
65
+
66
+ - **The Crew Map says where each module sits, and how to read the project.** `/crew-map` now has the model add,
67
+ to each module, its layer (`ui`, `api`, `service`, `data`, `infra`, `tool`, `library`) and the part of the
68
+ business it serves, and to the map a tour of up to eight stops a newcomer reads in order. Each rests on lines
69
+ the scan read; `--validate` refuses a layer not on the list, a domain written as a sentence, a tour stop at no
70
+ node, citing another node's lines or visiting a node twice, and a scan again drops what no longer holds.
71
+ A module's README is read too: its first line of prose is a line a description may rest on (measured on a real
72
+ project, four of ten modules had nothing else to describe them by).
73
+
74
+ - **How right the review is, in numbers.** `eval/review-bench.sh` runs the review on labelled cases and reports
75
+ recall, precision and every false alarm by name; the pre-pass arm costs nothing, the agent arm runs Claude Code
76
+ headless as the review agent under a per-case and a per-run dollar cap, isolated from the user's own settings.
77
+ Crewforth's 27 cases come from its own history (a fix undone, its explanation left out) and 8 are held out; the
78
+ pre-pass's floor on them is a CI gate. Measured with Sonnet: 18 of 20 defects found, 34 of 49 findings about the defect, $1.62.
79
+
80
+ - **The review starts from a deterministic pre-pass.** `eval/lib/review-prepass/prepass.sh` reads the lines a
81
+ change adds (staged, or `--base <ref>` for a branch) and writes `.crewforth/review-prepass.json` in the findings
82
+ shape: a secret pattern (cited by hash), a debug call or a TODO comment left in code, shellcheck on shell files, a lock
83
+ file that moved without its manifest, a very large change. The review agent carries those over and writes its
84
+ own findings to `.crewforth/review.json`, blockers through the refuter. What it could not check (no shellcheck,
85
+ a file edited after staging, no Node) is said, never read as clean.
86
+
87
+ - **In `auto`, approve a commit or a push with one click.** A fixed question, asked with AskUserQuestion: Commit
88
+ only, Commit + push, Push only, No. It is handed to the session when `crew-commit-agent` is called, so `/crew-ship`
89
+ asks it with the message, and a refused commit offers it too (measured in real sessions: offered by the refusal
90
+ alone, it never came up, since the session asks before trying a commit). A new gate, `guard-question.sh`, answers `ask` on it,
14
91
  so a hook that fills in the answer cannot answer it for you (measured in 34 runs and by hand in the terminal), and
15
92
  `prompt-approval.sh` records your click exactly as it records `/crew-approve`: tied to what is staged, `HEAD`, the
16
93
  session, for 30 minutes.
@@ -60,9 +137,6 @@ versioning follows [SemVer](https://semver.org/).
60
137
  - **Studio's Models view marks the classes whose agents consulted the advisor.** The cost cell carries an advisor tag
61
138
  whose hover says how many calls and how much of the cost they were, already included; a class whose agents consulted
62
139
  none has no tag.
63
- - **Read-only remote viewers for Studio.** The Machines tab makes a viewer link that only reads, lists each viewer
64
- with its last request, and revokes it. The server still binds to `127.0.0.1`; watching from another device goes
65
- through a tunnel you run (`ssh -L`, Tailscale).
66
140
  - **A visible way to the repository.** `/crew-doctor` now shows the star line once per version under its own marker,
67
141
  so a line already shown at install no longer silences it, and a plugin install shows it too, with the plugin's
68
142
  version. `/crew-update` keeps it to one line per update. The READMEs and the site's home page have a Star on GitHub
@@ -79,6 +153,34 @@ versioning follows [SemVer](https://semver.org/).
79
153
 
80
154
  ### Changed
81
155
 
156
+ - **A typed approval is caught at the turn's end.** The one-click question reached the user only from a refused
157
+ commit or a call to the commit agent, and a session that wrote the message itself asked the user to type
158
+ `/crew-approve` without trying either: 9 of 18 typed asks in two real `auto` sessions. Now, when a reply asks for
159
+ `/crew-approve` in `auto` with the question open and it was not asked since the user's last message, the session
160
+ is told once to ask the click. It never blocks the turn; a question that was refused or dismissed leaves the typed
161
+ way, and a turn's end does not end an approval.
162
+
163
+ - **A refused commit asks one way.** In `auto`, with the approval question open, the refusal offered the click and
164
+ the typed `/crew-approve` side by side, and a session picked the typed one 7 times in 9. The click is now the way
165
+ to ask, and the typed command is only the fallback for a question that cannot be asked.
166
+
167
+ - **A task card on one line is read.** `files: … · change: … · verify: …` (with `;` or `|` too, after an optional
168
+ `Card:`, and `above:` on the same line) is read by its labels and held to the rules of the three-line card, and a
169
+ kind with a note in parentheses (`architecture (planning only)`) is that kind, on either form. Measured on one
170
+ machine's sessions: 37 calls had been refused for their card, every one followed by a second call; 17 of them now
171
+ pass on the first.
172
+
173
+ - **A model is chosen to cost what the work needs.** Measured on a real project: 186 crew tasks, 0 on haiku, 73 (39%)
174
+ above their card's floor, and no verify command ever passing, so the cheaper path never opened. Now:
175
+ - the card's model is the default: a call above it runs on it (the gate rewrites the call's model), unless the
176
+ card adds `above: <why>`;
177
+ - outside the crew, a call asking for opus or fable runs on sonnet, and so does a general-purpose call that names
178
+ no model in a session that is not on haiku (it took the session's model; measured on Claude Code 2.1.299, the
179
+ project's settings `env` could not change that, so the gate does);
180
+ - a verify command in a sub-folder (`cd apps/x && <command>`, 41 of 71 refusals) is run in that folder;
181
+ `none (why)` is none; and `/crew-doctor` lists the commands that were never allowed to run, with the line that
182
+ allows each.
183
+
82
184
  - **A commit's command is read by one reader.** `guard-bash.sh` paired quotes in three places: the reader of a call,
83
185
  an older walk that looks for a path or `-a` in a commit, and the check of a call the user's approval covers. The
84
186
  last two now take their text from the first, and keep only their own question. What a call the user approved may
@@ -100,6 +202,10 @@ versioning follows [SemVer](https://semver.org/).
100
202
 
101
203
  ### Security
102
204
 
205
+ - **The session-link trailer is a trace.** Claude Code asks the model to end a commit message with a line linking the
206
+ session on claude.ai; the trace scan had no pattern for it, and six commits of a project with Crewforth installed
207
+ carried it to a remote. The trailer and its address are both caught now, in the message and in staged files.
208
+
103
209
  - **A `PermissionRequest` hook could approve a commit with nobody at the keyboard.** Such a hook answers a
104
210
  permission prompt before the person does, and the answer cannot be told from a click: measured, it approved the
105
211
  Bash prompt `guard-bash.sh` raises for a commit in `default`, and it answered the new question too. Before it
@@ -114,6 +220,85 @@ versioning follows [SemVer](https://semver.org/).
114
220
 
115
221
  ### Fixed
116
222
 
223
+ - **Crew Map reads a `fetch` call's method from its own options only.** The method was taken from the next four
224
+ lines, so in a file of one-line calls a `{ method: 'POST' }` on a later call was given to the earlier ones, and the
225
+ map reported `POST /api/products is called but no endpoint in this repo provides it` (endpoint-missing) for a
226
+ plain `fetch('/api/products')`. The scan now reads the call's arguments up to its own closing parenthesis, over
227
+ strings and nested brackets; a method on a later line of the same call is still read.
228
+ - **A push approval covers a tag on the approved commit.** `git push <remote> <tag>` had no way to be approved: the
229
+ click and `/crew-approve push` both record a push of the branch, and the tag push of a release was refused after
230
+ either, so the session handed the user the command to run. It now passes when the tag points at the commit the
231
+ approval covers (after a commit + push, the new one) and no branch has its name; a tag on another commit, a full
232
+ ref, a refspec and `--tags` are still refused.
233
+ - **`change: review` is read as `audit`.** The model table of the working rules names review with audit, and
234
+ `crew-review-agent` cards that said `change: review` were refused. It now takes the audit floor, on a critical path
235
+ too; a kind that is not on the list is still refused.
236
+ - **A commit refusal names a trace in the message even when the staged content also fails.** The content gate
237
+ scanned the `-m` message only when the staged change was clean, so a refusal for a silenced lint check said nothing about
238
+ the session-link trailer in the same message, and the session showed the user the traced message again. Both are
239
+ named in one refusal now, and a trace in the message adds that a harness reminder to end a message with a
240
+ trailer does not override §4.1. The git `commit-msg` hook still stops the trailer at the commit itself.
241
+ - **A one-line task card may open with `Kart`.** Sessions that write in Turkish lead the card with `Kart —` as
242
+ others write `Card —`, and the call was refused as having no card. The Turkish word is read like the English one;
243
+ the fields after it are held to the same rules.
244
+ - **`above:` is read anywhere among a task card's three lines.** A card with `above: <why>` between `change:` and
245
+ `verify:` was refused ("line 3 of the task is not one of files:, change:, verify:") and the call was sent again
246
+ with the line moved down. It is now read in any position and keeps the model as it does after the three; the three
247
+ are still all required.
248
+ - **A click on the approval question is recorded whatever order its keys come back in.** Claude Code hands the
249
+ answered question back either as it was asked or with its keys sorted, and the answer side matched it byte for
250
+ byte: in real `auto` sessions 30 of 30 answers in the asked order were recorded and 7 of 7 sorted ones refused
251
+ ("the question answered is not the fixed one"), so the user clicked and was then told to type `/crew-approve`. The
252
+ answer side now checks each text of the fixed question wherever it sits; the exact match stays where the question
253
+ is asked, and the answer is still bound to that call.
254
+
255
+ - **The Crew Map says what a project is made of.** A Flutter project read as a pile of Gradle modules and its
256
+ server as an external API (seen on a real project). The scanner now reads Dart: one module per `pubspec.yaml`,
257
+ its packages and `path:` references, and a Flutter app's `android/` and `ios/` folders as platforms of the app.
258
+ Each module says what it is built with (Hono, Cloudflare Workers, Flutter, Riverpod…, from `rules/tech.json`)
259
+ and what is inside it (folders with file counts, tests apart). A module served at a host its wrangler routes or
260
+ `fly.toml` name is called, module to module, by any module that writes that host; a Dart call to that host or a
261
+ Dio call's relative path is matched to the endpoints provided. A `.dart` file no `pubspec.yaml` owns is listed
262
+ as not measured, as is Dart package usage: Flutter declares packages nothing imports.
263
+
264
+ - **A commit message and a reworded paragraph no longer go to the expensive model.** Two floors of the model gate
265
+ raised prose for no gain (seen on a real project): with no verify command a commit message was held to sonnet,
266
+ though the commit hook checks it, and a two-sentence fix under a critical path such as `legal/` was held to opus.
267
+ A text change now runs on haiku without a verify command, the commit agent too; on a critical path a text change
268
+ whose every file is prose (`.md`, `.txt`, `.rst`, `.adoc`) is written on sonnet, and the audit of it stays on opus.
269
+ One code file on the card keeps it on opus, and an agent on sonnet still writes no code there. A project that
270
+ wants opus for such text says so in `.claude/crew-model-rules` (`floor * text critical opus`).
271
+ - **The gate log is written in every project, and Studio shows a refused agent call.** A project that tracks
272
+ `.claude/` had no gate log at all: no record of a refusal, no 🛡 on the status line, no note of a model lowered to
273
+ its card's floor (seen on a real project). The log now goes inside the repository's own `.git` there
274
+ (`crewforth-gate-log.tsv`, never committed); one rule (`eval/lib/gate-log.sh`) serves every gate and reader. A
275
+ call the model gate refused starts no agent, so the canvas showed nothing while the session said "2 failed"; the
276
+ session's card now says how many calls were refused and not made again, with the gate's words on hover.
277
+ - **`/crew-doctor` offers a verify rule that works, in every mode.** For `cd sub && ./check.sh` it offered the line
278
+ with the `cd` in it, which allows nothing (the rule names the command alone), and in default mode it offered
279
+ nothing, because that refusal is worded differently. Measured on Windows.
280
+ - **Studio keeps answering with many conversations open.** Each conversation pane held its own stream, and a
281
+ browser opens six connections to one host: at five panes a message sent and every request the page made waited
282
+ for a pane to close (on Windows a send did not arrive in 35 s; on macOS the page's own request timed out at 4 s).
283
+ Every open conversation now shares one stream, resumed from each session's last event when it reconnects.
284
+ - **Studio finds sessions under a moved config dir.** It read `~/.claude/projects` only, so sessions under
285
+ `CLAUDE_CONFIG_DIR` were not listed and read a cost of 0. It now reads `projects` there, as Claude Code writes.
286
+ - **Studio says when it needs a restart.** A running server keeps the code it started with, and an update
287
+ replaces the files the page is read from: on a real project the Map view of the new page asked a server from
288
+ before it and showed "Not read — HTTP 404". The server now says whether its code changed on disk since it
289
+ started; a page that hears so, or talks to a server too old to say, shows a note to restart it, and Map says the
290
+ same instead of the 404.
291
+ - **Two agents starting together no longer share one model record on Windows.** A record was taken by renaming
292
+ it, which is a lock on macOS and Linux; on Windows two hook processes both reported the rename as done (seen
293
+ twice on the Windows CI). Each record is now locked first with an exclusive create, a shell builtin that costs no
294
+ process, and only the hook that made the lock takes it.
295
+ - **An rc says it is one, and its final is offered.** A release candidate installed `.claude/VERSION` as the final's
296
+ number (3.2.0 for 3.2.0-rc.1), so the update check, doctor and the refresh's "what's new" all read it as the final,
297
+ and someone who tried the rc was never told the final shipped. The rc's archive and npm package now carry its own
298
+ version, and one definition (`eval/lib/version.sh`) orders an rc before its final. A version off the network is
299
+ still held to X.Y.Z.
300
+ - **`adopt.sh --help` prints its usage.** It ran the project detection instead; the usage now comes in English and
301
+ Turkish, before anything is read.
117
302
  - **The crew map reads what two real projects showed it missing.** Scanned before release, they put three rules
118
303
  well over a 20% false-positive rate, and one scan refused its own document. Read now: a route with an empty path,
119
304
  a decorator whose path is on the next line, `api.get<T>(` and a call split over lines, a client made in one file
package/README.md CHANGED
@@ -33,6 +33,8 @@ Then open Claude Code and run `/crew-doctor` to confirm the setup. Requires bash
33
33
  - **40 skills** holding the method: testing, migrations, API contracts, observability, accessibility, deployment.
34
34
  - **13 commands**, each started with its `/crew-…` name: `/crew-plan`, `/crew-review`, `/crew-ship`, `/crew-handoff`, `/crew-brainstorm`, `/crew-update`, `/crew-doctor`, `/crew-gates`, `/crew-skill`, `/crew-studio`, `/crew-approve`, `/crew-loosen`, `/crew-map`.
35
35
  - In `auto` mode a commit or a push waits for you: type `/crew-approve` and what was just refused is approved, tied to what is staged.
36
+ - A stopped destructive command says what it would have touched; a review starts from a deterministic pre-pass, and `crew-refuter` tries to refute each finding that blocks.
37
+ - **Organisation presets.** `npx crewforth init --preset strict` (or `solo`, or your own JSON file) carries critical paths, model floors and verify commands to every project.
36
38
  - **Safe adoption.** `adopt` lands everything on a branch, staged and uncommitted; `main` is never touched.
37
39
 
38
40
  ## Updating
package/VERSION CHANGED
@@ -1 +1 @@
1
- 3.2.0
1
+ 3.2.0-rc.10
package/adopt.sh CHANGED
@@ -11,7 +11,8 @@
11
11
  set -uo pipefail
12
12
  # The 2.x names of the variables a user can set still work (one helper: eval/lib/crew-env.sh).
13
13
  _crew_d="${BASH_SOURCE%/*}"; [ "$_crew_d" = "${BASH_SOURCE}" ] && _crew_d=.
14
- [ -f "$_crew_d/kit/eval/lib/crew-env.sh" ] && . "$_crew_d/kit/eval/lib/crew-env.sh"; unset _crew_d
14
+ [ -f "$_crew_d/kit/eval/lib/crew-env.sh" ] && . "$_crew_d/kit/eval/lib/crew-env.sh"
15
+ [ -f "$_crew_d/kit/eval/lib/version.sh" ] && . "$_crew_d/kit/eval/lib/version.sh"; unset _crew_d # an rc orders before its final
15
16
  HERE="$(CDPATH= cd "$(dirname "$0")" && pwd)"
16
17
 
17
18
  # --version (or -v) is answered first, before the payload check below: it reads only VERSION, so it works
@@ -23,24 +24,68 @@ for _a in "$@"; do
23
24
  fi
24
25
  done
25
26
 
27
+ # --help (or -h) is answered here too, before anything is read: an unknown flag is ignored further down, so without
28
+ # this `adopt.sh --help` ran the detection instead of saying what the script does. The language comes from --lang or
29
+ # CREW_LANG; the rest of the script asks for it later, which is too late for a help text.
30
+ _hl="${CREW_LANG:-}"; _hh=0; _ht=0
31
+ for _a in "$@"; do
32
+ if [ "$_ht" = 1 ]; then _ht=0; _hl="$_a"; continue; fi
33
+ case "$_a" in -h|--help) _hh=1 ;; --lang) _ht=1 ;; --lang=*) _hl="${_a#--lang=}" ;; esac
34
+ done
35
+ if [ "$_hh" = 1 ]; then
36
+ if [ "$_hl" = tr ]; then
37
+ cat <<'USAGE_TR'
38
+ Kullanım: bash adopt.sh [SEÇENEKLER] (ya da: npx crewforth adopt [SEÇENEKLER])
39
+ Crewforth'u MEVCUT bir projeye kurar ya da kurulu olanı yeniler. Önce projeyi okur ve ne yapacağını gösterir;
40
+ projenin kendi dosyalarına, hook'larına ve status line'ına dokunmaz. Değişiklikleri hazırlar (stage), commit atmaz.
41
+ --here değişiklikleri şu anki dalda yap
42
+ --new-branch değişiklikleri yeni bir inceleme dalında yap (ilk kurulumda varsayılan)
43
+ --yes, -y her soruya evet de (gözetimsiz kurulum)
44
+ --lang tr|en dil (verilmezse sorulur)
45
+ --version, -v Crewforth sürümünü yazdırıp çık
46
+ --help, -h bu metni yazdırıp çık
47
+ USAGE_TR
48
+ else
49
+ cat <<'USAGE'
50
+ Usage: bash adopt.sh [OPTIONS] (or: npx crewforth adopt [OPTIONS])
51
+ Installs Crewforth into an EXISTING project, or refreshes an install. It reads the project first and shows what it
52
+ will do; the project's own files, hooks and status line are left alone. Changes are staged, never committed.
53
+ --here make the changes on the current branch
54
+ --new-branch make the changes on a fresh review branch (the default for a first adopt)
55
+ --yes, -y answer yes at every gate (unattended)
56
+ --lang tr|en language (asked when not given)
57
+ --version, -v print Crewforth's version and exit
58
+ --help, -h print this text and exit
59
+ USAGE
60
+ fi
61
+ exit 0
62
+ fi
63
+ unset _hl _hh _ht
64
+
26
65
  SRC="$HERE/kit"
27
66
  [ -d "$SRC" ] || { echo "ERROR: kit/ not found (must be in the same directory as adopt.sh)."; exit 1; }
28
67
 
29
68
  # --- flags (B5 — where a refresh lands) --- --here: the current branch · --new-branch: a fresh review branch.
30
69
  # Left empty, Stage 2 picks a smart default (first adopt -> new; update + untracked .claude -> here; update +
31
70
  # tracked -> ask). Unknown flags are ignored here (start.sh owns --backend/--frontend/… ; adopt auto-detects shape).
32
- BRANCH_MODE=""; ASSUME_YES=0
71
+ BRANCH_MODE=""; ASSUME_YES=0; PRESET=""; _preset_take=0
33
72
  _lang_flag=""; _lang_take=0
34
73
  for _a in "$@"; do
35
74
  # `--lang` with no value must not swallow the next flag: `--lang --yes` is --yes with no language given.
36
75
  if [ "$_lang_take" = 1 ]; then _lang_take=0; case "$_a" in -*) ;; *) _lang_flag="$_a"; continue ;; esac; fi
76
+ if [ "$_preset_take" = 1 ]; then _preset_take=0; PRESET="$_a"; continue; fi
37
77
  case "$_a" in
78
+ --preset=*) PRESET="${_a#--preset=}" ;;
79
+ --preset) _preset_take=1 ;;
38
80
  --here) BRANCH_MODE=here ;;
39
81
  --new-branch) BRANCH_MODE=new ;;
40
82
  --yes|-y) ASSUME_YES=1 ;; # assume "yes" at every gate — for agent-driven / CI updates (no TTY to prompt)
41
83
  --lang=*) _lang_flag="${_a#--lang=}" ;;
42
84
  --lang) _lang_take=1 ;;
43
85
  esac; done
86
+ [ "$_preset_take" = 1 ] && { echo "ERROR: --preset needs a name or a file (default, strict, solo, or ./org.json)."; exit 1; }
87
+ # A preset is checked before anything is changed: one that is not valid stops the update here.
88
+ if [ -n "$PRESET" ]; then bash "$SRC/eval/lib/preset.sh" --check "$PRESET" || exit 1; fi
44
89
 
45
90
  # ---- CREW-I18N (the twin of start.sh's; see the long note there for why the English string is the key) ----
46
91
  # Short version, because the reasoning belongs in one place: `m 'text'` prints the translation of that text
@@ -1535,19 +1580,23 @@ fi
1535
1580
  # set up in Turkish to English from its first unattended update on.
1536
1581
  [ "$_LANG_CHOSEN" = 1 ] && echo "lang=$CREW_LANG"
1537
1582
  } > .claude/kit.conf
1583
+ # The organisation's preset (checked before anything was changed): a block at the top of crew-model-rules. Without
1584
+ # --preset an update leaves the block that is there, with the project's own lines, as they were.
1585
+ if [ -n "$PRESET" ]; then bash .claude/eval/lib/preset.sh --apply "$PRESET" || exit 1; fi
1538
1586
  # What changed, for /crew-update to report: the package's own CHANGELOG sections newer than the version this
1539
1587
  # project had, and no newer than the one being installed — read from the package on disk, never from the network.
1540
1588
  # A fresh adopt has no "before", so it gets no file (and a stale one from an earlier update is removed).
1541
1589
  rm -f .claude/.state/whats-new.md 2>/dev/null
1542
1590
  _kv="$(printf '%s' "$KIT_VER" | tr -d '\r')"
1543
- case "$_kv" in *[!0-9.]*|'') _kv="" ;; [0-9]*.[0-9]*.[0-9]*) ;; *) _kv="" ;; esac # an odd old version compares as 0.0.0 — every section would look new
1591
+ _kv="$(crew_local_version "$_kv" 2>/dev/null)" || _kv="" # an odd old version compares as 0.0.0 — every section would look new
1544
1592
  if [ "$KIT_PRESENT" = 1 ] && [ -n "$_kv" ] && [ -f "$HERE/CHANGELOG.md" ] && [ -f "$HERE/VERSION" ]; then
1545
1593
  mkdir -p .claude/.state 2>/dev/null
1546
- awk -v from="$_kv" -v to="$(head -1 "$HERE/VERSION" | tr -d '\r')" '
1547
- function cmp(a,b, x,y,i){ split(a,x,"."); split(b,y,"."); for(i=1;i<=3;i++){ if(x[i]+0>y[i]+0) return 1; if(x[i]+0<y[i]+0) return -1 } return 0 }
1594
+ # The upper bound is the target's X.Y.Z: an rc's notes are its final's section ("[Unreleased] — 3.2.0").
1595
+ awk -v from="$_kv" -v to="$(head -1 "$HERE/VERSION" | tr -d '\r')" "${CREW_VCMP_AWK:-}"'
1596
+ BEGIN { sub(/-.*/, "", to) }
1548
1597
  /^## \[/ { v=$0; sub(/^## \[/,"",v); w=v; sub(/\].*/,"",v)
1549
1598
  if (v=="Unreleased") { v=w; sub(/^[^—]*— */,"",v); sub(/[^0-9.].*/,"",v) }
1550
- keep = (v ~ /^[0-9]+\.[0-9]+\.[0-9]+$/ && cmp(v,from)>0 && cmp(v,to)<=0) }
1599
+ keep = (v ~ /^[0-9]+\.[0-9]+\.[0-9]+$/ && vcmp(v,from)>0 && vcmp(v,to)<=0) }
1551
1600
  keep' "$HERE/CHANGELOG.md" | tr -d '\r' > .claude/.state/whats-new.md 2>/dev/null
1552
1601
  [ -s .claude/.state/whats-new.md ] || rm -f .claude/.state/whats-new.md
1553
1602
  fi
package/bin/cli.js CHANGED
@@ -18,12 +18,13 @@ if (sub === '--help' || sub === '-h' || sub === 'help') {
18
18
  console.log(`Crewforth
19
19
 
20
20
  Usage:
21
- npx crewforth [init] [--private|--shared] [--yes]
21
+ npx crewforth [init] [--private|--shared] [--yes] [--preset <name|file.json>]
22
22
  Set up Crewforth in a fresh project (start.sh wizard). Every install ships everything and
23
23
  is stack-agnostic: the stack is recorded per project in CLAUDE.md ## Stack.
24
24
  (--dotnet was removed in 3.0; it is still accepted, warns, and installs the same setup.)
25
- npx crewforth adopt
25
+ npx crewforth adopt [--preset <name|file.json>]
26
26
  Hand Crewforth over onto an existing project (adopt.sh).
27
+ --preset applies an organisation preset (default, strict, solo, or your own JSON file).
27
28
  npx crewforth@latest update
28
29
  Refresh a project that already has Crewforth. Alias of 'adopt': it reads .claude/kit.conf,
29
30
  migrates a pre-3.0 .NET install (its pattern skill stays, as a project skill), and
package/kit/CLAUDE.md CHANGED
@@ -55,16 +55,16 @@ sequence. All are installed: "no owner" is an `ls`.
55
55
 
56
56
  **Model per call — RISK decides, not size.** A `crew-*` task opens with its card, and the call names `model`:
57
57
  `files:` files/globs · `change:` text|feature|fix-known|fix-unknown|refactor|migration|security|architecture|test-run|test-write|audit
58
- · `verify:` the command that proves it, or `none`. **crew-planner** writes the card; with no plan, you do.
58
+ · `verify:` the command that proves it, or `none`. **crew-planner** writes the card, or you do.
59
59
 
60
- | `model` | the card |
60
+ | `model` (`guard-agent-model.sh`: floor = default; `above: <why>` for more) | the card |
61
61
  |---|---|
62
- | `haiku` | text, test-run, mechanical work — only with a `verify:` command |
63
- | `sonnet` | feature, fix-known, refactor, test-write, audit, review; anything with `verify: none` |
64
- | `opus` | **critical**: migration, security, architecture, fix-unknown, or a file on a critical path (auth, payments, migrations…) — its test-write, audit and review too |
62
+ | `haiku` | text, commit agent; test-run, mechanical work with `verify:` |
63
+ | `sonnet` | feature, fix-known, refactor, test-write, audit, review; other `verify: none`; critical-path prose |
64
+ | `opus` | **critical**: migration, security, architecture, fix-unknown, or a critical-path file (auth, payments, legal…), and its referees |
65
65
 
66
66
  The referee follows the RISK, not the author's model. Verify fails → the agent fixes once; again → re-run ONCE one
67
- model up; again → AskUserQuestion. The same for a report that ends `confidence: low`. `guard-agent-model.sh` holds it.
67
+ model up; again → AskUserQuestion. The same for a report that ends `confidence: low`.
68
68
 
69
69
  **Naming an agent in prose is a hope; `@agent-<name>` is a guarantee** — measured here: 0/3 vs 3/3. Use that form
70
70
  whenever an agent must run, and tell the user they can too.
@@ -94,16 +94,18 @@ write is refused because the file is on a critical path, do not reach it another
94
94
  proceed" is not approval (§4.4).
95
95
  The tool-level gate `guard-bash.sh` intercepts commit/push in **every** permission mode: in normal modes it raises an
96
96
  approval prompt only the user can answer — so present the message FIRST, then run the commit yourself and let the user
97
- approve it at the prompt. Never hand the user a command to paste into their own terminal. In `auto` and `dontAsk`
98
- the prompt reaches nobody: stage, then ask in this shape and with nothing after it — the message in ONE code block,
97
+ approve it at the prompt. Never hand the user a command to paste into their own terminal. In `auto`, when this agent
98
+ is called Crewforth hands the session the one-click approval question (below): ask it, with the message, instead of
99
+ asking for `/crew-approve`. In `dontAsk`, and in `auto` when Crewforth gives no question or says it is closed, the
100
+ prompt reaches nobody: ask in this shape and with nothing after it — the message in ONE code block,
99
101
  and under it one sentence in the user's language ("If you approve, send only this:") followed by the command
100
102
  on a line of its own: `/crew-approve`. With nothing after it, it approves what the gate just refused in this
101
103
  session (a commit, a push, or both when both were refused); spelled out it is `/crew-approve commit`,
102
104
  `/crew-approve push` or `/crew-approve commit+push`. The user approves by sending
103
105
  that command alone; a sentence that says yes is not an approval, and you cannot run `/crew-approve` for them. It covers what is staged on that HEAD. If the gate says the approval path is closed in this session (the
104
106
  recording hook is not wired), say so and offer Shift+Tab or their own terminal instead.
105
- In `auto` the refusal may also offer the approval question: show the message, then call AskUserQuestion with
106
- exactly the input the refusal prints (the fixed question and its four answers, no answers of your own). The
107
+ The approval question: call AskUserQuestion with exactly the input Crewforth prints (the fixed question and its four
108
+ answers, no answers of your own; never write the question yourself). The
107
109
  user's click is recorded the same way; read the line Crewforth adds and run what it names. Where a
108
110
  `PermissionRequest` hook may be wired the gate closes the question, and in `default` / `acceptEdits` its own
109
111
  prompt too, because such a hook can answer either before the user does: then `/crew-approve` is the way.
@@ -30,6 +30,8 @@ Read-only; the trigger for the `crew-code-review` skill.
30
30
  Before a work package closes (pre-commit), on the changed diff.
31
31
 
32
32
  ## How (applies the `crew-code-review` skill)
33
+ - First the deterministic pre-pass (`eval/lib/review-prepass/prepass.sh`), carried over as it is; your own findings
34
+ go to `.crewforth/review.json` in the same findings shape, blockers through crew-refuter (skill §0).
33
35
  - Simplicity: flag when 200 lines could be 50.
34
36
  - Surgical: catch out-of-scope touches.
35
37
  - Readability: naming, dead code, comment traps (S125 — commented-out code-like prose).
@@ -7,7 +7,9 @@
7
7
  set -uo pipefail
8
8
  # The 2.x names of the variables a user can set still work (one helper: eval/lib/crew-env.sh).
9
9
  _crew_d="${BASH_SOURCE%/*}"; [ "$_crew_d" = "${BASH_SOURCE}" ] && _crew_d=.
10
- [ -f "$_crew_d/lib/crew-env.sh" ] && . "$_crew_d/lib/crew-env.sh"; unset _crew_d
10
+ [ -f "$_crew_d/lib/crew-env.sh" ] && . "$_crew_d/lib/crew-env.sh"
11
+ [ -f "$_crew_d/lib/version.sh" ] && . "$_crew_d/lib/version.sh" # an rc orders before its final
12
+ _DOC_LIB="$(cd "$_crew_d/lib" 2>/dev/null && pwd)"; unset _crew_d # absolute: the doctor cd's into ROOT below
11
13
  ROOT="${1:-.}"
12
14
  cd "$ROOT" 2>/dev/null || { echo "doctor: cannot enter '$ROOT'"; exit 2; }
13
15
 
@@ -106,6 +108,8 @@ _mt() {
106
108
  'advisor: off (CLAUDE_CODE_DISABLE_ADVISOR_TOOL=1)') s='advisor: kapalı (CLAUDE_CODE_DISABLE_ADVISOR_TOOL=1)' ;;
107
109
  "status line: Crewforth's (from %s)") s="durum satırı: Crewforth'unki (%s içinden)" ;;
108
110
  "status line: your own (from %s); Crewforth's is not used") s="durum satırı: seninki (%s içinden); Crewforth'unki kullanılmıyor" ;;
111
+ "organisation preset: %s") s="kurum preset'i: %s" ;;
112
+ "verify commands never run, %s time(s), because nothing allows them; to let the hook run one, add its line to .claude/crew-model-rules: %s") s="doğrulama komutları %s kez hiç koşmadı, çünkü hiçbir şey izin vermiyor; hook'un koşturması için satırını .claude/crew-model-rules dosyasına ekle: %s" ;;
109
113
  "status line: none; Crewforth's: bash .claude/eval/lib/statusline.sh --install (Bash tool, not PowerShell)") s="durum satırı: yok; Crewforth'unki: bash .claude/eval/lib/statusline.sh --install (Bash tool, not PowerShell)" ;;
110
114
  "advisor: off (no advisorModel set; Crewforth never turns it on)") s='advisor: kapalı (advisorModel ayarlı değil; Crewforth onu hiç açmaz)' ;;
111
115
  'advisorModel is %s in %s, but CLAUDE_CODE_DISABLE_ADVISOR_TOOL=1 turns the advisor off') s="advisorModel %s (%s içinde), ama CLAUDE_CODE_DISABLE_ADVISOR_TOOL=1 advisor'ı kapatıyor" ;;
@@ -218,8 +222,7 @@ if [ -f .claude/VERSION ] && [ -f .claude/.state/update-check ]; then
218
222
  read -r DLATEST _ < .claude/.state/update-check 2>/dev/null || DLATEST=""
219
223
  DLATEST="$(printf '%s' "${DLATEST:-}" | tr -cd '0-9A-Za-z.-')"
220
224
  DCUR="$(head -1 .claude/VERSION 2>/dev/null | tr -cd '0-9A-Za-z.-')"
221
- if [ -n "$DLATEST" ] && awk -v a="$DLATEST" -v b="$DCUR" 'BEGIN{split(a,x,".");split(b,y,".");
222
- for(i=1;i<=3;i++){if(x[i]+0>y[i]+0)exit 0; if(x[i]+0<y[i]+0)exit 1} exit 1}'; then
225
+ if [ -n "$DLATEST" ] && awk -v a="$DLATEST" -v b="$DCUR" "${CREW_VCMP_AWK:-}"' BEGIN { exit !(vcmp(a, b) > 0) }' 2>/dev/null; then
223
226
  warn "Crewforth v%s installed, v%s published — update with /crew-update" "$DCUR" "$DLATEST"
224
227
  fi
225
228
  elif [ -f .claude/VERSION ]; then
@@ -772,6 +775,37 @@ case "$_sl" in
772
775
  *) [ -f .claude/eval/lib/statusline.sh ] && ok "status line: none; Crewforth's: bash .claude/eval/lib/statusline.sh --install (Bash tool, not PowerShell)" ;;
773
776
  esac
774
777
 
778
+ # 9e) What model routing costs. Advisory. A verify command the hook was never allowed to run cannot vouch for
779
+ # cheaper work: the ones refused for that reason are listed with the line that allows them (measured on one
780
+ # project: 23 of 71 refusals).
781
+ [ -f .claude/eval/lib/verify-cmd.sh ] && . .claude/eval/lib/verify-cmd.sh # the rule names the command a cd prefix leads to
782
+ _vbn=0; _vbl=""
783
+ for _f in .claude/state/crew-model/results/*; do
784
+ [ -f "$_f" ] || continue
785
+ _vb=0; _vw=""; _vc=""
786
+ while IFS= read -r _l || [ -n "$_l" ]; do
787
+ case "$_l" in verify=blocked) _vb=1 ;; whynot=*) _vw="${_l#whynot=}" ;; cmd=*) _vc="${_l#cmd=}" ;; esac
788
+ done < "$_f"
789
+ [ "$_vb" = 1 ] || continue
790
+ # Both ways of saying "nothing allows it": in auto/bypassPermissions (not on the runner list) and in the other
791
+ # modes (Claude Code itself would not run it unallowed). Measured on Windows: the second was not listed at all.
792
+ case "$_vw" in *"not a test or build command on this hook's list"*|*"does not run a command the user has not allowed"*) ;; *) continue ;; esac
793
+ # A rule names the command alone: `cd sub && ./check.sh` is allowed by `verify ./check.sh` (measured on Windows:
794
+ # the line with the cd in it did not allow it).
795
+ declare -F crew_verify_split >/dev/null && { crew_verify_split "$_vc" .; _vc="$_AMVC"; }
796
+ _vc="${_vc:0:80}"; [ -n "$_vc" ] || continue
797
+ _vbn=$((_vbn+1))
798
+ case "$_vc" in *'('*|*' + '*) continue ;; esac # a sentence, not a command: counted, not offered as a rule
799
+ case "$_vbl" in *"| verify $_vc |"*) ;; *) _vbl="$_vbl| verify $_vc |" ;; esac
800
+ done
801
+ if [ "$_vbn" -gt 0 ]; then
802
+ _vsep=' || '; _vdot=' · '; _vbs="${_vbl//"$_vsep"/$_vdot}"; _vbs="${_vbs#| }"; _vbs="${_vbs% |}"; _vbs="${_vbs:0:300}"
803
+ warn "verify commands never run, %s time(s), because nothing allows them; to let the hook run one, add its line to .claude/crew-model-rules: %s" "$_vbn" "$_vbs"
804
+ fi
805
+ # The organisation preset in force: the block preset.sh wrote at the top of crew-model-rules, asked of preset.sh itself.
806
+ _pre=""; [ -n "$_DOC_LIB" ] && [ -f "$_DOC_LIB/preset.sh" ] && _pre="$(bash "$_DOC_LIB/preset.sh" --show 2>/dev/null)"
807
+ ok "organisation preset: %s" "${_pre:-none}"
808
+
775
809
  # 10) Gate activity. The suite proves the gates CAN fire; this reports whether anything actually tripped them.
776
810
  # Recording is on by default (rule names only, never the command), so "no log" here means no gate has
777
811
  # fired yet — a measured zero, not a gap. Never a failure either way.
@@ -29,6 +29,8 @@ HOOKS="./.claude/hooks"; [ -d "$HOOKS" ] || HOOKS="./hooks"
29
29
  [ -f "$HOOKS/guard-bash.sh" ] || { echo "gate-report: guard-bash.sh not found (run from the project root)" >&2; exit 4; }
30
30
 
31
31
  # Default log location, so `--log` is not required once logging is on.
32
+ # The same rule the gates write by (eval/lib/gate-log.sh): .claude/gate-log.tsv, or the repository's .git.
33
+ if [ -z "$LOG" ] && [ -f ./.claude/eval/lib/gate-log.sh ]; then . ./.claude/eval/lib/gate-log.sh; crew_gate_log_read; LOG="$_CREW_GL"; fi
32
34
  [ -z "$LOG" ] && [ -f "./.claude/gate-log.tsv" ] && LOG="./.claude/gate-log.tsv"
33
35
 
34
36
  TMP="${TMPDIR:-/tmp}/crew-gate-$$"; mkdir -p "$TMP"; trap 'rm -rf "$TMP"' EXIT