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.
- package/CHANGELOG.md +190 -5
- package/README.md +2 -0
- package/VERSION +1 -1
- package/adopt.sh +55 -6
- package/bin/cli.js +3 -2
- package/kit/CLAUDE.md +6 -6
- package/kit/agents/crew-commit-agent.md +6 -4
- package/kit/agents/crew-review-agent.md +2 -0
- package/kit/eval/doctor.sh +37 -3
- package/kit/eval/gate-report.sh +2 -0
- package/kit/eval/lib/crew-map/narrative.mjs +42 -8
- package/kit/eval/lib/crew-map/project.mjs +3 -2
- package/kit/eval/lib/crew-map/rules/tech.json +35 -0
- package/kit/eval/lib/crew-map/scan.mjs +56 -4
- package/kit/eval/lib/crew-map/stacks.mjs +114 -8
- package/kit/eval/lib/crew-map/surface.mjs +81 -3
- package/kit/eval/lib/gate-log.sh +52 -0
- package/kit/eval/lib/json-string.awk +27 -0
- package/kit/eval/lib/preset.sh +109 -0
- package/kit/eval/lib/presets/default.json +7 -0
- package/kit/eval/lib/presets/solo.json +7 -0
- package/kit/eval/lib/presets/strict.json +11 -0
- package/kit/eval/lib/preview.sh +150 -0
- package/kit/eval/lib/review-bench/bench.mjs +273 -0
- package/kit/eval/lib/review-prepass/prepass.mjs +203 -0
- package/kit/eval/lib/review-prepass/prepass.sh +23 -0
- package/kit/eval/lib/settings-default.sh +36 -0
- package/kit/eval/lib/settings-json.awk +14 -4
- package/kit/eval/lib/statusline.sh +4 -19
- package/kit/eval/lib/verify-cmd.sh +17 -0
- package/kit/eval/lib/version.sh +41 -0
- package/kit/eval/review-bench.sh +26 -0
- package/kit/eval/smoke-test.sh +424 -26
- package/kit/hooks/agent-outcome.sh +38 -9
- package/kit/hooks/board-sync.sh +2 -23
- package/kit/hooks/guard-agent-model.sh +163 -20
- package/kit/hooks/guard-bash.sh +69 -36
- package/kit/hooks/guard-commit-scan.sh +10 -2
- package/kit/hooks/guard-write.sh +3 -7
- package/kit/hooks/precompact-snapshot.sh +75 -0
- package/kit/hooks/prompt-approval.sh +74 -5
- package/kit/hooks/session-rehydrate.sh +25 -2
- package/kit/hooks/session-update-check.sh +9 -8
- package/kit/hooks/trace-blocklist.txt +9 -0
- package/kit/settings.json +18 -0
- package/kit/skills/crew-code-review/SKILL.md +16 -0
- package/kit/skills/crew-map/SKILL.md +16 -5
- package/kit/skills/crew-review/SKILL.md +2 -1
- package/kit/skills/crew-ship/SKILL.md +3 -1
- package/kit/studio/README.md +21 -10
- package/kit/studio/server/index.js +85 -22
- package/kit/studio/server/lib/code-stamp.js +35 -0
- package/kit/studio/server/lib/graph.js +9 -1
- package/kit/studio/server/lib/kit-telemetry.js +30 -5
- package/kit/studio/server/lib/lan.js +87 -0
- package/kit/studio/server/lib/owned-stream.js +56 -0
- package/kit/studio/server/lib/projects.js +11 -4
- package/kit/studio/server/lib/redact.js +60 -0
- package/kit/studio/server/lib/remote.js +104 -18
- package/kit/studio/server/lib/session.js +1 -1
- package/kit/studio/server/lib/terminal.js +51 -13
- package/kit/studio/server/lib/transcript.js +32 -0
- package/kit/studio/web/app.js +79 -11
- package/kit/studio/web/canvas.js +14 -2
- package/kit/studio/web/chat.js +114 -24
- package/kit/studio/web/graph-plan.js +3 -2
- package/kit/studio/web/liveness.js +14 -0
- package/kit/studio/web/map-plan.js +72 -3
- package/kit/studio/web/map.js +103 -10
- package/kit/studio/web/qr.js +200 -0
- package/kit/studio/web/style.css +40 -0
- package/kit/studio/web/tokens.css +6 -0
- package/package.json +1 -1
- 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
|
-
- **
|
|
13
|
-
|
|
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"
|
|
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
|
-
|
|
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
|
-
|
|
1547
|
-
|
|
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]+$/ &&
|
|
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
|
|
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
|
|
63
|
-
| `sonnet` | feature, fix-known, refactor, test-write, audit, review;
|
|
64
|
-
| `opus` | **critical**: migration, security, architecture, fix-unknown, or a
|
|
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`.
|
|
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
|
|
98
|
-
|
|
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
|
-
|
|
106
|
-
|
|
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).
|
package/kit/eval/doctor.sh
CHANGED
|
@@ -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"
|
|
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{
|
|
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.
|
package/kit/eval/gate-report.sh
CHANGED
|
@@ -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
|