crewforth 3.0.0-rc.3 → 3.0.1-rc.1
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 +94 -1
- package/VERSION +1 -1
- package/adopt.sh +186 -19
- package/kit/CLAUDE.md +5 -4
- package/kit/README.md +1 -1
- package/kit/agents/crew-frontend-expert.md +6 -6
- package/kit/agents/crew-session-manager.md +1 -1
- package/kit/eval/doctor.sh +72 -18
- package/kit/eval/golden-routing.txt +15 -0
- package/kit/eval/preflight.sh +2 -2
- package/kit/eval/smoke-test.sh +625 -29
- package/kit/hooks/board-sync.sh +1 -1
- package/kit/hooks/board.sh +4 -3
- package/kit/hooks/guard-bash.sh +195 -21
- package/kit/hooks/guard-commit-scan.sh +2 -2
- package/kit/hooks/guard-powershell.sh +328 -0
- package/kit/hooks/guard-write.sh +28 -3
- package/kit/hooks/route-hint.sh +5 -1
- package/kit/hooks/session-update-check.sh +2 -2
- package/kit/hooks/skill-trust.sh +22 -11
- package/kit/legacy-blobs.tsv +208 -0
- package/kit/settings.json +10 -0
- package/kit/skills/automode-policy/SKILL.md +1 -1
- package/kit/skills/automode-policy/scripts/check.sh +2 -2
- package/kit/skills/crew-board/SKILL.md +1 -1
- package/kit/skills/crew-doctor/SKILL.md +3 -3
- package/kit/skills/crew-gates/SKILL.md +2 -1
- package/kit/skills/crew-handoff/SKILL.md +1 -1
- package/kit/skills/crew-skill/SKILL.md +2 -2
- package/kit/skills/crew-studio/SKILL.md +3 -2
- package/kit/skills/crew-update/SKILL.md +3 -3
- package/kit/skills/frontend/SKILL.md +26 -2
- package/kit/skills/frontend-rn-expo/SKILL.md +3 -0
- package/kit/skills/handoff/SKILL.md +2 -2
- package/kit/skills/reflect/SKILL.md +2 -2
- package/kit/skills/teamboard/SKILL.md +1 -1
- package/kit/skills/token-budget/SKILL.md +2 -2
- package/kit/skills/trace-scan/SKILL.md +4 -3
- package/kit/studio/README.md +1 -1
- package/package.json +1 -1
- package/start.sh +15 -8
package/CHANGELOG.md
CHANGED
|
@@ -5,7 +5,100 @@ Crewforth was named Claude Starter Kit until 3.0.0.
|
|
|
5
5
|
Notable changes to this project are recorded here. Format follows [Keep a Changelog](https://keepachangelog.com/en/),
|
|
6
6
|
versioning follows [SemVer](https://semver.org/).
|
|
7
7
|
|
|
8
|
-
## [Unreleased] — 3.0.
|
|
8
|
+
## [Unreleased] — 3.0.1
|
|
9
|
+
|
|
10
|
+
### Security
|
|
11
|
+
|
|
12
|
+
- A chained command could write `core.hooksPath` through the gate: `git config --get core.hooksPath && git config
|
|
13
|
+
core.hooksPath <path>` passed, because a `--get` anywhere in the line exempted all of it. Fixed.
|
|
14
|
+
- The same shape let a harmless command carry a gated one in three more rules — infrastructure teardown
|
|
15
|
+
(`terraform --help && terraform destroy -auto-approve`), `.env` reads (`cat .env.example; cat .env`) and private
|
|
16
|
+
keys / credentials (`cat ~/.ssh/id_rsa.pub; cat ~/.ssh/id_rsa`). An exemption now covers only the command it is in.
|
|
17
|
+
- `(cat .env)`, `$(cat .env)` and `` `cat .env` `` read the file without being stopped, and
|
|
18
|
+
`cat .env.example $(cat .env)` hid the read behind the template in the same line. Fixed.
|
|
19
|
+
- `core.hooksPath` could be redirected past the gate by spellings git accepts: a lower-case key
|
|
20
|
+
(`core.hookspath`), `git -C <dir> config …`, a backslash-newline between `config` and the key, and removing or
|
|
21
|
+
renaming the whole `[core]` section. All blocked now.
|
|
22
|
+
|
|
23
|
+
- In the plugin edition, a shell command could delete, overwrite or rename the gate scripts and their wiring
|
|
24
|
+
(`rm <plugin>/hooks/guard-bash.sh` passed, while the same command on `.claude/hooks/` was refused), and the Write
|
|
25
|
+
tool guarded only the gate scripts it knew by name. Everything under the plugin's `hooks/` and `.claude-plugin/` —
|
|
26
|
+
in this version and in every other version of the plugin in Claude Code's cache, however the path is spelled — is
|
|
27
|
+
now guarded like `.claude/hooks/`. Reading them stays free.
|
|
28
|
+
- The gates read `eval/lib/crew-env.sh` on every call, but the file itself was not guarded: overwritten, it switched
|
|
29
|
+
every rule off. It is now guarded in both editions.
|
|
30
|
+
|
|
31
|
+
### Fixed
|
|
32
|
+
|
|
33
|
+
- Reading `core.hooksPath` without a value (`git config core.hooksPath`, with or without `--local` / `--global` /
|
|
34
|
+
`--system`) was refused as tampering. It passes now; every write form stays blocked.
|
|
35
|
+
- Updating a project that carried `cqrs-aop-module` but was recorded as `stack=generic` left the skill untrusted,
|
|
36
|
+
and its first session asked whether to trust the pattern skill Crewforth itself had shipped. The update now
|
|
37
|
+
vouches for the skill when every file in it is byte-for-byte a copy Crewforth shipped (2.12–2.13, or
|
|
38
|
+
`devarch-module` renamed in place), whatever the recorded stack. An edited copy is named instead, and the next
|
|
39
|
+
session asks — an edited skill is the user's work.
|
|
40
|
+
- When recording that trust failed, the update said nothing. It now says so, with the reason.
|
|
41
|
+
- Updating a pre-1.1 install (discipline inline in `CLAUDE.md`) on the current branch stopped with
|
|
42
|
+
`TS: unbound variable` when it replaced the inline block. Fixed.
|
|
43
|
+
- In the plugin edition, skills and agents told the model to run `bash .claude/…`, a path a plugin install does not
|
|
44
|
+
have: `/crew-handoff`, the fill reading, `reflect`, `handoff`, the board and the auto-mode policy scripts all
|
|
45
|
+
exited 127. They now name the plugin's own directory, quoted. `/crew-gates` and the skill-usage report say they are
|
|
46
|
+
file-install tools; `trace-scan` names the plugin's pattern list; the panel's Node lookup is quoted.
|
|
47
|
+
- `/crew-doctor`'s shell-gate check could say "watch both Bash and PowerShell" for an install whose Bash guard
|
|
48
|
+
watched only Bash, stayed silent when the guard was not wired at all, and printed its ❌ after "healthy ✅". It now
|
|
49
|
+
reads the guard's own entry with the JSON reader, and its result counts in the verdict.
|
|
50
|
+
- The board printed a path a plugin install cannot run when it was started from its own folder. Fixed.
|
|
51
|
+
- `bash start.sh --help` answered "kit/ not found" and exited 1 when `kit/` was not beside it. Help now needs nothing
|
|
52
|
+
but the script.
|
|
53
|
+
- A site deploy could report success while GitHub Pages kept serving the previous version (at the 3.0.0 launch the
|
|
54
|
+
site showed the release candidate's changelog for half an hour). The site workflow now reads what Pages serves after
|
|
55
|
+
each deploy and fails if it is still the old build.
|
|
56
|
+
- The unvetted-component notice offered one command, `--trust`, that accepted every foreign component at once —
|
|
57
|
+
including ones the user never looked at — and told the model to ask before anything else, so a user's urgent first
|
|
58
|
+
message waited behind it. Each component now carries its own `--trust-one` command, and the model answers the
|
|
59
|
+
user first and asks at the end of that first reply; until then the components are not used.
|
|
60
|
+
- When a subagent handed its report back, the routing hint read the report as a new request and suggested
|
|
61
|
+
delegating the work that had just been done — "Use the crew-database-expert subagent" right after that agent
|
|
62
|
+
finished. A hand-back now gets no hint.
|
|
63
|
+
- A message that only named the frontend in passing ("if there is work for acme_ui, pass it over — the frontend
|
|
64
|
+
team owns that") was routed to the `frontend` skill: the bare word "frontend" and a project called `<x>_ui` each
|
|
65
|
+
matched. The skill now triggers on phrases that carry the intent ("in the frontend", "the UI", "UI component").
|
|
66
|
+
|
|
67
|
+
### Added
|
|
68
|
+
|
|
69
|
+
- When the model sends one of Crewforth's own scripts through the PowerShell tool — where `bash` can be WSL's and
|
|
70
|
+
the script fails — the call is stopped and the model is told to use the Bash tool. "Crewforth's own" is every `.sh`
|
|
71
|
+
it installs, except a skill of your own. Every other PowerShell command passes as before, including searches and
|
|
72
|
+
messages that merely mention such a command.
|
|
73
|
+
- An update moves aside what an older version installed and Crewforth no longer ships — the v1 `-cck` agents, the
|
|
74
|
+
plain v1 commands (`/plan`, `/review`, …), `vps-deploy`, `code-review` — when every file is byte-for-byte a copy
|
|
75
|
+
Crewforth shipped. They go to `.claude/.legacy-backup/<time>/` and the update prints the one line that puts them
|
|
76
|
+
back. A component with an edit, an extra file or a symlink stays where it is and is named. Nothing is deleted, and
|
|
77
|
+
it works without an install manifest. A moved skill is no longer raised as unvetted at the next session start.
|
|
78
|
+
- `/crew-doctor` reports when the install is missing what its manifest lists, or has no manifest next to `VERSION`
|
|
79
|
+
— the last update did not finish, or `.claude/` was copied from another project — and suggests
|
|
80
|
+
`npx crewforth update --here`. It changes nothing. A component removed on purpose looks the same on disk, so the
|
|
81
|
+
line says to ignore it in that case.
|
|
82
|
+
|
|
83
|
+
### Changed
|
|
84
|
+
|
|
85
|
+
- The discipline now says where a project's own `CLAUDE.md` stands: it wins on conflict, and in §4 it can only
|
|
86
|
+
tighten, never loosen — the one exception is the §4.1 trace allowlist chosen at adoption. The § numbers in the
|
|
87
|
+
discipline refer to the discipline itself. The updater's summary, handover and decision record say the same.
|
|
88
|
+
- Every place that tells the model to run one of Crewforth's scripts now says to use the Bash tool, not PowerShell.
|
|
89
|
+
- The client side is stack-neutral too. The install summary, help and install page named the scope "backend, web and
|
|
90
|
+
mobile (RN/Expo)"; they now say the stack is read from the project, and React Native/Expo is one optional layer. The
|
|
91
|
+
first client task resolves the stack the way the backend does — the request, `Client:` in `## Stack`, the repo's
|
|
92
|
+
manifests (`package.json`, `pubspec.yaml`, `Package.swift`, `build.gradle`, a MAUI `*.csproj`) — and in an empty repo
|
|
93
|
+
asks once, recommending nothing, then records it. `frontend-rn-expo` applies only to an RN/Expo project; Flutter,
|
|
94
|
+
SwiftUI and Compose requests go to `crew-frontend-expert`.
|
|
95
|
+
- `/crew-doctor` and the doctor said skills past the listing budget "stop being picked" / "stop matching requests".
|
|
96
|
+
They are picked less often on their own, not never; the warning now says so, in English and Turkish.
|
|
97
|
+
- The Bash guard no longer starts a process per rule on the read-only path: `git status` went from 14 processes per
|
|
98
|
+
call to 1 (macOS: ~35 ms to ~10 ms per call). On a Windows machine where one process was measured at 1.3–3.9 s,
|
|
99
|
+
the old path cost 37–66 s per call.
|
|
100
|
+
|
|
101
|
+
## [3.0.0] — 2026-09-28
|
|
9
102
|
|
|
10
103
|
### BREAKING — Claude Starter Kit is now Crewforth
|
|
11
104
|
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
3.0.
|
|
1
|
+
3.0.1
|
package/adopt.sh
CHANGED
|
@@ -125,6 +125,9 @@ _mt() { # $1 = English text (the key); further args fill %s; result in _M
|
|
|
125
125
|
"stack=%s %s") s='stack=%s %s' ;;
|
|
126
126
|
"stack=%s · via %s") s='stack=%s · kuran: %s' ;;
|
|
127
127
|
"stack=dotnet — 3.0 records generic; the pattern skill stays as a project skill") s="stack=dotnet — 3.0 generic kaydeder; desen skill'i proje skill'i olarak kalır" ;;
|
|
128
|
+
'⚠️ cqrs-aop-module could not be recorded as trusted (skill-trust.sh exit %s: %s) — the next session will ask about it') s="⚠️ cqrs-aop-module güvenilir olarak kaydedilemedi (skill-trust.sh çıkış kodu %s: %s) — bir sonraki oturum onu soracak" ;;
|
|
129
|
+
"cqrs-aop-module could not be checked against the copies Crewforth shipped (git or the shipped list is missing) — not vouched for; the next session will ask whether to trust it.") s="cqrs-aop-module, Crewforth'un gönderdiği kopyalarla karşılaştırılamadı (git ya da gönderilen liste yok) — güvenilir sayılmadı; bir sonraki oturum güvenip güvenmeyeceğinizi soracak." ;;
|
|
130
|
+
"cqrs-aop-module is 2.x's .NET pattern, changed since Crewforth shipped it — not vouched for; the next session will ask whether to trust it.") s="cqrs-aop-module 2.x'in .NET deseni; Crewforth'un gönderdiği halinden değiştirilmiş — güvenilir sayılmadı; bir sonraki oturum güvenip güvenmeyeceğinizi soracak." ;;
|
|
128
131
|
"cqrs-aop-module is now a project skill (Crewforth no longer ships it); backend-expert applies it as your project's pattern.") s="cqrs-aop-module artık bir proje skill'i (Crewforth onu artık taşımıyor); backend-expert onu projenizin deseni olarak uygular." ;;
|
|
129
132
|
"CSK_CORRECT_STACK has no effect since 3.0 — there is one backend shape; the stack lives in CLAUDE.md ## Stack.") s="CSK_CORRECT_STACK 3.0'dan beri etkisiz — tek bir backend biçimi var; yığın CLAUDE.md ## Stack bölümünde durur." ;;
|
|
130
133
|
"§4.2: DevArchitecture stays armed in the trace blocklist (it was armed before this update)") s="§4.2: DevArchitecture iz engel listesinde devrede kalıyor (güncellemeden önce de devredeydi)" ;;
|
|
@@ -134,7 +137,7 @@ _mt() { # $1 = English text (the key); further args fill %s; result in _M
|
|
|
134
137
|
"no/untracked") s='hayır / izlenmiyor' ;;
|
|
135
138
|
".claude/CLAUDE.md in git") s=".claude/CLAUDE.md git'te mi" ;;
|
|
136
139
|
"supply-chain scan flagged existing project skills/agents (advisory — review before trusting them):") s='tedarik zinciri taraması projedeki bazı skill/ajanları işaretledi (yalnız uyarı — güvenmeden önce inceleyin):' ;;
|
|
137
|
-
"full report after install: %s (
|
|
140
|
+
"full report after install: %s (Bash tool, not PowerShell; heuristic — a security skill can score low by design)") s="kurulumdan sonra tam rapor: %s (PowerShell değil, Bash aracıyla; sezgisel — güvenlik skill'leri doğası gereği düşük puan alabilir)" ;;
|
|
138
141
|
"supply-chain scan") s='tedarik zinciri taraması' ;;
|
|
139
142
|
"existing project skills/agents look clean (no red flags)") s='projedeki skill/ajanlar temiz görünüyor (şüpheli bir şey yok)' ;;
|
|
140
143
|
"[3] 7 handover decisions — SMART SUGGESTION") s='[3] Devralma için 7 karar — AKILLI ÖNERİ' ;;
|
|
@@ -148,7 +151,7 @@ _mt() { # $1 = English text (the key); further args fill %s; result in _M
|
|
|
148
151
|
"no custom agents found in the project") s='projede özel ajan yok' ;;
|
|
149
152
|
"2 Precedence") s='2 Öncelik' ;;
|
|
150
153
|
"project wins (fixed)") s='proje önde (sabit)' ;;
|
|
151
|
-
"on conflict the project's rules
|
|
154
|
+
"on conflict the project's rules win; in DISCIPLINE.md §4 they can only tighten, never loosen (the one exception: the §4.1 trace allowlist you choose at adoption); Crewforth fills gaps (not overridable)") s="çakışmada projenin kuralı geçerli; DISCIPLINE.md §4'te proje yalnız sıkılaştırabilir, gevşetemez (tek istisna: kurulumda sizin seçtiğiniz §4.1 iz izin listesi); Crewforth boşlukları doldurur (değiştirilemez)" ;;
|
|
152
155
|
"3 Trace gate") s='3 İz kapısı' ;;
|
|
153
156
|
"loosen (.trace-allowlist)") s='gevşet (.trace-allowlist)' ;;
|
|
154
157
|
"co-author/sign-off present in git log — may be a convention") s='git geçmişinde co-author/sign-off var — ekibin alışkanlığı olabilir' ;;
|
|
@@ -227,6 +230,15 @@ _mt() { # $1 = English text (the key); further args fill %s; result in _M
|
|
|
227
230
|
"The name is the invocation: a leftover COMMAND still lists in the / picker (/review twice), and a") s='Burada adın kendisi çağrıdır: artakalan bir KOMUT / menüsünde hâlâ görünür (/review iki kez), artakalan' ;;
|
|
228
231
|
"leftover SKILL still matches prompts, so it competes with whatever replaced it.") s='bir SKILL de istemlerle eşleşmeye devam eder ve yerine gelenle yarışır.' ;;
|
|
229
232
|
"Nothing is deleted for you — one of these may be a file you customised. To drop them all:") s='Hiçbiri sizin yerinize silinmez — aralarında özelleştirdiğiniz bir dosya olabilir. Hepsini kaldırmak için:' ;;
|
|
233
|
+
'older Crewforth components could not be checked against what Crewforth shipped (git or the shipped list is missing) — left in place:%s') s="eski Crewforth bileşenleri Crewforth'un dağıttıklarıyla karşılaştırılamadı (git ya da dağıtım listesi yok) — yerinde bırakıldı:%s" ;;
|
|
234
|
+
'moved aside — each one exactly as an older Crewforth shipped it, and no longer shipped:%s') s="kenara alındı — her biri eski bir Crewforth'un dağıttığıyla birebir aynı ve artık dağıtılmıyor:%s" ;;
|
|
235
|
+
'to put them back: cp -R %s/. .claude/') s='geri koymak için: cp -R %s/. .claude/' ;;
|
|
236
|
+
'left in place — installed by an older Crewforth, no longer shipped, and changed since (yours now?):%s') s="yerinde bırakıldı — eski bir Crewforth kurmuştu, artık dağıtılmıyor ve o günden beri değişmiş (artık sizin mi?):%s" ;;
|
|
237
|
+
'Each still answers its name (a command lists in the / picker, a skill matches prompts). To drop them:') s='Her biri adıyla çağrılmaya devam eder (komut / menüsünde görünür, skill istemlerle eşleşir). Kaldırmak için:' ;;
|
|
238
|
+
'%s (an unchanged copy Crewforth shipped) moved aside; your %s was not touched') s="%s (Crewforth'un dağıttığı, değişmemiş kopya) yedeğe taşındı; sizin %s dosyanıza dokunulmadı" ;;
|
|
239
|
+
'%s (a second, unchanged copy Crewforth shipped) moved aside; %s already answers that command') s="%s (Crewforth'un dağıttığı ikinci, değişmemiş kopya) yedeğe taşındı; o komutu zaten %s karşılıyor" ;;
|
|
240
|
+
'older Crewforth components in a symlinked (shared?) directory are left in place — moving them out would remove them from every project that uses it:%s') s='sembolik bağlantılı (paylaşılan?) bir dizindeki eski Crewforth bileşenleri yerinde bırakıldı — taşımak onları bu dizini kullanan her projeden kaldırırdı:%s' ;;
|
|
241
|
+
'(an unchanged copy put back is moved aside again by the next update — to keep one, change it)') s='(geri konan değişmemiş bir kopya sonraki güncellemede yine kenara alınır — birini tutmak için onu değiştirin)' ;;
|
|
230
242
|
"Coexist summary") s='Kurulum özeti' ;;
|
|
231
243
|
"%s · %s skipped") s='%s · %s atlandı' ;;
|
|
232
244
|
"+%s added") s='+%s eklendi' ;;
|
|
@@ -301,7 +313,7 @@ _mt() { # $1 = English text (the key); further args fill %s; result in _M
|
|
|
301
313
|
"staged") s='stage edilen' ;;
|
|
302
314
|
"see it: open the Source Control / Changes panel (every added + changed file is listed) · or: %s") s='görmek için: Source Control / Changes panelini açın (eklenen ve değişen her dosya orada) · ya da: %s' ;;
|
|
303
315
|
"panel: %s opens it from this project (or: %s)") s='panel: bu projede %s ile açılır (ya da: %s)' ;;
|
|
304
|
-
"panel: needs Node 18+, absent here — Crewforth can fetch one: %s") s='panel: Node 18+ gerekiyor ve bu makinede yok — Crewforth indirebilir: %s' ;;
|
|
316
|
+
"panel: needs Node 18+, absent here — Crewforth can fetch one (Bash tool, not PowerShell): %s") s='panel: Node 18+ gerekiyor ve bu makinede yok — Crewforth indirebilir (PowerShell değil, Bash aracıyla): %s' ;;
|
|
305
317
|
"(it asks first, verifies the checksum, and touches nothing outside %s)") s='(önce sorar, sağlama toplamını doğrular ve %s dışında hiçbir şeye dokunmaz)' ;;
|
|
306
318
|
"If Claude Code is running in this project, run /clear (or quit and relaunch it) — a new session loads the") s='Claude Code bu projede açıksa /clear çalıştırın (ya da kapatıp yeniden açın) — yeni oturum güncel' ;;
|
|
307
319
|
"updated CLAUDE.md and discipline; a session opened before this run keeps the old rules until then.") s='CLAUDE.md ve disiplini yükler; bu çalıştırmadan önce açılmış oturum o zamana kadar eski kuralları uygular.' ;;
|
|
@@ -631,7 +643,7 @@ if { [ "$N_PAGENTS" != 0 ] || [ "$N_PSKILLS" != 0 ]; } && [ -f "$SRC/eval/scan-s
|
|
|
631
643
|
if printf '%s' "$SCANOUT" | grep -qE 'DANGER|REVIEW'; then
|
|
632
644
|
warnm 'supply-chain scan flagged existing project skills/agents (advisory — review before trusting them):'
|
|
633
645
|
printf '%s\n' "$SCANOUT" | grep -E 'DANGER|REVIEW' | sed 's/^/ /'
|
|
634
|
-
_mt 'full report after install: %s (
|
|
646
|
+
_mt 'full report after install: %s (Bash tool, not PowerShell; heuristic — a security skill can score low by design)' 'bash .claude/eval/scan-skill.sh .claude'
|
|
635
647
|
sub " $_M"
|
|
636
648
|
else
|
|
637
649
|
rowm 'supply-chain scan' 'existing project skills/agents look clean (no red flags)'
|
|
@@ -655,7 +667,7 @@ elif [ "$N_PAGENTS" != 0 ]; then
|
|
|
655
667
|
else
|
|
656
668
|
propm '1 Role clash' 'none' 'no custom agents found in the project'
|
|
657
669
|
fi
|
|
658
|
-
propm '2 Precedence' 'project wins (fixed)' "on conflict the project's rules
|
|
670
|
+
propm '2 Precedence' 'project wins (fixed)' "on conflict the project's rules win; in DISCIPLINE.md §4 they can only tighten, never loosen (the one exception: the §4.1 trace allowlist you choose at adoption); Crewforth fills gaps (not overridable)"
|
|
659
671
|
if [ "$COAUTHOR" = 1 ]; then
|
|
660
672
|
propm '3 Trace gate' 'loosen (.trace-allowlist)' 'co-author/sign-off present in git log — may be a convention'
|
|
661
673
|
else
|
|
@@ -872,8 +884,8 @@ if [ "$KIT_PRESENT" = 1 ] && [ -d .claude/skills/devarch-module ]; then
|
|
|
872
884
|
say '⚠️ both devarch-module and cqrs-aop-module are present — nothing moved; remove the old one when ready'
|
|
873
885
|
fi
|
|
874
886
|
fi
|
|
875
|
-
|
|
876
|
-
|
|
887
|
+
# Whether the updater vouches for cqrs-aop-module is decided AFTER the manifest is written — see "THE PATTERN SKILL'S
|
|
888
|
+
# TRUST" below; the notice is printed there with the verdict.
|
|
877
889
|
# THE 3.0 NAME MIGRATION: <x>-csk -> crew-<x>, for the KIT'S OWN components only (agents, commands, the code-review
|
|
878
890
|
# skill), and only on a project the kit was installed on. Same principle as the devarch-module rename above: move,
|
|
879
891
|
# never delete. The names come from the payload, so a project file that merely ends in -csk (my-helper-csk.md) is
|
|
@@ -893,7 +905,6 @@ if [ "$KIT_PRESENT" = 1 ]; then
|
|
|
893
905
|
else mv "$old" "$new" 2>/dev/null && { LEGACY_MOVED=$((LEGACY_MOVED+1)); say '3.0 rename: %s → %s' "${old#.claude/}" "${new#.claude/}"; }
|
|
894
906
|
fi
|
|
895
907
|
done
|
|
896
|
-
[ -n "$LEGACY_BOTH" ] && warnm '3.0 rename: both the old and the new name exist for:%s — nothing moved; keep one' "$LEGACY_BOTH"
|
|
897
908
|
fi
|
|
898
909
|
# THE 3.0 COMMANDS -> SKILLS MOVE. Claude Code merged custom commands into skills (`.claude/commands/` is "the
|
|
899
910
|
# older format"), and a skill and a command of the same name are the same `/name` — the skill wins. So the kit's
|
|
@@ -928,8 +939,6 @@ for kf in $CMD_SKILLS; do
|
|
|
928
939
|
fi
|
|
929
940
|
done
|
|
930
941
|
done
|
|
931
|
-
[ -n "$CMD_BOTH" ] && warnm '3.0 commands are skills: a skill of that name already exists for:%s — nothing moved; keep one' "$CMD_BOTH"
|
|
932
|
-
[ -n "$CMD_DUP" ] && warnm '3.0 commands are skills: an older copy of an already-moved command is left in place:%s — remove it' "$CMD_DUP"
|
|
933
942
|
[ -n "$CMD_LINK" ] && warnm '3.0 commands are skills: symlinked command file(s) left as they are:%s — the skill of that name now answers /name' "$CMD_LINK"
|
|
934
943
|
[ "$CMD_DIR_SHARED" = 1 ] && [ "$KIT_PRESENT" = 1 ] && warnm '3.0 commands are skills: .claude/commands is a symlink (shared?) — nothing was moved out of it; the skills of those names now answer /name'
|
|
935
944
|
[ -n "$CMD_MINE" ] && warnm 'your own command(s) keep their name — the Crewforth skill of the same name was not installed:%s' "$CMD_MINE"
|
|
@@ -981,6 +990,130 @@ if [ "$KIT_PRESENT" = 1 ] && [ -f "$_AMS" ] && grep -qE '"CSK (Uncommitted Work
|
|
|
981
990
|
fi
|
|
982
991
|
rm -f "$_AMS.crew-new"
|
|
983
992
|
fi
|
|
993
|
+
# THE LEGACY SWEEP. Components Crewforth shipped in 1.x-2.x and no longer ships (kit/legacy-blobs.tsv, generated from the
|
|
994
|
+
# release tags) stay on disk after an update, and for every kind the NAME IS THE INVOCATION: a leftover command lists
|
|
995
|
+
# in the / picker, a leftover skill competes for prompts, and the next session's trust gate asks about each skill as
|
|
996
|
+
# if the user had brought it in. One that is byte-for-byte a copy Crewforth shipped is MOVED ASIDE to
|
|
997
|
+
# .claude/.legacy-backup/<time>/ with a one-line restore; anything else — an edit, an extra file, a symlink — is the
|
|
998
|
+
# user's work and stays where it is, named. Nothing is deleted. The rule is "every file is a blob shipped for ITS path",
|
|
999
|
+
# not "all from one release": a field install carried vps-deploy with SKILL.md from 2.0-2.6 and references from 1.4.
|
|
1000
|
+
# cqrs-aop-module / devarch-module are excluded: 3.0 keeps the pattern skill as the project's own (its trust is below).
|
|
1001
|
+
# Runs after the rename and commands->skills moves, so what reaches it is what they left: a v1 -cck agent, a plain
|
|
1002
|
+
# v1 command, a -csk copy next to its crew- name. Works without an install manifest — the list is the kit's, not the
|
|
1003
|
+
# project's.
|
|
1004
|
+
#
|
|
1005
|
+
# _untouched_copies <component>...: prints each component (skills/<n>, agents/<n>.md, commands/<n>.md) that is an
|
|
1006
|
+
# untouched shipped copy. rc 2 = cannot tell (no git, no list). Same hardening as the
|
|
1007
|
+
# pattern skill's check, which uses it: a symlink or special file anywhere refuses the component (`find -type f` does
|
|
1008
|
+
# not list one, so a link would be "checked" by checking nothing); zero files is not a match; CR is stripped only when
|
|
1009
|
+
# every CR ends a line; paths reach awk as data, never through -v. Batched: one find, one git, one awk for all of
|
|
1010
|
+
# them — per-file hashing was 5 processes a file, which is seconds on Git Bash.
|
|
1011
|
+
_untouched_copies(){
|
|
1012
|
+
local lst="$SRC/legacy-blobs.tsv" NL=$'\n' TB=$'\t' CR=$'\r' c p bad fo f h hl cr crl rows="" i=0
|
|
1013
|
+
local live=() fl=() hs=()
|
|
1014
|
+
[ -f "$lst" ] && command -v git >/dev/null 2>&1 || return 2
|
|
1015
|
+
for c in "$@"; do [ -e ".claude/$c" ] || [ -L ".claude/$c" ] && live+=(".claude/$c"); done # a link is kept, for find to refuse
|
|
1016
|
+
[ "${#live[@]}" -gt 0 ] || return 0
|
|
1017
|
+
# A newline, tab or CR in a name would split or bend a line below (git's line reader drops a trailing CR), so it
|
|
1018
|
+
# disqualifies its component like a symlink does.
|
|
1019
|
+
bad="$(find "${live[@]}" \( ! -type f ! -type d \) -o -name "*$NL*" -o -name "*$TB*" -o -name "*$CR*" 2>/dev/null)"
|
|
1020
|
+
for p in "${live[@]}"; do
|
|
1021
|
+
case "$NL$bad$NL" in *"$NL$p$NL"*|*"$NL$p/"*) continue ;; esac
|
|
1022
|
+
# find's status is read: a subdirectory it cannot enter hides files, and those would travel with the component.
|
|
1023
|
+
fo="$(find "$p" -type f 2>/dev/null)" || continue
|
|
1024
|
+
while IFS= read -r f; do [ -n "$f" ] && fl+=("$f"); done <<< "$fo"
|
|
1025
|
+
done
|
|
1026
|
+
[ "${#fl[@]}" -gt 0 ] || return 0
|
|
1027
|
+
while IFS= read -r h; do hs+=("$h"); done < <(printf '%s\n' "${fl[@]}" | git hash-object --no-filters --stdin-paths 2>/dev/null)
|
|
1028
|
+
# One file git cannot read stops the batch. Then each file is hashed on its own, and one that fails is simply not a
|
|
1029
|
+
# match — it refuses its own component, not every component.
|
|
1030
|
+
if [ "${#hs[@]}" != "${#fl[@]}" ]; then
|
|
1031
|
+
hs=(); for f in "${fl[@]}"; do h="$(git hash-object --no-filters -- "$f" 2>/dev/null)"; hs+=("${h:--}"); done
|
|
1032
|
+
fi
|
|
1033
|
+
# Only a file that holds a CR pays for the CR-stripped hash (none of the shipped blobs has one).
|
|
1034
|
+
local crf; crf="$NL$(grep -l $'\r' -- "${fl[@]}" 2>/dev/null)$NL"
|
|
1035
|
+
while [ "$i" -lt "${#fl[@]}" ]; do
|
|
1036
|
+
f="${fl[$i]}"; h="${hs[$i]}"; hl="$h"
|
|
1037
|
+
case "$crf" in *"$NL$f$NL"*)
|
|
1038
|
+
cr="$(tr -dc '\r' < "$f" 2>/dev/null | wc -c | tr -d ' ')"; crl="$(grep -c $'\r$' "$f" 2>/dev/null)"
|
|
1039
|
+
[ "$cr" = "${crl:-x}" ] && hl="$(tr -d '\r' < "$f" 2>/dev/null | git hash-object --no-filters --stdin 2>/dev/null)" ;;
|
|
1040
|
+
esac
|
|
1041
|
+
rows="$rows$f$TB$h$TB${hl:-$h}$NL"; i=$((i+1))
|
|
1042
|
+
done
|
|
1043
|
+
printf '%s' "$rows" | awk -F'\t' 'FNR == NR { if ($0 !~ /^#/) ok[$2 SUBSEP $3] = 1; next }
|
|
1044
|
+
{ c = substr($1, 9); split(c, a, "/"); if (a[1] == "skills") c = a[1] "/" a[2]
|
|
1045
|
+
n[c]++; if (!((($1 SUBSEP $2) in ok) || (($1 SUBSEP $3) in ok))) bad[c] = 1 }
|
|
1046
|
+
END { for (c in n) if (!(c in bad)) print c }' "$lst" - | LC_ALL=C sort
|
|
1047
|
+
}
|
|
1048
|
+
LEG_MOVED=""; LEG_KEPT=""; LEG_BK=""; _LNL=$'\n'
|
|
1049
|
+
if [ "$KIT_PRESENT" = 1 ] && [ -f "$SRC/legacy-blobs.tsv" ]; then
|
|
1050
|
+
_lon=""; _lsh=""
|
|
1051
|
+
for c in $(awk -F'\t' '!/^#/ && $1 != "skills/cqrs-aop-module" && $1 != "skills/devarch-module" && !s[$1]++ { print $1 }' "$SRC/legacy-blobs.tsv"); do
|
|
1052
|
+
[ -e ".claude/$c" ] || [ -L ".claude/$c" ] || continue
|
|
1053
|
+
# A symlinked .claude/ or .claude/<kind>/ is likely shared by several projects: moving a file OUT of it would take
|
|
1054
|
+
# it from every one of them (the same reason the commands->skills move leaves a linked commands/ alone).
|
|
1055
|
+
if [ -L .claude ] || [ -L ".claude/${c%%/*}" ]; then _lsh="$_lsh $c"; continue; fi
|
|
1056
|
+
# A 2.x command or skill beside a skills/crew-<x>/ of the user's that has no SKILL.md: the kit's skill is not
|
|
1057
|
+
# installed there, so this one is the only thing answering the name — it stays, and "keep one" below says why.
|
|
1058
|
+
case "$c" in commands/*-csk.md) _b="${c#commands/}"; _b="${_b%-csk.md}" ;; skills/*-csk) _b="${c#skills/}"; _b="${_b%-csk}" ;; *) _b="" ;; esac
|
|
1059
|
+
[ -n "$_b" ] && [ -d ".claude/skills/crew-$_b" ] && [ ! -f ".claude/skills/crew-$_b/SKILL.md" ] && continue
|
|
1060
|
+
_lon="$_lon $c"
|
|
1061
|
+
done
|
|
1062
|
+
[ -n "$_lsh" ] && warnm 'older Crewforth components in a symlinked (shared?) directory are left in place — moving them out would remove them from every project that uses it:%s' "$_lsh"
|
|
1063
|
+
if [ -n "$_lon" ]; then
|
|
1064
|
+
_lun="$(_untouched_copies $_lon)"; _lrc=$?
|
|
1065
|
+
if [ "$_lrc" = 2 ]; then
|
|
1066
|
+
LEG_KEPT="$_lon"
|
|
1067
|
+
warnm 'older Crewforth components could not be checked against what Crewforth shipped (git or the shipped list is missing) — left in place:%s' "$_lon"
|
|
1068
|
+
else
|
|
1069
|
+
LEG_BK=".claude/.legacy-backup/$(date +%Y%m%d-%H%M%S)"; _b="$LEG_BK"; i=2
|
|
1070
|
+
while [ -e "$_b" ]; do _b="$LEG_BK-$i"; i=$((i+1)); done; LEG_BK="$_b"
|
|
1071
|
+
for c in $_lon; do
|
|
1072
|
+
case "$_LNL$_lun$_LNL" in *"$_LNL$c$_LNL"*)
|
|
1073
|
+
if mkdir -p "$LEG_BK/${c%/*}" 2>/dev/null && mv ".claude/$c" "$LEG_BK/$c" 2>/dev/null; then
|
|
1074
|
+
LEG_MOVED="$LEG_MOVED $c"
|
|
1075
|
+
# A v1 -cck agent was counted as the project's own (KIT_OLD knows the 2.x names); moved aside, it is neither.
|
|
1076
|
+
case "$c" in agents/*) _la="${c#agents/}"; case "$KIT_OLD" in *" ${_la%.md} "*) ;; *) N_PAGENTS=$((N_PAGENTS-1)) ;; esac ;; esac
|
|
1077
|
+
continue
|
|
1078
|
+
fi ;;
|
|
1079
|
+
esac
|
|
1080
|
+
LEG_KEPT="$LEG_KEPT $c"
|
|
1081
|
+
done
|
|
1082
|
+
if [ -n "$LEG_MOVED" ]; then
|
|
1083
|
+
# A team that shares .claude/ should not commit the backup by accident; the restore reads the files, not git.
|
|
1084
|
+
[ -f .claude/.legacy-backup/.gitignore ] || printf '*\n' > .claude/.legacy-backup/.gitignore 2>/dev/null
|
|
1085
|
+
say 'moved aside — each one exactly as an older Crewforth shipped it, and no longer shipped:%s' "$LEG_MOVED"
|
|
1086
|
+
say 'to put them back: cp -R %s/. .claude/' "$LEG_BK"
|
|
1087
|
+
say '(an unchanged copy put back is moved aside again by the next update — to keep one, change it)'
|
|
1088
|
+
else rmdir "$LEG_BK" .claude/.legacy-backup 2>/dev/null || true
|
|
1089
|
+
fi
|
|
1090
|
+
if [ -n "$LEG_KEPT" ]; then
|
|
1091
|
+
warnm 'left in place — installed by an older Crewforth, no longer shipped, and changed since (yours now?):%s' "$LEG_KEPT"
|
|
1092
|
+
_mt 'Each still answers its name (a command lists in the / picker, a skill matches prompts). To drop them:'; printf ' %s\n' "$_M"
|
|
1093
|
+
printf ' rm -r'; for e in $LEG_KEPT; do printf ' .claude/%s' "$e"; done; echo
|
|
1094
|
+
fi
|
|
1095
|
+
fi
|
|
1096
|
+
fi
|
|
1097
|
+
fi
|
|
1098
|
+
[ -n "$LEG_MOVED" ] && [ "$CMD_DIR_SHARED" = 0 ] && { rmdir .claude/commands 2>/dev/null || true; } # emptied by the sweep
|
|
1099
|
+
# The "keep one" notices wait for the sweep: a -csk copy it moved aside is no longer one of two. When it moved one,
|
|
1100
|
+
# the line names BOTH files, so the user reads from one line what went, why, and that their own file was not touched.
|
|
1101
|
+
_lb=""; for o in $LEGACY_BOTH; do
|
|
1102
|
+
if [ -e "$o" ]; then _lb="$_lb $o"; continue; fi
|
|
1103
|
+
_lo="${o#.claude/}"; _lx=""; case "$_lo" in *.md) _lx=.md ;; esac; _ln="${_lo%/*}/crew-$(_b="${_lo##*/}"; _b="${_b%.md}"; printf '%s' "${_b%-csk}")$_lx"
|
|
1104
|
+
say '%s (an unchanged copy Crewforth shipped) moved aside; your %s was not touched' "$_lo" "$_ln"
|
|
1105
|
+
done
|
|
1106
|
+
[ -n "$_lb" ] && warnm '3.0 rename: both the old and the new name exist for:%s — nothing moved; keep one' "$_lb"
|
|
1107
|
+
_lb=""; for o in $CMD_BOTH; do
|
|
1108
|
+
if [ -e ".claude/$o" ]; then _lb="$_lb $o"; continue; fi
|
|
1109
|
+
_b="${o##*/}"; _b="${_b%.md}"; say '%s (an unchanged copy Crewforth shipped) moved aside; your %s was not touched' "$o" "skills/crew-${_b%-csk}/"
|
|
1110
|
+
done
|
|
1111
|
+
[ -n "$_lb" ] && warnm '3.0 commands are skills: a skill of that name already exists for:%s — nothing moved; keep one' "$_lb"
|
|
1112
|
+
_lb=""; for o in $CMD_DUP; do
|
|
1113
|
+
if [ -e ".claude/$o" ]; then _lb="$_lb $o"; continue; fi
|
|
1114
|
+
_b="${o##*/}"; _b="${_b%.md}"; _b="${_b#crew-}"; say '%s (a second, unchanged copy Crewforth shipped) moved aside; %s already answers that command' "$o" "skills/crew-${_b%-csk}/"
|
|
1115
|
+
done
|
|
1116
|
+
[ -n "$_lb" ] && warnm '3.0 commands are skills: an older copy of an already-moved command is left in place:%s — remove it' "$_lb"
|
|
984
1117
|
# #1 keepmine: your overlapping agents own those roles, so the kit's matching crew- agents are NOT installed.
|
|
985
1118
|
[ "$COLLIDE_MODE" = keepmine ] && for b in $COLLIDE; do EXCL_A="$EXCL_A crew-$b.md"; done
|
|
986
1119
|
# kit-owned trees: FORCE-refresh on a re-adopt (KIT_PRESENT) so kit updates land; never-overwrite on a fresh adopt
|
|
@@ -1011,11 +1144,6 @@ done
|
|
|
1011
1144
|
# a plain -x match read it as disarmed and the refresh switched §4.2 off without a word (measured in review).
|
|
1012
1145
|
VENDOR_ARMED=0; grep -qxE $'DevArchitecture\r?' .claude/hooks/trace-blocklist.txt 2>/dev/null && VENDOR_ARMED=1
|
|
1013
1146
|
copy_noclobber "$SRC/hooks" .claude/hooks "$KIT_PRESENT"; H_ADD=$ret_add; H_SKIP=$ret_skip
|
|
1014
|
-
# The 3.0 migration made cqrs-aop-module a project skill; it is the one Crewforth shipped until now, so the updater
|
|
1015
|
-
# vouches for it rather than having the next session open by asking whether to trust it. Only that component, and
|
|
1016
|
-
# only after the refreshed hook (which knows --trust-one) is in place.
|
|
1017
|
-
[ "$LEGACY_DOTNET" = 1 ] && [ -f .claude/skills/cqrs-aop-module/SKILL.md ] \
|
|
1018
|
-
&& bash .claude/hooks/skill-trust.sh --trust-one skills/cqrs-aop-module >/dev/null 2>&1 || true
|
|
1019
1147
|
copy_noclobber "$SRC/eval" .claude/eval "$KIT_PRESENT"; E_ADD=$ret_add; E_SKIP=$ret_skip
|
|
1020
1148
|
# The Studio panel. This is the line that answers "I updated and `/crew-studio` says the panel
|
|
1021
1149
|
# is missing": with KIT_PRESENT=1 it is a force-refresh, so a project that already has the kit
|
|
@@ -1199,6 +1327,7 @@ if [ -f .claude/kit-manifest.txt ]; then
|
|
|
1199
1327
|
[ -e "$SRC/$entry" ] && continue
|
|
1200
1328
|
# The 3.0 migration keeps the former .NET pattern skill as the project's own; this sweep's advice is `rm -r`.
|
|
1201
1329
|
[ "$entry" = skills/cqrs-aop-module ] && continue
|
|
1330
|
+
case " $LEG_KEPT " in *" $entry "*) continue ;; esac # the legacy sweep above has already named it
|
|
1202
1331
|
[ -e ".claude/$entry" ] && STALE="$STALE $entry"
|
|
1203
1332
|
done < .claude/kit-manifest.txt
|
|
1204
1333
|
if [ -n "$STALE" ]; then
|
|
@@ -1219,6 +1348,44 @@ fi
|
|
|
1219
1348
|
for f in "$SRC"/agents/*.md; do [ -e "$f" ] && echo "agents/$(basename "$f")"; done
|
|
1220
1349
|
} > .claude/kit-manifest.txt 2>/dev/null || true
|
|
1221
1350
|
|
|
1351
|
+
# THE PATTERN SKILL'S TRUST. 3.0 keeps cqrs-aop-module as the project's own; the updater vouches for it only when it is
|
|
1352
|
+
# PROVABLY the copy Crewforth shipped — every file in it hashes (raw, or with CR removed) to a blob id listed for its
|
|
1353
|
+
# path in legacy-blobs.tsv (cqrs-aop-module 2.12-2.13, and devarch-module 1.0.0-2.11.0 renamed in place), and nothing
|
|
1354
|
+
# else is in the directory. An edited copy is the user's work, and a skill is executable instruction: it is named,
|
|
1355
|
+
# not vouched for, and the next session asks. The old test was the recorded stack (`stack=dotnet`), which measured
|
|
1356
|
+
# the wrong thing: a generic-recorded install carrying the skill got nothing and its first session flagged it
|
|
1357
|
+
# (field report, reproduced in e2e). Asked only while the skill is still unvetted — the trust gate's own answer,
|
|
1358
|
+
# read after the manifest exists — so an update does not repeat itself once it is settled.
|
|
1359
|
+
# Returns 0 shipped · 1 not a shipped copy · 2 cannot tell (no git, no list). The check is the legacy sweep's
|
|
1360
|
+
# _untouched_copies (hardened in review: a symlink anywhere is refused — a link to arbitrary text used to be "checked"
|
|
1361
|
+
# by checking nothing, and --trust-one then recorded the target's digest; zero files is not a match; CR is stripped
|
|
1362
|
+
# only for a CRLF copy; paths never reach awk through -v, which would turn SKIL\114.md into SKILL.md).
|
|
1363
|
+
_cqrs_shipped(){
|
|
1364
|
+
local u rc
|
|
1365
|
+
u="$(_untouched_copies skills/cqrs-aop-module)"; rc=$?
|
|
1366
|
+
[ "$rc" = 2 ] && return 2
|
|
1367
|
+
[ "$u" = skills/cqrs-aop-module ] && [ -f .claude/skills/cqrs-aop-module/SKILL.md ] && [ ! -L .claude/skills/cqrs-aop-module/SKILL.md ]
|
|
1368
|
+
}
|
|
1369
|
+
# The gate's answer is read from its output, never through `| grep -q`: grep closing early SIGPIPEs the hook and
|
|
1370
|
+
# pipefail turns the whole test false — that is how the first version of this block never ran (e2e, C1).
|
|
1371
|
+
_ptrust=""; [ "$KIT_PRESENT" = 1 ] && [ -f .claude/skills/cqrs-aop-module/SKILL.md ] \
|
|
1372
|
+
&& _ptrust="$(CLAUDE_PROJECT_DIR="$PWD" bash .claude/hooks/skill-trust.sh </dev/null 2>/dev/null || true)"
|
|
1373
|
+
if grep -qE '(^|[[:space:]])skills/cqrs-aop-module([[:space:]]|$)' <<< "$_ptrust"; then # exact name: not skills/cqrs-aop-module-x
|
|
1374
|
+
_cqrs_shipped; _cqs=$?
|
|
1375
|
+
if [ "$_cqs" = 2 ]; then
|
|
1376
|
+
say "cqrs-aop-module could not be checked against the copies Crewforth shipped (git or the shipped list is missing) — not vouched for; the next session will ask whether to trust it."
|
|
1377
|
+
elif [ "$_cqs" = 0 ]; then
|
|
1378
|
+
_tout="$(bash .claude/hooks/skill-trust.sh --trust-one skills/cqrs-aop-module 2>&1)"; _trc=$?
|
|
1379
|
+
if [ "$_trc" = 0 ]; then
|
|
1380
|
+
say "cqrs-aop-module is now a project skill (Crewforth no longer ships it); backend-expert applies it as your project's pattern."
|
|
1381
|
+
else
|
|
1382
|
+
say '⚠️ cqrs-aop-module could not be recorded as trusted (skill-trust.sh exit %s: %s) — the next session will ask about it' "$_trc" "${_tout:-no output}"
|
|
1383
|
+
fi
|
|
1384
|
+
else
|
|
1385
|
+
say "cqrs-aop-module is 2.x's .NET pattern, changed since Crewforth shipped it — not vouched for; the next session will ask whether to trust it."
|
|
1386
|
+
fi
|
|
1387
|
+
fi
|
|
1388
|
+
|
|
1222
1389
|
# stack= is always generic since 3.0; the key is kept for older updaters. Rewritten WITHOUT the pre-2.0
|
|
1223
1390
|
# 'profile=' key: dropping it is what retires the migration notice, so a second refresh stays quiet — and
|
|
1224
1391
|
# writing generic over a former 'dotnet' retires the 3.0 notice the same way.
|
|
@@ -1297,7 +1464,7 @@ if [ -f CLAUDE.md ]; then
|
|
|
1297
1464
|
_mt 'the inline block is lines 1-%s; your project section starts at line %s' "$((BND-1))" "$BND"
|
|
1298
1465
|
printf ' %s%s%s\n' "$D" "$_M" "$R"
|
|
1299
1466
|
if ask_yes ' Replace that inline block with the single @import line? (a backup is written; this branch is reviewable)'; then
|
|
1300
|
-
BK=".claude/CLAUDE.md.pre-kit-$TS"
|
|
1467
|
+
BK=".claude/CLAUDE.md.pre-kit-${TS:-$(date +%Y%m%d-%H%M%S)}" # TS is set only when a handover branch was opened
|
|
1301
1468
|
cp CLAUDE.md "$BK"
|
|
1302
1469
|
{ printf '<!-- Crewforth discipline · on conflict the project rules BELOW win -->\n%s\n\n' "$IMPORT_LINE"
|
|
1303
1470
|
tail -n +"$BND" CLAUDE.md; } > CLAUDE.md.kit-tmp && mv CLAUDE.md.kit-tmp CLAUDE.md
|
|
@@ -1582,7 +1749,7 @@ cat > docs/HANDOVER.md <<HAND
|
|
|
1582
1749
|
| # | Decision | Value |
|
|
1583
1750
|
|---|---|---|
|
|
1584
1751
|
| 1 | Role clash | $D1 |
|
|
1585
|
-
| 2 | Precedence | $D2 (axis-by-axis) |
|
|
1752
|
+
| 2 | Precedence | $D2 (axis-by-axis; DISCIPLINE.md §4 tighten-only, except the §4.1 trace allowlist of decision 3) |
|
|
1586
1753
|
| 3 | Trace gate | $D3 |
|
|
1587
1754
|
| 4 | Share/hide | $D4 |
|
|
1588
1755
|
| 5 | Git hook | $D5 |
|
|
@@ -1623,7 +1790,7 @@ Goal: don't break the project, don't lose decisions already made, and don't leav
|
|
|
1623
1790
|
## Decision
|
|
1624
1791
|
- Crewforth's agents were installed under the crew- namespace; the project's agents are preserved side by side, untouched.
|
|
1625
1792
|
- Crewforth's discipline is active via .claude/DISCIPLINE.md + @import; the project CLAUDE.md is untouched.
|
|
1626
|
-
- On rule conflicts the PROJECT wins (axis-by-axis).
|
|
1793
|
+
- On rule conflicts the PROJECT wins (axis-by-axis); in DISCIPLINE.md §4 it can only tighten, never loosen — the one exception is the §4.1 trace allowlist chosen at adoption (decision 3).
|
|
1627
1794
|
- Git gates: $HOOKDESC.
|
|
1628
1795
|
- Every change is on a reviewable git branch; rollback = git.
|
|
1629
1796
|
|
|
@@ -1713,7 +1880,7 @@ subm 'see it: open the Source Control / Changes panel (every added + changed f
|
|
|
1713
1880
|
if bash "$SRC/eval/preflight.sh" --has node 2>/dev/null; then
|
|
1714
1881
|
subm 'panel: %s opens it from this project (or: %s)' /crew-studio 'node .claude/studio/server/index.js --open'
|
|
1715
1882
|
else
|
|
1716
|
-
subm 'panel: needs Node 18+, absent here — Crewforth can fetch one: %s' 'bash .claude/studio/ensure-node.sh --plan'
|
|
1883
|
+
subm 'panel: needs Node 18+, absent here — Crewforth can fetch one (Bash tool, not PowerShell): %s' 'bash .claude/studio/ensure-node.sh --plan'
|
|
1717
1884
|
_mt '(it asks first, verifies the checksum, and touches nothing outside %s)' '~/.claude/studio-runtime'; sub " $_M"
|
|
1718
1885
|
fi
|
|
1719
1886
|
sub "$ACCEPT_LINE"
|
package/kit/CLAUDE.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
The discipline, identical in every project. The installer writes it to `.claude/DISCIPLINE.md`; your `./CLAUDE.md`
|
|
4
4
|
imports it with one `@.claude/DISCIPLINE.md` line. **Crewforth-owned**: updates overwrite it, so put your own rules in
|
|
5
|
-
`./CLAUDE.md`, where they win on conflict.
|
|
5
|
+
`./CLAUDE.md`, where they win on conflict; in §4 they can only tighten, never loosen (§4.1 names the one exception).
|
|
6
6
|
|
|
7
7
|
## Four working principles
|
|
8
8
|
1. Think, then write. State assumptions explicitly; if unsure, **STOP and ask**.
|
|
@@ -13,6 +13,7 @@ imports it with one `@.claude/DISCIPLINE.md` line. **Crewforth-owned**: updates
|
|
|
13
13
|
**When two rules collide** (they do, and improvising an order is how the wrong one wins): §4 prohibitions and
|
|
14
14
|
safety first, then the user's explicit in-session instruction, then scope-as-asked, then quality, then speed. A
|
|
15
15
|
lower one never overrides a higher one. Say which you applied and why — a silent trade-off is an unreviewable one.
|
|
16
|
+
A project `CLAUDE.md` can tighten §4, never loosen it (§4.1 names the one exception). The § numbers in this file refer only to this file.
|
|
16
17
|
**A skill's output format is not on this ladder**: it shapes what you write, not whether the work continues.
|
|
17
18
|
"Final reply = the report" ends that skill's step, not the task.
|
|
18
19
|
|
|
@@ -118,7 +119,7 @@ Last line of a reply, after the next step: `🔋 Session: [low/medium/high fill]
|
|
|
118
119
|
|
|
119
120
|
**Never guess the fill.** You cannot run `/context`; the `UserPromptSubmit` hook injects the measured line
|
|
120
121
|
`🔋 Session %NN.N → level` every turn (`input + cache_read + cache_creation` = the `/context` figure). Use it. Exact
|
|
121
|
-
reading: `bash .claude/hooks/context-usage.sh --verbose
|
|
122
|
+
reading: `bash .claude/hooks/context-usage.sh --verbose` (Bash tool, not PowerShell). Never invent a number. **No line → run that command
|
|
122
123
|
once; if it also fails, say so ONCE and drop the 🔋 line for the rest of the session.** Repeating "could not
|
|
123
124
|
measure" every turn is noise that reads as a fault.
|
|
124
125
|
|
|
@@ -203,7 +204,7 @@ Runtime: <e.g. Node 22 · Go 1.23 · Python 3.12 · .NET 10 · Java 21>
|
|
|
203
204
|
Web framework: <e.g. Fastify · chi · FastAPI · ASP.NET Core · Spring Boot>
|
|
204
205
|
Database: <e.g. PostgreSQL 17 (+ Redis cache) · SQLite · MongoDB> · Migrations: <tool>
|
|
205
206
|
Architecture pattern: <layered · clean/hexagonal · vertical slice · CQRS>
|
|
206
|
-
Client: <e.g. web
|
|
207
|
+
Client: <e.g. web · native iOS/Android · Flutter · React Native · .NET MAUI · KMP · desktop — the user chooses; empty = ask, recommend none>
|
|
207
208
|
|
|
208
209
|
## Project skills
|
|
209
210
|
Domain-specific "how"s live under `.claude/skills/` (e.g. payment-contract, notification-rules).
|
|
@@ -225,4 +226,4 @@ read this section and follow it verbatim; with nothing declared they fall back t
|
|
|
225
226
|
Behavior · four principles · DoD · Prohibitions (§4) · session management · sources live in
|
|
226
227
|
`.claude/DISCIPLINE.md`, pulled in by the `@.claude/DISCIPLINE.md` line at the top of this file. That file is
|
|
227
228
|
Crewforth-owned: an update overwrites it, so put **nothing** of your own there — this file is where your rules go, and
|
|
228
|
-
on conflict the rules here win. No dependency on Home (`~/.claude`) — everything stays inside the repo (handover §3).
|
|
229
|
+
on conflict the rules here win; in DISCIPLINE.md §4 they can only tighten, never loosen (its §4.1 names the one exception). No dependency on Home (`~/.claude`) — everything stays inside the repo (handover §3).
|
package/kit/README.md
CHANGED
|
@@ -37,7 +37,7 @@ summarizes what lives under `.claude/` and how it works.
|
|
|
37
37
|
- **settings.json** — permissions and the hook chain (PreToolUse · UserPromptSubmit · Stop · SessionStart).
|
|
38
38
|
- **`DISCIPLINE.md`** — behavior, four principles, workflow, definition of done, token discipline, and prohibitions.
|
|
39
39
|
Crewforth-owned: an update **overwrites** it, so keep nothing of your own here. Your `./CLAUDE.md` pulls it in with a
|
|
40
|
-
single `@.claude/DISCIPLINE.md` line and holds your project rules, which win on conflict.
|
|
40
|
+
single `@.claude/DISCIPLINE.md` line and holds your project rules, which win on conflict; in DISCIPLINE.md §4 they can only tighten, never loosen (its §4.1 names the one exception).
|
|
41
41
|
- **`kit.conf`** — which installer ran and at what version. `stack=` is always `generic` since 3.0 (kept for older
|
|
42
42
|
updaters); the project's stack itself lives in the `## Stack` section of `./CLAUDE.md`.
|
|
43
43
|
- **`kit-manifest.txt`** — the component names Crewforth ships, one per line. It is what separates Crewforth-owned from
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
name: crew-frontend-expert
|
|
3
3
|
color: purple
|
|
4
4
|
description: |
|
|
5
|
-
Stack-agnostic frontend expert — web (
|
|
6
|
-
The "how" lives in the `frontend` skill (
|
|
5
|
+
Stack-agnostic frontend expert — web, mobile (native, Flutter, React Native, MAUI, KMP), desktop.
|
|
6
|
+
The "how" lives in the `frontend` skill (an RN/Expo project adds `frontend-rn-expo`). **Use proactively — owns everything the user sees or
|
|
7
7
|
interacts with:** screens, components, navigation, client state, i18n, accessibility, responsive, native
|
|
8
8
|
bridges, and all visual work — design systems, token/theme layers, typography, dark mode, "it doesn't look
|
|
9
9
|
premium". Any request about it is yours whatever its size, wording or language.
|
|
@@ -16,11 +16,11 @@ metadata:
|
|
|
16
16
|
# Frontend Expert (stack-agnostic)
|
|
17
17
|
|
|
18
18
|
<!-- routing-eval reads the next line; why it sits in the body: AGENT_TEMPLATE.md -->
|
|
19
|
-
Trigger phrases: "screen size", "component", "page", "navigation", "client routing", "react router", "UI polish", "responsive", "i18n interface", "state management", "visual design", "design system", "design token", "dark mode", "look premium"
|
|
19
|
+
Trigger phrases: "screen size", "component", "page", "navigation", "client routing", "react router", "UI polish", "responsive", "i18n interface", "state management", "visual design", "design system", "design token", "dark mode", "look premium", "flutter", "swiftui", "jetpack compose", "maui", "kotlin multiplatform", "mobile screen"
|
|
20
20
|
|
|
21
|
-
The role is general; the "how" varies per project.
|
|
22
|
-
(
|
|
23
|
-
|
|
21
|
+
The role is general; the "how" varies per project. Resolve the client stack first, in the order the `frontend`
|
|
22
|
+
skill gives (Client stack: the request → `## Stack` `Client:` → the repo's manifests → ask once and record it). No
|
|
23
|
+
stack is a default and none is suggested; then follow that project's conventions, not your own preferences.
|
|
24
24
|
|
|
25
25
|
## Expertise stance (senior product engineer)
|
|
26
26
|
- **Design states up front**: loading / empty / error / offline — not just "populated".
|
|
@@ -38,7 +38,7 @@ Appends a single line to the very END of the response:
|
|
|
38
38
|
|
|
39
39
|
**Actual fill is MEASURED, guessing is FORBIDDEN.** The `UserPromptSubmit` hook runs `context-usage.sh` each turn,
|
|
40
40
|
automatically injecting the real `🔋 Session: %.. (token) → level` line into the context — use that value. If you want an
|
|
41
|
-
exact/fresh reading, run it by hand: `bash .claude/hooks/context-usage.sh`
|
|
41
|
+
exact/fresh reading, run it by hand with the Bash tool, not PowerShell: `bash .claude/hooks/context-usage.sh`
|
|
42
42
|
(the `input + cache_read + cache_creation` of the last main-context turn in the transcript = the `/context` count).
|
|
43
43
|
If there's no injected line (hook off / transcript unreachable) **don't invent a %** — run `bash .claude/hooks/context-usage.sh --verbose` once; if that also fails, say so once, drop the 🔋 line for the rest of the session, and only report a topic change.
|
|
44
44
|
|