crewforth 3.1.0 โ†’ 3.2.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.
Files changed (70) hide show
  1. package/CHANGELOG.md +151 -0
  2. package/README.md +4 -3
  3. package/VERSION +1 -1
  4. package/adopt.sh +91 -11
  5. package/kit/AGENT_TEMPLATE.md +2 -1
  6. package/kit/CLAUDE.md +9 -8
  7. package/kit/agents/crew-commit-agent.md +7 -2
  8. package/kit/agents/crew-docs-agent.md +71 -0
  9. package/kit/agents/crew-planner.md +2 -0
  10. package/kit/agents/crew-refuter.md +77 -0
  11. package/kit/agents/crew-security-expert.md +3 -1
  12. package/kit/agents/crew-session-manager.md +11 -9
  13. package/kit/eval/doctor.sh +91 -3
  14. package/kit/eval/golden-routing.txt +9 -0
  15. package/kit/eval/lib/crew-map/crew-map.sh +26 -0
  16. package/kit/eval/lib/crew-map/detect.mjs +238 -0
  17. package/kit/eval/lib/crew-map/narrative.mjs +98 -0
  18. package/kit/eval/lib/crew-map/parse.mjs +305 -0
  19. package/kit/eval/lib/crew-map/project.mjs +163 -0
  20. package/kit/eval/lib/crew-map/rules/names.json +38 -0
  21. package/kit/eval/lib/crew-map/rules/packages.json +142 -0
  22. package/kit/eval/lib/crew-map/rules/python-stdlib.json +197 -0
  23. package/kit/eval/lib/crew-map/scan.mjs +214 -0
  24. package/kit/eval/lib/crew-map/stacks.mjs +441 -0
  25. package/kit/eval/lib/crew-map/surface.mjs +457 -0
  26. package/kit/eval/lib/findings/findings.mjs +410 -0
  27. package/kit/eval/lib/findings/findings.schema.json +122 -0
  28. package/kit/eval/lib/findings/findings.sh +29 -0
  29. package/kit/eval/lib/settings-json.awk +5 -0
  30. package/kit/eval/lib/star.sh +23 -7
  31. package/kit/eval/lib/statusline.settings.json +6 -0
  32. package/kit/eval/lib/statusline.sh +115 -0
  33. package/kit/eval/smoke-test.sh +465 -38
  34. package/kit/hooks/agent-outcome.sh +6 -3
  35. package/kit/hooks/guard-agent-model.sh +15 -5
  36. package/kit/hooks/guard-bash.sh +240 -182
  37. package/kit/hooks/guard-question.sh +73 -0
  38. package/kit/hooks/guard-write.sh +21 -0
  39. package/kit/hooks/prompt-approval.sh +320 -103
  40. package/kit/hooks/refuter-ledger.sh +80 -0
  41. package/kit/owned-blobs.tsv +4 -1
  42. package/kit/settings.json +33 -0
  43. package/kit/skills/crew-approve/SKILL.md +7 -0
  44. package/kit/skills/crew-doctor/SKILL.md +5 -1
  45. package/kit/skills/crew-handoff/SKILL.md +3 -2
  46. package/kit/skills/crew-map/SKILL.md +44 -0
  47. package/kit/skills/crew-review/SKILL.md +2 -0
  48. package/kit/skills/crew-update/SKILL.md +1 -1
  49. package/kit/skills/incident-runbook/SKILL.md +2 -2
  50. package/kit/skills/security-scan/references/verify.md +6 -3
  51. package/kit/studio/README.md +12 -0
  52. package/kit/studio/server/index.js +114 -11
  53. package/kit/studio/server/lib/crew-map.js +113 -0
  54. package/kit/studio/server/lib/model-outcomes.js +130 -15
  55. package/kit/studio/server/lib/pricing.js +3 -0
  56. package/kit/studio/server/lib/remote.js +99 -0
  57. package/kit/studio/server/lib/usage.js +74 -14
  58. package/kit/studio/web/app.js +214 -12
  59. package/kit/studio/web/canvas.js +44 -120
  60. package/kit/studio/web/graph-plan.js +10 -4
  61. package/kit/studio/web/index.html +10 -5
  62. package/kit/studio/web/map-plan.js +341 -0
  63. package/kit/studio/web/map.js +503 -0
  64. package/kit/studio/web/models-plan.js +61 -7
  65. package/kit/studio/web/models.js +19 -3
  66. package/kit/studio/web/style.css +125 -0
  67. package/kit/studio/web/usage.js +12 -0
  68. package/kit/studio/web/viewport.js +160 -0
  69. package/package.json +6 -5
  70. package/start.sh +10 -0
