crewforth 0.0.1 → 3.0.0-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 +3784 -0
- package/LICENSE +21 -0
- package/README.md +37 -7
- package/VERSION +1 -0
- package/adopt.sh +1670 -0
- package/bin/cli.js +497 -0
- package/kit/AGENT_TEMPLATE.md +92 -0
- package/kit/CLAUDE.md +228 -0
- package/kit/README.md +107 -0
- package/kit/agents/crew-backend-expert.md +95 -0
- package/kit/agents/crew-commit-agent.md +96 -0
- package/kit/agents/crew-database-expert.md +84 -0
- package/kit/agents/crew-devops-expert.md +82 -0
- package/kit/agents/crew-frontend-expert.md +85 -0
- package/kit/agents/crew-performance-expert.md +78 -0
- package/kit/agents/crew-planner.md +60 -0
- package/kit/agents/crew-privacy-agent.md +71 -0
- package/kit/agents/crew-review-agent.md +95 -0
- package/kit/agents/crew-security-expert.md +73 -0
- package/kit/agents/crew-session-manager.md +77 -0
- package/kit/agents/crew-test-expert.md +64 -0
- package/kit/eval/doctor.sh +465 -0
- package/kit/eval/gate-report.sh +137 -0
- package/kit/eval/golden-holdout.txt +53 -0
- package/kit/eval/golden-routing.txt +199 -0
- package/kit/eval/lib/crew-env.sh +14 -0
- package/kit/eval/lib/settings-json.awk +154 -0
- package/kit/eval/lib/star.sh +70 -0
- package/kit/eval/parser-conformance.sh +612 -0
- package/kit/eval/preflight.sh +186 -0
- package/kit/eval/routing-eval.sh +209 -0
- package/kit/eval/scan-skill.sh +162 -0
- package/kit/eval/smoke-test.sh +6693 -0
- package/kit/eval/update-guard.sh +81 -0
- package/kit/eval/utilization.sh +130 -0
- package/kit/hooks/board-sync.sh +106 -0
- package/kit/hooks/board.sh +962 -0
- package/kit/hooks/commit-msg +49 -0
- package/kit/hooks/context-usage.sh +353 -0
- package/kit/hooks/floor-blocklist.txt +124 -0
- package/kit/hooks/guard-bash.sh +1431 -0
- package/kit/hooks/guard-commit-scan.sh +511 -0
- package/kit/hooks/guard-write.sh +513 -0
- package/kit/hooks/pre-commit +577 -0
- package/kit/hooks/route-hint.sh +185 -0
- package/kit/hooks/secret-blocklist.txt +82 -0
- package/kit/hooks/session-guard.sh +149 -0
- package/kit/hooks/session-rehydrate.sh +41 -0
- package/kit/hooks/session-stats.sh +252 -0
- package/kit/hooks/session-update-check.sh +236 -0
- package/kit/hooks/skill-trust.sh +150 -0
- package/kit/hooks/trace-blocklist.txt +53 -0
- package/kit/settings.json +132 -0
- package/kit/skills/a11y/SKILL.md +44 -0
- package/kit/skills/adr/SKILL.md +74 -0
- package/kit/skills/api-design/SKILL.md +50 -0
- package/kit/skills/automode-policy/SKILL.md +91 -0
- package/kit/skills/automode-policy/references/policy.json +13 -0
- package/kit/skills/automode-policy/scripts/apply.sh +84 -0
- package/kit/skills/automode-policy/scripts/check.sh +102 -0
- package/kit/skills/backend-architecture/SKILL.md +103 -0
- package/kit/skills/brainstorm/SKILL.md +47 -0
- package/kit/skills/ci-pipeline/SKILL.md +38 -0
- package/kit/skills/commit-message/SKILL.md +82 -0
- package/kit/skills/commit-message/references/examples.md +8 -0
- package/kit/skills/confidence-check/SKILL.md +86 -0
- package/kit/skills/crew-board/SKILL.md +55 -0
- package/kit/skills/crew-brainstorm/SKILL.md +19 -0
- package/kit/skills/crew-code-review/SKILL.md +151 -0
- package/kit/skills/crew-code-review/references/history-search.md +19 -0
- package/kit/skills/crew-code-review/references/panel-mode.md +32 -0
- package/kit/skills/crew-code-review/references/receiving.md +23 -0
- package/kit/skills/crew-doctor/SKILL.md +36 -0
- package/kit/skills/crew-gates/SKILL.md +25 -0
- package/kit/skills/crew-handoff/SKILL.md +14 -0
- package/kit/skills/crew-plan/SKILL.md +18 -0
- package/kit/skills/crew-review/SKILL.md +23 -0
- package/kit/skills/crew-ship/SKILL.md +19 -0
- package/kit/skills/crew-skill/SKILL.md +61 -0
- package/kit/skills/crew-studio/SKILL.md +129 -0
- package/kit/skills/crew-update/SKILL.md +43 -0
- package/kit/skills/db-migration/SKILL.md +120 -0
- package/kit/skills/db-migration/references/backup-restore.md +33 -0
- package/kit/skills/db-migration/references/raw-sql.md +30 -0
- package/kit/skills/db-migration/references/tool-matrix.md +35 -0
- package/kit/skills/dependency-audit/SKILL.md +61 -0
- package/kit/skills/dependency-upgrade/SKILL.md +82 -0
- package/kit/skills/deploy/SKILL.md +100 -0
- package/kit/skills/deploy/references/proxy-ssl.md +56 -0
- package/kit/skills/deploy/references/rollback.md +18 -0
- package/kit/skills/deploy/references/scripts.md +9 -0
- package/kit/skills/deploy/references/topology-a.md +128 -0
- package/kit/skills/deploy/references/topology-b.md +44 -0
- package/kit/skills/docs-writer/SKILL.md +53 -0
- package/kit/skills/docs-writer/references/coauthoring.md +34 -0
- package/kit/skills/eval-grader/SKILL.md +75 -0
- package/kit/skills/eval-grader/references/method.md +51 -0
- package/kit/skills/frontend/SKILL.md +56 -0
- package/kit/skills/frontend/references/verify-contract.md +41 -0
- package/kit/skills/frontend-design/SKILL.md +71 -0
- package/kit/skills/frontend-design/references/design-review.md +34 -0
- package/kit/skills/frontend-rn-expo/SKILL.md +25 -0
- package/kit/skills/handoff/SKILL.md +71 -0
- package/kit/skills/handoff/references/cold-reader.md +51 -0
- package/kit/skills/i18n-integrity/SKILL.md +31 -0
- package/kit/skills/incident-runbook/SKILL.md +40 -0
- package/kit/skills/incident-runbook/references/postmortem.md +16 -0
- package/kit/skills/iterate/SKILL.md +40 -0
- package/kit/skills/mcp-builder/SKILL.md +54 -0
- package/kit/skills/mcp-builder/references/building.md +71 -0
- package/kit/skills/observability/SKILL.md +50 -0
- package/kit/skills/performance/SKILL.md +38 -0
- package/kit/skills/performance/references/bottlenecks.md +10 -0
- package/kit/skills/privacy-compliance/SKILL.md +75 -0
- package/kit/skills/red-team/SKILL.md +48 -0
- package/kit/skills/reflect/SKILL.md +57 -0
- package/kit/skills/release/SKILL.md +44 -0
- package/kit/skills/security-scan/SKILL.md +99 -0
- package/kit/skills/security-scan/references/ai-agents.md +107 -0
- package/kit/skills/security-scan/references/fronts.md +99 -0
- package/kit/skills/security-scan/references/prompting.md +23 -0
- package/kit/skills/security-scan/references/reporting.md +75 -0
- package/kit/skills/security-scan/references/verify.md +131 -0
- package/kit/skills/sonarqube-check/SKILL.md +108 -0
- package/kit/skills/sonarqube-check/references/local-engines.md +32 -0
- package/kit/skills/sonarqube-check/references/stand-up-server.md +25 -0
- package/kit/skills/spec-planning/SKILL.md +92 -0
- package/kit/skills/systematic-debugging/SKILL.md +57 -0
- package/kit/skills/systematic-debugging/references/techniques.md +44 -0
- package/kit/skills/teamboard/SKILL.md +88 -0
- package/kit/skills/teamboard/references/setup.md +37 -0
- package/kit/skills/testing/SKILL.md +49 -0
- package/kit/skills/testing/references/flaky-triage.md +51 -0
- package/kit/skills/threat-model/SKILL.md +57 -0
- package/kit/skills/threat-model/references/interview.md +51 -0
- package/kit/skills/threat-model/references/schema.md +42 -0
- package/kit/skills/token-budget/SKILL.md +51 -0
- package/kit/skills/trace-scan/SKILL.md +43 -0
- package/kit/skills/worktree/SKILL.md +40 -0
- package/kit/studio/README.md +266 -0
- package/kit/studio/ensure-node.sh +471 -0
- package/kit/studio/package.json +14 -0
- package/kit/studio/server/hooks/studio-gate.sh +169 -0
- package/kit/studio/server/index.js +902 -0
- package/kit/studio/server/lib/crew-env.js +11 -0
- package/kit/studio/server/lib/fleet.js +146 -0
- package/kit/studio/server/lib/graph.js +533 -0
- package/kit/studio/server/lib/instance.js +135 -0
- package/kit/studio/server/lib/kit-telemetry.js +182 -0
- package/kit/studio/server/lib/kit.js +158 -0
- package/kit/studio/server/lib/palette.js +118 -0
- package/kit/studio/server/lib/peers.js +72 -0
- package/kit/studio/server/lib/permissions.js +150 -0
- package/kit/studio/server/lib/projects.js +294 -0
- package/kit/studio/server/lib/roster.js +191 -0
- package/kit/studio/server/lib/session.js +309 -0
- package/kit/studio/server/lib/terminal.js +91 -0
- package/kit/studio/server/lib/transcript.js +100 -0
- package/kit/studio/web/app.js +1098 -0
- package/kit/studio/web/canvas.js +1179 -0
- package/kit/studio/web/chat.js +543 -0
- package/kit/studio/web/index.html +86 -0
- package/kit/studio/web/md.js +110 -0
- package/kit/studio/web/storage-migrate.js +23 -0
- package/kit/studio/web/style.css +1453 -0
- package/package.json +46 -8
- package/start.sh +748 -0
- package/bin/crewforth.js +0 -10
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# What did the gates actually DO? — reads CREW_GATE_LOG and reports it against the rule inventory.
|
|
3
|
+
#
|
|
4
|
+
# Crewforth's claim is that rules are enforced at the tool level rather than remembered. That claim is only worth
|
|
5
|
+
# what its evidence is worth, and until now the evidence was a green test suite: proof the gates CAN fire, never
|
|
6
|
+
# a record that they DID. This reads the other half.
|
|
7
|
+
#
|
|
8
|
+
# Two failure modes it is built around, both learned here the hard way:
|
|
9
|
+
# 1. "0 firings" and "never measured" leave identical artifacts. So a rule is reported as `not observed`, never
|
|
10
|
+
# as dead, and a missing log is reported as NOT MEASURED — never as zero.
|
|
11
|
+
# 2. A hand-maintained rule list drifts from the rules. So the inventory is DERIVED from the installed hooks
|
|
12
|
+
# on every run: a rule added to guard-bash.sh shows up here without anyone remembering to add it.
|
|
13
|
+
#
|
|
14
|
+
# Usage: gate-report.sh [--log <path>] [--json]
|
|
15
|
+
# Exit: 0 report produced · 3 no log to read (not an error: logging is opt-in) · 4 hooks not found
|
|
16
|
+
set -u
|
|
17
|
+
# The 2.x names of the variables a user can set still work (one helper: eval/lib/crew-env.sh).
|
|
18
|
+
_crew_d="${BASH_SOURCE%/*}"; [ "$_crew_d" = "${BASH_SOURCE}" ] && _crew_d=.
|
|
19
|
+
[ -f "$_crew_d/lib/crew-env.sh" ] && . "$_crew_d/lib/crew-env.sh"; unset _crew_d
|
|
20
|
+
|
|
21
|
+
LOG="${CREW_GATE_LOG:-}"; JSON=0
|
|
22
|
+
while [ $# -gt 0 ]; do case "$1" in
|
|
23
|
+
--log) LOG="${2:-}"; shift 2 ;;
|
|
24
|
+
--json) JSON=1; shift ;;
|
|
25
|
+
*) echo "gate-report.sh: unknown argument '$1'" >&2; exit 64 ;;
|
|
26
|
+
esac; done
|
|
27
|
+
|
|
28
|
+
HOOKS="./.claude/hooks"; [ -d "$HOOKS" ] || HOOKS="./hooks"
|
|
29
|
+
[ -f "$HOOKS/guard-bash.sh" ] || { echo "gate-report: guard-bash.sh not found (run from the project root)" >&2; exit 4; }
|
|
30
|
+
|
|
31
|
+
# Default log location, so `--log` is not required once logging is on.
|
|
32
|
+
[ -z "$LOG" ] && [ -f "./.claude/gate-log.tsv" ] && LOG="./.claude/gate-log.tsv"
|
|
33
|
+
|
|
34
|
+
TMP="${TMPDIR:-/tmp}/crew-gate-$$"; mkdir -p "$TMP"; trap 'rm -rf "$TMP"' EXIT
|
|
35
|
+
|
|
36
|
+
# Match on a KEY, display the full label. A rule label can carry a trailing parenthetical, and one of them
|
|
37
|
+
# interpolates a shell variable ("… that cannot prompt (${PERM_MODE:-unknown})"): the inventory reads the
|
|
38
|
+
# source, the log holds the expanded value, so the two never compare equal and the rule is reported as never
|
|
39
|
+
# observed forever — a false negative in the one direction that matters. Both sides drop the trailing (...).
|
|
40
|
+
key(){ printf '%s' "$1" | sed 's/[[:space:]]*([^()]*)[[:space:]]*$//'; }
|
|
41
|
+
|
|
42
|
+
# ---- inventory: derived from the hooks, one awk pass per file (no per-rule subprocess: Windows forks cost 62-135 ms)
|
|
43
|
+
awk '
|
|
44
|
+
# 23 rules of the form: … && block "RULE" "SECTION"
|
|
45
|
+
match($0, /&& *block +"[^"]*" +"[^"]*"/) {
|
|
46
|
+
s=substr($0,RSTART,RLENGTH); n=split(s,q,"\""); print q[4] "\t" q[2]; next
|
|
47
|
+
}
|
|
48
|
+
# direct calls: gatelog VERDICT SECTION "RULE"
|
|
49
|
+
/^[[:space:]]*gatelog +[A-Z]+ +[0-9.]+ +"/ {
|
|
50
|
+
n=split($0,q,"\""); split($0,f," "); print f[3] "\t" q[2]; next
|
|
51
|
+
}
|
|
52
|
+
' "$HOOKS/guard-bash.sh" > "$TMP/inv"
|
|
53
|
+
# guard-write.sh emits its one line inline rather than through a helper, so read the label out of the printf
|
|
54
|
+
# arguments the same way — the earlier version skipped the very line it needed (it matched CREW_GATE_LOG first)
|
|
55
|
+
# and the rule silently never appeared in the inventory, which reads exactly like a rule that never fires.
|
|
56
|
+
# guard-write.sh emits its one line inline rather than through a helper, so read the label out of the printf
|
|
57
|
+
# arguments. Two traps here, both hit on the way: the line also mentions CREW_GATE_LOG (skipping on that name
|
|
58
|
+
# skipped the rule itself), and the FIRST quoted field on the line is `${CREW_GATE_LOG:-}`, not the label — so
|
|
59
|
+
# take the last quoted field that is not a variable reference.
|
|
60
|
+
[ -f "$HOOKS/guard-write.sh" ] && awk '
|
|
61
|
+
/printf .BLOCK\\t/ {
|
|
62
|
+
n=split($0,q,"\""); lab=""
|
|
63
|
+
for (i=2; i<=n; i+=2) if (q[i] !~ /^\$/ && q[i] != "") lab=q[i]
|
|
64
|
+
if (lab != "") print "4.5\t" lab
|
|
65
|
+
}' "$HOOKS/guard-write.sh" >> "$TMP/inv"
|
|
66
|
+
|
|
67
|
+
sort -u "$TMP/inv" -o "$TMP/inv"
|
|
68
|
+
NRULES=$(grep -c . "$TMP/inv" 2>/dev/null); NRULES=$(( ${NRULES:-0} + 0 ))
|
|
69
|
+
|
|
70
|
+
# ---- no log. Since 2.5.0 recording is ON by default, so the absence of a file means one of two DIFFERENT
|
|
71
|
+
# things and the report must not blur them:
|
|
72
|
+
# .claude/ exists -> recording had somewhere to go and nothing has tripped a gate yet. That is a real
|
|
73
|
+
# measurement of zero, and it is the healthy reading.
|
|
74
|
+
# .claude/ missing -> there was nowhere to write. Nothing was measured; zero would be a lie.
|
|
75
|
+
if [ -z "$LOG" ] || [ ! -f "$LOG" ]; then
|
|
76
|
+
if [ -d "./.claude" ]; then
|
|
77
|
+
if [ "$JSON" = 1 ]; then printf '{"measured":true,"rules":%s,"decisions":0}\n' "$NRULES"; else
|
|
78
|
+
echo "== gate report =="
|
|
79
|
+
echo " ✅ no gate has fired in this project yet — $NRULES rules wired, recording on,"
|
|
80
|
+
echo " nothing has tripped one. (Recording writes .claude/gate-log.tsv, which is gitignored;"
|
|
81
|
+
echo " rule names and verdicts only, never the command — CREW_GATE_LOG_CMD=1 adds it for debugging.)"
|
|
82
|
+
fi
|
|
83
|
+
exit 0
|
|
84
|
+
fi
|
|
85
|
+
if [ "$JSON" = 1 ]; then printf '{"measured":false,"rules":%s,"reason":"nowhere to record"}\n' "$NRULES"; else
|
|
86
|
+
echo "== gate report =="
|
|
87
|
+
echo " · NOT MEASURED — no .claude/ to record into. $NRULES rules are wired; how often they fire is unknown."
|
|
88
|
+
echo " ↳ point it somewhere: export CREW_GATE_LOG=\"\$PWD/gate-log.tsv\""
|
|
89
|
+
fi
|
|
90
|
+
exit 3
|
|
91
|
+
fi
|
|
92
|
+
|
|
93
|
+
# ---- read the log: VERDICT \t §SECTION \t RULE \t COMMAND
|
|
94
|
+
awk -F'\t' 'NF>=3 { k=$3; sub(/[[:space:]]*\([^()]*\)[[:space:]]*$/,"",k); c[k]++; lab[k]=$3; v[$1]++; tot++ } END {
|
|
95
|
+
print "TOTAL\t" tot+0
|
|
96
|
+
for (k in v) print "VERDICT\t" k "\t" v[k]
|
|
97
|
+
for (k in c) print "RULE\t" k "\t" c[k] "\t" lab[k]
|
|
98
|
+
}' "$LOG" > "$TMP/agg"
|
|
99
|
+
|
|
100
|
+
TOT=$(awk -F'\t' '$1=="TOTAL"{print $2}' "$TMP/agg"); TOT="${TOT:-0}"
|
|
101
|
+
FIRST=$(head -1 "$LOG" 2>/dev/null | cut -f3); LINES=$(grep -c . "$LOG" 2>/dev/null); LINES=$(( ${LINES:-0} + 0 ))
|
|
102
|
+
|
|
103
|
+
if [ "$JSON" = 1 ]; then
|
|
104
|
+
awk -F'\t' -v n="$NRULES" -v t="$TOT" '
|
|
105
|
+
$1=="RULE" { r=r sep "{\"rule\":\"" $4 "\",\"count\":" $3 "}"; sep="," }
|
|
106
|
+
END { printf "{\"measured\":true,\"rules\":%s,\"decisions\":%s,\"fired\":[%s]}\n", n, t, r }' "$TMP/agg"
|
|
107
|
+
exit 0
|
|
108
|
+
fi
|
|
109
|
+
|
|
110
|
+
echo "== gate report =="
|
|
111
|
+
echo " log: $LOG ($LINES line(s), $TOT decision(s))"
|
|
112
|
+
echo
|
|
113
|
+
echo " verdicts:"
|
|
114
|
+
awk -F'\t' '$1=="VERDICT"{printf " %-6s %s\n",$2,$3}' "$TMP/agg" | sort
|
|
115
|
+
echo
|
|
116
|
+
echo " rules that fired:"
|
|
117
|
+
awk -F'\t' '$1=="RULE"{printf " %5s %s\n",$3,$4}' "$TMP/agg" | sort -rn || true
|
|
118
|
+
[ "$TOT" = 0 ] && echo " (none)"
|
|
119
|
+
echo
|
|
120
|
+
# One awk over both files instead of sort|comm|join: that chain compared a two-field inventory line against a
|
|
121
|
+
# one-field fired list, so a rule could land in BOTH lists at once (it did — `chmod world-writable` showed as
|
|
122
|
+
# fired and as unseen in the same report). It also spawned four processes per run, which is real cost on
|
|
123
|
+
# Windows. Here the fired set and the inventory meet in one pass, keyed the same way.
|
|
124
|
+
UNSEEN="$(awk -F'\t' '
|
|
125
|
+
FILENAME==f1 && $1=="RULE" { fired[$2]=1; next }
|
|
126
|
+
FILENAME==f2 {
|
|
127
|
+
lbl=$2; k=lbl; sub(/[[:space:]]*\([^()]*\)[[:space:]]*$/,"",k)
|
|
128
|
+
if (!(k in fired) && !(k in seen)) { seen[k]=1; printf " \302\267 %s\n", lbl }
|
|
129
|
+
}' f1="$TMP/agg" f2="$TMP/inv" "$TMP/agg" "$TMP/inv")"
|
|
130
|
+
NUNSEEN=$(printf '%s' "$UNSEEN" | grep -c . 2>/dev/null || true); NUNSEEN="${NUNSEEN:-0}"
|
|
131
|
+
echo " wired but not observed in this log ($NUNSEEN of $NRULES):"
|
|
132
|
+
[ -n "$UNSEEN" ] && printf '%s\n' "$UNSEEN"
|
|
133
|
+
|
|
134
|
+
echo
|
|
135
|
+
echo " Not observed is NOT dead. It means one of: nobody attempted it, the model never reached for that"
|
|
136
|
+
echo " command, or the rule cannot match. Only the first two are healthy, and this report cannot tell them"
|
|
137
|
+
echo " apart — smoke-test is what proves a rule CAN fire; this shows whether anything tripped it."
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# HELD-OUT routing set: <example user prompt>|<expected target>
|
|
2
|
+
#
|
|
3
|
+
# Same format as golden-routing.txt and read by the same matcher. The difference is the method, and it is the
|
|
4
|
+
# whole point: these prompts were written from the TASK a user would describe, in the words a user would use,
|
|
5
|
+
# WITHOUT reading the target's trigger phrases first. In the working set the prompt is where the trigger came
|
|
6
|
+
# from, so a passing row proves only that the author copied one into the other. Here the two were produced
|
|
7
|
+
# independently, so a row can fail — and a failure means the trigger is too narrow to reach a request nobody
|
|
8
|
+
# tuned it for, which is the only thing this eval was ever supposed to measure.
|
|
9
|
+
#
|
|
10
|
+
# WHEN A ROW FAILS, the fix is a decision, not a reflex:
|
|
11
|
+
# · widen the trigger — and re-run, because the negative rows in golden-routing.txt exist to catch a widening
|
|
12
|
+
# that starts stealing a neighbour's work;
|
|
13
|
+
# · or judge the prompt unreasonable for that target and say so here, in a comment, rather than deleting it
|
|
14
|
+
# quietly. A row removed without a reason is how a held-out set decays back into a working set.
|
|
15
|
+
# Never "fix" a failure by editing the prompt toward the trigger. That is the thing being measured.
|
|
16
|
+
|
|
17
|
+
my checkout is failing on the pipeline and I cannot tell why|ci-pipeline
|
|
18
|
+
the page is unusable with a keyboard, tab order jumps everywhere|a11y
|
|
19
|
+
we are adding a column to the users table next week|db-migration
|
|
20
|
+
customers in Germany are asking what we store about them|privacy-compliance
|
|
21
|
+
someone pasted an API key into a config file, what now|security-scan
|
|
22
|
+
the app takes eight seconds to open the dashboard|performance
|
|
23
|
+
I need to cut a new version and tell people what changed|release
|
|
24
|
+
this endpoint returns different shapes depending on the caller|api-design
|
|
25
|
+
we keep discovering the same outage twice a month|incident-runbook
|
|
26
|
+
I want to know if this library is still maintained|dependency-audit
|
|
27
|
+
three of us are working on the same repo and duplicating work|teamboard
|
|
28
|
+
I am about to run out of room in this conversation|handoff
|
|
29
|
+
nobody knows why we chose this queue over the other one|adr
|
|
30
|
+
the tests pass locally and fail in the pipeline every third run|testing
|
|
31
|
+
I want to be sure this change did not make anything slower|performance
|
|
32
|
+
we need to put this build on the server tonight|deploy
|
|
33
|
+
|
|
34
|
+
# --- REJECTED, with the reason. Not deleted: a row removed without a reason is how a held-out set decays -----
|
|
35
|
+
#
|
|
36
|
+
# "the German strings are missing on the checkout page" -> i18n-integrity
|
|
37
|
+
# The only phrase that could carry it is "strings are missing", which is a verbatim slice of this sentence —
|
|
38
|
+
# memorising the test, not widening the trigger — AND it steals: "the connection strings are missing from
|
|
39
|
+
# appsettings" then routes to the translation skill. Matching is contiguous, so "missing string" never
|
|
40
|
+
# reaches it either. The class is real; no phrase separates it from a config problem. Reported, not forced.
|
|
41
|
+
#
|
|
42
|
+
# "the model keeps ignoring my instructions when it reads a file" -> red-team
|
|
43
|
+
# The intent (indirect prompt injection) does belong here, but the only substrings that could carry it —
|
|
44
|
+
# "my instructions", "ignoring my instructions", "reads a file" — are indistinguishable from two far more
|
|
45
|
+
# common requests: a user saying the assistant is not following instructions, and a parser bug on file read.
|
|
46
|
+
# Any phrase that catches this prompt takes both of those with it.
|
|
47
|
+
#
|
|
48
|
+
# Both stay listed so the next person sees the class was considered and why the trigger cannot express it. If
|
|
49
|
+
# route-hint ever scores on more than contiguous substrings, reopen them.
|
|
50
|
+
# 3.0 rows for backend-architecture. Honesty note: written by the same person who wrote that skill's triggers,
|
|
51
|
+
# in the same change — so these are weaker held-out evidence than the rows above.
|
|
52
|
+
empty repo, nobody has picked a language or framework for the api yet|backend-architecture
|
|
53
|
+
we cannot agree whether this service should be layered or split per feature|backend-architecture
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
# Golden routing: <example user prompt>|<expected target agent/skill>
|
|
2
|
+
#
|
|
3
|
+
# THIS FILE IS THE WORKING SET, and what it can and cannot prove is worth stating. AGENT_TEMPLATE.md tells the
|
|
4
|
+
# author to add the line FIRST and then choose trigger phrases against it — "you're designing the description
|
|
5
|
+
# against a concrete prompt" — and the matcher below is a substring test. So a row here proves the trigger was
|
|
6
|
+
# chosen for this prompt; it cannot prove the trigger generalises, because the prompt is where the trigger came
|
|
7
|
+
# from. `golden-holdout.txt` is the other half: prompts written for the same target WITHOUT looking at its
|
|
8
|
+
# triggers. Only a holdout row can fail for the reason this eval exists.
|
|
9
|
+
# routing-eval.sh reads this: the expected target's trigger phrase must appear in the prompt (substring).
|
|
10
|
+
# If the target isn't installed in this profile, the line is SKIPPED (won't fail a pruned install).
|
|
11
|
+
# NEGATIVE line: <prompt>|!<target> asserts the prompt must NOT route to <target> (none of its triggers match) —
|
|
12
|
+
# a guard against over-broad triggers. See the "must NOT route" block at the end of this file.
|
|
13
|
+
add a new handler for the order endpoint|crew-backend-expert
|
|
14
|
+
write a migration for this table|crew-database-expert
|
|
15
|
+
build a responsive component for the login screen|crew-frontend-expert
|
|
16
|
+
# Positive twins of the seven over-broad-word rows below: the replacement phrase must still route.
|
|
17
|
+
cut a release and tag it|release
|
|
18
|
+
review the code in this PR|crew-code-review
|
|
19
|
+
the page layout is broken on mobile|frontend-design
|
|
20
|
+
requests are timing out under load|crew-performance-expert
|
|
21
|
+
add client routing to the app|crew-frontend-expert
|
|
22
|
+
add a new rn screen with expo router|frontend-rn-expo
|
|
23
|
+
run an OWASP check and a full security scan|crew-security-expert
|
|
24
|
+
we need a kvkk privacy audit|crew-privacy-agent
|
|
25
|
+
commit the changes|crew-commit-agent
|
|
26
|
+
run tests and give me a coverage report|crew-test-expert
|
|
27
|
+
do a PR review and look at the diff|crew-review-agent
|
|
28
|
+
plan first and produce a task breakdown|crew-planner
|
|
29
|
+
deploy this and go to production on the VPS|deploy
|
|
30
|
+
prepare the changelog and cut a tag|release
|
|
31
|
+
set up a ci pipeline with github actions|ci-pipeline
|
|
32
|
+
write an adr for this architecture decision|adr
|
|
33
|
+
run npm audit for a dependency audit|dependency-audit
|
|
34
|
+
check i18n for any missing translation|i18n-integrity
|
|
35
|
+
run the sonarqube quality gate|sonarqube-check
|
|
36
|
+
prepare a handoff with the current session state|handoff
|
|
37
|
+
add a structured log with a correlation id|observability
|
|
38
|
+
update the README and write the API docs|docs-writer
|
|
39
|
+
run an accessibility audit and check WCAG|a11y
|
|
40
|
+
handle api versioning with an openapi contract|api-design
|
|
41
|
+
the page is slow, do a performance optimization|performance
|
|
42
|
+
write an incident runbook and a postmortem|incident-runbook
|
|
43
|
+
red team the prompt injection defenses|red-team
|
|
44
|
+
ship to prod and be ready to rollback|crew-devops-expert
|
|
45
|
+
set up a reverse proxy and ssl|crew-devops-expert
|
|
46
|
+
keep going until the tests pass and the review is clean|iterate
|
|
47
|
+
debug this and find the root cause of the intermittent bug|systematic-debugging
|
|
48
|
+
improve the visual design — the spacing and typography look off|frontend-design
|
|
49
|
+
build an MCP server to expose these tools to Claude|mcp-builder
|
|
50
|
+
build a threat model and map the attack surface before we scan|threat-model
|
|
51
|
+
set up an eval to measure output quality against a baseline|eval-grader
|
|
52
|
+
|
|
53
|
+
# --- How a user actually phrases it -> the AGENT that must own the work ---
|
|
54
|
+
# Added after a real install where a design request produced good analysis and no delegation. Every case here
|
|
55
|
+
# asserts an AGENT, deliberately: the skills already carried this vocabulary ("visual design", "typography",
|
|
56
|
+
# "memory leak") so the skill fired and the gate stayed green, while the agent that OWNS the work was
|
|
57
|
+
# unreachable by the words anyone actually types. Testing the skill proved the mapping existed; it never proved
|
|
58
|
+
# a real sentence reached the delegation layer.
|
|
59
|
+
the app does not look premium, improve the visual design|crew-frontend-expert
|
|
60
|
+
add a dark mode and a design token layer|crew-frontend-expert
|
|
61
|
+
the icons are inconsistent, we need a design system|crew-frontend-expert
|
|
62
|
+
we have a memory leak in production|crew-performance-expert
|
|
63
|
+
the app feels laggy after the last release|crew-performance-expert
|
|
64
|
+
users report the app crashes on startup|systematic-debugging
|
|
65
|
+
the login is broken and I do not know why|systematic-debugging
|
|
66
|
+
# Narrowing a trigger has to be paid for with a positive case, or the fix for a false route quietly creates an
|
|
67
|
+
# unreachable component. token-budget had NO positive case at all before this line.
|
|
68
|
+
the context is full, check the token budget before we continue|token-budget
|
|
69
|
+
|
|
70
|
+
# --- Core components that had NO positive case until 1.11.0 ---
|
|
71
|
+
# An audit found 12 skills and one agent that the routing gate never checked — including security-scan,
|
|
72
|
+
# code-review, testing and spec-planning, i.e. most of what Crewforth advertises. A component with no case is a
|
|
73
|
+
# component whose reachability is assumed, which is exactly how crew-frontend-expert went unreachable for a whole
|
|
74
|
+
# class of request while every gate stayed green.
|
|
75
|
+
i am not sure what we want here yet, help me scope it|brainstorm
|
|
76
|
+
review the code I just wrote before I push|crew-code-review
|
|
77
|
+
write a commit message for the staged changes|commit-message
|
|
78
|
+
add column last_login to the users table|db-migration
|
|
79
|
+
add a query handler with a validator for this module|backend-architecture
|
|
80
|
+
the settings screen needs a state management fix|frontend
|
|
81
|
+
we need a consent flow before we store any of this|privacy-compliance
|
|
82
|
+
let us step back and see what we missed this week|reflect
|
|
83
|
+
scan for vulnerabilities in the auth flow|security-scan
|
|
84
|
+
how do we split this into tasks with acceptance criteria|spec-planning
|
|
85
|
+
are the tests green after that change|testing
|
|
86
|
+
check for AI trace before the commit|trace-scan
|
|
87
|
+
is a handover needed, the context status looks high|crew-session-manager
|
|
88
|
+
|
|
89
|
+
# --- 3.0: the stack is resolved per project by backend-architecture (it replaced cqrs-aop-module) ---
|
|
90
|
+
which stack should we use for this backend|backend-architecture
|
|
91
|
+
set up the project structure for a new API|backend-architecture
|
|
92
|
+
choose a database for this service|backend-architecture
|
|
93
|
+
|
|
94
|
+
# --- Negative: prompt must NOT route to target (over-broad-trigger guard) ---
|
|
95
|
+
#
|
|
96
|
+
# The seven rows below are the ones that were REPORTED FROM LIVE USE, not invented. Each generic English word
|
|
97
|
+
# was a standalone trigger, and a single trigger of six characters or more clears the score floor on its own —
|
|
98
|
+
# so any sentence containing the word routed somewhere unrelated. Measured before the fix: all seven misrouted.
|
|
99
|
+
# "context" -> token-budget "version" -> release "review" -> crew-review-agent
|
|
100
|
+
# "routing" -> crew-frontend-expert "timeout" -> performance-expert "screen" -> crew-frontend-expert
|
|
101
|
+
# "layout" -> frontend-design
|
|
102
|
+
# None of them came from this release's widening: all seven were triggers in 2.6.0 and earlier, which the diff
|
|
103
|
+
# against v2.6.0 confirms. Each was replaced by the phrase that carries the intent rather than deleted, and the
|
|
104
|
+
# POSITIVE twin of every row sits in the block above — a guard that only proves silence would be satisfied by
|
|
105
|
+
# a component with no triggers at all.
|
|
106
|
+
give me more context on this bug|!token-budget
|
|
107
|
+
review the jira ticket before I send it|!crew-review-agent
|
|
108
|
+
the image version is 3.2, rebuild it|!release
|
|
109
|
+
the routing table is misconfigured|!crew-frontend-expert
|
|
110
|
+
the request timeout is 30 seconds|!crew-performance-expert
|
|
111
|
+
the screen froze during the demo|!crew-frontend-expert
|
|
112
|
+
fix the layout of this json file|!frontend-design
|
|
113
|
+
design the database schema|!frontend-design
|
|
114
|
+
read the model context and summarize it|!mcp-builder
|
|
115
|
+
optimize the slow sql query performance|!systematic-debugging
|
|
116
|
+
# `build` contains `ui`, and `UI` used to be a trigger, so a CI failure routed to the frontend expert. A wrong
|
|
117
|
+
# route is worse than none: it looks like Crewforth worked. Fixed by word-bounded matching AND by retiring the
|
|
118
|
+
# bare `UI` trigger for `UI polish`; this line fails if either regresses.
|
|
119
|
+
the build fails on CI|!crew-frontend-expert
|
|
120
|
+
the docker build is broken in the pipeline|!crew-frontend-expert
|
|
121
|
+
# Same class, found while verifying the fix above: a bare `token` trigger sent "design token layer" to the
|
|
122
|
+
# session-context skill. A one-word trigger for a word that lives in two vocabularies is not a trigger.
|
|
123
|
+
add a dark mode and a design token layer|!token-budget
|
|
124
|
+
isolate this work in a git worktree before the parallel agents run|worktree
|
|
125
|
+
before I start, am I sure enough to implement this|confidence-check
|
|
126
|
+
upgrade dependencies to current versions|dependency-upgrade
|
|
127
|
+
is this endpoint fast enough on the hot path|crew-performance-expert
|
|
128
|
+
who is working on which sprint item right now|teamboard
|
|
129
|
+
audit this against ccpa and lgpd for data protection regulation|crew-privacy-agent
|
|
130
|
+
is my auto mode classifier policy still intact|automode-policy
|
|
131
|
+
|
|
132
|
+
# --- negative guards added with the trigger widening -------------------------------------------------
|
|
133
|
+
# Each widening below opened a class of request; each row here is a request that belongs to a NEIGHBOUR
|
|
134
|
+
# and must NOT reach the widened component. A widening without its guard is a steal nobody would notice.
|
|
135
|
+
add keyboard shortcuts for power users on the dashboard|!a11y
|
|
136
|
+
pick a color palette for the dark mode|!a11y
|
|
137
|
+
the focus this sprint is the new checkout component|!a11y
|
|
138
|
+
add a mouse hover state to the card|!a11y
|
|
139
|
+
reorder the tab bar items on the profile screen|!a11y
|
|
140
|
+
bump the default text size in the design system|!a11y
|
|
141
|
+
add a motion blur transition between routes|!a11y
|
|
142
|
+
make the empty state look more premium on the profile page|!a11y
|
|
143
|
+
is the lodash version in our lockfile affected by a published CVE|!security-scan
|
|
144
|
+
payments are down in production, who owns the incident right now|!security-scan
|
|
145
|
+
we have a memory leak in the worker process after the release|!security-scan
|
|
146
|
+
map the trust boundaries and entry points before we start the audit|!security-scan
|
|
147
|
+
the goroutine leaked memory and the pod restarted|!security-scan
|
|
148
|
+
the connection pool leaked connections under load|!security-scan
|
|
149
|
+
trim what we store in the session cache so redis stops growing|!privacy-compliance
|
|
150
|
+
someone pushed a database password to the repo, rotate the credentials|!privacy-compliance
|
|
151
|
+
delete the old event rows from the analytics table to save disk|!privacy-compliance
|
|
152
|
+
fix the sql injection in the search filter before release|!red-team
|
|
153
|
+
you keep ignoring my instructions about the commit format|!red-team
|
|
154
|
+
the importer crashes when it reads a large file|!red-team
|
|
155
|
+
the ci build takes 20 minutes and blocks every pr|!performance
|
|
156
|
+
the webpack build takes ninety seconds to finish in the pipeline|!performance
|
|
157
|
+
add a duration metric and a correlation id to this handler|!performance
|
|
158
|
+
the github actions workflow needs a build and test stage before the pr gate|!testing
|
|
159
|
+
the docker build step fails in the pipeline but works on my machine|!testing
|
|
160
|
+
users hit an intermittent bug on checkout that I cannot reproduce|!testing
|
|
161
|
+
add a column chart to the analytics dashboard|!db-migration
|
|
162
|
+
the results grid needs an extra column on the settings screen|!db-migration
|
|
163
|
+
we are migrating the frontend build to vite next week|!db-migration
|
|
164
|
+
the login endpoint throws a 500 for half the requests|!api-design
|
|
165
|
+
add a new endpoint that returns the user profile|!api-design
|
|
166
|
+
upgrading the sdk to v4 is not backward compatible|!api-design
|
|
167
|
+
users get a different response from the cache than the database|!api-design
|
|
168
|
+
bump versions of the outdated packages and verify the build|!dependency-audit
|
|
169
|
+
wire up a new dependency injection container for the module|!dependency-audit
|
|
170
|
+
we send analytics events to a third party service|!dependency-audit
|
|
171
|
+
the npm ci step fails on the windows runner|!dependency-audit
|
|
172
|
+
the context is full, check the token budget before we continue|!handoff
|
|
173
|
+
is it time to clear, how healthy is this session|!handoff
|
|
174
|
+
the disk is about to run out of space on the server tonight|!handoff
|
|
175
|
+
resume the half-finished migration on the users table|!handoff
|
|
176
|
+
run two agents in parallel without them touching the same files|!teamboard
|
|
177
|
+
isolate this refactor in a sandbox worktree so the parallel edits do not collide|!teamboard
|
|
178
|
+
break the release work down into tasks with acceptance criteria|!teamboard
|
|
179
|
+
my two agents are working on the same files|!teamboard
|
|
180
|
+
nobody knows why the nightly job fails every other night|!adr
|
|
181
|
+
write the README section explaining how the retry queue works|!adr
|
|
182
|
+
how do we split this into tasks with acceptance criteria|!adr
|
|
183
|
+
explain the reasoning behind this stack trace|!adr
|
|
184
|
+
the unit tests are failing after my refactor|!ci-pipeline
|
|
185
|
+
cut the release, write the changelog and tag it|!ci-pipeline
|
|
186
|
+
the deploy to the server failed last night|!ci-pipeline
|
|
187
|
+
add a build step to the github actions workflow|!deploy
|
|
188
|
+
we have an outage in production, start the incident runbook|!deploy
|
|
189
|
+
bump the version and add the changelog entry|!deploy
|
|
190
|
+
tail the nginx logs on the server for me|!deploy
|
|
191
|
+
the cron job on the server never fires|!deploy
|
|
192
|
+
the tests fail on the server but pass in my ide|!deploy
|
|
193
|
+
we push to produce a release candidate every friday|!deploy
|
|
194
|
+
add a link to product detail from the search results|!deploy
|
|
195
|
+
build a responsive component for the login screen|!i18n-integrity
|
|
196
|
+
we picked the wrong language for this service, write an adr|!i18n-integrity
|
|
197
|
+
write the README in english for the public repo|!i18n-integrity
|
|
198
|
+
the connection strings are missing from appsettings|!i18n-integrity
|
|
199
|
+
add language support for kotlin to the parser|!i18n-integrity
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Sourced, not run. The ONE bash place that still reads the pre-3.0 CSK_* names.
|
|
2
|
+
#
|
|
3
|
+
# 3.0 renamed every CSK_* variable to CREW_*. The ones a user can have set — because a README, --help, a command
|
|
4
|
+
# file or a message names them — keep working for the whole 3.x line: when CREW_<X> is not set at all and CSK_<X>
|
|
5
|
+
# is, CREW_<X> takes its value. A CREW_<X> that is set, even to 0 or empty, wins. Internal and test variables were
|
|
6
|
+
# renamed with no fallback. The old names go in 4.0.
|
|
7
|
+
#
|
|
8
|
+
# Builtins only (a loop of `eval`s): sourcing this costs no process, which matters in hooks on Git Bash.
|
|
9
|
+
# Kept after this file runs: the updater and doctor read it to say which set 2.x names still work.
|
|
10
|
+
_crew_legacy=" LANG NO_STAR NO_UPDATE_CHECK NO_BOARD GATE_LOG GATE_LOG_CMD STUDIO_TOKEN STUDIO_PEERS STUDIO_RUNTIME MAX_FILE_BYTES ALLOW_SOURCE_INSTALL "
|
|
11
|
+
for _crew_v in $_crew_legacy; do
|
|
12
|
+
eval "if [ -z \"\${CREW_$_crew_v+x}\" ] && [ -n \"\${CSK_$_crew_v+x}\" ]; then CREW_$_crew_v=\"\$CSK_$_crew_v\"; export CREW_$_crew_v; fi"
|
|
13
|
+
done
|
|
14
|
+
unset _crew_v
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# settings-json.awk — Crewforth's one JSON reader for settings.json, POSIX awk only (no jq, no python3), so the
|
|
2
|
+
# same code runs on macOS BSD awk, Linux mawk/gawk and Windows Git-Bash gawk.
|
|
3
|
+
#
|
|
4
|
+
# awk -v op=validate -f settings-json.awk FILE rc 0 = FILE is a JSON object, 1 = it is not
|
|
5
|
+
# awk -v op=get -v path=hooks.Stop.0 -f settings-json.awk FILE the value there, as the merge would print it
|
|
6
|
+
# awk -v op=len -v path=hooks.Stop -f settings-json.awk FILE how many entries an array/object holds
|
|
7
|
+
# awk -v op=strings -v path=permissions.deny -f settings-json.awk FILE each string element of that array, one per
|
|
8
|
+
# line, quotes off, escapes as written (rc 1 = not an array)
|
|
9
|
+
# path = dot-separated keys, a number indexes an array (0-based), empty = the root. rc 1 = no such path
|
|
10
|
+
# (or, for len, a scalar there), 10 = FILE is not a JSON object.
|
|
11
|
+
# awk -v op=overlay -v key=K [-v setk=M -v setv=RAWJSON] -f settings-json.awk FILE1 FILE2
|
|
12
|
+
# FILE1 with FILE1.K shallow-merged from FILE2.K (FILE2 wins, FILE1's other members and order kept), then
|
|
13
|
+
# member M set to RAWJSON. rc 10 = FILE1 (or its K) is not an object, 11 = FILE2 has no object at K.
|
|
14
|
+
# awk -v op=merge -v retired='R1|R2' -f settings-json.awk KIT PROJECT > OUT
|
|
15
|
+
# rc 0 = merged JSON on stdout · 10 = PROJECT is not a JSON object · 11 = KIT is not · 12 = "hooks" has a
|
|
16
|
+
# shape the merge cannot place (not an object of arrays). Nothing is printed on a non-zero rc. The codes sit
|
|
17
|
+
# clear of awk's own 2 (bad program, unreadable file), so a caller never reads a missing file as bad JSON.
|
|
18
|
+
#
|
|
19
|
+
# Reader contract: a recursive-descent parse into nodes (T = o/a/s, N = child count, K = key, C = child, V = raw
|
|
20
|
+
# scalar text). Strings are kept as their raw token, escapes included and never decoded, so they are written back
|
|
21
|
+
# byte-for-byte; two spellings of one string (\u0041 vs A) therefore count as different. Object key order and
|
|
22
|
+
# array order are preserved. Numbers are checked against the JSON grammar and kept as written.
|
|
23
|
+
#
|
|
24
|
+
# Merge semantics (what the former jq program in adopt.sh did, held to it by the parity fixture in
|
|
25
|
+
# packaging/e2e.sh): objects deep-merge, the PROJECT's scalar wins, arrays concat+dedup with Crewforth's entries
|
|
26
|
+
# first, and "hooks" is rebuilt per event as Crewforth's entries followed by the project's entries that do not
|
|
27
|
+
# reference .claude/hooks/ (in "command" or "args"). permissions.ask then loses every rule named in -v retired.
|
|
28
|
+
# Output is pretty-printed with two-space indent, the layout jq writes, so merging Crewforth onto itself gives its
|
|
29
|
+
# own bytes back.
|
|
30
|
+
# The text is split into CH[] once. substr(S,P,1) is O(len) per call on BSD awk, which made a 1 MB file take ~20 s;
|
|
31
|
+
# array indexing is O(1), so parsing is linear. split(s, CH, "") splits into characters in onetrue/BSD awk, gawk
|
|
32
|
+
# and mawk alike.
|
|
33
|
+
function sk(){ while(P<=L && index(" \t\r\n", CH[P])) P++ }
|
|
34
|
+
# Strings are held to RFC 8259, the grammar Claude Code's JSON.parse applies: an escape is one of \" \\ \/ \b \f \n
|
|
35
|
+
# \r \t or \u + 4 hex digits, and no raw control character (below 0x20). The raw token is returned, escapes kept.
|
|
36
|
+
function ps( t,c,i){ t=CH[P]; P++
|
|
37
|
+
while(P<=L){ c=CH[P]; P++
|
|
38
|
+
if(c=="\\"){ if(P>L) return ""; c=CH[P]; P++
|
|
39
|
+
if(c=="u"){ t=t "\\u"; for(i=0;i<4;i++){ if(P>L || !index("0123456789abcdefABCDEF", CH[P])) return ""; t=t CH[P]; P++ }; continue }
|
|
40
|
+
if(!index("\"\\/bfnrt", c)) return ""; t=t "\\" c; continue }
|
|
41
|
+
if(index(CTL, c)) return ""
|
|
42
|
+
t=t c; if(c=="\"") return t }
|
|
43
|
+
return "" }
|
|
44
|
+
function pv( id,c,k,q,st){ sk(); c=CH[P]; id=++NN; N[id]=0
|
|
45
|
+
if(c=="{" || c=="["){ T[id]=(c=="{" ? "o" : "a"); P++; sk()
|
|
46
|
+
if(CH[P]==(c=="{" ? "}" : "]")){ P++; return id }
|
|
47
|
+
while(1){ k=""
|
|
48
|
+
if(c=="{"){ sk(); if(CH[P]!="\"" || (k=ps())=="") return 0; sk(); if(CH[P++]!=":") return 0 }
|
|
49
|
+
if(!(q=pv())) return 0
|
|
50
|
+
N[id]++; K[id,N[id]]=k; C[id,N[id]]=q; sk(); st=CH[P++]
|
|
51
|
+
if(st==(c=="{" ? "}" : "]")) return id
|
|
52
|
+
if(st!=",") return 0 } }
|
|
53
|
+
T[id]="s"
|
|
54
|
+
if(c=="\""){ V[id]=ps(); return (V[id]=="" ? 0 : id) }
|
|
55
|
+
st=""; while(P<=L && CH[P] ~ /[-+.0-9A-Za-z]/){ st=st CH[P]; P++ }; V[id]=st
|
|
56
|
+
return (V[id] ~ /^(true|false|null|-?(0|[1-9][0-9]*)([.][0-9]+)?([eE][-+]?[0-9]+)?)$/ ? id : 0) }
|
|
57
|
+
# doc(f): parse file number f. CH[] is built from that file's lines directly: gluing the lines into one string
|
|
58
|
+
# first was itself quadratic (each append copies the whole buffer) on a many-line file.
|
|
59
|
+
function doc(f, r,i,j,n,tc){ split("",CH); L=0
|
|
60
|
+
for(i=1;i<=NL[f];i++){ n=split(LN[f,i],tc,""); for(j=1;j<=n;j++) CH[++L]=tc[j]; CH[++L]="\n" }
|
|
61
|
+
P=1
|
|
62
|
+
if(CTL==""){ for(r=1;r<32;r++) CTL=CTL sprintf("%c",r) }
|
|
63
|
+
r=pv(); sk(); return (r && P>L && T[r]=="o") ? r : 0 }
|
|
64
|
+
# A duplicated key: the LAST value wins, as in JSON.parse and jq — the value Claude Code actually reads.
|
|
65
|
+
function get(o,k, i){ for(i=N[o];i>=1;i--) if(K[o,i]==k) return C[o,i]; return 0 }
|
|
66
|
+
function isnull(id){ return T[id]=="s" && V[id]=="null" }
|
|
67
|
+
function at(id,path, n,sg,i){ n=split(path,sg,".")
|
|
68
|
+
for(i=1;i<=n && id;i++) id=(T[id]=="o") ? get(id,"\"" sg[i] "\"") : (T[id]=="a" && sg[i] ~ /^[0-9]+$/ && sg[i]<N[id]) ? C[id,sg[i]+1] : 0
|
|
69
|
+
return id }
|
|
70
|
+
function mk(t){ NN++; T[NN]=t; N[NN]=0; return NN }
|
|
71
|
+
function add(o,k,c){ N[o]++; K[o,N[o]]=k; C[o,N[o]]=c }
|
|
72
|
+
function canon(id, i,j,n,s,t,ord){
|
|
73
|
+
if(T[id]=="s") return V[id]
|
|
74
|
+
if(T[id]=="a"){ s="["; for(i=1;i<=N[id];i++) s=s (i>1 ? "," : "") canon(C[id,i]); return s "]" }
|
|
75
|
+
n=N[id]; for(i=1;i<=n;i++) ord[i]=i
|
|
76
|
+
for(i=2;i<=n;i++) for(j=i;j>1 && K[id,ord[j-1]]>K[id,ord[j]];j--){ t=ord[j]; ord[j]=ord[j-1]; ord[j-1]=t }
|
|
77
|
+
s="{"; for(i=1;i<=n;i++) s=s (i>1 ? "," : "") K[id,ord[i]] ":" canon(C[id,ord[i]]); return s "}" }
|
|
78
|
+
function cat(a,b, r,i,c,seen){ r=mk("a")
|
|
79
|
+
for(i=1;i<=N[a];i++){ c=canon(C[a,i]); if(!(c in seen)){ seen[c]=1; add(r,"",C[a,i]) } }
|
|
80
|
+
for(i=1;i<=N[b];i++){ c=canon(C[b,i]); if(!(c in seen)){ seen[c]=1; add(r,"",C[b,i]) } }
|
|
81
|
+
return r }
|
|
82
|
+
function dm(a,b, r,i,x){ r=mk("o")
|
|
83
|
+
for(i=1;i<=N[a];i++){ x=get(b,K[a,i]); add(r,K[a,i], !x ? C[a,i] : (T[C[a,i]]=="o" && T[x]=="o") ? dm(C[a,i],x) : (T[C[a,i]]=="a" && T[x]=="a") ? cat(C[a,i],x) : x) }
|
|
84
|
+
for(i=1;i<=N[b];i++) if(!get(a,K[b,i])) add(r,K[b,i],C[b,i])
|
|
85
|
+
return r }
|
|
86
|
+
function has_kit(s){ return T[s]=="s" && index(V[s], ".claude/hooks/") }
|
|
87
|
+
function is_kit(e, hs,i,h,a,j){ hs=get(e,"\"hooks\""); if(T[e]!="o" || T[hs]!="a") return 0
|
|
88
|
+
for(i=1;i<=N[hs];i++){ h=C[hs,i]; if(T[h]!="o") continue
|
|
89
|
+
if(has_kit(get(h,"\"command\""))) return 1
|
|
90
|
+
a=get(h,"\"args\""); if(T[a]=="a") for(j=1;j<=N[a];j++) if(has_kit(C[a,j])) return 1 }
|
|
91
|
+
return 0 }
|
|
92
|
+
function hooks(kh,ph, r,i,e,a,x,y,j){ r=mk("o")
|
|
93
|
+
for(i=1;i<=N[kh];i++){ e=K[kh,i]; if(!get(r,e)) add(r,e,mk("a")) }
|
|
94
|
+
for(i=1;i<=N[ph];i++){ e=K[ph,i]; if(!get(r,e)) add(r,e,mk("a")) }
|
|
95
|
+
for(i=1;i<=N[r];i++){ e=K[r,i]; a=C[r,i]; x=get(kh,e); y=get(ph,e)
|
|
96
|
+
if(isnull(x)) x=0; if(isnull(y)) y=0 # null event = no entries, as the jq merge's `// []` read it
|
|
97
|
+
if((x && T[x]!="a") || (y && T[y]!="a")) return 0
|
|
98
|
+
for(j=1;j<=N[x];j++) add(a,"",C[x,j])
|
|
99
|
+
for(j=1;j<=N[y];j++) if(!is_kit(C[y,j])) add(a,"",C[y,j]) }
|
|
100
|
+
return r }
|
|
101
|
+
function put(id,ind, i,s,o){
|
|
102
|
+
if(T[id]=="s") return V[id]
|
|
103
|
+
o=(T[id]=="o"); if(!N[id]) return (o ? "{}" : "[]")
|
|
104
|
+
s=(o ? "{" : "["); for(i=1;i<=N[id];i++) s=s (i>1 ? "," : "") "\n" ind " " (o ? K[id,i] ": " : "") put(C[id,i], ind " ")
|
|
105
|
+
return s "\n" ind (o ? "}" : "]") }
|
|
106
|
+
# dls(s): parse one string (a JSONL record) the same way.
|
|
107
|
+
function dls(s, r,n){ split("",CH); L=split(s,CH,""); P=1
|
|
108
|
+
if(CTL==""){ for(r=1;r<32;r++) CTL=CTL sprintf("%c",r) }
|
|
109
|
+
r=pv(); sk(); return (r && P>L && T[r]=="o") ? r : 0 }
|
|
110
|
+
FILENAME!=cur{ f++; cur=FILENAME } { LN[f,++NL[f]]=$0 }
|
|
111
|
+
END{
|
|
112
|
+
# op=usage (hooks/context-usage.sh): FILE is a transcript tail (JSONL). Walking from the LAST line back, print the
|
|
113
|
+
# context total of the last record that is a real top-level `type: assistant`, not a sidechain, carrying
|
|
114
|
+
# message.usage.cache_read_input_tokens — the predicate the former jq program applied. Each candidate is PARSED:
|
|
115
|
+
# a regex over the line matched the same key names inside tool inputs and results, and could count a nested
|
|
116
|
+
# object instead of the record's own usage. A line that does not parse (the window's cut first line) is skipped.
|
|
117
|
+
if(op=="usage"){ for(i=NL[1];i>=1;i--){ l=LN[1,i]; if(!index(l,"cache_read_input_tokens")) continue
|
|
118
|
+
if(!(r=dls(l))) continue
|
|
119
|
+
x=get(r,"\"isSidechain\""); if(x && !(isnull(x) || V[x]=="false")) continue
|
|
120
|
+
x=get(r,"\"type\""); if(!x || V[x]!="\"assistant\"") continue
|
|
121
|
+
u=at(r,"message.usage"); if(T[u]!="o") continue
|
|
122
|
+
cr=get(u,"\"cache_read_input_tokens\""); if(!cr || isnull(cr)) continue
|
|
123
|
+
it=get(u,"\"input_tokens\""); cc=get(u,"\"cache_creation_input_tokens\"")
|
|
124
|
+
print (it && !isnull(it) ? V[it] : 0) + V[cr] + (cc && !isnull(cc) ? V[cc] : 0); exit 0 }
|
|
125
|
+
exit 0 }
|
|
126
|
+
if(op=="validate") exit !doc(1)
|
|
127
|
+
if(op=="get" || op=="len"){ if(!(r=doc(1))) exit 10; if(!(r=at(r,path))) exit 1
|
|
128
|
+
if(op=="get"){ print put(r,""); exit 0 } if(T[r]=="s") exit 1; print N[r]; exit 0 }
|
|
129
|
+
if(op=="strings"){ if(!(r=doc(1))) exit 10; if(!(r=at(r,path))) exit 1; if(T[r]!="a") exit 1
|
|
130
|
+
for(i=1;i<=N[r];i++){ c=C[r,i]; if(T[c]=="s" && substr(V[c],1,1)=="\"") print substr(V[c],2,length(V[c])-2) }
|
|
131
|
+
exit 0 }
|
|
132
|
+
if(op=="overlay"){ if(!(t=doc(1))) exit 10; if(!(q=doc(2))) exit 11; kk="\"" key "\""
|
|
133
|
+
qv=get(q,kk); if(T[qv]!="o") exit 11; tv=get(t,kk); if(tv && T[tv]!="o") exit 10
|
|
134
|
+
nv=mk("o"); for(i=1;i<=N[tv];i++){ x=get(qv,K[tv,i]); add(nv,K[tv,i], x ? x : C[tv,i]) }
|
|
135
|
+
for(i=1;i<=N[qv];i++) if(!get(nv,K[qv,i])) add(nv,K[qv,i],C[qv,i])
|
|
136
|
+
if(setk!=""){ sn=mk("s"); V[sn]=setv; x=0
|
|
137
|
+
for(i=1;i<=N[nv];i++) if(K[nv,i]=="\"" setk "\""){ C[nv,i]=sn; x=1 }
|
|
138
|
+
if(!x) add(nv,"\"" setk "\"",sn) }
|
|
139
|
+
x=0; for(i=1;i<=N[t];i++) if(K[t,i]==kk){ C[t,i]=nv; x=1 }
|
|
140
|
+
if(!x) add(t,kk,nv)
|
|
141
|
+
print put(t,""); exit 0 }
|
|
142
|
+
if(!(k=doc(1))) exit 11
|
|
143
|
+
if(!(p=doc(2))) exit 10
|
|
144
|
+
kh=get(k,"\"hooks\""); ph=get(p,"\"hooks\""); if(isnull(kh)) kh=0; if(isnull(ph)) ph=0
|
|
145
|
+
if((kh && T[kh]!="o") || (ph && T[ph]!="o")) exit 12
|
|
146
|
+
r=dm(k,p); if(!(h=hooks(kh ? kh : mk("o"), ph ? ph : mk("o")))) exit 12
|
|
147
|
+
for(i=1;i<=N[r];i++) if(K[r,i]=="\"hooks\""){ C[r,i]=h; h=0 }
|
|
148
|
+
if(h) add(r,"\"hooks\"",h)
|
|
149
|
+
n=split(retired,rr,"|"); for(i=1;i<=n;i++) ret["\"" rr[i] "\""]=1
|
|
150
|
+
pm=get(r,"\"permissions\""); ak=(T[pm]=="o" ? get(pm,"\"ask\"") : 0)
|
|
151
|
+
if(T[ak]=="a"){ na=mk("a"); for(i=1;i<=N[ak];i++) if(T[C[ak,i]]!="s" || !(V[C[ak,i]] in ret)) add(na,"",C[ak,i])
|
|
152
|
+
for(i=1;i<=N[pm];i++) if(K[pm,i]=="\"ask\"") C[pm,i]=na }
|
|
153
|
+
print put(r,"")
|
|
154
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# The one "leave a star" line. Single source of its URL, its text in both languages, and when it stays quiet.
|
|
3
|
+
#
|
|
4
|
+
# star.sh --once <project> ONCE PER KIT VERSION per clone. The installers (first install, and every update)
|
|
5
|
+
# and doctor.sh on a healthy verdict all call it this way and share one marker, so a
|
|
6
|
+
# version shows the line once — whichever of them reaches it first — and never again
|
|
7
|
+
# until the Crewforth version changes. The marker holds the version it was shown for.
|
|
8
|
+
# star.sh unconditional (tests, and anyone who wants to see it).
|
|
9
|
+
#
|
|
10
|
+
# Nothing else prints it: not a hook, not a session start. Cost: one line (~30 tokens) per version, and only when
|
|
11
|
+
# the model relays it — /crew-update and /crew-doctor tell it to pass the line through as the last line.
|
|
12
|
+
#
|
|
13
|
+
# The marker lives in the git dir (`git rev-parse --git-path`), NOT under .claude/: a project that tracks .claude/
|
|
14
|
+
# would otherwise commit it, and one person's "seen" would silence the line for the whole team. Outside a git
|
|
15
|
+
# repository it falls back to <project>/.claude/star-shown.
|
|
16
|
+
#
|
|
17
|
+
# Quiet when CREW_NO_STAR is set to anything but 0, or when CI is defined at all: an unattended run has nobody to
|
|
18
|
+
# read it. A silenced run writes NO marker, so the line is still owed to whoever runs it by hand later.
|
|
19
|
+
set -u
|
|
20
|
+
# The 2.x names of the variables a user can set still work (one helper: eval/lib/crew-env.sh).
|
|
21
|
+
_crew_d="${BASH_SOURCE%/*}"; [ "$_crew_d" = "${BASH_SOURCE}" ] && _crew_d=.
|
|
22
|
+
[ -f "$_crew_d/./crew-env.sh" ] && . "$_crew_d/./crew-env.sh"; unset _crew_d
|
|
23
|
+
CREW_REPO_URL="https://github.com/Crewforth/crewforth"
|
|
24
|
+
|
|
25
|
+
case "${CREW_NO_STAR:-}" in ''|0) ;; *) exit 0 ;; esac
|
|
26
|
+
[ -n "${CI+set}" ] && exit 0
|
|
27
|
+
|
|
28
|
+
MARK=""; VER=""
|
|
29
|
+
if [ "${1:-}" = --once ]; then
|
|
30
|
+
_dir="${2:-.}"
|
|
31
|
+
# Builtin reads, not `head | tr` in a $( ): on Git Bash those two pipelines were 4 of ~11 processes and ~250 ms
|
|
32
|
+
# of ~395 ms per call (measured on stock Windows). `read` takes the first line; the CR is stripped by expansion.
|
|
33
|
+
# The braces matter: a failed `<` reports before a trailing 2>/dev/null applies (measured: 450 bytes of stderr).
|
|
34
|
+
VER=""; { IFS= read -r VER < "$_dir/.claude/VERSION"; } 2>/dev/null; VER="${VER%$'\r'}"; VER="${VER:-unknown}"
|
|
35
|
+
# GIT_DIR/GIT_WORK_TREE dropped: exported by a caller (a hook), they would point the marker at another repo.
|
|
36
|
+
MARK="$(cd "$_dir" 2>/dev/null && env -u GIT_DIR -u GIT_WORK_TREE git rev-parse --git-path crewforth-star 2>/dev/null)" || MARK=""
|
|
37
|
+
case "$MARK" in
|
|
38
|
+
'') MARK="$_dir/.claude/star-shown" ;;
|
|
39
|
+
/*|[A-Za-z]:*) ;; # absolute (POSIX or a Windows drive path from git)
|
|
40
|
+
*) MARK="$_dir/$MARK" ;; # git answers relative to the directory it was asked in
|
|
41
|
+
esac
|
|
42
|
+
_seen=""; { IFS= read -r _seen < "$MARK"; } 2>/dev/null; [ "${_seen%$'\r'}" = "$VER" ] && exit 0
|
|
43
|
+
fi
|
|
44
|
+
|
|
45
|
+
# ---- CREW-I18N ------------------------------------------------------------------------------------------
|
|
46
|
+
# Same contract as preflight.sh: the installer exports CREW_LANG; run on its own (doctor), the locale decides.
|
|
47
|
+
case "${CREW_LANG:-}" in tr|en) ;; *)
|
|
48
|
+
_loc="${LC_ALL:-}"; [ -n "$_loc" ] || _loc="${LC_MESSAGES:-}"; [ -n "$_loc" ] || _loc="${LANG:-}"
|
|
49
|
+
case "$_loc" in tr*|TR*) CREW_LANG=tr ;; *) CREW_LANG=en ;; esac ;;
|
|
50
|
+
esac
|
|
51
|
+
_mt() {
|
|
52
|
+
[ -n "${1:-}" ] || { _M=""; return 0; }
|
|
53
|
+
local s="$1"; shift
|
|
54
|
+
if [ "$CREW_LANG" = tr ]; then
|
|
55
|
+
case "$s" in
|
|
56
|
+
"⭐ If Crewforth saves you a review round, a star helps others find it: %s") s='⭐ Crewforth bir inceleme turunu kurtardıysa, bir yıldız başkalarının da bulmasına yardım eder: %s' ;;
|
|
57
|
+
*) [ -n "${CREW_I18N_MISS:-}" ] && printf '%s\n' "$s" >> "$CREW_I18N_MISS" ;;
|
|
58
|
+
esac
|
|
59
|
+
fi
|
|
60
|
+
# shellcheck disable=SC2059
|
|
61
|
+
printf -v _M -- "$s" "$@"
|
|
62
|
+
}
|
|
63
|
+
# ---- /CREW-I18N -----------------------------------------------------------------------------------------
|
|
64
|
+
|
|
65
|
+
_mt '⭐ If Crewforth saves you a review round, a star helps others find it: %s' "$CREW_REPO_URL"
|
|
66
|
+
# The marker only after the line actually went out: a closed stdout must not record "shown".
|
|
67
|
+
if printf '%s\n' "$_M" && [ -n "$MARK" ]; then
|
|
68
|
+
{ printf '%s\n' "$VER" > "$MARK"; } 2>/dev/null
|
|
69
|
+
fi
|
|
70
|
+
exit 0
|