crewforth 3.1.1 → 3.2.0-rc.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +323 -0
- package/README.md +6 -3
- package/VERSION +1 -1
- package/adopt.sh +66 -6
- package/bin/cli.js +3 -2
- package/kit/AGENT_TEMPLATE.md +2 -1
- package/kit/CLAUDE.md +15 -14
- package/kit/agents/crew-commit-agent.md +11 -4
- 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-review-agent.md +2 -0
- package/kit/agents/crew-security-expert.md +3 -1
- package/kit/agents/crew-session-manager.md +11 -9
- package/kit/eval/doctor.sh +128 -6
- package/kit/eval/gate-report.sh +2 -0
- 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 +132 -0
- package/kit/eval/lib/crew-map/parse.mjs +305 -0
- package/kit/eval/lib/crew-map/project.mjs +164 -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/rules/tech.json +35 -0
- package/kit/eval/lib/crew-map/scan.mjs +266 -0
- package/kit/eval/lib/crew-map/stacks.mjs +547 -0
- package/kit/eval/lib/crew-map/surface.mjs +535 -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/gate-log.sh +52 -0
- package/kit/eval/lib/json-string.awk +27 -0
- package/kit/eval/lib/preset.sh +109 -0
- package/kit/eval/lib/presets/default.json +7 -0
- package/kit/eval/lib/presets/solo.json +7 -0
- package/kit/eval/lib/presets/strict.json +11 -0
- package/kit/eval/lib/preview.sh +150 -0
- package/kit/eval/lib/review-bench/bench.mjs +273 -0
- package/kit/eval/lib/review-prepass/prepass.mjs +203 -0
- package/kit/eval/lib/review-prepass/prepass.sh +23 -0
- package/kit/eval/lib/settings-default.sh +36 -0
- package/kit/eval/lib/settings-json.awk +19 -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/verify-cmd.sh +17 -0
- package/kit/eval/lib/version.sh +41 -0
- package/kit/eval/review-bench.sh +26 -0
- package/kit/eval/smoke-test.sh +878 -53
- package/kit/hooks/agent-outcome.sh +44 -12
- package/kit/hooks/board-sync.sh +2 -23
- package/kit/hooks/guard-agent-model.sh +177 -24
- package/kit/hooks/guard-bash.sh +295 -204
- package/kit/hooks/guard-commit-scan.sh +10 -2
- package/kit/hooks/guard-question.sh +73 -0
- package/kit/hooks/guard-write.sh +24 -7
- package/kit/hooks/precompact-snapshot.sh +75 -0
- package/kit/hooks/prompt-approval.sh +389 -103
- package/kit/hooks/refuter-ledger.sh +80 -0
- package/kit/hooks/session-rehydrate.sh +25 -2
- package/kit/hooks/session-update-check.sh +9 -8
- package/kit/hooks/trace-blocklist.txt +9 -0
- package/kit/owned-blobs.tsv +4 -4
- package/kit/settings.json +51 -0
- package/kit/skills/crew-approve/SKILL.md +7 -0
- package/kit/skills/crew-code-review/SKILL.md +16 -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 +55 -0
- package/kit/skills/crew-review/SKILL.md +4 -1
- package/kit/skills/crew-ship/SKILL.md +3 -1
- 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 +25 -2
- package/kit/studio/server/index.js +187 -21
- 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/graph.js +9 -1
- package/kit/studio/server/lib/kit-telemetry.js +30 -5
- package/kit/studio/server/lib/lan.js +87 -0
- package/kit/studio/server/lib/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/redact.js +60 -0
- package/kit/studio/server/lib/remote.js +185 -0
- package/kit/studio/server/lib/session.js +1 -1
- package/kit/studio/server/lib/terminal.js +51 -13
- package/kit/studio/server/lib/transcript.js +32 -0
- package/kit/studio/server/lib/usage.js +74 -14
- package/kit/studio/web/app.js +287 -17
- package/kit/studio/web/canvas.js +58 -122
- package/kit/studio/web/chat.js +114 -24
- package/kit/studio/web/graph-plan.js +13 -6
- package/kit/studio/web/index.html +10 -5
- package/kit/studio/web/liveness.js +14 -0
- package/kit/studio/web/map-plan.js +410 -0
- package/kit/studio/web/map.js +596 -0
- package/kit/studio/web/models-plan.js +61 -7
- package/kit/studio/web/models.js +19 -3
- package/kit/studio/web/qr.js +200 -0
- package/kit/studio/web/style.css +165 -0
- package/kit/studio/web/tokens.css +6 -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 +24 -1
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# refuter-ledger.sh - the record of what crew-refuter concluded, written by the hook and not by the session.
|
|
3
|
+
#
|
|
4
|
+
# WHY. A findings document can say a finding was verified. That sentence is the session's own; the ledger is not.
|
|
5
|
+
# When a crew-refuter agent stops, this hook reads the TASK it was given (the first record of its own transcript)
|
|
6
|
+
# and the ANSWER it ended with (last_assistant_message), and adds one line when the two agree on the finding and
|
|
7
|
+
# the lens:
|
|
8
|
+
#
|
|
9
|
+
# ts session agent_id finding lens verdict .claude/state/crew-refuter.tsv
|
|
10
|
+
#
|
|
11
|
+
# kit/eval/lib/findings/findings.mjs `validate --ledger` holds a document to it: a medium-or-higher finding needs
|
|
12
|
+
# verifiers, and every verifier it names must be a line here, with the same finding, lens and verdict.
|
|
13
|
+
#
|
|
14
|
+
# Wired to SubagentStop with a matcher on the agent type, so it starts for crew-refuter only; the type is checked
|
|
15
|
+
# again here. It works whatever CREW_MODEL_ROUTING says: this is not model routing.
|
|
16
|
+
# THIS NEVER BLOCKS. It exits 0 on every path: a stop hook that failed closed would keep an agent running.
|
|
17
|
+
# The ledger is protected from the session the way the routing records are (guard-bash.sh, guard-write.sh): a plain
|
|
18
|
+
# attempt to write it is refused; a determined one (an interpreter, a name built from pieces) is not, and that is
|
|
19
|
+
# the stated scope of those guards.
|
|
20
|
+
set -uo pipefail
|
|
21
|
+
export LC_ALL=C
|
|
22
|
+
_rl_here="${BASH_SOURCE%[/\\]*}"; [ "$_rl_here" = "${BASH_SOURCE}" ] && _rl_here=.
|
|
23
|
+
[ -f "$_rl_here/guard-agent-model.sh" ] || exit 0
|
|
24
|
+
. "$_rl_here/guard-agent-model.sh"
|
|
25
|
+
declare -F _json_slice >/dev/null 2>&1 || exit 0
|
|
26
|
+
|
|
27
|
+
IFS= read -r -d '' INPUT || true
|
|
28
|
+
_json_slice "$INPUT" hook_event_name >/dev/null; [ "$_JS" = SubagentStop ] || exit 0
|
|
29
|
+
_json_slice "$INPUT" agent_type >/dev/null; [ "${_JS##*:}" = crew-refuter ] || exit 0
|
|
30
|
+
_json_slice "$INPUT" agent_id >/dev/null; AID="$_JS"
|
|
31
|
+
case "$AID" in ''|*[!A-Za-z0-9._-]*) exit 0 ;; esac
|
|
32
|
+
_json_slice "$INPUT" session_id >/dev/null; SID="$_JS"
|
|
33
|
+
case "$SID" in ''|*[!A-Za-z0-9._-]*) SID=- ;; esac
|
|
34
|
+
|
|
35
|
+
# One field of a text: the value after "<key>:" at the start of a line, first occurrence, trimmed.
|
|
36
|
+
_rl_field(){ # $1 text, $2 key (any case) -> _RLF
|
|
37
|
+
local line k; _RLF=""
|
|
38
|
+
while IFS= read -r line; do
|
|
39
|
+
line="${line%$'\r'}"; line="${line#"${line%%[![:space:]]*}"}"
|
|
40
|
+
k="${line%%:*}"; [ "$k" = "$line" ] && continue
|
|
41
|
+
shopt -s nocasematch
|
|
42
|
+
if [[ "$k" == "$2" ]]; then shopt -u nocasematch
|
|
43
|
+
_RLF="${line#*:}"; _RLF="${_RLF#"${_RLF%%[![:space:]]*}"}"; _RLF="${_RLF%"${_RLF##*[![:space:]]}"}"; return 0
|
|
44
|
+
fi
|
|
45
|
+
shopt -u nocasematch
|
|
46
|
+
done <<< "$1"
|
|
47
|
+
return 1
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
# The task: the first record of the agent's own transcript (a string, or a list whose first block holds the text).
|
|
51
|
+
_json_slice "$INPUT" agent_transcript_path >/dev/null; _json_unescape "$_JS" >/dev/null; ATP="${_JU//\\//}"
|
|
52
|
+
[ -f "$ATP" ] || exit 0
|
|
53
|
+
L1=""; IFS= read -r L1 < "$ATP" || true
|
|
54
|
+
_json_slice "$L1" content >/dev/null; TXT="$_JS"
|
|
55
|
+
[ -n "$TXT" ] || { _json_slice "$L1" text >/dev/null; TXT="$_JS"; }
|
|
56
|
+
_json_unescape "${TXT:0:8000}" >/dev/null; TASK="$_JU"
|
|
57
|
+
_rl_field "$TASK" finding || exit 0; F_TASK="$_RLF"
|
|
58
|
+
_rl_field "$TASK" lens || exit 0; L_TASK="$_RLF"
|
|
59
|
+
|
|
60
|
+
# The answer, and it must be about the same finding and lens.
|
|
61
|
+
_json_slice "$INPUT" last_assistant_message >/dev/null; _json_unescape "$_JS" >/dev/null; ANS="$_JU"
|
|
62
|
+
_rl_field "$ANS" finding || exit 0; F_ANS="$_RLF"
|
|
63
|
+
_rl_field "$ANS" lens || exit 0; L_ANS="$_RLF"
|
|
64
|
+
_rl_field "$ANS" verdict || exit 0; V="$_RLF"
|
|
65
|
+
[ "$F_TASK" = "$F_ANS" ] && [ "$L_TASK" = "$L_ANS" ] || exit 0
|
|
66
|
+
[[ "$F_TASK" =~ ^[a-z0-9][a-z0-9._:-]*$ ]] && [ "${#F_TASK}" -le 200 ] || exit 0
|
|
67
|
+
case "$L_TASK" in reachability|protection|reproduction|exclusion|blast-radius|claim) ;; *) exit 0 ;; esac
|
|
68
|
+
case "$V" in TRUE_POSITIVE|FALSE_POSITIVE|CANNOT_VERIFY) ;; *) exit 0 ;; esac
|
|
69
|
+
|
|
70
|
+
P="${CLAUDE_PROJECT_DIR:-$PWD}"; P="${P//\\//}"; P="${P%/}"
|
|
71
|
+
D="$P/.claude/state"; T="$D/crew-refuter.tsv"
|
|
72
|
+
mkdir -p "$D" 2>/dev/null || exit 0
|
|
73
|
+
# An agent that is resumed stops again: it answered once, and that answer stands.
|
|
74
|
+
if [ -f "$T" ]; then
|
|
75
|
+
while IFS=$'\t' read -r _ _ a _; do [ "$a" = "$AID" ] && exit 0; done < "$T"
|
|
76
|
+
fi
|
|
77
|
+
[ -s "$T" ] || printf 'ts\tsession\tagent_id\tfinding\tlens\tverdict\n' > "$T" 2>/dev/null
|
|
78
|
+
TS="$(date -u +%Y-%m-%dT%H:%M:%SZ 2>/dev/null)"; case "$TS" in [0-9][0-9][0-9][0-9]-*Z) ;; *) TS=- ;; esac
|
|
79
|
+
printf '%s\t%s\t%s\t%s\t%s\t%s\n' "$TS" "$SID" "$AID" "$F_TASK" "$L_TASK" "$V" >> "$T" 2>/dev/null
|
|
80
|
+
exit 0
|
|
@@ -68,11 +68,34 @@ if [ -f "$ROOT/.claude/hooks/pre-commit" ] && git -C "$ROOT" rev-parse --git-dir
|
|
|
68
68
|
esac
|
|
69
69
|
fi
|
|
70
70
|
[ -n "$HOOKMSG" ] && MSG="${MSG:+$MSG }$HOOKMSG"
|
|
71
|
-
|
|
71
|
+
|
|
72
|
+
# THE STATE BEFORE THE COMPACTION. precompact-snapshot.sh wrote it just before this context was compacted: the
|
|
73
|
+
# branch, the uncommitted files, the last gate refusal, the findings on record. Read back only after a compaction and
|
|
74
|
+
# only into the session that wrote it (same session id); its summary is already JSON-escaped, so it is put into the
|
|
75
|
+
# output as it is, and the rest of MSG stays fixed text that needs no escaping and no extra process.
|
|
76
|
+
SNAP=""
|
|
77
|
+
_rh="${BASH_SOURCE%[/\\]*}"; [ "$_rh" = "${BASH_SOURCE}" ] && _rh=.
|
|
78
|
+
if [ "$SRC" = compact ] && [ -f "$_rh/../eval/lib/gate-log.sh" ]; then
|
|
79
|
+
. "$_rh/../eval/lib/gate-log.sh"
|
|
80
|
+
_sf=""
|
|
81
|
+
if crew_git_dir "$ROOT" && [ -f "$_CREW_GD/crewforth-session-snapshot.json" ]; then _sf="$_CREW_GD/crewforth-session-snapshot.json"
|
|
82
|
+
elif [ -f "$ROOT/.claude/state/session-snapshot.json" ]; then _sf="$ROOT/.claude/state/session-snapshot.json"; fi
|
|
83
|
+
_sid(){ _SID=""; local v="${1#*\"session_id\"}"; [ "$v" = "$1" ] && return 0; v="${v#*\"}"; _SID="${v%%\"*}"; }
|
|
84
|
+
if [ -n "$_sf" ] && IFS= read -r _sj < "$_sf" 2>/dev/null; then
|
|
85
|
+
_sid "$IN"; _mine="$_SID"; _sid "$_sj"
|
|
86
|
+
case "$_sj" in *'"summary":"'*)
|
|
87
|
+
if [ -n "$_mine" ] && [ "$_mine" = "$_SID" ]; then
|
|
88
|
+
_sum="${_sj#*\"summary\":\"}"; _sum="${_sum%\"\}*}"
|
|
89
|
+
SNAP="Before this context was compacted, Crewforth wrote down the state of the work from the files and the gates: $_sum. It is a record of that moment; check the files before relying on it."
|
|
90
|
+
fi ;;
|
|
91
|
+
esac
|
|
92
|
+
fi
|
|
93
|
+
fi
|
|
94
|
+
[ -n "$MSG$SNAP" ] || exit 0 # nothing to offer -> stay silent
|
|
72
95
|
|
|
73
96
|
# hookSpecificOutput.additionalContext is the documented channel that injects text into the model's context.
|
|
74
97
|
# ONE PATH, no jq: MSG is built from fixed constants with no quote, backslash, control character or newline, so it needs no
|
|
75
98
|
# escaping and every machine emits the same bytes. If you ever make MSG dynamic, escape it the way board-sync.sh
|
|
76
99
|
# does (jq-identical, measured) — a raw quote or tab here makes JSON the CLI silently drops.
|
|
77
|
-
printf '{"hookSpecificOutput":{"hookEventName":"SessionStart","additionalContext":"%s"}}\n' "$MSG"
|
|
100
|
+
printf '{"hookSpecificOutput":{"hookEventName":"SessionStart","additionalContext":"%s%s%s"}}\n' "$MSG" "${MSG:+${SNAP:+ }}" "$SNAP"
|
|
78
101
|
exit 0
|
|
@@ -44,7 +44,9 @@
|
|
|
44
44
|
set -uo pipefail
|
|
45
45
|
# The 2.x names of the variables a user can set still work (one helper: eval/lib/crew-env.sh).
|
|
46
46
|
_crew_d="${BASH_SOURCE%/*}"; [ "$_crew_d" = "${BASH_SOURCE}" ] && _crew_d=.
|
|
47
|
-
[ -f "$_crew_d/../eval/lib/crew-env.sh" ] && . "$_crew_d/../eval/lib/crew-env.sh"
|
|
47
|
+
[ -f "$_crew_d/../eval/lib/crew-env.sh" ] && . "$_crew_d/../eval/lib/crew-env.sh"
|
|
48
|
+
# How an install's own version is read and ordered (an rc before its final): one definition, eval/lib/version.sh.
|
|
49
|
+
[ -f "$_crew_d/../eval/lib/version.sh" ] && . "$_crew_d/../eval/lib/version.sh"; unset _crew_d
|
|
48
50
|
|
|
49
51
|
URL="${CREW_UPDATE_URL:-https://registry.npmjs.org/-/package/crewforth/dist-tags}"
|
|
50
52
|
# plugin-stable, not main: the marketplace installs the plugin from that branch, and only an approved release
|
|
@@ -139,7 +141,10 @@ PJSON="$PR_ROOT/.claude-plugin/plugin.json"
|
|
|
139
141
|
# plugin's copy steps aside whenever a project install is present, instead of asking the project's question too.
|
|
140
142
|
if [ "$IS_PLUGIN" = 1 ] && [ -f "$CL/VERSION" ]; then exit 0; fi
|
|
141
143
|
if [ -f "$CL/VERSION" ]; then
|
|
142
|
-
|
|
144
|
+
# The install's own file, so an rc (X.Y.Z-rc.N) is a version here; what npm answers is still held to X.Y.Z.
|
|
145
|
+
read -r _cv < "$CL/VERSION" 2>/dev/null || _cv=""
|
|
146
|
+
declare -F crew_local_version >/dev/null || exit 0
|
|
147
|
+
CUR="$(crew_local_version "$_cv")" || exit 0
|
|
143
148
|
STATE="$CL/.state" # repo-local: the install lives in the repo
|
|
144
149
|
FEED="${CREW_UPDATE_URL:-$URL}"; KEY=latest; EDITION=project
|
|
145
150
|
ANSWER='bash .claude/hooks/session-update-check.sh --answer'
|
|
@@ -186,12 +191,8 @@ fi
|
|
|
186
191
|
[ -n "$LATEST" ] || exit 0
|
|
187
192
|
LATEST="$(sane_version "$LATEST")" || exit 0
|
|
188
193
|
|
|
189
|
-
#
|
|
190
|
-
|
|
191
|
-
awk -v a="$LATEST" -v b="$CUR" 'BEGIN{
|
|
192
|
-
split(a,x,"."); split(b,y,".");
|
|
193
|
-
for(i=1;i<=3;i++){ if(x[i]+0 > y[i]+0) exit 0; if(x[i]+0 < y[i]+0) exit 1 }
|
|
194
|
-
exit 1 }' || exit 0
|
|
194
|
+
# One awk, eval/lib/version.sh's order: 3.2.0 is newer than an installed 3.2.0-rc.1, so the rc's user is asked.
|
|
195
|
+
awk -v a="$LATEST" -v b="$CUR" "${CREW_VCMP_AWK:-}"' BEGIN { exit !(vcmp(a, b) > 0) }' 2>/dev/null || exit 0
|
|
195
196
|
|
|
196
197
|
# Skipped for good? Asked (or answered "later") within the last day?
|
|
197
198
|
if [ -f "$STATE/update-skip" ]; then read -r _sk < "$STATE/update-skip" 2>/dev/null || _sk=""; [ "${_sk//[!0-9.]/}" = "$LATEST" ] && exit 0; fi
|
|
@@ -12,6 +12,15 @@
|
|
|
12
12
|
|
|
13
13
|
Co-Authored-By
|
|
14
14
|
#test: Co-Authored-By: An Assistant <noreply@example.com>
|
|
15
|
+
# The session-link trailer Claude Code asks the model to end a commit message with: it reached six pushed commits of a
|
|
16
|
+
# project with Crewforth installed (2026-10-18), because nothing here named it. The trailer and the address are both
|
|
17
|
+
# caught, so a message that keeps only one of them is still stopped. One line, not two: each line is one grep on every
|
|
18
|
+
# commit (a process, 62-135 ms on Git Bash).
|
|
19
|
+
Claude-Session:|claude\.ai/code/session_
|
|
20
|
+
#test: Claude-Session: https://claude.ai/code/session_0123456789abcdef
|
|
21
|
+
#test: see https://claude.ai/code/session_0123456789abcdef for the run
|
|
22
|
+
#test-clean: the claude session limit is reached
|
|
23
|
+
#test-clean: https://claude.ai/code is the web app
|
|
15
24
|
# "Generated by/with" alone matched every machine-written header in existence — a pubspec.lock, an EF Core
|
|
16
25
|
# scaffold, protoc, openapi-generator. A live --dotnet install could not make its FIRST commit because the
|
|
17
26
|
# cloned base carries a Dart lockfile saying "Generated by pub". §4.1 is about AI AUTHORSHIP, so the pattern
|
package/kit/owned-blobs.tsv
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
# Generated by packaging/gen-legacy-blobs.sh from the final release tags v1.0.0..v3.1.
|
|
1
|
+
# Generated by packaging/gen-legacy-blobs.sh from the final release tags v1.0.0..v3.1.1 (below VERSION 3.2.0) — do not edit by hand.
|
|
2
2
|
# The flat files Crewforth owns: a copy whose bytes (CR aside) are one of these ids was never edited.
|
|
3
3
|
# install_path blob_sha1 first_tag last_tag
|
|
4
4
|
.claude/AGENT_TEMPLATE.md 075e2c26706c06f56af01652cd24c36918513bee v3.0.0 v3.0.4
|
|
5
5
|
.claude/AGENT_TEMPLATE.md 3fdc551d221ee3665b21ef8cc3563de861a9abe6 v1.4.0 v1.6.3
|
|
6
6
|
.claude/AGENT_TEMPLATE.md 686fed306c873b45c245c77a95a8e6de6ee383d5 v1.7.0 v2.11.0
|
|
7
|
-
.claude/AGENT_TEMPLATE.md 7ab19417b6ff3ac732e2c894be17ffb3ec091f7d v3.1.0 v3.1.
|
|
7
|
+
.claude/AGENT_TEMPLATE.md 7ab19417b6ff3ac732e2c894be17ffb3ec091f7d v3.1.0 v3.1.1
|
|
8
8
|
.claude/AGENT_TEMPLATE.md 981df9ea27b6f1c5ab5cb509d40875a07603858d v2.12.0 v2.13.0
|
|
9
9
|
.claude/AGENT_TEMPLATE.md b38b9c9b799156e07280ca886230524563acc75c v1.0.5 v1.3.0
|
|
10
10
|
.claude/AGENT_TEMPLATE.md faec09e45543b75763c43bb24114a7b6355649ec v1.0.0 v1.0.4
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
.claude/DISCIPLINE.md 76d73a4e25c559ea361618862cde8480b1a6d8c2 v3.0.0 v3.0.0
|
|
27
27
|
.claude/DISCIPLINE.md b1ab7549b571dd2fc80d5777e0ae3185d29bd7b6 v2.13.0 v2.13.0
|
|
28
28
|
.claude/DISCIPLINE.md b7e1066451a8590e0a2607ad0a6fe80b21599c53 v1.1.3 v1.1.8
|
|
29
|
-
.claude/DISCIPLINE.md b90a067ee323a08146862b889293f8feb49f8e7e v3.1.0 v3.1.
|
|
29
|
+
.claude/DISCIPLINE.md b90a067ee323a08146862b889293f8feb49f8e7e v3.1.0 v3.1.1
|
|
30
30
|
.claude/DISCIPLINE.md bf062c2b51a9cf3cc4d0caac389c148cf6fdc977 v2.12.0 v2.12.0
|
|
31
31
|
.claude/DISCIPLINE.md c693f1bd5832ce570058c6eedcacb5dff13d89e8 v1.10.0 v1.10.0
|
|
32
32
|
.claude/DISCIPLINE.md d63a01a183c9a80d739c840a511d338dc2765820 v1.4.0 v1.4.4
|
|
@@ -43,4 +43,4 @@
|
|
|
43
43
|
.claude/README.md 97d4dd1f7f1695052cc1d06cc43f5bcdc603c989 v1.0.0 v1.0.4
|
|
44
44
|
.claude/README.md a3419891cfda7385564008e4befc1007bc5364cb v1.1.1 v1.7.0
|
|
45
45
|
.claude/README.md a3b76e0fa31267610d28b742c122a962f6db59c4 v3.0.1 v3.0.4
|
|
46
|
-
.claude/README.md b6c9b57a67d25b7e4781e4eb61ff9637e6abe5ef v3.1.0 v3.1.
|
|
46
|
+
.claude/README.md b6c9b57a67d25b7e4781e4eb61ff9637e6abe5ef v3.1.0 v3.1.1
|
package/kit/settings.json
CHANGED
|
@@ -108,6 +108,17 @@
|
|
|
108
108
|
"timeout": 600
|
|
109
109
|
}
|
|
110
110
|
]
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
"matcher": "AskUserQuestion",
|
|
114
|
+
"hooks": [
|
|
115
|
+
{
|
|
116
|
+
"type": "command",
|
|
117
|
+
"shell": "bash",
|
|
118
|
+
"command": "cd \"$CLAUDE_PROJECT_DIR\" 2>/dev/null; bash .claude/hooks/guard-question.sh",
|
|
119
|
+
"timeout": 600
|
|
120
|
+
}
|
|
121
|
+
]
|
|
111
122
|
}
|
|
112
123
|
],
|
|
113
124
|
"UserPromptSubmit": [
|
|
@@ -142,6 +153,12 @@
|
|
|
142
153
|
"shell": "bash",
|
|
143
154
|
"command": "cd \"$CLAUDE_PROJECT_DIR\" 2>/dev/null; bash .claude/hooks/session-guard.sh",
|
|
144
155
|
"timeout": 60
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
"type": "command",
|
|
159
|
+
"shell": "bash",
|
|
160
|
+
"command": "cd \"$CLAUDE_PROJECT_DIR\" 2>/dev/null; bash .claude/hooks/prompt-approval.sh",
|
|
161
|
+
"timeout": 60
|
|
145
162
|
}
|
|
146
163
|
]
|
|
147
164
|
}
|
|
@@ -168,6 +185,17 @@
|
|
|
168
185
|
"timeout": 600
|
|
169
186
|
}
|
|
170
187
|
]
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
"matcher": "(.*:)?crew-refuter",
|
|
191
|
+
"hooks": [
|
|
192
|
+
{
|
|
193
|
+
"type": "command",
|
|
194
|
+
"shell": "bash",
|
|
195
|
+
"command": "cd \"$CLAUDE_PROJECT_DIR\" 2>/dev/null; bash .claude/hooks/refuter-ledger.sh",
|
|
196
|
+
"timeout": 30
|
|
197
|
+
}
|
|
198
|
+
]
|
|
171
199
|
}
|
|
172
200
|
],
|
|
173
201
|
"PostToolUse": [
|
|
@@ -181,6 +209,29 @@
|
|
|
181
209
|
"timeout": 60
|
|
182
210
|
}
|
|
183
211
|
]
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
"matcher": "AskUserQuestion",
|
|
215
|
+
"hooks": [
|
|
216
|
+
{
|
|
217
|
+
"type": "command",
|
|
218
|
+
"shell": "bash",
|
|
219
|
+
"command": "cd \"$CLAUDE_PROJECT_DIR\" 2>/dev/null; bash .claude/hooks/prompt-approval.sh",
|
|
220
|
+
"timeout": 60
|
|
221
|
+
}
|
|
222
|
+
]
|
|
223
|
+
}
|
|
224
|
+
],
|
|
225
|
+
"PreCompact": [
|
|
226
|
+
{
|
|
227
|
+
"hooks": [
|
|
228
|
+
{
|
|
229
|
+
"type": "command",
|
|
230
|
+
"shell": "bash",
|
|
231
|
+
"command": "cd \"$CLAUDE_PROJECT_DIR\" 2>/dev/null; bash .claude/hooks/precompact-snapshot.sh",
|
|
232
|
+
"timeout": 30
|
|
233
|
+
}
|
|
234
|
+
]
|
|
184
235
|
}
|
|
185
236
|
],
|
|
186
237
|
"SessionStart": [
|
|
@@ -26,4 +26,11 @@ Read the line Crewforth added to this turn and do what it says:
|
|
|
26
26
|
the argument was not `commit`, `push` or `commit+push`. Say which, and in the first case run the command and let
|
|
27
27
|
the gate's own prompt ask.
|
|
28
28
|
|
|
29
|
+
In `auto` the same approval can be given with one click: the gate's refusal prints a fixed AskUserQuestion, and
|
|
30
|
+
the user's answer is recorded exactly like this command. Where a `PermissionRequest` hook may be wired (in the
|
|
31
|
+
user's, the project's or a plugin's settings, or in a skill's or an agent's front matter), that question is closed,
|
|
32
|
+
and so is the gate's own prompt in `default` / `acceptEdits`, because such a hook answers a prompt before the user
|
|
33
|
+
does. This command is then the one approval left, in those modes too. Settings passed with `--settings` and
|
|
34
|
+
managed settings delivered by a server cannot be read, so a hook there is past the check.
|
|
35
|
+
|
|
29
36
|
Force-push and the rest of §4.5 are not opened by this.
|
|
@@ -17,6 +17,21 @@ Trigger phrases: "code-review", "review the code", "review the PR", "review my c
|
|
|
17
17
|
A review runs in four steps — **group, plan, review, fact-check** — and reports few findings that are right rather
|
|
18
18
|
than many that might be. Every step below says what it produces.
|
|
19
19
|
|
|
20
|
+
## 0. The pre-pass first, and the findings in one shape
|
|
21
|
+
Before reading the diff, run the deterministic pre-pass (`eval/lib/review-prepass/` in either edition):
|
|
22
|
+
`bash <dir>/prepass.sh` for the staged change, `--base <ref>` for a branch. It writes
|
|
23
|
+
`.crewforth/review-prepass.json`: secret patterns, a debug call or TODO left in code, shellcheck on shell files, a
|
|
24
|
+
lock file that moved without its manifest, a very large change. **Carry those over, do not re-derive them**; your
|
|
25
|
+
time goes to what a pattern cannot see. rc 3 (no Node) or a `notMeasured` line means that part was NOT checked:
|
|
26
|
+
say so in the review, never read it as clean.
|
|
27
|
+
|
|
28
|
+
Write your own findings in the same shape (`crewforth.findings/1`: `rule` `review-<category>`, `severity`,
|
|
29
|
+
`confidence`, `evidence` with `file`, `line` and a `text` quote, `detail` = what to do instead) and store them with
|
|
30
|
+
`bash <dir>/../findings/findings.sh write review <file>`. A `critical` or `high` finding first goes to
|
|
31
|
+
**crew-refuter** (claim + `file:line`, one call per lens, in parallel) and the document is written with
|
|
32
|
+
`--verified`, which then refuses any `medium`-or-higher finding without its verifiers. This is model discipline:
|
|
33
|
+
no hook runs the pre-pass or reads `review.json`.
|
|
34
|
+
|
|
20
35
|
## 1. Scope
|
|
21
36
|
- **Review the lines this change adds or edits.** Deleted code is context for understanding them, not a target.
|
|
22
37
|
- Code that is correct, or that the change did not touch, gets no comment.
|
|
@@ -142,6 +157,7 @@ five questions that decide *answer in the thread* vs *edit the code*: **`referen
|
|
|
142
157
|
item leaves with a disposition from the triage table above — a reasoned refusal is an answer, silence is not.
|
|
143
158
|
|
|
144
159
|
## DoD (this skill's contribution)
|
|
160
|
+
- The pre-pass ran (or its rc 3 / `notMeasured` is reported as unchecked), and the findings are in `.crewforth/review.json`.
|
|
145
161
|
- The plan (what changed, the risks) came before the findings; every file in each group was read.
|
|
146
162
|
- Every finding has a severity, a category and a reason, in the finding format above.
|
|
147
163
|
- Scope creep and hidden complexity are flagged.
|
|
@@ -24,7 +24,7 @@ Verify Crewforth is actually *active* in this project (not just present on disk)
|
|
|
24
24
|
5. Summarise: **healthy**, or the precise fixes applied/needed, plus the readiness score. If it's not a git repo,
|
|
25
25
|
note that the commit-time gates need `git init` + `git config core.hooksPath .claude/hooks`.
|
|
26
26
|
If the output contains a line starting with ⭐, pass it to the user verbatim as the LAST line of your summary —
|
|
27
|
-
it prints once per Crewforth version, and it is meant for the user, not for you.
|
|
27
|
+
it prints once per Crewforth version here (the installer has its own once), and it is meant for the user, not for you.
|
|
28
28
|
|
|
29
29
|
If `.claude/eval/doctor.sh` doesn't exist, this is not a full (start.sh / adopt.sh) install — Crewforth is likely
|
|
30
30
|
running as a **plugin**, whose hooks are managed by Claude Code itself; there's nothing for the doctor to check,
|
|
@@ -52,3 +52,7 @@ found no Git Bash: tell the user the gates do not run and that the fix is Git fo
|
|
|
52
52
|
scripts are developer instruments: they inspect an installation from outside it, and the plugin edition has no
|
|
53
53
|
installation to inspect. Say this plainly when someone asks why `/crew-doctor` reports only the listing (and, on Windows, Git Bash) on a plugin install,
|
|
54
54
|
rather than treating it as a defect. The Studio panel ships in both editions (`/crew-studio`).
|
|
55
|
+
|
|
56
|
+
**On a plugin install, end with the star line** the full install's doctor prints: `bash "${CLAUDE_PLUGIN_ROOT}/eval/lib/star.sh" --once . doctor`
|
|
57
|
+
(Bash tool, not PowerShell). It prints once per plugin version, or nothing; if it prints a line starting with ⭐, pass it to the
|
|
58
|
+
user verbatim as the LAST line of your summary.
|
|
@@ -6,8 +6,9 @@ metadata:
|
|
|
6
6
|
---
|
|
7
7
|
# /crew-handoff
|
|
8
8
|
applies the `handoff` skill:
|
|
9
|
-
1.
|
|
10
|
-
|
|
9
|
+
1. **crew-session-manager** (@agent-crew-session-manager) measures the fill (`context-usage.sh`, never a guess), gives the
|
|
10
|
+
recommendation and drafts the handover. The steps below work from its draft.
|
|
11
|
+
2. Write the handover to `docs/SESSION_STATE.md`: Done · In progress (exactly where) · Next step · Open decisions · File pointers · Blockers.
|
|
11
12
|
3. Preserve decision rationale; the next session should not start from scratch.
|
|
12
13
|
4. **Cold-reader pass** (`handoff/references/cold-reader.md`): the questions are written from the work BEFORE the
|
|
13
14
|
file, then answered from the file alone. Anything unanswerable goes back into the handover and is re-checked.
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: crew-map
|
|
3
|
+
description: Map the project — modules, what they touch, the gaps between them — and describe each module from the lines the scan cites.
|
|
4
|
+
disable-model-invocation: true
|
|
5
|
+
metadata:
|
|
6
|
+
kind: command
|
|
7
|
+
---
|
|
8
|
+
# /crew-map
|
|
9
|
+
|
|
10
|
+
Write `.crewforth/crew-map.json`, then describe it. The scanner draws the map from the files; you add only what
|
|
11
|
+
it cannot: a sentence or two on what each module is for, and a short overview. Every description points at lines
|
|
12
|
+
the scan itself read, and a gate holds it to that. **Never commit.** Show the diff and stop.
|
|
13
|
+
|
|
14
|
+
1. **Resolve the scanner.** Both editions carry it, in different places:
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
PLUGIN_MAP=${CLAUDE_PLUGIN_ROOT}/eval/lib/crew-map/crew-map.sh
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Claude Code replaces `${CLAUDE_PLUGIN_ROOT}` in this text before you read it, and only in the plugin. If the
|
|
21
|
+
line above names a real path, that is the scanner; if it still shows `${CLAUDE_PLUGIN_ROOT}`, this is a full
|
|
22
|
+
install and the scanner is `.claude/eval/lib/crew-map/crew-map.sh`. Below, `MAP` is whichever it is.
|
|
23
|
+
|
|
24
|
+
2. **Scan:** `bash MAP` at the project root (Bash tool, not PowerShell). It writes the map and keeps the
|
|
25
|
+
descriptions of an earlier run that still cite only lines this scan cites; it says how many it kept and dropped.
|
|
26
|
+
rc 3 is no usable Node: say so and stop, it is not a pass.
|
|
27
|
+
|
|
28
|
+
3. **Describe.** Read `.crewforth/crew-map.json`, and as much of the cited source as you need. Then edit that
|
|
29
|
+
file, changing ONLY these fields:
|
|
30
|
+
- each `module` node's `narrative`: `{ "text": "…", "cites": ["file:line", …], "layer": "…", "domain": "…" }`.
|
|
31
|
+
What the module is for, in at most two sentences. Every cite is a `file:line` the scan read for THAT node: its
|
|
32
|
+
`evidence`, its `entryPoint`, or its `about` (the first prose line of its README); cite at least one. `layer` is where it sits, one of `ui` (what a person uses: an app, a site),
|
|
33
|
+
`api` (serves requests to others), `service` (runs work: a worker, a job), `data` (holds or shapes data),
|
|
34
|
+
`infra` (deploys and runs the rest), `tool` (a script or CLI for the team), `library` (code other modules
|
|
35
|
+
import); `domain` is the part of the business it serves, a short phrase ("payments", "mail import"). Both
|
|
36
|
+
are optional and rest on the same cites: leave one out rather than guess. Leave a node `null` when its
|
|
37
|
+
evidence does not tell you what it is for: an honest gap beats a guess.
|
|
38
|
+
- the document's `overview`: the same shape, at most 2000 characters, for someone new to the project: the entry
|
|
39
|
+
points, where data goes, the risky places. Its cites may be any node's evidence.
|
|
40
|
+
- the document's `tour`: `[{ "node": "<id>", "text": "…", "cites": ["file:line"] }, …]`, one to eight stops a
|
|
41
|
+
newcomer reads in order, usually the entry point first and the riskiest place last. Each stop names a node of
|
|
42
|
+
the map, says in at most two sentences why to look there, and cites lines from that node's own evidence.
|
|
43
|
+
Describe what the cited lines show. A connection, a purpose or a risk the scan did not find is not added here:
|
|
44
|
+
say it to the user instead.
|
|
45
|
+
|
|
46
|
+
4. **Validate:** `bash MAP --validate` (Bash tool, not PowerShell). It refuses a description that cites a line
|
|
47
|
+
the scan did not read, cites nothing, or runs past two sentences; a layer not on the list or a domain written
|
|
48
|
+
as a sentence; a tour stop at no node, citing another node's lines, or at a node twice; and a map whose
|
|
49
|
+
scanner fields were changed.
|
|
50
|
+
Fix what it names, in the descriptions only, until it holds.
|
|
51
|
+
|
|
52
|
+
5. **Hand over:** the scan's summary line, how many modules you described (and gave a layer) and how many you
|
|
53
|
+
left `null` and why, the tour's stops,
|
|
54
|
+
then `git diff --stat .crewforth/crew-map.json`. Studio's **Map** view (`/crew-studio`) draws the result.
|
|
55
|
+
Committing it is the user's call.
|
|
@@ -9,12 +9,15 @@ Run the change set through the review trio (read-only). **The audits go out in p
|
|
|
9
9
|
1. **At once, in ONE message** — several `Agent` calls, because none of these writes code and so there is
|
|
10
10
|
nothing to serialise:
|
|
11
11
|
- @agent-crew-security-expert (security-scan) — auth/IDOR/injection/secret; findings with severity.
|
|
12
|
+
Each `medium`-or-higher one then goes to **crew-refuter** (@agent-crew-refuter; claim + `file:line`, one call per lens, in
|
|
13
|
+
parallel) before it is reported; `findings.sh write --verified` refuses one its ledger does not back.
|
|
12
14
|
- @agent-crew-performance-expert (`performance`) — hot path, query/loop, render, payload. Reports
|
|
13
15
|
**candidates** (reasoned) and **findings** (measured) separately; an unmeasured claim is never a verdict.
|
|
14
16
|
- @agent-crew-privacy-agent (`privacy-compliance`) — **when the diff touches personal data**: legal basis,
|
|
15
17
|
minimisation, retention, transfer.
|
|
16
18
|
2. (if SonarQube is in use) **sonarqube-check** — 0/0/0/0 gate (language-agnostic).
|
|
17
|
-
3. **Last, once 1-2 leave no blocker:** @agent-crew-review-agent (crew-code-review) —
|
|
19
|
+
3. **Last, once 1-2 leave no blocker:** @agent-crew-review-agent (crew-code-review) — it starts from the
|
|
20
|
+
deterministic pre-pass (`.crewforth/review-prepass.json`) and writes `.crewforth/review.json`; "does it improve overall
|
|
18
21
|
code health": the plan (what changed, the risks), then findings with a severity and a category. "Clean" means
|
|
19
22
|
what crew-code-review means by it: **no critical or high finding open**. A medium or low audit finding does not
|
|
20
23
|
hold the reviewer back; hand it over in the reviewer's prompt and keep it in the report. A critical or high one
|
|
@@ -9,7 +9,9 @@ Closure flow — in order:
|
|
|
9
9
|
1. **DoD gate:** was `/simplify` applied · are tests green (@agent-crew-test-expert) · (if SonarQube is used) `sonarqube-check` 0/0/0/0.
|
|
10
10
|
If any one is red, **STOP**, report what is missing; no deferral.
|
|
11
11
|
2. If clean, @agent-crew-commit-agent (commit-message) **proposes** an atomic Conventional Commit from the staged diff.
|
|
12
|
-
3. Commit runs ONLY if the user says "commit" (§4.4). "Done" is not approval.
|
|
12
|
+
3. Commit runs ONLY if the user says "commit" (§4.4). "Done" is not approval. In `auto`, Crewforth hands the session the
|
|
13
|
+
one-click approval question when crew-commit-agent is called: ask it with the message; the typed `/crew-approve` is
|
|
14
|
+
for when no question is offered.
|
|
13
15
|
4. **Close the board item too** (`teamboard`), when one is held. Closing work is one movement: ask what shipped,
|
|
14
16
|
run `board.sh done <id> "<note>"`, name the items it unblocked, and offer the next one as options. Do not
|
|
15
17
|
leave this to the user to remember — a claim nobody closes reads to the whole team as still in progress, and
|
|
@@ -32,7 +32,7 @@ Bring the Crewforth install up to the latest published version:
|
|
|
32
32
|
(no flags) in **their own terminal**, where both the npx and the interactive prompts work.
|
|
33
33
|
7. **After:** run `bash .claude/eval/update-guard.sh post` (Bash tool, not PowerShell) and show the user its lists — added, changed, moved,
|
|
34
34
|
removed.
|
|
35
|
-
8. **Verify:** run
|
|
35
|
+
8. **Verify:** run `CREW_NO_STAR=1 bash .claude/eval/doctor.sh` (Bash tool, not PowerShell) so a bad/partial update surfaces immediately; the updater has already shown the star line, if it was due.
|
|
36
36
|
If the updater's or doctor's output contains a line starting with ⭐, pass it to the user verbatim as the LAST
|
|
37
37
|
line of your summary — it prints once per Crewforth version, and it is meant for the user, not for you.
|
|
38
38
|
9. **Report** old → new + the headline changes. Take them from `.claude/.state/whats-new.md` — the updater writes
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: incident-runbook
|
|
3
3
|
description: |
|
|
4
|
-
|
|
4
|
+
Incident response: diagnose → mitigate → resolve, then a blameless postmortem and a repeatable
|
|
5
5
|
runbook. Stop the impact first, root cause second.
|
|
6
|
-
Use when production is degraded or broken,
|
|
6
|
+
Use when production is degraded or broken, an outage keeps recurring, or for the postmortem.
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
# Incident Response & Runbook
|
|
@@ -19,9 +19,12 @@ audit). Each verifier:
|
|
|
19
19
|
Three passes inside one context window are not three verifiers. The second pass has already read the first one's
|
|
20
20
|
reasoning; the blindness above is then a wish, not a property, and the agreement it produces is one argument
|
|
21
21
|
counted three times. So:
|
|
22
|
-
- **Each verifier is its own
|
|
23
|
-
context window: it cannot see what the others concluded, which is the whole point. Read-only work,
|
|
24
|
-
`worktree` is needed — nothing is being written.
|
|
22
|
+
- **Each verifier is its own `crew-refuter` call** (one call per verifier and lens, run in parallel). A subagent
|
|
23
|
+
has its own context window: it cannot see what the others concluded, which is the whole point. Read-only work,
|
|
24
|
+
so no `worktree` is needed — nothing is being written. Its task is the card, then `finding:`, `lens:`, `cite:`
|
|
25
|
+
and `claim:` (its agent file has the exact form); when it stops, `hooks/refuter-ledger.sh` records the verdict
|
|
26
|
+
in the refuter ledger, and `findings.sh write --verified` refuses a `medium`-or-higher finding whose verifiers
|
|
27
|
+
are not there. The ledger is the gate; the prompt that carries no reasoning is still discipline.
|
|
25
28
|
- **Give it the finding and the file:line, never your reasoning.** Not "I think X is exploitable because Y" —
|
|
26
29
|
just "claim: X at file:line. Try to refute it."
|
|
27
30
|
- **If you did not isolate, say so.** Write `verifiers: 1 (single pass, not isolated)` in the verdict block. A
|
package/kit/studio/README.md
CHANGED
|
@@ -198,6 +198,27 @@ reported as unreachable, never as empty.
|
|
|
198
198
|
Nothing listens on a network interface in this arrangement: both Studios stay on
|
|
199
199
|
loopback and the tunnel does the crossing.
|
|
200
200
|
|
|
201
|
+
## Remote viewers
|
|
202
|
+
|
|
203
|
+
A viewer is a second token that reads and changes nothing, for watching the
|
|
204
|
+
panel from a phone on the same Wi-Fi without handing it the panel. Add one in
|
|
205
|
+
the Machines tab; its link is shown once, as a QR code (`web/qr.js`, made in the
|
|
206
|
+
page). The server refuses a viewer's every request that is not a `GET`, and its
|
|
207
|
+
reads of `/api/remote`, with 403 before any handler runs (`server/lib/remote.js`).
|
|
208
|
+
A viewer lasts eight hours, then its link answers 401 and the page says Session
|
|
209
|
+
expired. Revoke ends the token at once and closes a stream it holds open.
|
|
210
|
+
Viewers live in memory and end with the server.
|
|
211
|
+
|
|
212
|
+
Transcripts are hidden unless the viewer was added with them shown: the routes
|
|
213
|
+
that are only transcript are refused, and every other answer, the event stream
|
|
214
|
+
included, passes through `server/lib/redact.js`, which drops every string whose
|
|
215
|
+
key is not on its allow-list.
|
|
216
|
+
|
|
217
|
+
While a viewer is live the server also listens on the machine's private Wi-Fi
|
|
218
|
+
address (`server/lib/lan.js`), and there it takes a viewer's token only, never
|
|
219
|
+
the panel's. With none, it is on loopback alone. Every viewer connection and
|
|
220
|
+
refusal is logged (`GET /api/remote`, and the server's output).
|
|
221
|
+
|
|
201
222
|
## What Crewforth already measured
|
|
202
223
|
|
|
203
224
|
The panel does not recompute any of it. Selecting the session node opens what
|
|
@@ -249,8 +270,10 @@ channel that does not exist.
|
|
|
249
270
|
|
|
250
271
|
## Security
|
|
251
272
|
|
|
252
|
-
The server binds to `127.0.0.1
|
|
253
|
-
|
|
273
|
+
The server binds to `127.0.0.1`, and the host is deliberately not configurable —
|
|
274
|
+
it reads a developer's live sessions and can start new ones. The one exception is
|
|
275
|
+
the Wi-Fi listener above: open only while a viewer is live, for viewer tokens
|
|
276
|
+
only, read-only.
|
|
254
277
|
|
|
255
278
|
A token is always required for `/api/`. Supply one, or let the server generate
|
|
256
279
|
one and print it with the URL:
|