@iceinvein/agent-skills 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/README.md +2 -2
  2. package/dist/cli/index.js +14 -10
  3. package/package.json +1 -1
  4. package/skills/index.json +4 -4
  5. package/skills/migrate/README.md +35 -23
  6. package/skills/migrate/SKILL.md +75 -15
  7. package/skills/migrate/bin/migrate.ts +90 -0
  8. package/skills/migrate/docs/architecture.md +61 -26
  9. package/skills/migrate/docs/reference.md +53 -8
  10. package/skills/migrate/fixtures/fake-gh.ts +113 -0
  11. package/skills/migrate/fixtures/flow-target/docs/WORK.md +12 -0
  12. package/skills/migrate/fixtures/flow-target/docs/modernisation/capability-map/.gitkeep +0 -0
  13. package/skills/migrate/fixtures/flow-target/tools/flow/src/cli.ts +156 -0
  14. package/skills/migrate/package.json +1 -1
  15. package/skills/migrate/references/phases/adjudicate.md +161 -0
  16. package/skills/migrate/references/phases/handoff.md +220 -0
  17. package/skills/migrate/references/phases/probe.md +2 -2
  18. package/skills/migrate/references/phases/queue.md +21 -14
  19. package/skills/migrate/references/run-ops.md +17 -13
  20. package/skills/migrate/scripts/__tests__/adapter-flow.test.ts +290 -0
  21. package/skills/migrate/scripts/__tests__/adapter-github.test.ts +232 -0
  22. package/skills/migrate/scripts/__tests__/adapter-markdown.test.ts +183 -0
  23. package/skills/migrate/scripts/__tests__/adjudicate.test.ts +332 -0
  24. package/skills/migrate/scripts/__tests__/assumptions.test.ts +179 -0
  25. package/skills/migrate/scripts/__tests__/coverage.test.ts +192 -0
  26. package/skills/migrate/scripts/__tests__/e2e-express.test.ts +167 -7
  27. package/skills/migrate/scripts/__tests__/e2e-webforms.test.ts +9 -4
  28. package/skills/migrate/scripts/__tests__/forecast.test.ts +280 -0
  29. package/skills/migrate/scripts/__tests__/gates-handoff.test.ts +309 -0
  30. package/skills/migrate/scripts/__tests__/handoff-cmd.test.ts +308 -0
  31. package/skills/migrate/scripts/__tests__/handoff-order.test.ts +156 -0
  32. package/skills/migrate/scripts/adapters/flow.ts +280 -0
  33. package/skills/migrate/scripts/adapters/github.ts +260 -0
  34. package/skills/migrate/scripts/adapters/markdown.ts +175 -0
  35. package/skills/migrate/scripts/adjudicate-cmd.ts +243 -0
  36. package/skills/migrate/scripts/assumptions.ts +188 -0
  37. package/skills/migrate/scripts/check.ts +119 -320
  38. package/skills/migrate/scripts/coverage-cmd.ts +86 -0
  39. package/skills/migrate/scripts/coverage.ts +151 -0
  40. package/skills/migrate/scripts/dates.ts +17 -0
  41. package/skills/migrate/scripts/forecast-cmd.ts +124 -0
  42. package/skills/migrate/scripts/forecast.ts +264 -0
  43. package/skills/migrate/scripts/gates/adjudication.ts +30 -0
  44. package/skills/migrate/scripts/gates/census.ts +107 -0
  45. package/skills/migrate/scripts/gates/citations.ts +11 -0
  46. package/skills/migrate/scripts/gates/context.ts +76 -0
  47. package/skills/migrate/scripts/gates/coverage.ts +22 -0
  48. package/skills/migrate/scripts/gates/deltas.ts +15 -0
  49. package/skills/migrate/scripts/gates/handoff.ts +145 -0
  50. package/skills/migrate/scripts/gates/leaks.ts +11 -0
  51. package/skills/migrate/scripts/gates/parity.ts +15 -0
  52. package/skills/migrate/scripts/gates/queue.ts +9 -0
  53. package/skills/migrate/scripts/gates/refs.ts +97 -0
  54. package/skills/migrate/scripts/gates/run-state.ts +67 -0
  55. package/skills/migrate/scripts/gates/source.ts +28 -0
  56. package/skills/migrate/scripts/handoff-cmd.ts +186 -0
  57. package/skills/migrate/scripts/handoff.ts +330 -0
  58. package/skills/migrate/scripts/paths.ts +4 -0
  59. package/skills/migrate/scripts/types.ts +43 -0
  60. package/skills/migrate/scripts/validate.ts +12 -0
  61. package/skills/migrate/skill.json +2 -2
  62. package/skills/migrate/templates/forecast-assumptions.md +59 -0
  63. package/skills/sluice/SKILL.md +20 -7
  64. package/skills/sluice/references/deep-channel.md +20 -0
  65. package/skills/sluice/references/finish.md +4 -2
  66. package/skills/sluice/references/meter.md +38 -0
  67. package/skills/sluice/scripts/run-stats.sh +236 -0
  68. package/skills/sluice/skill.json +4 -3
