@iceinvein/agent-skills 0.23.2 → 0.23.3
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/README.md +7 -12
- package/package.json +1 -1
- package/skills/index.json +1 -1
- package/skills/sluice/references/status.md +18 -6
- package/skills/sluice/scripts/postinstall.sh +5 -4
- package/skills/sluice/scripts/session-start.sh +1 -1
- package/skills/sluice/scripts/status.sh +87 -2
- package/skills/sluice/scripts/statusline-command.sh +9 -3
- package/skills/sluice/scripts/statusline.sh +20 -8
- package/skills/sluice/scripts/stop-guard.sh +20 -0
- package/skills/sluice/skill.json +1 -1
package/README.md
CHANGED
|
@@ -44,8 +44,8 @@ Each skill is also a plugin in the `iceinvein-skills` marketplace:
|
|
|
44
44
|
|
|
45
45
|
| Skill | What it does |
|
|
46
46
|
|-------|--------------|
|
|
47
|
-
| **migrate** | Walks a legacy codebase through probe, enumerate, seam, extract, parity, queue, adjudicate and handoff: measured surface coverage, an empirically derived capability seam, cited requirements, a parity plan, a batch decision queue, and work items emitted to markdown, GitHub or a flow target with coverage and forecast read back. Bundles a Bun CLI that enforces the coverage arithmetic and phase ordering instead of trusting it. |
|
|
48
|
-
| **scope-estimate** | The lighter path for a proposal: read-only discovery, a capability register with status and disposition, sized functional requirements, contracts-first work packages, and a build schedule across engineers x worktrees at fast, planning and floor rates. Bundles a Bun scheduler that refuses a plan missing or double-counting a requirement, so every figure comes from one reproducible run. |
|
|
47
|
+
| **migrate** | Walks a legacy codebase through probe, enumerate, seam, extract, parity, queue, adjudicate and handoff: measured surface coverage, an empirically derived capability seam, cited requirements, a parity plan, a batch decision queue, and work items emitted to markdown, GitHub or a flow target with coverage and forecast read back. Bundles a Bun CLI that enforces the coverage arithmetic and phase ordering instead of trusting it. Claude Code only. |
|
|
48
|
+
| **scope-estimate** | The lighter path for a proposal: read-only discovery, a capability register with status and disposition, sized functional requirements, contracts-first work packages, and a build schedule across engineers x worktrees at fast, planning and floor rates. Bundles a Bun scheduler that refuses a plan missing or double-counting a requirement, so every figure comes from one reproducible run. Claude Code only. |
|
|
49
49
|
|
|
50
50
|
### Code Architecture
|
|
51
51
|
|
|
@@ -100,7 +100,7 @@ Skills that check code does what it was meant to do, with evidence rather than o
|
|
|
100
100
|
|
|
101
101
|
| Skill | Based on | What it does |
|
|
102
102
|
|-------|----------|--------------|
|
|
103
|
-
| **bug-hunter** | QA scenario testing; intent-first reproduction | Proves logic bugs with failing repros checked against stated intent. Writes scenarios before reading the code, and reports bugs, ambiguities, and covered scenarios. |
|
|
103
|
+
| **bug-hunter** | QA scenario testing; intent-first reproduction | Proves logic bugs with failing repros checked against stated intent. Writes scenarios before reading the code, and reports bugs, ambiguities, and covered scenarios. Claude Code only. |
|
|
104
104
|
|
|
105
105
|
### Communication
|
|
106
106
|
|
|
@@ -128,7 +128,7 @@ Skills that shape how work gets done rather than analyzing code.
|
|
|
128
128
|
|
|
129
129
|
| Skill | What it does |
|
|
130
130
|
|-------|--------------|
|
|
131
|
-
| **sluice** | 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.
|
|
131
|
+
| **sluice** | 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. Eight rules live as one-liners in the router; the full treatment sits in references read only on friction. Each run closes 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, which requires its own fixed pipeline up front for anything that adds to or changes what the software does, not just code edits; disable superpowers before installing. |
|
|
132
132
|
|
|
133
133
|
### Orchestration
|
|
134
134
|
|
|
@@ -137,7 +137,7 @@ Skills that compose the other audit skills into higher-level workflows.
|
|
|
137
137
|
| Skill | What it does |
|
|
138
138
|
|-------|--------------|
|
|
139
139
|
| **improve-my-codebase** | Runs every applicable audit skill in parallel against your codebase, ranks findings by convergence (multiple audits agreeing) and per-file rollup, and writes a prioritized improvement report. Default sweeps everything; positional args switch modes (`quick`, `diff`, `interactive`) or narrow scope (`focus <area>`, `module <path>`). |
|
|
140
|
-
| **magpie** | Interactive PR review pipeline. Runs five parallel specialist subagents (security, bugs, performance, code-smells, architecture), dedupes findings, applies a critic rubric, peer-reviews via `codex exec`, and serves an interactive HTML report for selecting findings to post via `gh`. A local mode reviews the current branch diff before a PR exists and hands the findings back in the terminal. |
|
|
140
|
+
| **magpie** | Interactive PR review pipeline. Runs five parallel specialist subagents (security, bugs, performance, code-smells, architecture), dedupes findings, applies a critic rubric, peer-reviews via `codex exec`, and serves an interactive HTML report for selecting findings to post via `gh`. A local mode reviews the current branch diff before a PR exists and hands the findings back in the terminal. Claude Code only. |
|
|
141
141
|
|
|
142
142
|
## Commands
|
|
143
143
|
|
|
@@ -174,7 +174,7 @@ bunx @iceinvein/agent-skills list # show installed versions
|
|
|
174
174
|
After any command other than `update`, the CLI runs a background freshness check (at most once every 24 hours) and prints a one-line notice if anything is out of date:
|
|
175
175
|
|
|
176
176
|
```
|
|
177
|
-
some-skill (v0.2.0 → v0.3.0)
|
|
177
|
+
⚠ Updates available: some-skill (v0.2.0 → v0.3.0)
|
|
178
178
|
Run: agent-skills update --all
|
|
179
179
|
```
|
|
180
180
|
|
|
@@ -195,12 +195,7 @@ If a skill declares activation support and you're installing for Claude Code int
|
|
|
195
195
|
|
|
196
196
|
## Supported Tools
|
|
197
197
|
|
|
198
|
-
|
|
199
|
-
|------|:---:|:---:|
|
|
200
|
-
| Claude Code | Yes | Yes |
|
|
201
|
-
| Cursor | Yes | Yes |
|
|
202
|
-
| Codex | Yes | – |
|
|
203
|
-
| Gemini CLI | Yes | Yes |
|
|
198
|
+
Claude Code, Cursor, Codex and Gemini CLI. Most skills install into all four; bug-hunter, magpie, migrate, scope-estimate and sluice are Claude Code only, and the CLI skips the other tools for them.
|
|
204
199
|
|
|
205
200
|
## How It Works
|
|
206
201
|
|
package/package.json
CHANGED
package/skills/index.json
CHANGED
|
@@ -295,7 +295,7 @@
|
|
|
295
295
|
"name": "sluice",
|
|
296
296
|
"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.",
|
|
297
297
|
"type": "prompt",
|
|
298
|
-
"version": "0.25.
|
|
298
|
+
"version": "0.25.3"
|
|
299
299
|
},
|
|
300
300
|
{
|
|
301
301
|
"name": "temporal-coupling-detector",
|
|
@@ -145,6 +145,16 @@ the run it names is really there. `close` removes a note naming its own tree,
|
|
|
145
145
|
because a note that outlived its run would hand the next run opened in that
|
|
146
146
|
tree to whoever reads the main tree.
|
|
147
147
|
|
|
148
|
+
One note cannot serve a repo whose own rules cut worktrees by hand, outside the
|
|
149
|
+
repo, where no worktree tool will enter them: every controller sits in the main
|
|
150
|
+
tree at once, each with its own run elsewhere, and none of them ran `move`. So
|
|
151
|
+
`init` also records the session that opened the run (`--session`, defaulting to
|
|
152
|
+
`$CLAUDE_CODE_SESSION_ID`) and leaves `.sluice/sessions/<id>` at the main
|
|
153
|
+
worktree naming the run's tree. A tree with no run of its own follows that
|
|
154
|
+
pointer before the note, only while the run there is still that session's.
|
|
155
|
+
`move` points it at the new tree and `close` removes it, so the statusline, the
|
|
156
|
+
SessionStart hook and the Stop hook each find the run of the session asking.
|
|
157
|
+
|
|
148
158
|
One file for several writers is one file to contend on, so `init`, `task`,
|
|
149
159
|
`preflight`, `final`, `pause`, `resume`, `close` and `move` take a lock first, `move` taking the
|
|
150
160
|
destination tree's as well as its own: two flips issued at the same moment
|
|
@@ -380,10 +390,10 @@ is live and contends with nothing for width when one is, which is what lets the
|
|
|
380
390
|
bar be wide and the task carry its name rather than only its number.
|
|
381
391
|
|
|
382
392
|
Capture it wherever the statusline command builds its other lines, passing the
|
|
383
|
-
directory the session is in and nothing else:
|
|
393
|
+
directory the session is in and the session's id, and nothing else:
|
|
384
394
|
|
|
385
395
|
```bash
|
|
386
|
-
sluice_line=$(bash "$HOME/.claude/skills/sluice/scripts/statusline.sh" --dir "$cwd" 2>/dev/null)
|
|
396
|
+
sluice_line=$(bash "$HOME/.claude/skills/sluice/scripts/statusline.sh" --dir "$cwd" --session "$session_id" 2>/dev/null)
|
|
387
397
|
```
|
|
388
398
|
|
|
389
399
|
then print it last, after whatever else the command emits:
|
|
@@ -392,10 +402,12 @@ then print it last, after whatever else the command emits:
|
|
|
392
402
|
if [ -n "$sluice_line" ]; then printf '%s\n' "$sluice_line"; fi
|
|
393
403
|
```
|
|
394
404
|
|
|
395
|
-
`$cwd` is `workspace.current_dir`
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
405
|
+
`$cwd` is `workspace.current_dir` and `$session_id` is `session_id`, both from
|
|
406
|
+
the JSON the harness sends on stdin. The id is what finds a run this session
|
|
407
|
+
opened in a worktree it never entered, which is every run in a repo that cuts
|
|
408
|
+
its worktrees by hand. Substitute the install path:
|
|
409
|
+
`${CLAUDE_CONFIG_DIR:-$HOME/.claude}` where a session may set one, or
|
|
410
|
+
`$cwd/.claude/skills/sluice/scripts/statusline.sh` for a project-local install.
|
|
399
411
|
|
|
400
412
|
Those two lines are the whole contract, and they are deliberately empty of
|
|
401
413
|
judgement. Whether a run is visible from a given directory is a question about
|
|
@@ -96,11 +96,12 @@ sluice: to show a live run on your status line, add these to your statusline
|
|
|
96
96
|
command. The first goes wherever it builds its other lines, the second last of
|
|
97
97
|
all, after everything else it prints:
|
|
98
98
|
|
|
99
|
-
sluice_line=\$(bash "$RENDER" --dir "\$cwd" 2>/dev/null)
|
|
99
|
+
sluice_line=\$(bash "$RENDER" --dir "\$cwd" --session "\$session_id" 2>/dev/null)
|
|
100
100
|
|
|
101
101
|
if [ -n "\$sluice_line" ]; then printf '%s\\n' "\$sluice_line"; fi
|
|
102
102
|
|
|
103
|
-
\$cwd is workspace.current_dir
|
|
104
|
-
two lines carry no knowledge of where a run
|
|
105
|
-
skill changes; every install updates the
|
|
103
|
+
\$cwd is workspace.current_dir and \$session_id is session_id, both from the JSON
|
|
104
|
+
the harness sends on stdin. Those two lines carry no knowledge of where a run
|
|
105
|
+
lives, so they stay correct as this skill changes; every install updates the
|
|
106
|
+
script behind them.
|
|
106
107
|
EOF
|
|
@@ -137,7 +137,7 @@ stamp_baseline "$sid" "$source"
|
|
|
137
137
|
[ -d "$cwd" ] || exit 0
|
|
138
138
|
[ -f "$STATUS" ] || exit 0
|
|
139
139
|
|
|
140
|
-
shown="$(bash "$STATUS" show --dir "$cwd" 2>/dev/null)" || exit 0
|
|
140
|
+
shown="$(bash "$STATUS" show --dir "$cwd" ${sid:+--session "$sid"} 2>/dev/null)" || exit 0
|
|
141
141
|
[ -n "$shown" ] || exit 0
|
|
142
142
|
|
|
143
143
|
echo "A sluice run is live. Its state, from .sluice/run.json:"
|
|
@@ -43,6 +43,11 @@
|
|
|
43
43
|
# neither records anything. Its exit 1 also covers a tree whose changed tests
|
|
44
44
|
# cannot be read.
|
|
45
45
|
#
|
|
46
|
+
# --session <id> names the session asking (default: $CLAUDE_CODE_SESSION_ID).
|
|
47
|
+
# `init` records it in the run and leaves <main tree>/.sluice/sessions/<id>
|
|
48
|
+
# naming the tree the run is in, so that session finds its run from the main
|
|
49
|
+
# tree however many other runs the set holds.
|
|
50
|
+
#
|
|
46
51
|
# A flag value may not begin with `--`: unchecked, an omitted value silently
|
|
47
52
|
# becomes the next flag and the field holds a flag name.
|
|
48
53
|
|
|
@@ -95,6 +100,7 @@ in_set() {
|
|
|
95
100
|
# subcommand's own flag loop ever sees it. Everything after `--` is verify's
|
|
96
101
|
# command, kept verbatim: a `--dir` in it belongs to that command, not to us.
|
|
97
102
|
DIR="$PWD"
|
|
103
|
+
SESSION="${CLAUDE_CODE_SESSION_ID-}"
|
|
98
104
|
ARGS=()
|
|
99
105
|
while [ $# -gt 0 ]; do
|
|
100
106
|
case "$1" in
|
|
@@ -108,6 +114,11 @@ while [ $# -gt 0 ]; do
|
|
|
108
114
|
DIR="$2"
|
|
109
115
|
shift 2
|
|
110
116
|
;;
|
|
117
|
+
--session)
|
|
118
|
+
need_value --session $# "${2-}"
|
|
119
|
+
SESSION="$2"
|
|
120
|
+
shift 2
|
|
121
|
+
;;
|
|
111
122
|
*)
|
|
112
123
|
ARGS+=("$1")
|
|
113
124
|
shift
|
|
@@ -116,6 +127,12 @@ while [ $# -gt 0 ]; do
|
|
|
116
127
|
done
|
|
117
128
|
set -- "${ARGS[@]+"${ARGS[@]}"}"
|
|
118
129
|
|
|
130
|
+
# The id becomes a file name under .sluice/sessions, so one that could climb out
|
|
131
|
+
# of that directory is refused rather than followed.
|
|
132
|
+
case "$SESSION" in
|
|
133
|
+
*/* | .*) err "session id $SESSION is not a file name"; exit 4 ;;
|
|
134
|
+
esac
|
|
135
|
+
|
|
119
136
|
SUB="${1-}"
|
|
120
137
|
[ $# -gt 0 ] && shift
|
|
121
138
|
if [ -z "$SUB" ]; then
|
|
@@ -132,6 +149,40 @@ main_tree() { # <dir>
|
|
|
132
149
|
git -C "$1" worktree list --porcelain 2>/dev/null | sed -n '1s/^worktree //p'
|
|
133
150
|
}
|
|
134
151
|
|
|
152
|
+
# The tree a session's run is in, from the pointer `init` left at the main tree.
|
|
153
|
+
# `run.at` cannot answer this: it is one note for the whole set, and a repo that
|
|
154
|
+
# cuts worktrees by hand has every controller sitting in the main tree at once,
|
|
155
|
+
# each with its own run elsewhere. Followed only while the run there is still
|
|
156
|
+
# that session's, so a pointer that outlived its run never hands over the next
|
|
157
|
+
# one opened in that tree. Empty when there is no such run.
|
|
158
|
+
session_run() { # <main tree>
|
|
159
|
+
[ -n "$SESSION" ] && [ -n "$1" ] && [ -f "$1/.sluice/sessions/$SESSION" ] || return 0
|
|
160
|
+
local at=""
|
|
161
|
+
IFS= read -r at <"$1/.sluice/sessions/$SESSION" 2>/dev/null || true
|
|
162
|
+
[ -n "$at" ] && [ -f "$at/.sluice/run.json" ] || return 0
|
|
163
|
+
[ "$(jq -r '.session // empty' "$at/.sluice/run.json" 2>/dev/null)" = "$SESSION" ] || return 0
|
|
164
|
+
printf '%s\n' "$at"
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
# Like `leave_note`, a pointer that could not be written is a blank statusline
|
|
168
|
+
# for one session rather than a failed command, so the caller reports it.
|
|
169
|
+
point_session() { # <main tree> <session id> <tree the run is in>
|
|
170
|
+
mkdir -p "$1/.sluice/sessions" 2>/dev/null || return 1
|
|
171
|
+
[ -e "$1/.sluice/.gitignore" ] || printf '*\n' >"$1/.sluice/.gitignore" 2>/dev/null || true
|
|
172
|
+
printf '%s\n' "$3" >"$1/.sluice/sessions/$2" 2>/dev/null
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
# The session a run was opened by, read back from the run itself rather than
|
|
176
|
+
# from whoever is asking: `move` and `close` may come from another session.
|
|
177
|
+
run_owner() { # <state file>
|
|
178
|
+
local owner
|
|
179
|
+
owner="$(jq -r '.session // empty' "$1" 2>/dev/null)"
|
|
180
|
+
case "$owner" in
|
|
181
|
+
*/* | .*) return 0 ;;
|
|
182
|
+
esac
|
|
183
|
+
printf '%s' "$owner"
|
|
184
|
+
}
|
|
185
|
+
|
|
135
186
|
# A tree's own run comes first, and only a tree with none reads the set's. Two
|
|
136
187
|
# layouts share this script and pull opposite ways. A deep run plans in the main
|
|
137
188
|
# tree and cuts implementer worktrees after the plan: the run directory ignores
|
|
@@ -160,8 +211,12 @@ if [ "$SUB" != "init" ] && [ ! -f "$DIR/.sluice/run.json" ] \
|
|
|
160
211
|
# package straight past that worktree's run to the main tree's.
|
|
161
212
|
OWN_TREE="$(git -C "$DIR" rev-parse --show-toplevel 2>/dev/null)"
|
|
162
213
|
MAIN_TREE="$(main_tree "$DIR")"
|
|
214
|
+
# The session's own run outranks the set's, which may be anybody's.
|
|
215
|
+
SESSION_TREE="$(session_run "${MAIN_TREE:-}")"
|
|
163
216
|
if [ -n "${OWN_TREE:-}" ] && [ -f "$OWN_TREE/.sluice/run.json" ]; then
|
|
164
217
|
DIR="$OWN_TREE"
|
|
218
|
+
elif [ -n "$SESSION_TREE" ]; then
|
|
219
|
+
DIR="$SESSION_TREE"
|
|
165
220
|
elif [ -n "${MAIN_TREE:-}" ] && [ -d "$MAIN_TREE" ]; then
|
|
166
221
|
DIR="$MAIN_TREE"
|
|
167
222
|
|
|
@@ -841,12 +896,20 @@ case "$SUB" in
|
|
|
841
896
|
--arg channel "$CHANNEL" \
|
|
842
897
|
--arg started "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
|
|
843
898
|
--arg plan "$PLAN" \
|
|
844
|
-
--arg record "$RECORD"
|
|
899
|
+
--arg record "$RECORD" \
|
|
900
|
+
--arg session "$SESSION" '
|
|
845
901
|
{schema: 1, topic: $topic, channel: $channel, started: $started}
|
|
846
902
|
+ (if $plan == "" then {} else {plan: $plan} end)
|
|
847
903
|
+ (if $record == "" then {} else {record: $record} end)
|
|
904
|
+
+ (if $session == "" then {} else {session: $session} end)
|
|
848
905
|
+ {tasks: []}
|
|
849
|
-
' | write_state
|
|
906
|
+
' | write_state || exit $?
|
|
907
|
+
|
|
908
|
+
POINTER_TREE="$(main_tree "$DIR")"
|
|
909
|
+
if [ -n "$SESSION" ] && [ -n "$POINTER_TREE" ] && [ -d "$POINTER_TREE" ] \
|
|
910
|
+
&& ! point_session "$POINTER_TREE" "$SESSION" "$(cd "$DIR" && pwd -P)"; then
|
|
911
|
+
err "note: the run is open, but $POINTER_TREE/.sluice/sessions/$SESSION could not be written, so this session reads no run from $POINTER_TREE"
|
|
912
|
+
fi
|
|
850
913
|
;;
|
|
851
914
|
|
|
852
915
|
task)
|
|
@@ -1263,6 +1326,20 @@ case "$SUB" in
|
|
|
1263
1326
|
elif ! leave_note "$NOTE_TREE" "$TO"; then
|
|
1264
1327
|
err "note: the run moved, but $NOTE_TREE/.sluice/run.at could not be written, so a session in $NOTE_TREE will read no run until it moves to $TO"
|
|
1265
1328
|
fi
|
|
1329
|
+
# The note above names only the last run moved; the owner's pointer
|
|
1330
|
+
# keeps finding this one after another run is moved past it. Only a
|
|
1331
|
+
# pointer still naming the tree left behind follows the run: one
|
|
1332
|
+
# naming elsewhere is on a later run the same session opened.
|
|
1333
|
+
OWNER="$(run_owner "$DEST_STATE")"
|
|
1334
|
+
if [ -n "$OWNER" ]; then
|
|
1335
|
+
AT=""
|
|
1336
|
+
[ -f "$NOTE_TREE/.sluice/sessions/$OWNER" ] \
|
|
1337
|
+
&& IFS= read -r AT <"$NOTE_TREE/.sluice/sessions/$OWNER" 2>/dev/null
|
|
1338
|
+
if { [ -z "$AT" ] || [ "$AT" = "$(cd "$DIR" && pwd -P)" ]; } \
|
|
1339
|
+
&& ! point_session "$NOTE_TREE" "$OWNER" "$TO"; then
|
|
1340
|
+
err "note: the run moved, but $NOTE_TREE/.sluice/sessions/$OWNER could not be written"
|
|
1341
|
+
fi
|
|
1342
|
+
fi
|
|
1266
1343
|
fi
|
|
1267
1344
|
|
|
1268
1345
|
echo "moved $(jq -r '.topic // "run"' "$DEST_STATE" 2>/dev/null || echo run) to $TO"
|
|
@@ -1300,6 +1377,7 @@ case "$SUB" in
|
|
|
1300
1377
|
# file aside intact rather than leaving it wedged in the tree.
|
|
1301
1378
|
started="$(jq -r '.started // empty' "$STATE" 2>/dev/null)"
|
|
1302
1379
|
topic="$(jq -r '.topic // empty' "$STATE" 2>/dev/null)"
|
|
1380
|
+
owner="$(run_owner "$STATE")"
|
|
1303
1381
|
stamp="$(printf '%s' "$started" | tr -cd '0-9TZ')"
|
|
1304
1382
|
slug="$(printf '%s' "${topic:-run}" | tr -cs 'A-Za-z0-9._-' '-')"
|
|
1305
1383
|
[ -n "$stamp" ] || stamp="unknown"
|
|
@@ -1328,6 +1406,13 @@ case "$SUB" in
|
|
|
1328
1406
|
IFS= read -r AT <"$NOTE_TREE/.sluice/run.at" 2>/dev/null || true
|
|
1329
1407
|
[ "$AT" = "$(cd "$DIR" && pwd -P)" ] && rm -f "$NOTE_TREE/.sluice/run.at"
|
|
1330
1408
|
fi
|
|
1409
|
+
# The same holds for the owner's pointer, which may by now name a later
|
|
1410
|
+
# run the same session opened elsewhere.
|
|
1411
|
+
if [ -n "$NOTE_TREE" ] && [ -n "$owner" ] && [ -f "$NOTE_TREE/.sluice/sessions/$owner" ]; then
|
|
1412
|
+
AT=""
|
|
1413
|
+
IFS= read -r AT <"$NOTE_TREE/.sluice/sessions/$owner" 2>/dev/null || true
|
|
1414
|
+
[ "$AT" = "$(cd "$DIR" && pwd -P)" ] && rm -f "$NOTE_TREE/.sluice/sessions/$owner"
|
|
1415
|
+
fi
|
|
1331
1416
|
# The summary needs parseable state and close is the one command that
|
|
1332
1417
|
# does not, so an unreadable run still gets a line naming where it went.
|
|
1333
1418
|
[ -n "$summary" ] || summary="closed $(basename "$dest"): state was unreadable, no summary"
|
|
@@ -36,7 +36,13 @@ IFS= read -r -d "" -t 2 input || true
|
|
|
36
36
|
# `cwd` is the older spelling of the same field and both are still sent, so it
|
|
37
37
|
# is a fallback rather than a bug. Neither present is not an error worth a row:
|
|
38
38
|
# it means this is not the JSON we were written against.
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
#
|
|
40
|
+
# One jq for both fields, a line each: the render budget has no room for a
|
|
41
|
+
# second fork, and a newline cannot appear in either value.
|
|
42
|
+
{
|
|
43
|
+
IFS= read -r dir
|
|
44
|
+
IFS= read -r session
|
|
45
|
+
} < <(printf '%s' "$input" | jq -r '(.workspace.current_dir // .cwd // ""), (.session_id // "")' 2>/dev/null || true)
|
|
46
|
+
[ -n "${dir:-}" ] || exit 0
|
|
41
47
|
|
|
42
|
-
bash "$RENDER" --dir "$dir" 2>/dev/null || exit 0
|
|
48
|
+
bash "$RENDER" --dir "$dir" ${session:+--session "$session"} 2>/dev/null || exit 0
|
|
@@ -3,12 +3,13 @@
|
|
|
3
3
|
# call. The caller passes the directory the session is in and prints whatever
|
|
4
4
|
# comes back.
|
|
5
5
|
#
|
|
6
|
-
# statusline.sh [--dir <path>]
|
|
6
|
+
# statusline.sh [--dir <path>] [--session <id>]
|
|
7
7
|
#
|
|
8
|
-
# --dir defaults to $PWD.
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
8
|
+
# --dir defaults to $PWD. --session is the harness's session_id, which finds the
|
|
9
|
+
# run that session opened in another worktree while it sits in the main tree.
|
|
10
|
+
# Always exits 0 in silence on anything it cannot render: a status bar draws on
|
|
11
|
+
# every keystroke and has nowhere to put an error, so a message here would be
|
|
12
|
+
# permanent clutter rather than a report anyone acts on.
|
|
12
13
|
#
|
|
13
14
|
# Why this exists as its own script rather than as a snippet in the caller.
|
|
14
15
|
# Whether a run is visible from a given directory is a question about sluice's
|
|
@@ -36,6 +37,7 @@ here="$(cd "$(dirname "$0")" && pwd)"
|
|
|
36
37
|
STATUS="$here/status.sh"
|
|
37
38
|
|
|
38
39
|
DIR="$PWD"
|
|
40
|
+
SESSION=""
|
|
39
41
|
while [ $# -gt 0 ]; do
|
|
40
42
|
case "$1" in
|
|
41
43
|
--dir)
|
|
@@ -44,6 +46,11 @@ while [ $# -gt 0 ]; do
|
|
|
44
46
|
DIR="$2"
|
|
45
47
|
shift 2
|
|
46
48
|
;;
|
|
49
|
+
--session)
|
|
50
|
+
[ $# -ge 2 ] && [ "${2#--}" = "$2" ] || exit 0
|
|
51
|
+
SESSION="$2"
|
|
52
|
+
shift 2
|
|
53
|
+
;;
|
|
47
54
|
*) exit 0 ;;
|
|
48
55
|
esac
|
|
49
56
|
done
|
|
@@ -65,11 +72,13 @@ done
|
|
|
65
72
|
d="$(cd "$DIR" 2>/dev/null && pwd -P)" || exit 0
|
|
66
73
|
[ -n "$d" ] || exit 0
|
|
67
74
|
|
|
68
|
-
#
|
|
75
|
+
# Six answers end the walk. A state file is a run, wherever it was found. So is
|
|
69
76
|
# a forwarding note beside where one used to be: the run moved out into a
|
|
70
77
|
# worktree, this tree is where the session that moved it is still sitting, and
|
|
71
78
|
# testing only the state file blanked its bar on a live run -- ahead of the
|
|
72
|
-
# `.git` directory below, which would otherwise answer for this tree first.
|
|
79
|
+
# `.git` directory below, which would otherwise answer for this tree first. So
|
|
80
|
+
# is this session's pointer, for a run it opened in a worktree it never
|
|
81
|
+
# entered, which is the same blank bar reached without a `move`. A
|
|
73
82
|
# `.git` that is a regular file is a linked worktree or a submodule, which may
|
|
74
83
|
# hold no state of its own and still belong to a set that does, so it is a maybe
|
|
75
84
|
# and status.sh resolves it. A `.git` that is a directory is the top of an
|
|
@@ -83,6 +92,7 @@ d="$(cd "$DIR" 2>/dev/null && pwd -P)" || exit 0
|
|
|
83
92
|
while :; do
|
|
84
93
|
[ -f "$d/.sluice/run.json" ] && break
|
|
85
94
|
[ -f "$d/.sluice/run.at" ] && break
|
|
95
|
+
[ -n "$SESSION" ] && [ -f "$d/.sluice/sessions/$SESSION" ] && break
|
|
86
96
|
[ -f "$d/.git" ] && break
|
|
87
97
|
[ -d "$d/.git" ] && exit 0
|
|
88
98
|
[ "$d" = "${HOME:-}" ] && exit 0
|
|
@@ -100,6 +110,8 @@ done
|
|
|
100
110
|
# already coloured: the mapping from state to colour belongs next to the state,
|
|
101
111
|
# and a caller that applied it would have to re-derive each cell's meaning from
|
|
102
112
|
# its glyph.
|
|
103
|
-
|
|
113
|
+
# --session only when given, so a caller passing none leaves status.sh its own
|
|
114
|
+
# default rather than an empty override.
|
|
115
|
+
rendered="$(bash "$STATUS" line --full --dir "$DIR" ${SESSION:+--session "$SESSION"} 2>/dev/null)" || exit 0
|
|
104
116
|
[ -n "$rendered" ] || exit 0
|
|
105
117
|
printf '%s\n' "$rendered"
|
|
@@ -319,10 +319,30 @@ fi
|
|
|
319
319
|
# which is the right question for a render and the wrong one for a refusal. The
|
|
320
320
|
# tree named in the note is then passed as `--dir`, so what follows asks about
|
|
321
321
|
# one named tree and carries no layout knowledge of its own.
|
|
322
|
+
#
|
|
323
|
+
# The run this session opened in a worktree it never entered is its own too,
|
|
324
|
+
# found through the pointer `init` left at the main tree, and it comes ahead of
|
|
325
|
+
# the forward: the forward names whichever run was moved last, which with
|
|
326
|
+
# several controllers in the main tree may be another session's.
|
|
322
327
|
top="$(git -C "$cwd" rev-parse --show-toplevel 2>/dev/null)"
|
|
323
328
|
[ -n "$top" ] || top="$cwd"
|
|
329
|
+
session_tree=""
|
|
330
|
+
case "$sid" in
|
|
331
|
+
"" | */* | .*) ;;
|
|
332
|
+
*)
|
|
333
|
+
main="$(git -C "$cwd" worktree list --porcelain 2>/dev/null | sed -n '1s/^worktree //p')"
|
|
334
|
+
[ -n "$main" ] && [ -f "$main/.sluice/sessions/$sid" ] \
|
|
335
|
+
&& IFS= read -r session_tree <"$main/.sluice/sessions/$sid" 2>/dev/null
|
|
336
|
+
if [ -z "$session_tree" ] || [ ! -f "$session_tree/.sluice/run.json" ] \
|
|
337
|
+
|| [ "$(jq -r '.session // empty' "$session_tree/.sluice/run.json" 2>/dev/null)" != "$sid" ]; then
|
|
338
|
+
session_tree=""
|
|
339
|
+
fi
|
|
340
|
+
;;
|
|
341
|
+
esac
|
|
324
342
|
if [ -f "$top/.sluice/run.json" ]; then
|
|
325
343
|
tree="$top"
|
|
344
|
+
elif [ -n "$session_tree" ]; then
|
|
345
|
+
tree="$session_tree"
|
|
326
346
|
elif [ -f "$top/.sluice/run.at" ]; then
|
|
327
347
|
tree=""
|
|
328
348
|
IFS= read -r tree <"$top/.sluice/run.at" 2>/dev/null || exit 0
|
package/skills/sluice/skill.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sluice",
|
|
3
|
-
"version": "0.25.
|
|
3
|
+
"version": "0.25.3",
|
|
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.",
|
|
5
5
|
"author": "iceinvein",
|
|
6
6
|
"type": "prompt",
|