crewforth 3.1.1 → 3.2.0-rc.2
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 +171 -0
- package/README.md +4 -3
- package/VERSION +1 -1
- package/adopt.sh +56 -5
- package/kit/AGENT_TEMPLATE.md +2 -1
- package/kit/CLAUDE.md +14 -13
- package/kit/agents/crew-commit-agent.md +7 -2
- package/kit/agents/crew-docs-agent.md +71 -0
- package/kit/agents/crew-planner.md +2 -0
- package/kit/agents/crew-refuter.md +77 -0
- package/kit/agents/crew-security-expert.md +3 -1
- package/kit/agents/crew-session-manager.md +11 -9
- package/kit/eval/doctor.sh +117 -6
- package/kit/eval/golden-routing.txt +9 -0
- package/kit/eval/lib/crew-map/crew-map.sh +26 -0
- package/kit/eval/lib/crew-map/detect.mjs +238 -0
- package/kit/eval/lib/crew-map/narrative.mjs +98 -0
- package/kit/eval/lib/crew-map/parse.mjs +305 -0
- package/kit/eval/lib/crew-map/project.mjs +163 -0
- package/kit/eval/lib/crew-map/rules/names.json +38 -0
- package/kit/eval/lib/crew-map/rules/packages.json +142 -0
- package/kit/eval/lib/crew-map/rules/python-stdlib.json +197 -0
- package/kit/eval/lib/crew-map/scan.mjs +214 -0
- package/kit/eval/lib/crew-map/stacks.mjs +441 -0
- package/kit/eval/lib/crew-map/surface.mjs +457 -0
- package/kit/eval/lib/findings/findings.mjs +410 -0
- package/kit/eval/lib/findings/findings.schema.json +122 -0
- package/kit/eval/lib/findings/findings.sh +29 -0
- package/kit/eval/lib/settings-default.sh +36 -0
- package/kit/eval/lib/settings-json.awk +15 -4
- package/kit/eval/lib/star.sh +23 -7
- package/kit/eval/lib/statusline.settings.json +6 -0
- package/kit/eval/lib/statusline.sh +100 -0
- package/kit/eval/lib/version.sh +41 -0
- package/kit/eval/smoke-test.sh +576 -41
- package/kit/hooks/agent-outcome.sh +26 -12
- package/kit/hooks/guard-agent-model.sh +109 -9
- package/kit/hooks/guard-bash.sh +240 -182
- package/kit/hooks/guard-question.sh +73 -0
- package/kit/hooks/guard-write.sh +21 -0
- package/kit/hooks/prompt-approval.sh +320 -103
- package/kit/hooks/refuter-ledger.sh +80 -0
- package/kit/hooks/session-update-check.sh +9 -8
- package/kit/owned-blobs.tsv +4 -4
- package/kit/settings.json +33 -0
- package/kit/skills/crew-approve/SKILL.md +7 -0
- package/kit/skills/crew-doctor/SKILL.md +5 -1
- package/kit/skills/crew-handoff/SKILL.md +3 -2
- package/kit/skills/crew-map/SKILL.md +44 -0
- package/kit/skills/crew-review/SKILL.md +2 -0
- package/kit/skills/crew-update/SKILL.md +1 -1
- package/kit/skills/incident-runbook/SKILL.md +2 -2
- package/kit/skills/security-scan/references/verify.md +6 -3
- package/kit/studio/README.md +12 -0
- package/kit/studio/server/index.js +125 -11
- package/kit/studio/server/lib/code-stamp.js +35 -0
- package/kit/studio/server/lib/crew-map.js +113 -0
- package/kit/studio/server/lib/model-outcomes.js +130 -15
- package/kit/studio/server/lib/owned-stream.js +56 -0
- package/kit/studio/server/lib/pricing.js +3 -0
- package/kit/studio/server/lib/projects.js +11 -4
- package/kit/studio/server/lib/remote.js +99 -0
- package/kit/studio/server/lib/usage.js +74 -14
- package/kit/studio/web/app.js +223 -15
- package/kit/studio/web/canvas.js +44 -120
- package/kit/studio/web/chat.js +82 -17
- package/kit/studio/web/graph-plan.js +10 -4
- package/kit/studio/web/index.html +10 -5
- package/kit/studio/web/liveness.js +14 -0
- package/kit/studio/web/map-plan.js +341 -0
- package/kit/studio/web/map.js +503 -0
- package/kit/studio/web/models-plan.js +61 -7
- package/kit/studio/web/models.js +19 -3
- package/kit/studio/web/style.css +131 -0
- package/kit/studio/web/usage.js +12 -0
- package/kit/studio/web/viewport.js +160 -0
- package/package.json +6 -5
- package/start.sh +10 -0
package/kit/eval/lib/star.sh
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
# The one "leave a star" line. Single source of its URL, its text in both languages, and when it stays quiet.
|
|
3
3
|
#
|
|
4
|
-
# star.sh --once <project>
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
4
|
+
# star.sh --once <project> [doctor]
|
|
5
|
+
# ONCE PER KIT VERSION per clone and per caller. The installers (first install, and
|
|
6
|
+
# every update) share one marker; doctor (doctor.sh on a healthy verdict, and the
|
|
7
|
+
# plugin's /crew-doctor) keeps its own, `doctor`. So a version shows the line at most
|
|
8
|
+
# twice, once where it was installed and once where it was checked, and never again
|
|
9
|
+
# until the Crewforth version changes. A marker holds the version it was shown for.
|
|
10
|
+
# /crew-update runs its doctor with CREW_NO_STAR=1, so one command never shows it twice.
|
|
8
11
|
# star.sh unconditional (tests, and anyone who wants to see it).
|
|
9
12
|
#
|
|
10
13
|
# Nothing else prints it: not a hook, not a session start. Cost: one line (~30 tokens) per version, and only when
|
|
@@ -31,11 +34,21 @@ if [ "${1:-}" = --once ]; then
|
|
|
31
34
|
# Builtin reads, not `head | tr` in a $( ): on Git Bash those two pipelines were 4 of ~11 processes and ~250 ms
|
|
32
35
|
# of ~395 ms per call (measured on stock Windows). `read` takes the first line; the CR is stripped by expansion.
|
|
33
36
|
# 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'}"
|
|
37
|
+
VER=""; { IFS= read -r VER < "$_dir/.claude/VERSION"; } 2>/dev/null; VER="${VER%$'\r'}"
|
|
38
|
+
# The plugin edition has no .claude/VERSION: its version is the manifest's, beside this file at ../../.
|
|
39
|
+
if [ -z "$VER" ]; then
|
|
40
|
+
_here="${BASH_SOURCE%[/\\]*}"; [ "$_here" = "${BASH_SOURCE}" ] && _here=.
|
|
41
|
+
[ -f "$_here/../../.claude-plugin/plugin.json" ] && [ -f "$_here/settings-json.awk" ] && \
|
|
42
|
+
VER="$(awk -v op=get -v path=version -f "$_here/settings-json.awk" "$_here/../../.claude-plugin/plugin.json" 2>/dev/null)"
|
|
43
|
+
VER="${VER#\"}"; VER="${VER%\"}" # the reader prints a JSON string with its quotes
|
|
44
|
+
case "$VER" in *[!0-9A-Za-z.-]*) VER="" ;; esac
|
|
45
|
+
fi
|
|
46
|
+
VER="${VER:-unknown}"
|
|
47
|
+
_key=crewforth-star; [ "${3:-}" = doctor ] && _key=crewforth-star-doctor
|
|
35
48
|
# 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
|
|
49
|
+
MARK="$(cd "$_dir" 2>/dev/null && env -u GIT_DIR -u GIT_WORK_TREE git rev-parse --git-path "$_key" 2>/dev/null)" || MARK=""
|
|
37
50
|
case "$MARK" in
|
|
38
|
-
'') MARK="$_dir/.claude/star-shown" ;;
|
|
51
|
+
'') MARK="$_dir/.claude/star-shown"; [ "$_key" = crewforth-star ] || MARK="$MARK-doctor" ;;
|
|
39
52
|
/*|[A-Za-z]:*) ;; # absolute (POSIX or a Windows drive path from git)
|
|
40
53
|
*) MARK="$_dir/$MARK" ;; # git answers relative to the directory it was asked in
|
|
41
54
|
esac
|
|
@@ -65,6 +78,9 @@ _mt() {
|
|
|
65
78
|
_mt '⭐ If Crewforth saves you a review round, a star helps others find it: %s' "$CREW_REPO_URL"
|
|
66
79
|
# The marker only after the line actually went out: a closed stdout must not record "shown".
|
|
67
80
|
if printf '%s\n' "$_M" && [ -n "$MARK" ]; then
|
|
81
|
+
# A plugin project may have no .claude/ yet: without one the marker would never be written and the line would
|
|
82
|
+
# come back on every run.
|
|
83
|
+
[ -d "${MARK%/*}" ] || mkdir -p "${MARK%/*}" 2>/dev/null
|
|
68
84
|
{ printf '%s\n' "$VER" > "$MARK"; } 2>/dev/null
|
|
69
85
|
fi
|
|
70
86
|
exit 0
|
|
@@ -0,0 +1,100 @@
|
|
|
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
|
+
# The setting itself is data beside this file (statusline.settings.json); the rule for putting a default in is
|
|
29
|
+
# one helper, shared with the subagent model's default.
|
|
30
|
+
exec bash "$_here/settings-default.sh" --install statusLine "$_here/statusline.settings.json"
|
|
31
|
+
fi
|
|
32
|
+
|
|
33
|
+
IFS= read -r -d '' IN || true
|
|
34
|
+
[ -n "$IN" ] || exit 0
|
|
35
|
+
# One read of the four values the line needs.
|
|
36
|
+
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=""
|
|
37
|
+
IFS=$'\t' read -r SID PCT COST PDIR <<< "$F"
|
|
38
|
+
# The reader prints a string with its quotes; taken off by expansion, not a subshell (this runs every few seconds).
|
|
39
|
+
SID="${SID:-}"; SID="${SID#\"}"; SID="${SID%\"}"; PDIR="${PDIR:-}"; PDIR="${PDIR#\"}"; PDIR="${PDIR%\"}"
|
|
40
|
+
case "$SID" in ''|*[!A-Za-z0-9._-]*) SID="" ;; esac
|
|
41
|
+
[ -n "$PDIR" ] && [ -d "$PDIR/.claude" ] && cd "$PDIR" 2>/dev/null
|
|
42
|
+
|
|
43
|
+
OUT=()
|
|
44
|
+
# The session's baseline: made on its first status line. What is newer than it happened in this session.
|
|
45
|
+
BASE=""
|
|
46
|
+
if [ -n "$SID" ] && [ -d .claude ]; then
|
|
47
|
+
BASE=".claude/state/statusline/$SID"
|
|
48
|
+
if [ ! -e "$BASE" ]; then
|
|
49
|
+
mkdir -p .claude/state/statusline 2>/dev/null
|
|
50
|
+
: > "$BASE" 2>/dev/null || BASE=""
|
|
51
|
+
fi
|
|
52
|
+
fi
|
|
53
|
+
|
|
54
|
+
# 🛡 The gate's last word in this session.
|
|
55
|
+
GL="${CREW_GATE_LOG:-.claude/gate-log.tsv}"
|
|
56
|
+
if [ -n "$BASE" ] && [ -f "$GL" ] && [ "$GL" -nt "$BASE" ]; then
|
|
57
|
+
last="$(tail -n 1 "$GL" 2>/dev/null)"
|
|
58
|
+
IFS=$'\t' read -r verdict section _ <<< "$last"
|
|
59
|
+
case "$verdict" in BLOCK|ASK|ALLOW|NOTE) OUT+=("🛡 $verdict ${section:-}") ;; esac
|
|
60
|
+
fi
|
|
61
|
+
|
|
62
|
+
# ✓ An approval that still holds: the record is in the git directory (a worktree's .git is a file naming it).
|
|
63
|
+
G=""
|
|
64
|
+
if [ -d .git ]; then G=".git"
|
|
65
|
+
elif [ -f .git ]; then IFS= read -r l < .git 2>/dev/null; case "$l" in "gitdir: "*) G="${l#gitdir: }"; G="${G%$'\r'}" ;; esac; fi
|
|
66
|
+
if [ -n "$G" ] && [ -s "$G/crewforth-approval" ]; then
|
|
67
|
+
op=""; ts=""
|
|
68
|
+
while IFS='=' read -r k v; do v="${v%$'\r'}"; case "$k" in op) op="$v" ;; ts) ts="$v" ;; esac; done < "$G/crewforth-approval"
|
|
69
|
+
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
|
|
70
|
+
case "$ts" in ''|*[!0-9]*) ;; *)
|
|
71
|
+
left=$(( (1800 - (now - ts)) / 60 ))
|
|
72
|
+
case "$op" in commit|push|commit+push) [ "$left" -ge 0 ] && [ "$((now - ts))" -le 1800 ] && OUT+=("✓ $op approved ${left}m") ;; esac ;;
|
|
73
|
+
esac
|
|
74
|
+
fi
|
|
75
|
+
|
|
76
|
+
# ⚙ Crew agents started in this session and not stopped: a record under agents/ with no result beside it.
|
|
77
|
+
if [ -n "$BASE" ] && [ -d .claude/state/crew-model/agents ]; then
|
|
78
|
+
n=0
|
|
79
|
+
for a in .claude/state/crew-model/agents/*; do
|
|
80
|
+
[ -f "$a" ] || continue
|
|
81
|
+
case "$a" in *.claim|*.used) continue ;; esac
|
|
82
|
+
[ "$a" -nt "$BASE" ] || continue
|
|
83
|
+
[ -e ".claude/state/crew-model/results/${a##*/}" ] && continue
|
|
84
|
+
n=$((n + 1))
|
|
85
|
+
done
|
|
86
|
+
pl=s; [ "$n" -eq 1 ] && pl=""
|
|
87
|
+
[ "$n" -gt 0 ] && OUT+=("⚙ $n crew agent$pl")
|
|
88
|
+
fi
|
|
89
|
+
|
|
90
|
+
# 🔋 and ~$: Claude Code's own numbers, shown as they are.
|
|
91
|
+
case "${PCT:-}" in ''|null|*[!0-9.]*) ;; *) OUT+=("🔋 ${PCT%%.*}%") ;; esac
|
|
92
|
+
case "${COST:-}" in ''|null|*[!0-9.]*) ;; *)
|
|
93
|
+
printf -v c '%.2f' "$COST" 2>/dev/null && OUT+=("~\$$c") ;;
|
|
94
|
+
esac
|
|
95
|
+
|
|
96
|
+
[ "${#OUT[@]}" -gt 0 ] || exit 0
|
|
97
|
+
line="${OUT[0]}"
|
|
98
|
+
for p in "${OUT[@]:1}"; do line="$line · $p"; done
|
|
99
|
+
printf '%s\n' "$line"
|
|
100
|
+
exit 0
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Sourced, not run. The ONE definition of how Crewforth reads and orders its own version numbers.
|
|
2
|
+
#
|
|
3
|
+
# An install's VERSION holds X.Y.Z, or X.Y.Z-rc.N when it came from a release candidate. An rc comes before its
|
|
4
|
+
# final (3.2.0-rc.1 < 3.2.0), so someone who tried the rc is offered the final when it ships. Read by the update
|
|
5
|
+
# check (hooks/session-update-check.sh), doctor and adopt.sh; each used to split on "." and read 3.2.0-rc.1 as 3.2.0.
|
|
6
|
+
#
|
|
7
|
+
# Builtins only: sourcing this costs no process, which matters in hooks on Git Bash.
|
|
8
|
+
|
|
9
|
+
# The version an install may hold in its own VERSION file: X.Y.Z or X.Y.Z-rc.N, nothing else; printed, or rc 1.
|
|
10
|
+
# A value off the network is not read with this: the update check holds that to X.Y.Z alone.
|
|
11
|
+
crew_local_version(){
|
|
12
|
+
local v="${1:-}" base n
|
|
13
|
+
v="${v//[!0-9A-Za-z.-]/}"
|
|
14
|
+
base="${v%%-rc.*}"
|
|
15
|
+
case "$base" in
|
|
16
|
+
*[!0-9.]*|'') return 1 ;;
|
|
17
|
+
[0-9]*.[0-9]*.[0-9]*.*) return 1 ;;
|
|
18
|
+
[0-9]*.[0-9]*.[0-9]*) ;;
|
|
19
|
+
*) return 1 ;;
|
|
20
|
+
esac
|
|
21
|
+
if [ "$base" != "$v" ]; then
|
|
22
|
+
n="${v#"$base"-rc.}"
|
|
23
|
+
case "$n" in ''|*[!0-9]*) return 1 ;; esac
|
|
24
|
+
fi
|
|
25
|
+
printf '%s' "$v"
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
# vcmp(a, b): 1 when a is newer, -1 when older, 0 when the same. Prepended to an awk program:
|
|
29
|
+
# awk -v a="$X" -v b="$Y" "$CREW_VCMP_AWK"' BEGIN { exit !(vcmp(a, b) > 0) }'
|
|
30
|
+
CREW_VCMP_AWK='
|
|
31
|
+
function vrc(v) { return match(v, /-rc\.[0-9]+$/) ? substr(v, RSTART + 4) + 0 : -1 }
|
|
32
|
+
function vcmp(a, b, x, y, i, ra, rb) {
|
|
33
|
+
ra = vrc(a); rb = vrc(b); sub(/-.*/, "", a); sub(/-.*/, "", b)
|
|
34
|
+
split(a, x, "."); split(b, y, ".")
|
|
35
|
+
for (i = 1; i <= 3; i++) { if (x[i] + 0 > y[i] + 0) return 1; if (x[i] + 0 < y[i] + 0) return -1 }
|
|
36
|
+
if (ra == rb) return 0
|
|
37
|
+
if (ra < 0) return 1
|
|
38
|
+
if (rb < 0) return -1
|
|
39
|
+
return ra > rb ? 1 : -1
|
|
40
|
+
}
|
|
41
|
+
'
|