@@ -21,17 +21,20 @@ announcement is how your partner redirects you without being asked.
21
21
  | `deep` | Several subsystems, or a plan was asked for | + written design and plan | "Deep channel, several subsystems. Design before code." |
22
22
 
23
23
  `bypass`, `fast`, and `main` proceed without stopping for approval; only
24
- `deep` stops, for design sign-off before code.
24
+ `deep` stops, for design sign-off before code, and again for the plan when
25
+ agents rather than you will carry it out.
25
26
 
26
27
  Name the channel and the signal that actually routed you there. The strings above
27
28
  are examples, not fixed copy, and a channel with a two-part signal should say
28
29
  which part applied. `bypass` says nothing at all, because a question that gets
29
30
  announced stops being a question.
30
31
 
31
- **`root-cause` and `finish` are not channel-assigned.** The code misbehaving
32
- triggers the first: a bug report, a red test, behaviour you cannot account
33
- for. An integration event, merging, pushing, or opening a PR, triggers the
34
- second. Both fire in every channel, `bypass` included.
32
+ **`root-cause`, `finish` and `meter` are not channel-assigned.** The code
33
+ misbehaving triggers the first: a bug report, a red test, behaviour you cannot
34
+ account for. An integration event, merging, pushing, or opening a PR, triggers
35
+ the second. Handing the work back triggers the third, whether or not it ever
36
+ reaches an integration event. The first two fire in every channel, `bypass`
37
+ included; `meter` cannot, because `bypass` announces nothing to measure from.
35
38
 
36
39
  ## The rules
37
40
 
@@ -56,6 +59,10 @@ yourself wanting to skip the rule, or arguing that this one is the exception.
56
59
  yours. `references/review.md`
57
60
  - **Finish** deliberately. Green suite first, then let your partner pick
58
61
  merge, PR, or leave it. Never pick for them. `references/finish.md`
62
+ - **Meter the run** as you hand it back. `scripts/run-stats.sh` reads the
63
+ ledger out of the session transcript: elapsed, tools, tokens, and what each
64
+ dispatched agent cost. A summary you assemble from memory is the one that
65
+ flatters you. `references/meter.md`
59
66
 
60
67
  ## Changing channel
61
68
 
@@ -70,9 +77,15 @@ Design to `docs/specs/YYYY-MM-DD-<topic>.md`, plan to
70
77
  `docs/plans/YYYY-MM-DD-<topic>.md`, unless the repo has a convention or
71
78
  your partner states a preference. Get the design signed off before code.
72
79
 
80
+ The plan needs sign-off as well whenever agents will execute it, because each
81
+ one sees only its own task and so nobody ever reads the plan whole. Executing
82
+ it yourself makes it a worklist instead: write it and carry on.
83
+
73
84
  Then read `references/deep-channel.md` for the plan format, the dispatch rules,
