crewforth 3.5.0-rc.1 โ 3.5.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 +32 -0
- package/README.md +1 -1
- package/VERSION +1 -1
- package/kit/CLAUDE.md +1 -1
- package/kit/eval/lib/statusline.sh +22 -5
- package/kit/eval/lib/todo.awk +167 -0
- package/kit/eval/lib/todo.sh +120 -0
- package/kit/eval/smoke-test.sh +60 -2
- package/kit/hooks/prompt-approval.sh +5 -5
- package/kit/skills/crew-plan/SKILL.md +2 -1
- package/kit/skills/crew-todo/SKILL.md +31 -0
- package/kit/skills/spec-planning/SKILL.md +9 -2
- package/kit/studio/web/agent-spend.js +32 -0
- package/kit/studio/web/app.js +245 -236
- package/kit/studio/web/approvals.js +14 -13
- package/kit/studio/web/ask.js +10 -9
- package/kit/studio/web/canvas.js +56 -54
- package/kit/studio/web/chat.js +41 -41
- package/kit/studio/web/convo.js +21 -18
- package/kit/studio/web/dock.js +20 -19
- package/kit/studio/web/graph-plan.js +62 -22
- package/kit/studio/web/i18n-tr.js +760 -0
- package/kit/studio/web/i18n.js +79 -0
- package/kit/studio/web/index.html +1 -0
- package/kit/studio/web/inspect.js +27 -24
- package/kit/studio/web/list-plan.js +16 -15
- package/kit/studio/web/list.js +6 -5
- package/kit/studio/web/liveness.js +11 -9
- package/kit/studio/web/map-plan.js +33 -29
- package/kit/studio/web/map.js +74 -73
- package/kit/studio/web/models-plan.js +47 -42
- package/kit/studio/web/models.js +21 -20
- package/kit/studio/web/names.js +13 -10
- package/kit/studio/web/nav.js +34 -33
- package/kit/studio/web/newsession.js +25 -24
- package/kit/studio/web/style.css +2 -0
- package/kit/studio/web/theme.js +5 -2
- package/kit/studio/web/timeline-plan.js +19 -18
- package/kit/studio/web/timeline.js +23 -22
- package/kit/studio/web/usage.js +36 -32
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -34,6 +34,13 @@ versioning follows [SemVer](https://semver.org/).
|
|
|
34
34
|
|
|
35
35
|
### Added
|
|
36
36
|
|
|
37
|
+
- **`/crew-todo` shows the plan as a to-do list under the status line.** The status line ends with the plan's count
|
|
38
|
+
(`๐ 7/12`); the command opens a panel under it and closes it again: the plan's phases and tasks, done ones ticked,
|
|
39
|
+
open ones boxed, the phase and the task in work marked, cut to a window around the task in work (`CREW_TODO_LINES`).
|
|
40
|
+
It reads the plan's own boxes (`- [ ]`, `- [x]`, `- [~]` in work) under its headings and never writes the plan;
|
|
41
|
+
`spec-planning` now gives every task a box and tells whoever works the plan to tick it when its check passes.
|
|
42
|
+
`/crew-todo <file>` reads another plan, `/crew-todo show` prints the panel in the chat. The drawing is cached
|
|
43
|
+
until the plan changes, so a status line refresh starts no process for it.
|
|
37
44
|
- **A compaction no longer loses what the user asked for.** The PreCompact snapshot keeps the user's first and last
|
|
38
45
|
three prompts and the skills used in the session, and gives them back after the compaction, in the same session
|
|
39
46
|
only; secrets and `<private>` text are left out. The `handoff` template carries the user's words, the dead ends
|
|
@@ -127,6 +134,11 @@ versioning follows [SemVer](https://semver.org/).
|
|
|
127
134
|
- **Studio shows what a session it started has changed.** The strip above the conversation says how many files
|
|
128
135
|
changed since the session started (lines added and removed) and how many new files git does not track yet,
|
|
129
136
|
counted against the commit recorded at the start. It is read without git rewriting the index and kept in memory.
|
|
137
|
+
- **Studio speaks Turkish.** The **TR** button in the top bar, beside the theme button, puts the panel in Turkish
|
|
138
|
+
and **EN** puts it back; English stays the default, and the choice is kept per browser. What Studio shows from
|
|
139
|
+
elsewhere (the conversation, agent and tool names, a reason the server gave) stays as it was written. The Studio
|
|
140
|
+
gate fails when a sentence on screen has no Turkish, when a translation is left over, or when the two disagree
|
|
141
|
+
on a `{slot}`.
|
|
130
142
|
- **`/crew-doctor` names a linked worktree with no `.claude/settings.json`**, where a resumed session runs none of
|
|
131
143
|
Crewforth's hooks. One Claude Code makes under `.claude/worktrees/` is not named: it still runs this checkout's.
|
|
132
144
|
- **`npx crewforth remove` takes Crewforth out of a project.** By default it lists what it would change and changes
|
|
@@ -140,6 +152,11 @@ versioning follows [SemVer](https://semver.org/).
|
|
|
140
152
|
A `CREW_` variable set in the environment that no Crewforth file reads (a typo) is named, and so is a switch that
|
|
141
153
|
takes a whole number set to something else.
|
|
142
154
|
|
|
155
|
+
- **Studio's summary box says what each kind of agent spent.** Open **Agents** and, as **Cost** does for models, it
|
|
156
|
+
lists one line per agent type (`Backend`, `Test ร2`, โฆ) with its new tokens and estimated cost, the most spent
|
|
157
|
+
first; a type that ran more than once shows how many times. Without an agent to break down, the row still opens on
|
|
158
|
+
the token kinds.
|
|
159
|
+
|
|
143
160
|
### Changed
|
|
144
161
|
|
|
145
162
|
- **`dependency-audit` asks which registry answers an internal name** (scope mappings, `--index-url` against
|
|
@@ -161,6 +178,11 @@ versioning follows [SemVer](https://semver.org/).
|
|
|
161
178
|
- **Studio's Map spreads lines that share a side of a card**, so a module with three dependents shows three lines
|
|
162
179
|
instead of one. Measured first on the crew-map test fixtures and Crewforth's own map: 48 line ends shared a pixel
|
|
163
180
|
across 46 edges; now none do. Sideways lines between ends at two heights turn square instead of running slanted.
|
|
181
|
+
- **Studio's graph uses both sides of a wide window.** The session stands in the middle: the agents that build
|
|
182
|
+
(backend, frontend, database, test, devops, performance) grow to its left and every other agent to its right, so
|
|
183
|
+
one tall column beside an empty half of the pane becomes two short ones. The side follows the agent's type, never
|
|
184
|
+
the count, so an agent that arrives moves nothing already drawn. A narrow window keeps the left-to-right tree, and
|
|
185
|
+
**Order** keeps its waves.
|
|
164
186
|
- **Frontend work checks more of what users hit.** `a11y` separates criteria from conventions (a failed A/AA
|
|
165
187
|
criterion is a finding, AAA or a 44 px target a recommendation), adds a skip link, flashing, an error summary and
|
|
166
188
|
positive `tabindex`, a table of patterns to grep before finishing, and recipes for focus, forms and which
|
|
@@ -230,6 +252,16 @@ versioning follows [SemVer](https://semver.org/).
|
|
|
230
252
|
|
|
231
253
|
### Fixed
|
|
232
254
|
|
|
255
|
+
- **A session no longer tells the user that a command is not installed when it only cannot see it.** The commands
|
|
256
|
+
marked user-only (`/crew-interview`, `/crew-approve`, `/crew-pr`โฆ) are left out of the model's skill list, so a
|
|
257
|
+
session asked about `/crew-interview` answered that no such command existed, while the files were in
|
|
258
|
+
`.claude/skills`. The discipline now says so in one sentence and points at `ls .claude/skills`.
|
|
259
|
+
- **An approval refused in a background session now says so.** Claude Code reports a session started with
|
|
260
|
+
`claude --bg`, or from the agent view, as unattended (`CLAUDE_CODE_SESSION_ATTENDED=0`) even when the user attaches
|
|
261
|
+
and types into it, so `/crew-approve` was refused with "started with -p, or by a program" and no way on. Measured on
|
|
262
|
+
2.1.319: a background session and `claude -p` both report 0, and a prompt started by a program in the background is
|
|
263
|
+
recorded as a human one, so the gate cannot tell them apart and stays closed. The refusal now names the background
|
|
264
|
+
case and the two ways on: a regular session, or the commit in the user's own terminal.
|
|
233
265
|
- **`adopt` refuses a `settings.json` whose hook entries Claude Code would reject.** Claude Code ignores the whole
|
|
234
266
|
file when one hook entry has the wrong shape, so merging Crewforth's hooks into it installed gates that never ran.
|
|
235
267
|
adopt now names the entries and stops the merge, and the merged result is checked the same way before it is
|
package/README.md
CHANGED
|
@@ -32,7 +32,7 @@ Then open Claude Code and run `/crew-doctor` to confirm the setup. Requires bash
|
|
|
32
32
|
|
|
33
33
|
- **14 specialist agents**, from `crew-planner` to `crew-commit-agent`, each owning one domain.
|
|
34
34
|
- **40 skills** holding the method: testing, migrations, API contracts, observability, accessibility, deployment.
|
|
35
|
-
- **
|
|
35
|
+
- **18 commands**, each started with its `/crew-โฆ` name: `/crew-plan`, `/crew-review`, `/crew-ship`, `/crew-handoff`, `/crew-brainstorm`, `/crew-update`, `/crew-doctor`, `/crew-gates`, `/crew-skill`, `/crew-studio`, `/crew-approve`, `/crew-loosen`, `/crew-allow-verify`, `/crew-map`, `/crew-audit`, `/crew-interview`, `/crew-pr`, `/crew-todo`.
|
|
36
36
|
- In `auto` mode a commit or a push waits for you: type `/crew-approve` and what was just refused is approved, tied to what is staged.
|
|
37
37
|
- A stopped destructive command says what it would have touched; a review starts from a deterministic pre-pass, and `crew-refuter` tries to refute each finding that blocks.
|
|
38
38
|
- **Organisation presets.** `npx crewforth init --preset strict` (or `solo`, or your own JSON file) carries critical paths, model floors and verify commands to every project.
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
3.5.0-rc.
|
|
1
|
+
3.5.0-rc.2
|
package/kit/CLAUDE.md
CHANGED
|
@@ -46,7 +46,7 @@ What the user sees โ **crew-frontend-expert** ยท server behaviour โ **crew-b
|
|
|
46
46
|
**crew-privacy-agent** (personal data) ยท **crew-test-expert** ยท **crew-performance-expert** ยท docs/legal/store
|
|
47
47
|
text โ **crew-docs-agent** ยท what to build โ
|
|
48
48
|
**crew-planner** ยท unknown cause โ **general-purpose** + `systematic-debugging`. Two domains โ delegate in
|
|
49
|
-
sequence. All are installed: "no owner" is an `ls`.
|
|
49
|
+
sequence. All are installed: "no owner" is an `ls`. The user-only commands (`/crew-interview`, `/crew-approve`โฆ) are missing from your skill list by design, not uninstalled: `ls .claude/skills`.
|
|
50
50
|
|
|
51
51
|
1. **Diagnose, then plan** โ unknown cause โ `systematic-debugging` first; unclear scope โ **crew-planner** (`/crew-plan`).
|
|
52
52
|
2. **Produce** โ the domain owner above.
|
|
@@ -12,11 +12,14 @@
|
|
|
12
12
|
# โ crew agents started in this session that have not stopped yet (model routing's records)
|
|
13
13
|
# ๐ the context fill Claude Code measured (context_window.used_percentage), never an estimate
|
|
14
14
|
# ~$ what Claude Code puts the session's cost at (cost.total_cost_usd), at API list price
|
|
15
|
+
# ๐ the plan's count of done tasks (docs/PLAN.md, todo.sh); with the panel open (/crew-todo), the panel itself is
|
|
16
|
+
# drawn under the line instead: phases and tasks, โ done, โ open, โธ the phase and the task in work
|
|
15
17
|
#
|
|
16
18
|
# Wired by the installers only where no status line is set already (theirs is never replaced), from
|
|
17
19
|
# statusline.settings.json beside this file.
|
|
18
20
|
# 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.
|
|
21
|
+
# gate log), and on bash older than 4.2 one date; the rest is builtins. No git, no jq, no python. The plan panel
|
|
22
|
+
# adds one awk only when the plan (or its drawing code) changed since the last line; otherwise it reads its cache.
|
|
20
23
|
set -u
|
|
21
24
|
_here="${BASH_SOURCE%[/\\]*}"; [ "$_here" = "${BASH_SOURCE}" ] && _here=.
|
|
22
25
|
|
|
@@ -93,8 +96,22 @@ case "${COST:-}" in ''|null|*[!0-9.]*) ;; *)
|
|
|
93
96
|
printf -v c '%.2f' "$COST" 2>/dev/null && OUT+=("~\$$c") ;;
|
|
94
97
|
esac
|
|
95
98
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
99
|
+
# ๐ The plan: its count on the line, or with the panel open (/crew-todo), the panel under it.
|
|
100
|
+
PANEL=""
|
|
101
|
+
if [ -d .claude ] && [ -f "$_here/todo.sh" ]; then
|
|
102
|
+
. "$_here/todo.sh"
|
|
103
|
+
if [ -e .claude/state/todo/open ]; then
|
|
104
|
+
col=1; [ -n "${NO_COLOR:-}" ] && col=0
|
|
105
|
+
crew_todo_read "$col"; PANEL="$_CREW_TODO_PANEL"
|
|
106
|
+
elif [ -f docs/PLAN.md ] || [ -s .claude/state/todo/path ]; then
|
|
107
|
+
crew_todo_read 1; [ -n "$_CREW_TODO_HINT" ] && OUT+=("$_CREW_TODO_HINT")
|
|
108
|
+
fi
|
|
109
|
+
fi
|
|
110
|
+
|
|
111
|
+
if [ "${#OUT[@]}" -gt 0 ]; then
|
|
112
|
+
line="${OUT[0]}"
|
|
113
|
+
for p in "${OUT[@]:1}"; do line="$line ยท $p"; done
|
|
114
|
+
printf '%s\n' "$line"
|
|
115
|
+
fi
|
|
116
|
+
[ -n "$PANEL" ] && printf '%s\n' "$PANEL"
|
|
100
117
|
exit 0
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# todo.awk โ reads a plan (docs/PLAN.md by default) and draws its to-do panel: the phases and the tasks, a tick on
|
|
2
|
+
# what is done, an empty box on what is not, and the phase and the task in work marked.
|
|
3
|
+
#
|
|
4
|
+
# LC_ALL=C awk -v color=1 -v maxl=14 -v name=docs/PLAN.md -f todo.awk docs/PLAN.md
|
|
5
|
+
#
|
|
6
|
+
# Run it under LC_ALL=C: lengths are counted in bytes, so a cut never depends on the locale (and is mended so it never
|
|
7
|
+
# splits a UTF-8 character).
|
|
8
|
+
#
|
|
9
|
+
# What it reads, and nothing else:
|
|
10
|
+
# a task a list item whose text starts with a box: `- [ ] โฆ` `- [x] โฆ` `1. [ ] โฆ` (at the margin)
|
|
11
|
+
# [ ] open ยท [x] done ยท [~] or [>] in work. A box indented under a task is a sub-step, not a task.
|
|
12
|
+
# a phase a heading (## or deeper) with at least one task under it.
|
|
13
|
+
# the scope when the plan has a `## Tasks` heading (or Gรถrevler, ฤฐลler, To-do), only what is under it; else the
|
|
14
|
+
# whole file. An `Acceptance criteria` section is never a phase: its boxes are counted as AC n/m.
|
|
15
|
+
# code blocks are skipped (a template quoted in the plan is not the plan).
|
|
16
|
+
# The task in work is the first one marked [~]/[>]; with none marked, the first open one. Its phase is the active one.
|
|
17
|
+
#
|
|
18
|
+
# Output: line 1 the short count for the status line ("๐ 7/12"), or nothing when the plan has no task; then the panel.
|
|
19
|
+
# It decides nothing and changes nothing.
|
|
20
|
+
BEGIN {
|
|
21
|
+
if (maxl + 0 < 6) maxl = 14
|
|
22
|
+
wmax = 64
|
|
23
|
+
E = sprintf("%c", 27)
|
|
24
|
+
if (color + 0) { DIM = E "[2m"; BOLD = E "[1m"; ACT = E "[1;36m"; RST = E "[0m" } else { DIM = BOLD = ACT = RST = "" }
|
|
25
|
+
for (i = 128; i < 192; i++) CONT = CONT sprintf("%c", i)
|
|
26
|
+
for (i = 192; i < 256; i++) LEAD = LEAD sprintf("%c", i)
|
|
27
|
+
TICK = "โ"; BOX = "โ"; HERE = "โธ"
|
|
28
|
+
nev = 0; fence = ""
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
# A fenced code block: skipped whole.
|
|
32
|
+
/^[ ]*(```|~~~)/ {
|
|
33
|
+
f = $0; sub(/^[ ]*/, "", f); f = substr(f, 1, 3)
|
|
34
|
+
if (fence == "") fence = f; else if (f == fence) fence = ""
|
|
35
|
+
next
|
|
36
|
+
}
|
|
37
|
+
fence != "" { next }
|
|
38
|
+
|
|
39
|
+
/^#+[ \t]/ {
|
|
40
|
+
t = $0; match(t, /^#+/); lv = RLENGTH
|
|
41
|
+
t = substr(t, lv + 1); sub(/^[ \t]+/, "", t); sub(/[ \t]+#*[ \t]*$/, "", t); sub(/\r$/, "", t)
|
|
42
|
+
nev++; EV[nev] = "H"; LV[nev] = lv; TX[nev] = t
|
|
43
|
+
next
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
{
|
|
47
|
+
line = $0; sub(/\r$/, "", line)
|
|
48
|
+
match(line, /^ */); ind = RLENGTH
|
|
49
|
+
if (ind > 1) next
|
|
50
|
+
rest = substr(line, ind + 1)
|
|
51
|
+
if (match(rest, /^[-*+][ \t]+\[[ xX~>]\][ \t]+/)) num = ""
|
|
52
|
+
else if (match(rest, /^[0-9]+[.)][ \t]+\[[ xX~>]\][ \t]+/)) { num = rest; sub(/[.)].*/, "", num) }
|
|
53
|
+
else next
|
|
54
|
+
head = substr(rest, 1, RLENGTH); txt = substr(rest, RLENGTH + 1)
|
|
55
|
+
b = head; sub(/^[^[]*\[/, "", b); b = substr(b, 1, 1)
|
|
56
|
+
st = (b == "x" || b == "X") ? "d" : (b == "~" || b == ">") ? "w" : "o"
|
|
57
|
+
# The task's name is what comes before its first " โ " (the plan's fields: satisfies, size, riskโฆ).
|
|
58
|
+
k = index(txt, " โ "); if (k > 0) txt = substr(txt, 1, k - 1)
|
|
59
|
+
sub(/[ \t]+$/, "", txt)
|
|
60
|
+
if (num != "") txt = num ". " txt
|
|
61
|
+
nev++; EV[nev] = "I"; ST[nev] = st; TX[nev] = txt
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function isac(t) { return t ~ /^([Aa]cceptance [Cc]riteria|[Kk]abul [Kk]riter)/ }
|
|
65
|
+
function istasks(t) { return t ~ /^([Tt]asks|TASKS|[Gg]รถrevler|GรREVLER|[ฤฐi]ลler|ฤฐลLER|[Tt]o-?[Dd]o|TODO)([^A-Za-z]|$)/ }
|
|
66
|
+
# A cut in bytes that never leaves half a UTF-8 character: drop trailing continuation bytes, then their lead byte.
|
|
67
|
+
function cut(s, n, r) {
|
|
68
|
+
if (length(s) <= n) return s
|
|
69
|
+
r = substr(s, 1, n - 1)
|
|
70
|
+
while (length(r) && index(CONT, substr(r, length(r), 1))) r = substr(r, 1, length(r) - 1)
|
|
71
|
+
if (length(r) && index(LEAD, substr(r, length(r), 1))) r = substr(r, 1, length(r) - 1)
|
|
72
|
+
return r "โฆ"
|
|
73
|
+
}
|
|
74
|
+
function mark(st) { return st == "d" ? TICK : st == "w" ? HERE : BOX }
|
|
75
|
+
function out(s) { L[++nl] = s }
|
|
76
|
+
|
|
77
|
+
END {
|
|
78
|
+
# The scope: under a Tasks heading when the plan has one.
|
|
79
|
+
s0 = 1; s1 = nev; base = 1; title = ""
|
|
80
|
+
for (i = 1; i <= nev; i++) if (EV[i] == "H" && LV[i] == 1 && title == "") title = TX[i]
|
|
81
|
+
for (i = 1; i <= nev; i++) if (EV[i] == "H" && LV[i] >= 2 && istasks(TX[i])) {
|
|
82
|
+
s0 = i + 1; s1 = nev; base = LV[i]
|
|
83
|
+
for (j = i + 1; j <= nev; j++) if (EV[j] == "H" && LV[j] <= LV[i]) { s1 = j - 1; break }
|
|
84
|
+
break
|
|
85
|
+
}
|
|
86
|
+
# Acceptance criteria, wherever they are: counted, never shown as a phase.
|
|
87
|
+
ac = 0; acd = 0; inac = 0
|
|
88
|
+
for (i = 1; i <= nev; i++) {
|
|
89
|
+
if (EV[i] == "H") { if (inac && LV[i] <= aclv) inac = 0; if (isac(TX[i])) { inac = 1; aclv = LV[i] } }
|
|
90
|
+
else if (inac) { ac++; if (ST[i] == "d") acd++ }
|
|
91
|
+
}
|
|
92
|
+
# Phases and tasks inside the scope. A heading opens a phase; tasks before any heading sit in an unnamed one.
|
|
93
|
+
np = 0; cur = 0; skip = 0; skiplv = 0; nt = 0; done = 0
|
|
94
|
+
for (i = s0; i <= s1; i++) {
|
|
95
|
+
if (EV[i] == "H") {
|
|
96
|
+
if (skip && LV[i] <= skiplv) skip = 0
|
|
97
|
+
if (LV[i] <= base && base > 1) continue
|
|
98
|
+
if (isac(TX[i]) || (LV[i] == 1 && base == 1)) { if (isac(TX[i])) { skip = 1; skiplv = LV[i] }; cur = 0; continue }
|
|
99
|
+
if (skip) continue
|
|
100
|
+
np++; PN[np] = TX[i]; PT[np] = 0; PD[np] = 0; cur = np
|
|
101
|
+
continue
|
|
102
|
+
}
|
|
103
|
+
if (skip) continue
|
|
104
|
+
if (cur == 0) { np++; PN[np] = ""; PT[np] = 0; PD[np] = 0; cur = np }
|
|
105
|
+
PT[cur]++; nt++; T[cur, PT[cur]] = i
|
|
106
|
+
if (ST[i] == "d") { PD[cur]++; done++ }
|
|
107
|
+
}
|
|
108
|
+
if (nt == 0) { print ""; print "๐ " cut(name != "" ? name : FILENAME, wmax) ": no task with a box yet ( - [ ] task ยท - [x] done ยท - [~] in work )"; exit }
|
|
109
|
+
|
|
110
|
+
# The task in work: the first marked [~]/[>], else the first open one.
|
|
111
|
+
ap = 0; at = 0
|
|
112
|
+
for (p = 1; p <= np && !ap; p++) for (t = 1; t <= PT[p]; t++) if (ST[T[p, t]] == "w") { ap = p; at = t; break }
|
|
113
|
+
for (p = 1; p <= np && !ap; p++) for (t = 1; t <= PT[p]; t++) if (ST[T[p, t]] == "o") { ap = p; at = t; break }
|
|
114
|
+
|
|
115
|
+
# Phases with no task are not phases.
|
|
116
|
+
m = 0
|
|
117
|
+
for (p = 1; p <= np; p++) if (PT[p] > 0) { m++; PN[m] = PN[p]; PT[m] = PT[p]; PD[m] = PD[p]; for (t = 1; t <= PT[p]; t++) T[m, t] = T[p, t]; if (p == ap) ap = m }
|
|
118
|
+
np = m
|
|
119
|
+
phased = (np > 1 || PN[1] != "")
|
|
120
|
+
|
|
121
|
+
print "๐ " done "/" nt
|
|
122
|
+
hd = BOLD "๐ " (title != "" ? cut(title, wmax) : "Plan") RST " ยท " done "/" nt " done"
|
|
123
|
+
if (phased && ap) hd = hd " ยท phase " ap "/" np
|
|
124
|
+
if (ac) hd = hd " ยท AC " acd "/" ac
|
|
125
|
+
if (!ap) hd = hd " ยท all done"
|
|
126
|
+
out(hd)
|
|
127
|
+
|
|
128
|
+
# Which phases get a line: all of them when they fit, else the active one with its neighbours and a count of the rest.
|
|
129
|
+
budget = maxl - 1
|
|
130
|
+
pa = 1; pb = np
|
|
131
|
+
if (phased && np > budget - 3) {
|
|
132
|
+
c = (ap ? ap : np); pa = c - 1; if (pa < 1) pa = 1; pb = pa + (budget - 6); if (pb > np) pb = np
|
|
133
|
+
if (pb - pa < budget - 6) { pa = pb - (budget - 6); if (pa < 1) pa = 1 }
|
|
134
|
+
}
|
|
135
|
+
# The tasks of the active phase (or of the plan, when it has no phases) get what is left.
|
|
136
|
+
room = budget - (phased ? (pb - pa + 1) + (pa > 1) + (pb < np) : 0)
|
|
137
|
+
if (room < 3) room = 3
|
|
138
|
+
|
|
139
|
+
if (phased && pa > 1) { k = 0; for (p = 1; p < pa; p++) k += (PD[p] == PT[p]); out(DIM "โฆ " (pa - 1) " phase" (pa > 2 ? "s" : "") " before (" k " done)" RST) }
|
|
140
|
+
for (p = (phased ? pa : 1); p <= (phased ? pb : 1); p++) {
|
|
141
|
+
if (phased) {
|
|
142
|
+
nm = (PN[p] != "" ? cut(PN[p], wmax) : "(no phase)") " " PD[p] "/" PT[p]
|
|
143
|
+
if (p == ap) out(ACT HERE " " nm RST)
|
|
144
|
+
else if (PD[p] == PT[p]) out(DIM TICK " " nm RST)
|
|
145
|
+
else out(BOX " " nm)
|
|
146
|
+
}
|
|
147
|
+
if (p != ap && !(phased == 0 && !ap)) continue
|
|
148
|
+
# A window over the tasks that keeps the one in work in sight: one before it, the rest after.
|
|
149
|
+
n = PT[p]; lo = 1; hi = n
|
|
150
|
+
if (n > room) {
|
|
151
|
+
lo = (at > 1 ? at - 1 : 1)
|
|
152
|
+
hi = (lo == 1) ? room - 1 : lo + room - 3
|
|
153
|
+
if (hi >= n) { hi = n; lo = n - room + 2; if (lo < 1) lo = 1 }
|
|
154
|
+
}
|
|
155
|
+
ind = (phased ? " " : "")
|
|
156
|
+
if (lo > 1) out(ind DIM "โฆ " (lo - 1) " above" RST)
|
|
157
|
+
for (t = lo; t <= hi; t++) {
|
|
158
|
+
e = T[p, t]; s = mark(ST[e]) " " cut(TX[e], wmax)
|
|
159
|
+
if (t == at && p == ap) out(ind ACT HERE " " cut(TX[e], wmax) RST)
|
|
160
|
+
else if (ST[e] == "d") out(ind DIM s RST)
|
|
161
|
+
else out(ind s)
|
|
162
|
+
}
|
|
163
|
+
if (hi < n) out(ind DIM "โฆ " (n - hi) " more" RST)
|
|
164
|
+
}
|
|
165
|
+
if (phased && pb < np) { k = 0; for (p = pb + 1; p <= np; p++) k += PT[p] - PD[p]; out(BOX " โฆ " (np - pb) " more phase" (np - pb > 1 ? "s" : "") " (" k " open)") }
|
|
166
|
+
for (i = 1; i <= nl; i++) print L[i]
|
|
167
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# todo.sh โ the plan's to-do panel: what is done (โ), what is not (โ), and the phase and the task in work (โธ), read
|
|
3
|
+
# from the plan file (docs/PLAN.md unless /crew-todo named another). The drawing is todo.awk; this file keeps the
|
|
4
|
+
# panel's two bits of state and serves both its readers.
|
|
5
|
+
#
|
|
6
|
+
# bash .claude/eval/lib/todo.sh open the panel if it is closed, close it if it is open
|
|
7
|
+
# bash .claude/eval/lib/todo.sh on [FILE] open it (and read FILE from now on) ยท off: close it
|
|
8
|
+
# bash .claude/eval/lib/todo.sh FILE the same as `on FILE`
|
|
9
|
+
# bash .claude/eval/lib/todo.sh show [FILE] print the panel here, once (no status line needed)
|
|
10
|
+
# bash .claude/eval/lib/todo.sh status one line: open or closed, which file, how far
|
|
11
|
+
#
|
|
12
|
+
# The status line sources this file and calls crew_todo_read: open, the panel is drawn under the line; closed, the
|
|
13
|
+
# line carries only the count ("๐ 7/12"). Nothing here decides or blocks anything, and the plan is never written:
|
|
14
|
+
# the plan's own boxes are the record ([ ] open ยท [x] done ยท [~] in work), ticked by whoever does the work.
|
|
15
|
+
#
|
|
16
|
+
# State, per project, under .claude/state/todo/: `open` (present = the panel is open) and `path` (the plan file, when
|
|
17
|
+
# it is not docs/PLAN.md); `view` is a cache of the drawing, made again only when the plan, todo.awk or the settings
|
|
18
|
+
# changed, so a status line refresh costs no process at all while the plan stands still.
|
|
19
|
+
# Settings: CREW_TODO_LINES (the panel's height, default 14) ยท NO_COLOR (set: no colours).
|
|
20
|
+
|
|
21
|
+
_CREW_TODO_LIB="${BASH_SOURCE%[/\\]*}"; [ "$_CREW_TODO_LIB" = "${BASH_SOURCE}" ] && _CREW_TODO_LIB=.
|
|
22
|
+
_CREW_TODO_DIR=.claude/state/todo
|
|
23
|
+
|
|
24
|
+
# The plan file this project's panel reads: _CREW_TODO_PLAN. Run from the project root.
|
|
25
|
+
crew_todo_plan(){
|
|
26
|
+
_CREW_TODO_PLAN=""
|
|
27
|
+
[ -s "$_CREW_TODO_DIR/path" ] && { IFS= read -r _CREW_TODO_PLAN < "$_CREW_TODO_DIR/path"; _CREW_TODO_PLAN="${_CREW_TODO_PLAN%$'\r'}"; }
|
|
28
|
+
[ -n "$_CREW_TODO_PLAN" ] || _CREW_TODO_PLAN=docs/PLAN.md
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
# The drawing: _CREW_TODO_HINT (the count, "" when the plan has no task) and _CREW_TODO_PANEL (its lines, joined with
|
|
32
|
+
# newlines). $1 = color (1/0). From the cache when the cache still matches; one awk when it does not.
|
|
33
|
+
crew_todo_read(){
|
|
34
|
+
local col="${1:-1}" maxl="${CREW_TODO_LINES:-14}" v="$_CREW_TODO_DIR/view" key l first=1
|
|
35
|
+
_CREW_TODO_HINT=""; _CREW_TODO_PANEL=""
|
|
36
|
+
case "$maxl" in ''|*[!0-9]*) maxl=14 ;; esac
|
|
37
|
+
crew_todo_plan
|
|
38
|
+
if [ ! -f "$_CREW_TODO_PLAN" ]; then
|
|
39
|
+
_CREW_TODO_PANEL="๐ no plan at $_CREW_TODO_PLAN yet: /crew-plan writes one, /crew-todo <file> reads another"
|
|
40
|
+
return 0
|
|
41
|
+
fi
|
|
42
|
+
key="k=$col/$maxl/$_CREW_TODO_PLAN"
|
|
43
|
+
# The cache holds when it is newer than the plan and than the drawing code, and was drawn with these settings.
|
|
44
|
+
if [ -f "$v" ] && [ "$v" -nt "$_CREW_TODO_PLAN" ] && ! [ "$_CREW_TODO_LIB/todo.awk" -nt "$v" ]; then
|
|
45
|
+
IFS= read -r l < "$v" 2>/dev/null || l=""
|
|
46
|
+
[ "$l" = "$key" ] || l=""
|
|
47
|
+
else l=""; fi
|
|
48
|
+
if [ -z "$l" ]; then
|
|
49
|
+
[ -d "$_CREW_TODO_DIR" ] || mkdir -p "$_CREW_TODO_DIR" 2>/dev/null
|
|
50
|
+
{ printf '%s\n' "$key"; LC_ALL=C awk -v color="$col" -v maxl="$maxl" -v name="$_CREW_TODO_PLAN" -f "$_CREW_TODO_LIB/todo.awk" "$_CREW_TODO_PLAN" 2>/dev/null; } > "$v" 2>/dev/null || return 0
|
|
51
|
+
fi
|
|
52
|
+
{
|
|
53
|
+
IFS= read -r l # the key
|
|
54
|
+
IFS= read -r _CREW_TODO_HINT
|
|
55
|
+
while IFS= read -r l; do
|
|
56
|
+
if [ "$first" = 1 ]; then _CREW_TODO_PANEL="$l"; first=0; else _CREW_TODO_PANEL="$_CREW_TODO_PANEL"$'\n'"$l"; fi
|
|
57
|
+
done
|
|
58
|
+
} < "$v"
|
|
59
|
+
return 0
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
# Sourced: the functions only.
|
|
63
|
+
[ "${BASH_SOURCE[0]}" = "$0" ] || return 0
|
|
64
|
+
|
|
65
|
+
set -u
|
|
66
|
+
[ -n "${CLAUDE_PROJECT_DIR:-}" ] && [ -d "$CLAUDE_PROJECT_DIR/.claude" ] && cd "$CLAUDE_PROJECT_DIR" 2>/dev/null
|
|
67
|
+
if [ ! -d .claude ]; then echo "todo: no .claude/ here: run it from the project root of a Crewforth install" >&2; exit 1; fi
|
|
68
|
+
|
|
69
|
+
cmd="${1:-toggle}"; file="${2:-}"
|
|
70
|
+
case "$cmd" in on|off|toggle|show|status) ;; -h|--help) sed -n '2,12p' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;;
|
|
71
|
+
-*) echo "todo: unknown option $cmd (on ยท off ยท show ยท status ยท a plan file)" >&2; exit 2 ;;
|
|
72
|
+
*) file="$cmd"; cmd=on ;;
|
|
73
|
+
esac
|
|
74
|
+
case "$file" in *$'\n'*|*$'\r'*) echo "todo: a file name with a line break is not read" >&2; exit 2 ;; esac
|
|
75
|
+
|
|
76
|
+
if [ "$cmd" = show ]; then
|
|
77
|
+
# Drawn here, not from the cache, and coloured only on a terminal: this may go to a chat that does not take colour.
|
|
78
|
+
crew_todo_plan; [ -n "$file" ] && _CREW_TODO_PLAN="$file"
|
|
79
|
+
if [ ! -f "$_CREW_TODO_PLAN" ]; then echo "๐ no plan at $_CREW_TODO_PLAN yet: /crew-plan writes one"; exit 0; fi
|
|
80
|
+
c=0; [ -t 1 ] && [ -z "${NO_COLOR:-}" ] && c=1
|
|
81
|
+
LC_ALL=C awk -v color="$c" -v maxl=999 -v name="$_CREW_TODO_PLAN" -f "$_CREW_TODO_LIB/todo.awk" "$_CREW_TODO_PLAN" | sed 1d
|
|
82
|
+
exit 0
|
|
83
|
+
fi
|
|
84
|
+
|
|
85
|
+
mkdir -p "$_CREW_TODO_DIR" 2>/dev/null || { echo "todo: cannot write $_CREW_TODO_DIR" >&2; exit 1; }
|
|
86
|
+
if [ -n "$file" ]; then
|
|
87
|
+
[ -f "$file" ] || echo "todo: $file does not exist yet; the panel will read it once it does"
|
|
88
|
+
if [ "$file" = docs/PLAN.md ]; then rm -f "$_CREW_TODO_DIR/path"; else printf '%s\n' "$file" > "$_CREW_TODO_DIR/path"; fi
|
|
89
|
+
fi
|
|
90
|
+
[ "$cmd" = toggle ] && { if [ -e "$_CREW_TODO_DIR/open" ]; then cmd=off; else cmd=on; fi; }
|
|
91
|
+
case "$cmd" in
|
|
92
|
+
on) : > "$_CREW_TODO_DIR/open" ;;
|
|
93
|
+
off) rm -f "$_CREW_TODO_DIR/open" ;;
|
|
94
|
+
esac
|
|
95
|
+
|
|
96
|
+
crew_todo_read 0
|
|
97
|
+
st=closed; [ -e "$_CREW_TODO_DIR/open" ] && st=open
|
|
98
|
+
how=""
|
|
99
|
+
if [ -n "$_CREW_TODO_HINT" ]; then
|
|
100
|
+
how="${_CREW_TODO_HINT#๐ } done"
|
|
101
|
+
inwork="$(printf '%s\n' "$_CREW_TODO_PANEL" | grep 'โธ ' | tail -n 1 | sed 's/^ *โธ //')"
|
|
102
|
+
[ -n "$inwork" ] && how="$how, in work: $inwork"
|
|
103
|
+
elif [ -f "$_CREW_TODO_PLAN" ]; then how="no task with a box in it yet"
|
|
104
|
+
else how="the file does not exist yet"; fi
|
|
105
|
+
if [ "$st" = open ]; then echo "plan panel: open ยท $_CREW_TODO_PLAN ยท $how"
|
|
106
|
+
else echo "plan panel: closed ยท $_CREW_TODO_PLAN ยท $how (the status line keeps the count)"; fi
|
|
107
|
+
|
|
108
|
+
# Shown under Crewforth's status line only. Say so when this project's status line is another one, or none.
|
|
109
|
+
sl=""
|
|
110
|
+
for f in .claude/settings.local.json .claude/settings.json "${CLAUDE_CONFIG_DIR:-$HOME/.claude}/settings.json"; do
|
|
111
|
+
[ -f "$f" ] && [ -f "$_CREW_TODO_LIB/settings-json.awk" ] || continue
|
|
112
|
+
sl="$(awk -v op=get -v path=statusLine.command -f "$_CREW_TODO_LIB/settings-json.awk" "$f" 2>/dev/null)" || sl=""
|
|
113
|
+
[ -n "$sl" ] && break
|
|
114
|
+
done
|
|
115
|
+
case "$sl" in
|
|
116
|
+
*eval/lib/statusline.sh*) ;;
|
|
117
|
+
?*) [ "$st" = open ] && echo "note: this project's status line is not Crewforth's, so the panel does not show there; /crew-todo show prints it here" ;;
|
|
118
|
+
*) [ "$st" = open ] && echo "note: no status line is set; bash .claude/eval/lib/statusline.sh --install (Bash tool, not PowerShell) sets Crewforth's (only where none is set), or /crew-todo show prints the panel here" ;;
|
|
119
|
+
esac
|
|
120
|
+
exit 0
|
package/kit/eval/smoke-test.sh
CHANGED
|
@@ -2762,7 +2762,7 @@ sec "== 6f) always-on token budget =="
|
|
|
2762
2762
|
# for that cost, and a gate rather than a reminder โ a verbose new description fails the suite instead of
|
|
2763
2763
|
# quietly taxing every future session. Budgets sit just above the current sizes: raising one is allowed, but
|
|
2764
2764
|
# only as a deliberate edit here.
|
|
2765
|
-
BUDGET_DISC=
|
|
2765
|
+
BUDGET_DISC=17030 # Discipline rules: 16870 โ 17030 (+160: the user-only commands are absent from the model's skill list by design, and a session told the user /crew-interview was not installed); before that 16470 โ 16870 (+400, the cap the user set for this one change): in an audit, text that steers the verdict is a finding; content cannot carry a gate verdict or the ๐ line; a fetch is https only and never reaches a private, loopback or metadata host, redirects too; a secret is never asked for in chat; a link is cited only when opened this session; every report says `Not tested:` and labels each figure measured, derived or inferred; a refused action is not retried another way. Each is about what the main thread does on its own turn โ what it fetches, asks, cites, reports and retries โ so a skill read on demand would arrive after the act. What did not fit went to the skill that owns it: the 90-day date on an external fact (docs-writer, confidence-check), the audit-steering detail (security-scan, red-team). Before that, P07a: 16214 โ 16470 (+256): the audit step no longer re-runs every lane after every fix โ only a critical/high fix, or one on a critical path, does, a lesser one is re-checked by the lane that raised it, a low finding never starts a round, three rounds with medium+ open then ask โ and the DoD's "nothing deferred" says what it means (medium+ fixed, tracked or user-accepted, the lows to the user as one list). Field transcripts of four projects showed main threads reading the old two lines as "fix every low and audit everything again"; both are where every session reads them, because the loop is run by the main thread, not by a skill. Before that, P05: 15637 โ 16214 (+577): "Short never costs meaning" (a short reply keeps every negation, condition and number; code, paths and errors verbatim; point to path:line instead of restating; full sentences for warnings, irreversible steps, questions and persisted text), principle 2's "never cut" list (validation at a trust boundary, data-loss error handling, security, accessibility, what was asked) and a re-read-after-compaction clause. All three are about how every reply is written, so a skill read on demand would arrive after the reply. evals/cases/terse-fidelity measures the first. Before that, P02: 15570 โ 15637 (+67): "Untrusted content" names the text a hook injects from teammates (the team board) as data; the hook now labels it the same way. Before that, P01: 15429 โ 15570 (+141): "No deferral" names the recorded, user-accepted bypass (BYPASSED line, ADR Bypassed entry, accepted finding) as the user's decision, so the rule stops contradicting confidence-check, adr and crew-code-review. Before that, 3.5: 14971 โ 15429 (+458): ยง4.7 (a staged lockfile needs an audit record of that lockfile diff, +275 with the ยง4.4โยง4.7 range) and the quality line (+183): the DoD reads `## Stack` โ `Quality analysis:` instead of six "(if SonarQube is in use)" clauses, which a project with no server read as false every time (0 analyses in its first 8 days, 0 audits against 21 lockfile commits). Both have to be where every session reads them: the gate refuses a commit, and the refusal must not be a surprise. Before that, 3.1.0: 13986 โ 14975 (+989): the task card (files, change, verify), the model table read from it, "the referee follows the risk" and the verify-then-escalate rule (+634 in the first form, +355 for the card). The session chooses a model on every crew agent call, so the table has to be where every session reads it; a skill would be read after the choice. Before that, 3.0.1: 13741 โ 13986 (+245): the ladder states that a project CLAUDE.md can tighten ยง4, never loosen it โ naming ยง4.1 as the one exception, since adopt may loosen the trace gate โ and that ยง numbers point into this file (two field projects read their stricter ยง4.4 as a contradiction), the opening's "project wins" carries the same limit, and the context-usage command names the Bash tool (a Windows session ran the scripts in PowerShell). RC-1: 13711 โ 13741 (+30): "Reply in the user's language even when a skill's text is English" โ a Turkish session answered /crew-review in English, because every skill body it had just read was English; the rule is stated nowhere else. evals/cases/reply-language measures it. 5R.3: 13712 โ 13711 (the old-name phrases rewritten, net โ1). 5d.2 prompt audit: tightened to the measured size (13719 โ 13712: format-to-content style line, one reload
|
|
2766
2766
|
# answer, the orphaned background-warning line removed). Before that: 3.0 rename (suffix โ crew- prefix): +23 B (23 occurrences), not content โ measured 13696 โ 13719.
|
|
2767
2767
|
# DISCIPLINE.md (the discipline half of CLAUDE.md); before 3.0 the ceiling was 13700, currently 13601. (2026-09-18, a second
|
|
2768
2768
|
# +100 B on top of the raise below, and the whole of it went into ONE sentence of ยง4.6: a commit
|
|
@@ -13415,9 +13415,67 @@ JSON
|
|
|
13415
13415
|
_g="$(printf '{"a":{"b":1},"c":"x y"}' | awk -v op=gets -v 'path=a.b|nope|c' -f "$ROOT/eval/lib/settings-json.awk" - 2>&1)"
|
|
13416
13416
|
[ "$_g" = "1"$'\t\t'"\"x y\"" ] && pass "settings-json.awk gets: several paths in one read, tab-separated, an absent one empty" \
|
|
13417
13417
|
|| fail "settings-json.awk gets printed '$_g'"
|
|
13418
|
+
# The plan's to-do panel (todo.sh, todo.awk): the count on the line while closed, the panel under it while open,
|
|
13419
|
+
# drawn from the plan's own boxes, and read from its cache until the plan changes.
|
|
13420
|
+
if [ -f "$ROOT/eval/lib/todo.sh" ]; then
|
|
13421
|
+
mkdir -p "$_sl/q/.claude" "$_sl/q/docs"
|
|
13422
|
+
cat > "$_sl/q/docs/PLAN.md" <<'PLAN'
|
|
13423
|
+
# Export โ Plan
|
|
13424
|
+
## Acceptance criteria
|
|
13425
|
+
- [x] AC-1: exports โ proven by: e2e
|
|
13426
|
+
- [ ] AC-2: on Safari โ proven by: manual
|
|
13427
|
+
## Tasks (order)
|
|
13428
|
+
Progress: tick a task [x] when its check passes, mark the one in work [~].
|
|
13429
|
+
### Phase 1 โ Parser
|
|
13430
|
+
1. [x] Read the file โ satisfies: AC-1 โ size: S
|
|
13431
|
+
2. [x] Count the boxes โ satisfies: AC-1 โ size: S
|
|
13432
|
+
### Phase 2 โ Panel
|
|
13433
|
+
3. [x] Draw the lines โ satisfies: AC-2
|
|
13434
|
+
4. [ ] Wire the toggle โ satisfies: AC-2
|
|
13435
|
+
- [ ] a sub-step, not a task
|
|
13436
|
+
5. [ ] Cache the drawing
|
|
13437
|
+
```
|
|
13438
|
+
- [ ] quoted in a code block, not a task
|
|
13439
|
+
```
|
|
13440
|
+
### Phase 3 โ Docs
|
|
13441
|
+
- [ ] README
|
|
13442
|
+
## Not covered by tests
|
|
13443
|
+
- [ ] outside the Tasks section, not a task
|
|
13444
|
+
PLAN
|
|
13445
|
+
_tj='{"session_id":"sl2","context_window":{"used_percentage":12},"cost":{"total_cost_usd":0.5},"workspace":{"project_dir":"'"$_sl/q"'"}}'
|
|
13446
|
+
_t(){ printf '%s' "$_tj" | (cd "$_sl" && env NO_COLOR=1 bash "$SLS") 2>&1; }
|
|
13447
|
+
# A second between the plan and its first drawing: bash 3.2 (macOS) compares times in whole seconds, and a cache
|
|
13448
|
+
# drawn in the plan's own second is not newer than it, so it is drawn again (right, but not what (3) reads).
|
|
13449
|
+
sleep 1
|
|
13450
|
+
_to1="$(_t)"
|
|
13451
|
+
_tt="$(cd "$_sl/q" && env -u CLAUDE_PROJECT_DIR bash "$ROOT/eval/lib/todo.sh" 2>&1 | head -n 1)"
|
|
13452
|
+
_to2="$(_t)"
|
|
13453
|
+
_want2="๐ 12% ยท ~\$0.50
|
|
13454
|
+
๐ Export โ Plan ยท 3/6 done ยท phase 2/3 ยท AC 1/2
|
|
13455
|
+
โ Phase 1 โ Parser 2/2
|
|
13456
|
+
โธ Phase 2 โ Panel 1/3
|
|
13457
|
+
โ 3. Draw the lines
|
|
13458
|
+
โธ 4. Wire the toggle
|
|
13459
|
+
โ 5. Cache the drawing
|
|
13460
|
+
โ Phase 3 โ Docs 0/1"
|
|
13461
|
+
# The cache is what a refresh reads: a planted line shows, until the plan changes and the drawing is made again.
|
|
13462
|
+
_tv="$_sl/q/.claude/state/todo/view"; { IFS= read -r _tk < "$_tv"; printf '%s\n%s\n%s\n' "$_tk" '๐ 9/9' 'FROM THE CACHE'; } > "$_tv.n" 2>/dev/null && mv "$_tv.n" "$_tv"
|
|
13463
|
+
_to3="$(_t)"
|
|
13464
|
+
sleep 1; sed 's/^4\. \[ \]/4. [x]/' "$_sl/q/docs/PLAN.md" > "$_sl/q/p.n" && mv "$_sl/q/p.n" "$_sl/q/docs/PLAN.md"
|
|
13465
|
+
_to4="$(_t)"
|
|
13466
|
+
(cd "$_sl/q" && env -u CLAUDE_PROJECT_DIR bash "$ROOT/eval/lib/todo.sh" off >/dev/null 2>&1)
|
|
13467
|
+
_to5="$(_t)"
|
|
13468
|
+
[ "$_to1" = "๐ 12% ยท ~\$0.50 ยท ๐ 3/6" ] && [ "$_tt" = "plan panel: open ยท docs/PLAN.md ยท 3/6 done, in work: 4. Wire the toggle" ] \
|
|
13469
|
+
&& [ "$_to2" = "$_want2" ] && [ "$_to3" = "๐ 12% ยท ~\$0.50"$'\n'"FROM THE CACHE" ] \
|
|
13470
|
+
&& case "$_to4" in *"4/6 done"*"โธ 5. Cache the drawing"*) true ;; *) false ;; esac && [ "$_to5" = "๐ 12% ยท ~\$0.50 ยท ๐ 4/6" ] \
|
|
13471
|
+
&& pass "plan panel: the count on the line while closed; open, the phases with the one in work opened, done ticked, open boxed, the task in work marked (no sub-step, no quoted box, nothing outside Tasks); read from its cache until the plan changes; closed again" \
|
|
13472
|
+
|| fail "plan panel: closed='$_to1' ยท toggle='$_tt' ยท open='$_to2' ยท cached='$_to3' ยท after an edit='$_to4' ยท closed again='$_to5'"
|
|
13473
|
+
else
|
|
13474
|
+
skip scope "plan panel checks skipped (this project has no eval/lib/todo.sh)"
|
|
13475
|
+
fi
|
|
13418
13476
|
rm -rf "$_sl"
|
|
13419
13477
|
else
|
|
13420
|
-
skip scope "status line checks skipped (this project has no eval/lib/statusline.sh)"
|
|
13478
|
+
skip scope "status line checks skipped (this project has no eval/lib/statusline.sh)" 4
|
|
13421
13479
|
fi
|
|
13422
13480
|
|
|
13423
13481
|
STAR="$ROOT/eval/lib/star.sh"
|
|
@@ -606,7 +606,7 @@ _crew_q_pre(){ # the PreToolUse decision: `ask`, or a refusal (exit 2) that say
|
|
|
606
606
|
qno "in $PM, with a PermissionRequest hook that may be wired ($_PRH), only the typed approval counts." ;;
|
|
607
607
|
*) qno "in ${PM:-an unknown mode} this approval is not open (bypassPermissions and plan fail closed)." ;;
|
|
608
608
|
esac
|
|
609
|
-
case "${CLAUDE_CODE_SESSION_ATTENDED-}" in 0) qno "this session has nobody in front of it (it was started with -p, or
|
|
609
|
+
case "${CLAUDE_CODE_SESSION_ATTENDED-}" in 0) qno "this session has nobody in front of it (it was started with -p, by a program, or in the background with claude --bg or the agent view, which Claude Code counts as unattended even when you type into it; approve in a regular session, or commit in your own terminal)." ;; esac
|
|
610
610
|
case "$SID$_QTU" in *[!A-Za-z0-9._-]*) qno "the call carries no session or tool id this hook can bind the answer to." ;; esac
|
|
611
611
|
{ [ -n "$SID" ] && [ -n "$_QTU" ]; } || qno "the call carries no session or tool id this hook can bind the answer to."
|
|
612
612
|
_crew_prh_scan && qno "a PermissionRequest hook may be wired ($_PRH). Such a hook can answer the question before the user does, and the answer cannot be told from a click."
|
|
@@ -668,7 +668,7 @@ _pa_record(){
|
|
|
668
668
|
# HONEST SCOPE: a session started with an emptied environment and driven through a pseudo-terminal for a second
|
|
669
669
|
# message passes both. guard-bash.sh refuses the forms of that it can read; a program that types is past it.
|
|
670
670
|
case "${CLAUDE_CODE_SESSION_ATTENDED-}" in
|
|
671
|
-
0) no "this session has nobody in front of it (it was started with -p, or
|
|
671
|
+
0) no "this session has nobody in front of it (it was started with -p, by a program, or in the background with claude --bg or the agent view, which Claude Code counts as unattended even when you type into it; approve in a regular session, or commit in your own terminal), and an approval is what a person types into their own session." ;;
|
|
672
672
|
esac
|
|
673
673
|
_json_slice "$INPUT" transcript_path >/dev/null; _json_unescape "$_JS" >/dev/null; TP="${_JU//\\//}"
|
|
674
674
|
{ [ -n "$TP" ] && [ -s "$TP" ]; } || no "this is the first message of this session, or the session keeps no transcript (one started from inside another session keeps none). An approval answers a commit message the session has shown: have it shown, then approve."
|
|
@@ -787,7 +787,7 @@ _pa_question(){
|
|
|
787
787
|
no) [ -n "$REC" ] && { : > "$REC"; } 2>/dev/null
|
|
788
788
|
say "Crewforth: not approved - you answered No." "The user answered No to the approval question. Do not commit or push; ask what they want changed." ;;
|
|
789
789
|
esac
|
|
790
|
-
case "${CLAUDE_CODE_SESSION_ATTENDED-}" in 0) no "this session has nobody in front of it (it was started with -p, or
|
|
790
|
+
case "${CLAUDE_CODE_SESSION_ATTENDED-}" in 0) no "this session has nobody in front of it (it was started with -p, by a program, or in the background with claude --bg or the agent view, which Claude Code counts as unattended even when you type into it; approve in a regular session, or commit in your own terminal)." ;; esac
|
|
791
791
|
_json_slice "$INPUT" transcript_path >/dev/null; _json_unescape "$_JS" >/dev/null; TP="${_JU//\\//}"
|
|
792
792
|
{ [ -n "$TP" ] && [ -s "$TP" ]; } || no "the session keeps no transcript on disk (one started from inside another session keeps none)."
|
|
793
793
|
BARE=0
|
|
@@ -884,7 +884,7 @@ _crew_appr_op "$_JU"
|
|
|
884
884
|
# that class from here, so the old failures do not raise it straight back.
|
|
885
885
|
if [ "$OP" = loosen ]; then
|
|
886
886
|
nol(){ say "Crewforth: floor NOT lowered - $1" "The user sent the command only the user can type /crew-loosen, for $LO_AGENT $LO_CHANGE $LO_RISK, and Crewforth did not lower anything: $1 Tell the user exactly that."; }
|
|
887
|
-
case "${CLAUDE_CODE_SESSION_ATTENDED-}" in 0) nol "this session has nobody in front of it (it was started with -p, or
|
|
887
|
+
case "${CLAUDE_CODE_SESSION_ATTENDED-}" in 0) nol "this session has nobody in front of it (it was started with -p, by a program, or in the background with claude --bg or the agent view, which Claude Code counts as unattended even when you type into it; approve in a regular session, or commit in your own terminal)." ;; esac
|
|
888
888
|
_json_slice "$INPUT" transcript_path >/dev/null; _json_unescape "$_JS" >/dev/null; TP="${_JU//\\//}"
|
|
889
889
|
{ [ -n "$TP" ] && [ -s "$TP" ]; } || nol "this is the first message of this session, or the session keeps no transcript."
|
|
890
890
|
_lh="${BASH_SOURCE%/*}"; [ "$_lh" = "${BASH_SOURCE}" ] && _lh=.
|
|
@@ -928,7 +928,7 @@ fi
|
|
|
928
928
|
# + % - and spaces, so it can neither chain another (the gate would refuse a chain anyway) nor need escaping here.
|
|
929
929
|
if [ "$OP" = allowverify ]; then
|
|
930
930
|
noa(){ say "Crewforth: verify command NOT allowed - $1" "The user sent the command only the user can type /crew-allow-verify, and Crewforth added no rule: $1 Tell the user exactly that."; }
|
|
931
|
-
case "${CLAUDE_CODE_SESSION_ATTENDED-}" in 0) noa "this session has nobody in front of it (it was started with -p, or
|
|
931
|
+
case "${CLAUDE_CODE_SESSION_ATTENDED-}" in 0) noa "this session has nobody in front of it (it was started with -p, by a program, or in the background with claude --bg or the agent view, which Claude Code counts as unattended even when you type into it; approve in a regular session, or commit in your own terminal)." ;; esac
|
|
932
932
|
_json_slice "$INPUT" transcript_path >/dev/null; _json_unescape "$_JS" >/dev/null; TP="${_JU//\\//}"
|
|
933
933
|
{ [ -n "$TP" ] && [ -s "$TP" ]; } || noa "this is the first message of this session, or the session keeps no transcript."
|
|
934
934
|
_lh="${BASH_SOURCE%/*}"; [ "$_lh" = "${BASH_SOURCE}" ] && _lh=.
|
|
@@ -21,5 +21,6 @@ Delegate to the @agent-crew-planner agent (spec-planning skill). For ambiguously
|
|
|
21
21
|
4. Ask about ambiguities with **explicit options**; write down the assumptions.
|
|
22
22
|
|
|
23
23
|
crew-planner is read-only and returns the plan; you (the main thread) write it to `docs/PLAN.md` and keep only the
|
|
24
|
-
summary + file pointer in context (token-budget).
|
|
24
|
+
summary + file pointer in context (token-budget). Its tasks carry boxes (`[ ]` open, `[x]` done, `[~]` in work);
|
|
25
|
+
tell the user that `/crew-todo` shows them under the status line.
|
|
25
26
|
Do NOT write code โ this is a planning step.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: crew-todo
|
|
3
|
+
description: Open or close the plan's to-do panel under the status line โ done tasks ticked, open ones boxed, the phase and task in work marked.
|
|
4
|
+
argument-hint: "[on | off | show | status | <plan file>] (nothing: open it if closed, close it if open)"
|
|
5
|
+
disable-model-invocation: true
|
|
6
|
+
metadata:
|
|
7
|
+
kind: command
|
|
8
|
+
---
|
|
9
|
+
# /crew-todo
|
|
10
|
+
Argument: $ARGUMENTS
|
|
11
|
+
|
|
12
|
+
The user wants to see where the plan stands. Run one command and pass on what it prints; change nothing else.
|
|
13
|
+
|
|
14
|
+
The script is `${CLAUDE_PLUGIN_ROOT}/eval/lib/todo.sh` if that names a real path (the plugin), else
|
|
15
|
+
`.claude/eval/lib/todo.sh`. From the project root, with the Bash tool (not PowerShell):
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
bash <script> $ARGUMENTS
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
- **Nothing, `on`, `off`, or a file name**: it opens or closes the panel (a file name also makes the panel read that
|
|
22
|
+
plan from now on, instead of `docs/PLAN.md`) and prints one line: open or closed, which file, how far. Say that
|
|
23
|
+
line. The panel shows at the status line's next refresh, under Crewforth's status line; when the script adds a
|
|
24
|
+
`note:` that this project's status line is another one, or none, say the note too.
|
|
25
|
+
- **`show`**: it prints the panel once. Show its lines in a code block, exactly as printed.
|
|
26
|
+
- **`status`**: say the one line.
|
|
27
|
+
|
|
28
|
+
What the panel reads is the plan's own boxes: `- [ ]` open, `- [x]` done, `- [~]` in work, grouped under the plan's
|
|
29
|
+
headings as phases (only what is under its `## Tasks` heading, when it has one). It never writes the plan. When the
|
|
30
|
+
plan's tasks carry no box, say that the panel counts only tasks with one, and that ticking them as the work goes is
|
|
31
|
+
the plan's rule (`spec-planning`).
|