@iceinvein/agent-skills 0.23.4 → 0.23.7

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 (25) hide show
  1. package/package.json +1 -1
  2. package/skills/bug-hunter/SKILL.md +5 -0
  3. package/skills/bug-hunter/evals/README.md +26 -2
  4. package/skills/bug-hunter/evals/every-member-of-a-set-claim/case.yaml +4 -0
  5. package/skills/bug-hunter/evals/every-member-of-a-set-claim/fixture.sh +116 -0
  6. package/skills/bug-hunter/evals/every-member-of-a-set-claim/graders/bug-for-unlogged-cap-cut.md +50 -0
  7. package/skills/bug-hunter/evals/every-member-of-a-set-claim/graders/bug-hunter-fired.md +5 -0
  8. package/skills/bug-hunter/evals/every-member-of-a-set-claim/prompt.md +11 -0
  9. package/skills/bug-hunter/evals/false-success-is-a-bug/case.yaml +4 -0
  10. package/skills/bug-hunter/evals/false-success-is-a-bug/fixture.sh +112 -0
  11. package/skills/bug-hunter/evals/false-success-is-a-bug/graders/bug-for-close-after-rename.md +50 -0
  12. package/skills/bug-hunter/evals/false-success-is-a-bug/graders/bug-hunter-fired.md +5 -0
  13. package/skills/bug-hunter/evals/false-success-is-a-bug/prompt.md +11 -0
  14. package/skills/bug-hunter/evals/rules.json +14 -3
  15. package/skills/bug-hunter/skill.json +1 -1
  16. package/skills/index.json +4 -4
  17. package/skills/migrate/scripts/__tests__/e2e-express.test.ts +2 -1
  18. package/skills/migrate/scripts/__tests__/e2e-webforms.test.ts +2 -1
  19. package/skills/migrate/skill.json +1 -1
  20. package/skills/sluice/references/status.md +95 -4
  21. package/skills/sluice/scripts/board-index.sh +160 -0
  22. package/skills/sluice/scripts/board.sh +304 -0
  23. package/skills/sluice/scripts/status.sh +50 -3
  24. package/skills/sluice/scripts/stop-guard.sh +107 -44
  25. package/skills/sluice/skill.json +2 -2
@@ -62,6 +62,106 @@ if [ -n "$input" ]; then
62
62
  sid="$(printf '%s' "$input" | jq -r '.session_id // empty' 2>/dev/null || true)"
63
63
  transcript="$(printf '%s' "$input" | jq -r '.transcript_path // empty' 2>/dev/null || true)"
64
64
  fi