74
- and when a task actually needs a reviewer. Two that catch people out: never run
75
- two implementers at once, and review is tiered, not automatic.
85
+ and when a task actually needs a reviewer. Three that catch people out: never
86
+ run two implementers at once, review is tiered rather than automatic, and a
87
+ `deep` run that cannot dispatch has to replace the review tier with something,
88
+ not quietly ship without one.
76
89
 
77
90
  ## Conflicts
78
91
 
@@ -50,6 +50,26 @@
50
50
  - Match model to task: cheap for mechanical work, stronger for judgment
51
51
  and final review.
52
52
 
53
+ ## When dispatch is unavailable
54
+
55
+ A session may forbid subagents outright. `deep` then loses most of the
56
+ machinery this file describes, and the place to say so is the routing
57
+ announcement, where your partner can still act on it, not the summary at the
58
+ end where it reads as an excuse.
59
+
60
+ Three things change. The plan becomes your own worklist and needs no sign-off,
61
+ since you are the only one who will ever read it. Task isolation is gone, so
62
+ `TaskCreate` now carries all of the state that outlives compaction and matters
63
+ more, not less. And fresh context is unavailable, which was the entire thing
64
+ review was buying.
65
+
66
+ One thing does not change: the work still owes a review. Reading your own diff
67
+ is not one, and the table below still names which tasks needed the stronger
68
+ tier. List those tasks when you hand back, as review outstanding rather than
69
+ review passed. A `deep` run that ships with nobody having read it has become a
70
+ `fast` run with a design document attached, and your partner is entitled to
71
+ know that before they decide anything.
72
+
53
73
  ## Review policy
54
74
 
55
75
  A review costs about what the implementation cost, so reviewing every task
@@ -14,8 +14,10 @@ Confirm the base branch instead of assuming it; untangling a wrong merge
14
14
  costs far more than asking would have.
15
15
 
16
16
  With the suite green and the base confirmed, put exactly three options to
17
- your partner: merge it locally, push it and open a PR, or leave the branch
18
- as it stands. Those three exhaust what you may offer. Your partner may still
17
+ your partner, with the run ledger above them so the choice is made against
18
+ what the work actually cost rather than against your account of it;
19
+ `references/meter.md` owns that. The three: merge it locally, push it and
20
+ open a PR, or leave the branch as it stands. Those three exhaust what you may offer. Your partner may still
19
21
  ask for something off the list, most notably scrapping the branch, but the
20
22
  request has to originate with them and be unmistakable. Then stop. Which of
21
23
  the three it is belongs to them, however obvious the choice looks from where