@@ -0,0 +1,115 @@
1
+ #!/usr/bin/env bash
2
+ # statusline.sh โ€” Crewforth's status line: one line, from what Claude Code hands it on stdin and what the hooks
3
+ # have already written. It decides nothing and blocks nothing; it only says.
4
+ #
5
+ # ๐Ÿ›ก BLOCK ยง4.2 ยท โœ“ commit approved 24m ยท โš™ 2 crew agents ยท ๐Ÿ”‹ 34% ยท ~$1.20
6
+ #
7
+ # Each part is shown only when there is something to say, and only for THIS session:
8
+ # ๐Ÿ›ก the gate's last word in this session: the last line of the gate log, if it was written after the session's
9
+ # first status line (the log carries no time, so a line from last week must not read as now)
10
+ # โœ“ an approval the user gave for a commit or a push that still holds (the record prompt-approval.sh keeps in
11
+ # the git directory, 30 minutes, emptied at the user's next message)
12
+ # โš™ crew agents started in this session that have not stopped yet (model routing's records)
13
+ # ๐Ÿ”‹ the context fill Claude Code measured (context_window.used_percentage), never an estimate
14
+ # ~$ what Claude Code puts the session's cost at (cost.total_cost_usd), at API list price
15
+ #
16
+ # Wired by the installers only where no status line is set already (theirs is never replaced), from
17
+ # statusline.settings.json beside this file.
18
+ # COST. Claude Code runs this after every change, debounced at 300 ms. Measured: awk (the stdin JSON) and tail (the
19
+ # gate log), and on bash older than 4.2 one date; the rest is builtins. No git, no jq, no python.
20
+ set -u
21
+ _here="${BASH_SOURCE%[/\\]*}"; [ "$_here" = "${BASH_SOURCE}" ] && _here=.
22
+
23
+ # --install, from the project root: put this status line into .claude/settings.json only where NO status line is
24
+ # set (the project's settings, its local settings, or the user's own); a user's line is never replaced, because
25
+ # the project's setting would win over it. Prints one word: installed ยท kept (one is set) ยท none (no settings.json)
26
+ # ยท failed (the file is left as it was). A managed settings layer is not read. The merge is Crewforth's own JSON reader.
27
+ if [ "${1:-}" = --install ]; then
28
+ P=".claude/settings.json"; SJ="$_here/settings-json.awk"
29
+ [ -f "$P" ] && [ -f "$SJ" ] || { echo none; exit 0; }
30
+ for f in "$P" ".claude/settings.local.json" "${CLAUDE_CONFIG_DIR:-$HOME/.claude}/settings.json"; do
31
+ [ -f "$f" ] && awk -v op=get -v path=statusLine -f "$SJ" "$f" >/dev/null 2>&1 && { echo kept; exit 0; }
32
+ done
33
+ # The setting itself is data beside this file (statusline.settings.json): one definition, read here and nowhere else.
34
+ K="$_here/statusline.settings.json"; [ -f "$K" ] || { echo failed; exit 0; }
35
+ t="$P.statusline.$$"
36
+ r=failed
37
+ # The project's file goes FIRST: the merge refreshes the first file's .claude/hooks/ entries from the first file,
38
+ # so put second it would lose every Crewforth hook (measured: the installed project's own smoke failed 30
39
+ # checks). The second file has one member, statusLine, which the first lacks: that is the one change. Written
40
+ # through the file, so a symlink and its mode survive.
41
+ if awk -v op=merge -v retired= -f "$SJ" "$P" "$K" > "$t" 2>/dev/null && [ -s "$t" ] \
42
+ && awk -v op=validate -f "$SJ" "$t" 2>/dev/null && awk -v op=get -v path=statusLine -f "$SJ" "$t" >/dev/null 2>&1; then
43
+ cat "$t" > "$P" && r=installed
44
+ fi
45
+ rm -f "$t"; echo "$r"; exit 0
46
+ fi
47
+
48
+ IFS= read -r -d '' IN || true
49
+ [ -n "$IN" ] || exit 0
50
+ # One read of the four values the line needs.
51
+ F="$(printf '%s' "$IN" | awk -v op=gets -v 'path=session_id|context_window.used_percentage|cost.total_cost_usd|workspace.project_dir' -f "$_here/settings-json.awk" - 2>/dev/null)" || F=""
52
+ IFS=$'\t' read -r SID PCT COST PDIR <<< "$F"
53
+ # The reader prints a string with its quotes; taken off by expansion, not a subshell (this runs every few seconds).
54
+ SID="${SID:-}"; SID="${SID#\"}"; SID="${SID%\"}"; PDIR="${PDIR:-}"; PDIR="${PDIR#\"}"; PDIR="${PDIR%\"}"
55
+ case "$SID" in ''|*[!A-Za-z0-9._-]*) SID="" ;; esac
56
+ [ -n "$PDIR" ] && [ -d "$PDIR/.claude" ] && cd "$PDIR" 2>/dev/null
57
+
58
+ OUT=()
59
+ # The session's baseline: made on its first status line. What is newer than it happened in this session.
60
+ BASE=""
61
+ if [ -n "$SID" ] && [ -d .claude ]; then
62
+ BASE=".claude/state/statusline/$SID"
63
+ if [ ! -e "$BASE" ]; then
64
+ mkdir -p .claude/state/statusline 2>/dev/null
65
+ : > "$BASE" 2>/dev/null || BASE=""
66
+ fi
67
+ fi
68
+
69
+ # ๐Ÿ›ก The gate's last word in this session.
70
+ GL="${CREW_GATE_LOG:-.claude/gate-log.tsv}"
71
+ if [ -n "$BASE" ] && [ -f "$GL" ] && [ "$GL" -nt "$BASE" ]; then
72
+ last="$(tail -n 1 "$GL" 2>/dev/null)"
73
+ IFS=$'\t' read -r verdict section _ <<< "$last"
74
+ case "$verdict" in BLOCK|ASK|ALLOW|NOTE) OUT+=("๐Ÿ›ก $verdict ${section:-}") ;; esac
75
+ fi
76
+
77
+ # โœ“ An approval that still holds: the record is in the git directory (a worktree's .git is a file naming it).
78
+ G=""
79
+ if [ -d .git ]; then G=".git"
80
+ elif [ -f .git ]; then IFS= read -r l < .git 2>/dev/null; case "$l" in "gitdir: "*) G="${l#gitdir: }"; G="${G%$'\r'}" ;; esac; fi
81
+ if [ -n "$G" ] && [ -s "$G/crewforth-approval" ]; then
82
+ op=""; ts=""
83
+ while IFS='=' read -r k v; do v="${v%$'\r'}"; case "$k" in op) op="$v" ;; ts) ts="$v" ;; esac; done < "$G/crewforth-approval"
84
+ if [ "${BASH_VERSINFO[0]}" -ge 5 ] || { [ "${BASH_VERSINFO[0]}" -eq 4 ] && [ "${BASH_VERSINFO[1]}" -ge 2 ]; }; then printf -v now '%(%s)T' -1; else now="$(date +%s)"; fi
85
+ case "$ts" in ''|*[!0-9]*) ;; *)
86
+ left=$(( (1800 - (now - ts)) / 60 ))
87
+ case "$op" in commit|push|commit+push) [ "$left" -ge 0 ] && [ "$((now - ts))" -le 1800 ] && OUT+=("โœ“ $op approved ${left}m") ;; esac ;;
88
+ esac
89
+ fi
90
+
91
+ # โš™ Crew agents started in this session and not stopped: a record under agents/ with no result beside it.
92
+ if [ -n "$BASE" ] && [ -d .claude/state/crew-model/agents ]; then
93
+ n=0
94
+ for a in .claude/state/crew-model/agents/*; do
95
+ [ -f "$a" ] || continue
96
+ case "$a" in *.claim|*.used) continue ;; esac
97
+ [ "$a" -nt "$BASE" ] || continue
98
+ [ -e ".claude/state/crew-model/results/${a##*/}" ] && continue
99
+ n=$((n + 1))
100
+ done
101
+ pl=s; [ "$n" -eq 1 ] && pl=""
102
+ [ "$n" -gt 0 ] && OUT+=("โš™ $n crew agent$pl")
103
+ fi
104
+
105
+ # ๐Ÿ”‹ and ~$: Claude Code's own numbers, shown as they are.
106
+ case "${PCT:-}" in ''|null|*[!0-9.]*) ;; *) OUT+=("๐Ÿ”‹ ${PCT%%.*}%") ;; esac
107
+ case "${COST:-}" in ''|null|*[!0-9.]*) ;; *)
108
+ printf -v c '%.2f' "$COST" 2>/dev/null && OUT+=("~\$$c") ;;
109
+ esac
110
+
111
+ [ "${#OUT[@]}" -gt 0 ] || exit 0
112
+ line="${OUT[0]}"
113
+ for p in "${OUT[@]:1}"; do line="$line ยท $p"; done
114
+ printf '%s\n' "$line"
115
+ exit 0