65
+
66
+ # The run the session's own tree answers for, and only that: the state beside
67
+ # it, or the state it forwarded into a worktree when `move` sent the run on
68
+ # without the session. status.sh also lets a tree with no run of its own read
69
+ # the main worktree's, and that one is not taken here: a Stop in such a tree may
70
+ # be an unrelated session, and a remedy printed to it would reach into somebody
71
+ # else's run.
72
+ #
73
+ # The forward is read here rather than left to status.sh's resolution because
74
+ # the two questions differ. Resolution answers "which run can this tree read",
75
+ # which is the right question for a render and the wrong one for a refusal. The
76
+ # tree named in the note is then passed as `--dir`, so what follows asks about
77
+ # one named tree and carries no layout knowledge of its own.
78
+ #
79
+ # The run this session opened in a worktree it never entered is its own too,
80
+ # found through the pointer `init` left at the main tree, and it comes ahead of
81
+ # the forward: the forward names whichever run was moved last, which with
82
+ # several controllers in the main tree may be another session's.
83
+ #
84
+ # Sets top to the stop's tree and run_tree to the tree holding that run, or
85
+ # leaves run_tree empty when there is none. One function for the run guard and
86
+ # the board, so the run that keeps a deep session off the board is the run the
87
+ # guard would refuse over.
88
+ top=""
89
+ run_tree=""
90
+ resolved=""
91
+ resolve_run_tree() {
92
+ local main session_tree="" forward=""
93
+ resolved=1
94
+ run_tree=""
95
+ top="$(git -C "$cwd" rev-parse --show-toplevel 2>/dev/null)"
96
+ [ -n "$top" ] || top="$cwd"
97
+ case "$sid" in
98
+ "" | */* | .*) ;;
99
+ *)
100
+ main="$(git -C "$cwd" worktree list --porcelain 2>/dev/null | sed -n '1s/^worktree //p')"
101
+ [ -n "$main" ] && [ -f "$main/.sluice/sessions/$sid" ] \
102
+ && IFS= read -r session_tree <"$main/.sluice/sessions/$sid" 2>/dev/null
103
+ if [ -z "$session_tree" ] || [ ! -f "$session_tree/.sluice/run.json" ] \
104
+ || [ "$(jq -r '.session // empty' "$session_tree/.sluice/run.json" 2>/dev/null)" != "$sid" ]; then
105
+ session_tree=""
106
+ fi
107
+ ;;
108
+ esac
109
+ if [ -f "$top/.sluice/run.json" ]; then
110
+ run_tree="$top"
111
+ elif [ -n "$session_tree" ]; then
112
+ run_tree="$session_tree"
113
+ elif [ -f "$top/.sluice/run.at" ]; then
114
+ IFS= read -r forward <"$top/.sluice/run.at" 2>/dev/null || return 0
115
+ # A note outliving the run it named is stale, not a run to refuse a stop over.
116
+ [ -n "$forward" ] && [ -f "$forward/.sluice/run.json" ] || return 0
117
+ run_tree="$forward"
118
+ fi
119
+ }
120
+
121
+ # A stop let through is the session handing back, so the board marks it. A
122
+ # trap rather than a call before each exit: the hook lets a stop through from
123
+ # a dozen places, and a new one added later is covered without anyone having
124
+ # to remember the board. Every block decision sets refused first, since a
125
+ # refused stop is the session still working. The board's output and failure
126
+ # are dropped: stdout carries the decision, and the hook always exits 0.
127
+ #
128
+ # A deep session whose run is live keeps its entry, because the board draws the
129
+ # run's bar for it. Whether the run is live is asked here rather than taken
130
+ # from the run guard, since most exits come before the guard runs, and a deep
131
+ # session mid-run must not read as handed back on any of them. Resolving costs
132
+ # git calls on every stop, so it is skipped for a session with no entry, and
133
+ # not repeated when the run guard already did it.
134
+ #
135
+ # The run resolved is only the one this tree can reach, which through a forward
136
+ # or a shared tree may be another session's; that run's bar is not this
137
+ # session's work, so the entry is kept only for a run that names this session.
138
+ # A run naming no session is no session's own, as the board draws it, and a
139
+ # state file that cannot be read names nobody it can be kept for.
140
+ refused=""
141
+ board_handback() {
142
+ local keep="" root owner
143
+ [ -z "$refused" ] && [ -n "$sid" ] || return 0
144
+ case "$sid" in */* | .*) return 0 ;; esac
145
+ # Must match the root board-index.sh and board.sh resolve.
146
+ if [ -n "${SLUICE_BOARD_DIR:-}" ]; then
147
+ root="$SLUICE_BOARD_DIR"
148
+ elif [ -n "${CLAUDE_CONFIG_DIR:-}" ]; then
149
+ root="$CLAUDE_CONFIG_DIR/sluice/board"
150
+ elif [ -n "${HOME:-}" ]; then
151
+ root="$HOME/.claude/sluice/board"
152
+ else
153
+ return 0
154
+ fi
155
+ [ -f "$root/sessions/$sid.json" ] || return 0
156
+ [ -n "$resolved" ] || resolve_run_tree
157
+ if [ -n "$run_tree" ] && owner="$(jq -r '.session // empty' "$run_tree/.sluice/run.json" 2>/dev/null)" \
158
+ && [ "$owner" = "$sid" ]; then
159
+ keep="--keep-deep"
160
+ fi
161
+ bash "$here/board-index.sh" handback "$sid" $keep >/dev/null 2>&1 || true
162
+ }
163
+ trap board_handback EXIT
164
+
65
165
  [ "$active" = "true" ] && exit 0