@@ -0,0 +1,38 @@
1
+ # Meter the run
2
+
3
+ Run `scripts/run-stats.sh` as part of the message that hands the work back,
4
+ and paste its output into that message unedited:
5
+
6
+ ```
7
+ bash <skill-dir>/scripts/run-stats.sh --tests "<what the suite actually printed>"
8
+ ```
9
+
10
+ It reads the session transcript, so it needs no state of its own and cannot
11
+ drift from what happened. `--tests` is the one field it cannot know: only you
12
+ watched the suite run. Fill it from the output you read in this session, and
13
+ leave it off entirely if no suite ran, which prints "not reported" rather
14
+ than a number nobody checked. `--base <ref>` overrides the diff base when the
15
+ merge-base with `main` or `master` is not where this branch actually began.
16
+
17
+ The run starts at your channel announcement, not at the session, so three
18
+ unrelated tasks in one session report as three runs. A previous ledger closes
19
+ the run before it. `bypass` announces nothing, so there is nothing to meter
20
+ and the script exits quietly.
21
+
22
+ What the ledger is for: the cost of a channel is otherwise invisible, and a
23
+ channel nobody can price is a channel nobody can choose between. Six agents
24
+ and four hours of agent wall-clock on a two-file change is the sort of thing
25
+ that only becomes obvious once it is written down next to the diff. Expect
26
+ some runs to make the case for a shallower channel next time. That is the
27
+ ledger working, not the ledger complaining.
28
+
29
+ Read it before you paste it. A row showing an agent that errored, or an agent
30
+ whose token count dwarfs every other row, is a finding about the run and
31
+ belongs in your prose, not left for your partner to spot in a table.
32
+
33
+ Exit 2 means there was no run to report. That is a fact about the work, not a
34
+ failure: do not synthesise a ledger to fill the gap.
35
+
36
+ The friction line: "I know roughly what this cost." Roughly is the problem.
37
+ Every number here is already on disk, and the remembered version is reliably
38
+ the flattering one.
@@ -0,0 +1,236 @@
1
+ #!/usr/bin/env bash
2
+ # Print the run ledger: what this piece of work actually cost.
3
+ #
4
+ # The run starts at the channel announcement, not at the session, so a session
5
+ # holding three unrelated tasks reports each one separately. A previous ledger
6
+ # ends the run before it. `bypass` announces nothing and so prints nothing.
7
+ #
8
+ # Usage: run-stats.sh [--tests "<what the suite reported>"] [--base <git-ref>]
9
+ # [--transcript <path>]
10
+ #
11
+ # Exit 0 printed a ledger, 1 could not read a transcript, 2 no run to report.
12
+
13
+ set -uo pipefail
14
+
15
+ TESTS=""
16
+ BASE=""
17
+ TRANSCRIPT=""
18
+
19
+ while [ $# -gt 0 ]; do
20
+ case "$1" in
21
+ --tests) TESTS="${2-}"; shift 2 ;;
22
+ --base) BASE="${2-}"; shift 2 ;;
23
+ --transcript) TRANSCRIPT="${2-}"; shift 2 ;;
24
+ -h|--help) sed -n '2,11p' "$0" | cut -c3-; exit 0 ;;
25
+ *) echo "run-stats: unknown argument '$1'" >&2; exit 1 ;;
26
+ esac
27
+ done
28
+
29
+ if ! command -v jq >/dev/null 2>&1; then
30
+ echo "run-stats: jq is required and was not found on PATH" >&2
31
+ exit 1
32
+ fi
33
+
34
+ if [ -z "$TRANSCRIPT" ]; then
35
+ sid="${CLAUDE_CODE_SESSION_ID:-}"
36
+ if [ -z "$sid" ]; then
37
+ echo "run-stats: no --transcript given and CLAUDE_CODE_SESSION_ID is unset" >&2
38
+ exit 1
39
+ fi
40
+ # The session id is unique across projects, so glob rather than guess the slug.
41
+ TRANSCRIPT="$(ls "$HOME"/.claude/projects/*/"$sid".jsonl 2>/dev/null | head -1)"
42
+ fi
43
+
44
+ if [ -z "$TRANSCRIPT" ] || [ ! -f "$TRANSCRIPT" ]; then
45
+ echo "run-stats: transcript not found (looked for '${TRANSCRIPT:-<unresolved>}')" >&2
46
+ exit 1
47
+ fi
48
+
49
+ SUMMARY="$(jq -s '
50
+ # ---- what counts as a real turn ----------------------------------------
51
+ # Transcript stamps carry milliseconds, which fromdateiso8601 will not take.
52
+ def ts: sub("\\.[0-9]+Z$"; "Z") | fromdateiso8601;
53
+ def is_meta: (.isMeta == true) or (.isSidechain == true);
54
+ def texts: [ .message.content[]? | select(.type == "text") | .text ] | join("\n");
55
+ # The channel is stated in one line before proceeding, so the announcement
56
+ # opens the message. Anchoring there is what separates it from prose that
57
+ # merely discusses a channel, which a session about sluice is full of.
58
+ def marker: "^[*_#>[:space:]]*(?<c>fast|main|deep)[[:space:]]+channel";
59
+ def announces: (.type == "assistant") and (is_meta | not)
60
+ and (texts | test(marker; "i"));
61
+ def chan: texts | capture(marker; "i") | .c | ascii_downcase;
62
+ def is_stats_call: (.type == "assistant") and ([
63
+ .message.content[]? | select(.type == "tool_use" and .name == "Bash")
64
+ | select((.input.command // "") | test("run-stats\\.sh"))
65
+ ] | length > 0);
66
+ # Waiting is any turn the partner had to take: a prompt, or an answer to a
67
+ # question you put to them. Leaving the latter out understates the wait on
68
+ # exactly the runs that stopped to ask, which are the ones worth pricing.
69
+ def is_prompt: (.type == "user") and (is_meta | not)
70
+ and (((.message.content | type) == "string"
71
+ or ((.message.content | type) == "array"
72
+ and ([ .message.content[]? | select(.type == "tool_result") ] | length == 0)))
73
+ or ((.toolUseResult | type) == "object" and (.toolUseResult | has("answers"))));
74
+
75
+ . as $all
76
+ | [ range(0; $all | length) ] as $ix
77
+ | [ $ix[] | select($all[.] | announces) ] as $ann
78
+ | if ($ann | length) == 0 then { empty: true } else
79
+
80
+ # A previous ledger closes the run before it. The call running right now has
81
+ # no announcement after it, so it is skipped rather than closing this run.
82
+ ([ $ix[] | select($all[.] | is_stats_call) ]
83
+ | map(select(. < ($ann | max)))
84
+ | if length == 0 then -1 else max end) as $boundary
85
+ | [ $ann[] | select(. > $boundary) ] as $ann
86
+ | ($ann | min) as $from
87
+ | [ $all[$from:][] | select(.timestamp) ] as $run
88
+
89
+ # ---- channel, and the trail if it escalated ----------------------------
90
+ | ([ $ann[] | $all[.] | chan ]
91
+ | reduce .[] as $c ([]; if (. | last) == $c then . else . + [$c] end)) as $trail
92
+
93
+ # ---- clocks ------------------------------------------------------------
94
+ | ($run | map(.timestamp) | min) as $start
95
+ | ($run | map(.timestamp) | max) as $end
96
+ | (($end | ts) - ($start | ts)) as $elapsed
97
+ | ([ range(1; $run | length)
98
+ | select($run[.] | is_prompt)
99
+ | ($run[.].timestamp | ts) - ($run[. - 1].timestamp | ts) ]
100
+ | add // 0) as $waiting
101
+
102
+ # ---- tools and tokens, main loop only ----------------------------------
103
+ | [ $run[] | select((.type == "assistant") and (is_meta | not))
104
+ | .message.content[]? | select(.type == "tool_use") | .name ] as $tools
105
+ | ([ $run[] | select((.type == "assistant") and (is_meta | not))
106
+ | .message.usage.output_tokens // 0 ] | add // 0) as $out_tok
107
+ | ([ $run[] | select((.type == "assistant") and (is_meta | not))
108
+ | .message.usage.cache_read_input_tokens // 0 ] | add // 0) as $cache_tok
109
+
110
+ # ---- dispatched agents -------------------------------------------------
111
+ | ([ $run[] | select(.type == "assistant") | .message.content[]?
112
+ | select(.type == "tool_use" and (.name == "Agent" or .name == "Task"))
113
+ | { key: .id, value: (.input.description // .input.subagent_type // "agent") } ]
114
+ | from_entries) as $labels
115
+ # toolUseResult is whatever the tool returned: object, array or string.
116
+ | [ $run[] | select((.toolUseResult | type) == "object") | select(.toolUseResult.agentId)
117
+ | { label: ($labels[[ .message.content[]? | select(.type == "tool_result") | .tool_use_id ][0]] // "agent"),
118
+ model: (.toolUseResult.resolvedModel // "?"),
119
+ status: (.toolUseResult.status // "?"),
120
+ tokens: (.toolUseResult.totalTokens // 0),
121
+ ms: (.toolUseResult.totalDurationMs // 0),
122
+ tools: (.toolUseResult.totalToolUseCount // 0) } ] as $agents
123
+
124
+ | { empty: false,
125
+ trail: ($trail | join(" → ")),
126
+ elapsed: $elapsed, waiting: $waiting,
127
+ tool_total: ($tools | length),
128
+ tool_top: ([ $tools | group_by(.)[] | { n: length, name: (.[0] | ascii_downcase | split("__") | last) } ]
129
+ | sort_by(-.n) | .[0:3]),
130
+ out_tok: $out_tok, cache_tok: $cache_tok,
131
+ agents: $agents,
132
+ agent_tokens: ([ $agents[].tokens ] | add // 0),
133
+ agent_ms: ([ $agents[].ms ] | add // 0) }
134
+ end
135
+ ' "$TRANSCRIPT" 2>/dev/null)"
136
+
137
+ if [ -z "$SUMMARY" ]; then
138
+ echo "run-stats: could not parse '$TRANSCRIPT'" >&2
139
+ exit 1
140
+ fi
141
+
142
+ if [ "$(jq -r '.empty' <<<"$SUMMARY")" = "true" ]; then
143
+ exit 2
144
+ fi
145
+
146
+ # ---- formatting -------------------------------------------------------------
147
+
148
+ dur() { # seconds -> 1h05m | 3m11s | 42s
149
+ local s=${1%.*} h m
150
+ h=$((s / 3600)); m=$(((s % 3600) / 60)); s=$((s % 60))
151
+ if [ "$h" -gt 0 ]; then printf '%dh%02dm' "$h" "$m"
152
+ elif [ "$m" -gt 0 ]; then printf '%dm%ds' "$m" "$s"
153
+ else printf '%ds' "$s"; fi
154
+ }
155
+
156
+ tok() { # tokens -> 1.2M | 313k | 51.0k | 350
157
+ local n=$1
158
+ if [ "$n" -ge 1000000 ]; then awk -v n="$n" 'BEGIN{printf "%.1fM", n/1000000}'
159
+ elif [ "$n" -ge 100000 ]; then awk -v n="$n" 'BEGIN{printf "%dk", n/1000}'
160
+ elif [ "$n" -ge 1000 ]; then awk -v n="$n" 'BEGIN{printf "%.1fk", n/1000}'
161
+ else printf '%d' "$n"; fi
162
+ }
163
+
164
+ g() { jq -r "$1" <<<"$SUMMARY"; }
165
+
166
+ printf '─── run %s\n' "$(printf '─%.0s' $(seq 1 36))"
167
+ printf 'channel %s\n' "$(g '.trail')"
168
+
169
+ elapsed=$(g '.elapsed'); waiting=$(g '.waiting')
170
+ if [ "${waiting%.*}" -gt 0 ]; then
171
+ printf 'elapsed %s · %s of that waiting on you\n' "$(dur "$elapsed")" "$(dur "$waiting")"
172
+ else
173
+ printf 'elapsed %s\n' "$(dur "$elapsed")"
174
+ fi
175
+
176
+ top="$(g '[.tool_top[] | "\(.n) \(.name)"] | join(" · ")')"
177
+ if [ -n "$top" ]; then
178
+ printf 'tools %s · %s\n' "$(g '.tool_total')" "$top"
179
+ else
180
+ printf 'tools %s\n' "$(g '.tool_total')"
181
+ fi
182
+
183
+ printf 'tokens %s out · %s cache read\n' "$(tok "$(g '.out_tok')")" "$(tok "$(g '.cache_tok')")"
184
+ printf 'tests %s\n' "${TESTS:-not reported}"
185
+
186
+ # The diff is the branch as a whole against where it left the base, working
187
+ # tree included, because uncommitted work is still work this run produced.
188
+ if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
189
+ if [ -z "$BASE" ]; then
190
+ for cand in main master; do
191
+ if git rev-parse --verify --quiet "$cand" >/dev/null 2>&1; then
192
+ BASE="$(git merge-base HEAD "$cand" 2>/dev/null)" && break
193
+ fi
194
+ done
195
+ [ -z "$BASE" ] && BASE="HEAD"
196
+ fi
197
+ stat="$(git diff --shortstat "$BASE" 2>/dev/null | sed 's/^ *//')"
198
+ files="$(git diff --name-only "$BASE" 2>/dev/null | wc -l | tr -d ' ')"
199
+ # awk, not sed: BSD sed has no \+ and silently matches nothing.
200
+ ins="$(awk '{for(i=1;i<=NF;i++) if($i ~ /^insertion/) print $(i-1)}' <<<"$stat")"
201
+ del="$(awk '{for(i=1;i<=NF;i++) if($i ~ /^deletion/) print $(i-1)}' <<<"$stat")"
202
+ ins=${ins:-0}; del=${del:-0}
203
+
204
+ # A file the run created is not in the diff until it is staged, and staging
205
+ # it here would be a side effect on someone else's index. Count it instead.
206
+ while IFS= read -r f; do
207
+ [ -n "$f" ] || continue
208
+ files=$((files + 1))
209
+ ins=$((ins + $(wc -l <"$f" 2>/dev/null || echo 0)))
210
+ done < <(git ls-files --others --exclude-standard 2>/dev/null)
211
+
212
+ [ "$files" -gt 0 ] && printf 'diff +%s −%s across %s files\n' "$ins" "$del" "$files"
213
+ fi
214
+
215
+ count=$(g '.agents | length')
216
+ if [ "$count" -eq 0 ]; then
217
+ printf 'agents none dispatched\n'
218
+ else
219
+ printf 'agents %s dispatched · %s tok · %s wall\n' \
220
+ "$count" "$(tok "$(g '.agent_tokens')")" "$(dur "$(( $(g '.agent_ms') / 1000 ))")"
221
+
222
+ # Up to a dozen rows read as the narrative of the plan. Past that the order
223
+ # stops helping, so show what the run actually spent on and say what is cut.
224
+ if [ "$count" -le 12 ]; then rows='.agents[]'; else rows='(.agents | sort_by(-.tokens) | .[0:10][])'; fi
225
+ while IFS=$'\t' read -r label model status tokens ms tools; do
226
+ model="${model#claude-}"; model="$(sed 's/-[0-9]\{8\}$//' <<<"$model")"
227
+ [ "$status" = "completed" ] && status="" || status=" ($status)"
228
+ printf ' %-22.22s %-10s %6s %7s %s tools%s\n' \
229
+ "$label" "$model" "$(tok "$tokens")" "$(dur "$((ms / 1000))")" "$tools" "$status"
230
+ done < <(g "$rows | [.label, .model, .status, .tokens, .ms, .tools] | @tsv")
231
+
232
+ if [ "$count" -gt 12 ]; then
233
+ printf ' +%s more · %s tok (dearest 10 shown)\n' \
234
+ "$((count - 10))" "$(tok "$(g '(.agents | sort_by(-.tokens) | .[10:] | map(.tokens) | add) // 0')")"
235
+ fi
236
+ fi
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "sluice",
3
- "version": "1.0.0",
4
- "description": "Routes work by change shape into four channels (bypass, fast, main, deep) and applies only the rules each channel needs, so a one-line fix does not pay the cost of a multi-subsystem build. Carries six rules as one-liners in the router and the full treatment in references read only on friction. Claude Code only; conflicts with the superpowers plugin.",
3
+ "version": "0.1.1",
4
+ "description": "Routes work by change shape into four channels (bypass, fast, main, deep) and applies only the rules each channel needs, so a one-line fix does not pay the cost of a multi-subsystem build. Carries seven rules as one-liners in the router and the full treatment in references read only on friction, and closes each run with a ledger read out of the session transcript: elapsed, tools, tokens, and what every dispatched agent cost. Claude Code only; conflicts with the superpowers plugin.",
5
5
  "author": "iceinvein",
6
6
  "type": "prompt",
7
7
  "tools": [
@@ -12,7 +12,8 @@
12
12
  },
13
13
  "bundle": {
14
14
  "include": [
15
- "references"
15
+ "references",
16
+ "scripts"
16
17
  ]
17
18
  },
18
19
  "install": {