66
166
  [ -d "$cwd" ] || exit 0
67
167
 
@@ -175,6 +275,7 @@ entry_nudge() {
175
275
 
176
276
  mkdir -p "$root/nudged" 2>/dev/null || true
177
277
  : >"$root/nudged/$sid" 2>/dev/null || true
278
+ refused=1
178
279
  jq -n '{
179
280
  decision: "block",
180
281
  reason: "sluice: this session has changed the tree since it opened and no channel was ever announced, so the work is running with none of the rules that its shape calls for and nothing to meter it from. Invoke the sluice skill now, route what you have been doing, and say which channel it is. If it genuinely changed nothing you own (a scratch file, or an edit that was not yours), say so and stop; this will not ask twice."
@@ -225,6 +326,7 @@ verify_gate() {
225
326
  [ -f "$moved_tree/.sluice/verified" ] && recorded="$(sed -n '1p' "$moved_tree/.sluice/verified" 2>/dev/null)"
226
327
  [ "$recorded" = "$now" ] && return 0
227
328
 
329
+ refused=1
228
330
  jq -n --arg channel "$channel" '{
229
331
  decision: "block",
230
332
  reason: ("sluice: this \($channel) session has changed the tree and no passing check is recorded against it as it stands now, so the handback would carry a change nothing has proved. Run the check that proves it through the recorder: status.sh verify -- <cmd> (the tests, the build, whatever exercises this change). If no check can cover it (docs, config), record why: status.sh verify --none \"<reason>\". Then hand back.")
@@ -305,6 +407,7 @@ test_gate() {
305
407
 
306
408
  # Paths land in a reason the harness prints, so control bytes are stripped
307
409
  # as the run guard strips them from the topic and tree.
410
+ refused=1
308
411
  jq -n --arg paths "$missing" --arg base "$base" '{
309
412
  decision: "block",
310
413
  reason: ("sluice: this session changed existing tests in a way that asks less of the code (an edited or removed assertion, a deleted test, a skip or focus marker), and nothing records why: "
@@ -320,50 +423,9 @@ fi
320
423
 
321
424
  [ -f "$STATUS" ] || exit 0
322
425
 
323
- # The run the session's own tree answers for, and only that: the state beside
324
- # it, or the state it forwarded into a worktree when `move` sent the run on
325
- # without the session. status.sh also lets a tree with no run of its own read
326
- # the main worktree's, and that one is not taken here: a Stop in such a tree may
327
- # be an unrelated session, and a remedy printed to it would reach into somebody
328
- # else's run.
329
- #
330
- # The forward is read here rather than left to status.sh's resolution because
331
- # the two questions differ. Resolution answers "which run can this tree read",
332
- # which is the right question for a render and the wrong one for a refusal. The
333
- # tree named in the note is then passed as `--dir`, so what follows asks about
334
- # one named tree and carries no layout knowledge of its own.
335
- #
336
- # The run this session opened in a worktree it never entered is its own too,
337
- # found through the pointer `init` left at the main tree, and it comes ahead of
338
- # the forward: the forward names whichever run was moved last, which with
339
- # several controllers in the main tree may be another session's.
340
- top="$(git -C "$cwd" rev-parse --show-toplevel 2>/dev/null)"
341
- [ -n "$top" ] || top="$cwd"
342
- session_tree=""
343
- case "$sid" in
344
- "" | */* | .*) ;;
345
- *)
346
- main="$(git -C "$cwd" worktree list --porcelain 2>/dev/null | sed -n '1s/^worktree //p')"
347
- [ -n "$main" ] && [ -f "$main/.sluice/sessions/$sid" ] \
348
- && IFS= read -r session_tree <"$main/.sluice/sessions/$sid" 2>/dev/null
349
- if [ -z "$session_tree" ] || [ ! -f "$session_tree/.sluice/run.json" ] \
350
- || [ "$(jq -r '.session // empty' "$session_tree/.sluice/run.json" 2>/dev/null)" != "$sid" ]; then
351
- session_tree=""
352
- fi
353
- ;;
354
- esac
355
- if [ -f "$top/.sluice/run.json" ]; then
356
- tree="$top"
357
- elif [ -n "$session_tree" ]; then
358
- tree="$session_tree"
359
- elif [ -f "$top/.sluice/run.at" ]; then
360
- tree=""
361
- IFS= read -r tree <"$top/.sluice/run.at" 2>/dev/null || exit 0
362
- # A note outliving the run it named is stale, not a run to refuse a stop over.
363
- [ -n "$tree" ] && [ -f "$tree/.sluice/run.json" ] || exit 0
364
- else
365
- exit 0
366
- fi
426
+ resolve_run_tree
427
+ [ -n "$run_tree" ] || exit 0
428
+ tree="$run_tree"
367
429
 
368
430
  run="$(bash "$STATUS" show --json --dir "$tree" 2>/dev/null)" || exit 0
369
431
  [ -n "$run" ] || exit 0
@@ -413,6 +475,7 @@ verdict="$(printf '%s' "$run" | jq -c --argjson now "$(date -u +%s)" --arg tree
413
475
  # anyone into. What replaces it is the step `move` could not take -- the session
414
476
  # following the run -- because a controller guarded here is a controller sitting
415
477
  # in the tree its own run left.
478
+ refused=1
416
479
  printf '%s' "$verdict" | jq 2>/dev/null '{
417
480
  decision: "block",
418
481
  reason: ("sluice: the deep run \(.topic) is \(.progress) done with tasks still to go and nothing marked blocked or paused, so ending the turn here hands a live run back with nothing for your partner to decide. Still open: \(.open)."
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "sluice",
3
- "version": "0.25.4",
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 nine rules as one-liners in the router and the full treatment in references read only on friction. Checks the finished plan with plan.sh validate rather than trusting it to memory, seeds the run state from it, keeps a deep run's task breakdown in .sluice/run.json so a statusline segment, one status command and a SessionStart hook can answer where the run is (the hook prints a live run at every session start, compaction included), and closes each run with a ledger read out of the session transcript: elapsed, tools, tokens, and what each dispatched agent cost where the transcript recorded it. Two Stop hooks hold a handback: one refuses a tree that moved since its last recorded check, the other refuses a changed existing test not proven to fail on the session's starting code (or recorded with a reason). Claude Code only; stands down where the superpowers pipeline governs the repo.",
3
+ "version": "0.26.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 nine rules as one-liners in the router and the full treatment in references read only on friction. Checks the finished plan with plan.sh validate rather than trusting it to memory, seeds the run state from it, keeps a deep run's task breakdown in .sluice/run.json so a statusline segment, one status command and a SessionStart hook can answer where the run is (the hook prints a live run at every session start, compaction included), lists every live deep run and routed fast or main session on the machine in one board.sh view (drawn once, watched, or as JSON), and closes each run with a ledger read out of the session transcript: elapsed, tools, tokens, and what each dispatched agent cost where the transcript recorded it. Two Stop hooks hold a handback: one refuses a tree that moved since its last recorded check, the other refuses a changed existing test not proven to fail on the session's starting code (or recorded with a reason). Claude Code only; stands down where the superpowers pipeline governs the repo.",
5
5
  "author": "iceinvein",
6
6
  "type": "prompt",
7
7
  "tools": [