@iceinvein/agent-skills 0.23.1 → 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 CHANGED
@@ -22,14 +22,30 @@ bunx @iceinvein/agent-skills install # no args: opens pi
22
22
 
23
23
  Auto-detects Claude Code, Cursor, Codex, or Gemini CLI. Use `--tool claude` to target one, `-g` to install globally.
24
24
 
25
+ ### Claude Code plugin
26
+
27
+ Each skill is also a plugin in the `iceinvein-skills` marketplace:
28
+
29
+ ```
30
+ /plugin marketplace add iceinvein/agent-skills
31
+ /plugin install <skill>@iceinvein-skills
32
+ ```
33
+
34
+ - Plugin skills are namespaced: invoke them as `/<skill>:<skill>` (e.g. `/sluice:sluice`).
35
+ - Third-party marketplaces do not auto-update by default. Update from `/plugin`.
36
+ - Activation modes do not apply: sluice's hooks always run while the plugin is enabled, and terse is never auto-activated.
37
+ - Do not install sluice through both routes, or its hooks run twice. Its statusline segment is bunx-only.
38
+ - magpie, migrate and scope-estimate need Bun on PATH.
39
+ - improve-my-codebase's plugin carries every audit, so under the plugin route its audits do not need installing separately.
40
+
25
41
  ## Skills
26
42
 
27
43
  ### Migration
28
44
 
29
45
  | Skill | What it does |
30
46
  |-------|--------------|
31
- | **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. |
32
- | **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. |
33
49
 
34
50
  ### Code Architecture
35
51
 
@@ -84,7 +100,7 @@ Skills that check code does what it was meant to do, with evidence rather than o
84
100
 
85
101
  | Skill | Based on | What it does |
86
102
  |-------|----------|--------------|
87
- | **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. |
88
104
 
89
105
  ### Communication
90
106
 
@@ -112,7 +128,7 @@ Skills that shape how work gets done rather than analyzing code.
112
128
 
113
129
  | Skill | What it does |
114
130
  |-------|--------------|
115
- | **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. Seven 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. |
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. |
116
132
 
117
133
  ### Orchestration
118
134
 
@@ -121,7 +137,7 @@ Skills that compose the other audit skills into higher-level workflows.
121
137
  | Skill | What it does |
122
138
  |-------|--------------|
123
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>`). |
124
- | **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. |
125
141
 
126
142
  ## Commands
127
143
 
@@ -158,7 +174,7 @@ bunx @iceinvein/agent-skills list # show installed versions
158
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:
159
175
 
160
176
  ```
161
- some-skill (v0.2.0 → v0.3.0)
177
+ ⚠ Updates available: some-skill (v0.2.0 → v0.3.0)
162
178
  Run: agent-skills update --all
163
179
  ```
164
180
 
@@ -179,12 +195,7 @@ If a skill declares activation support and you're installing for Claude Code int
179
195
 
180
196
  ## Supported Tools
181
197
 
182
- | Tool | Prompt Skills | MCP Skills |
183
- |------|:---:|:---:|
184
- | Claude Code | Yes | Yes |
185
- | Cursor | Yes | Yes |
186
- | Codex | Yes | – |
187
- | 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.
188
199
 
189
200
  ## How It Works
190
201
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iceinvein/agent-skills",
3
- "version": "0.23.1",
3
+ "version": "0.23.3",
4
4
  "description": "Install agent skills into AI coding tools",
5
5
  "author": "iceinvein",
6
6
  "license": "MIT",
@@ -1,9 +1,12 @@
1
1
  {
2
2
  "name": "improve-my-codebase",
3
- "version": "1.1.0",
3
+ "version": "1.1.1",
4
4
  "description": "Orchestrator skill that runs every applicable audit skill in parallel and produces a prioritized, convergence-ranked improvement report",
5
5
  "author": "iceinvein",
6
6
  "type": "prompt",
7
+ "plugin": {
8
+ "bundleSiblings": true
9
+ },
7
10
  "tools": [
8
11
  "claude",
9
12
  "cursor",
package/skills/index.json CHANGED
@@ -211,7 +211,7 @@
211
211
  "name": "improve-my-codebase",
212
212
  "description": "Orchestrator skill that runs every applicable audit skill in parallel and produces a prioritized, convergence-ranked improvement report",
213
213
  "type": "prompt",
214
- "version": "1.1.0"
214
+ "version": "1.1.1"
215
215
  },
216
216
  {
217
217
  "name": "integration-pattern-auditor",
@@ -225,15 +225,15 @@
225
225
  },
226
226
  {
227
227
  "name": "magpie",
228
- "description": "Interactive PR review pipeline. Runs five parallel specialist subagents (security, bugs, performance, code-smells, architecture) plus a once-per-PR checks pass that finds tests and controls able to pass without proving their purpose, drops findings whose quoted evidence is not in the code, dedupes, runs critic subagents that re-check each finding against the worktree and set its risk label, peer-reviews via codex exec (falling back to a Claude second opinion when codex is unavailable), and serves an interactive HTML report for selecting or dismissing findings to post via gh, recording the outcome per finding as labels. Bundles a Bun CLI installed onto PATH via the skill's postinstall step. Use when the user asks to review a GitHub pull request. Local mode (setup --local) runs the same pipeline on the current branch's diff from its merge base, uncommitted and untracked work included, against the caller's live tree, with no server or posting, and hands the ranked findings back in the terminal for an implementing or orchestrating agent to address. Splits oversized diffs into budgeted shards and rebuilds the diff from the local clone when gh pr diff refuses it.",
228
+ "description": "Interactive PR review pipeline. Runs five parallel specialist subagents (security, bugs, performance, code-smells, architecture) plus a once-per-PR checks pass that finds tests and controls able to pass without proving their purpose, drops findings whose quoted evidence is not in the code, dedupes, runs critic subagents that re-check each finding against the worktree and set its risk label, peer-reviews via codex exec (falling back to a Claude second opinion when codex is unavailable), and serves an interactive HTML report for selecting or dismissing findings to post via gh, recording the outcome per finding as labels. Bundles a Bun CLI put on PATH by the skill's postinstall step under a bunx install, or by the plugin's bin/ under a Claude Code plugin install. Use when the user asks to review a GitHub pull request. Local mode (setup --local) runs the same pipeline on the current branch's diff from its merge base, uncommitted and untracked work included, against the caller's live tree, with no server or posting, and hands the ranked findings back in the terminal for an implementing or orchestrating agent to address. Splits oversized diffs into budgeted shards and rebuilds the diff from the local clone when gh pr diff refuses it.",
229
229
  "type": "prompt",
230
- "version": "0.14.1"
230
+ "version": "0.14.5"
231
231
  },
232
232
  {
233
233
  "name": "migrate",
234
234
  "description": "Source-agnostic legacy migration mapping. Walks a legacy codebase through probe, enumerate, seam, extract, parity, queue, adjudicate and handoff, building an auditable requirements ledger with mandatory citations and a `migrate check` gate in place of self-reported completeness. Use when the user asks to migrate, re-specify, replatform, or map a legacy system onto a new stack, to hand mapped requirements to a delivery team, or to resume, check, report, or forecast a mapping run already under way.",
235
235
  "type": "prompt",
236
- "version": "0.4.1"
236
+ "version": "0.4.3"
237
237
  },
238
238
  {
239
239
  "name": "module-secret-auditor",
@@ -269,7 +269,7 @@
269
269
  "name": "scope-estimate",
270
270
  "description": "Scopes and estimates a rebuild or respec of an existing system for a proposal: read-only discovery agents write cited inventories, a capability register gives every capability a status and a proposed disposition, area agents turn it into sized functional requirements, and a bundled Bun scheduler groups them into slices and contracts-first work packages and schedules them across engineers x worktrees at fast, planning and floor rates. The scheduler refuses a plan that misses or double-counts a requirement or has a dependency cycle, so every figure in the docs comes from one reproducible run. Also covers current and target architecture pages, proposal review, and a two-week calibration before committing to a date. Lighter than migrate: no store and no phase gates.",
271
271
  "type": "prompt",
272
- "version": "0.1.1"
272
+ "version": "0.1.3"
273
273
  },
274
274
  {
275
275
  "name": "seam-finder",
@@ -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.1"
298
+ "version": "0.25.3"
299
299
  },
300
300
  {
301
301
  "name": "temporal-coupling-detector",
@@ -19,7 +19,15 @@ Given a GitHub PR number, dispatches five specialist subagents in parallel (secu
19
19
  bunx @iceinvein/agent-skills install magpie -g
20
20
  ```
21
21
 
22
- This skill ships in two parts: the prompt (SKILL.md) and a companion Bun CLI (bin + scripts). The agent-skills installer writes both into `~/.claude/skills/magpie/` and then runs the bundled `install.sh` as a postinstall step, which symlinks `bin/magpie` onto your PATH (preferring `/usr/local/bin`, falling back to `~/.local/bin`). Removing the skill with `agent-skills remove magpie -g` runs `uninstall.sh` first to undo the PATH symlink.
22
+ As a Claude Code plugin, after `/plugin marketplace add iceinvein/agent-skills`:
23
+
24
+ ```
25
+ /plugin install magpie@iceinvein-skills
26
+ ```
27
+
28
+ This skill ships in two parts: the prompt (SKILL.md) and a companion Bun CLI (bin + scripts). The agent-skills installer writes both into `~/.claude/skills/magpie/` and then runs the bundled `install.sh` as a postinstall step, which symlinks `bin/magpie` onto your PATH (preferring `/usr/local/bin`, falling back to `~/.local/bin`). Set `MAGPIE_BIN_DIR` to link into that directory instead; it is created if missing, and install.sh fails rather than falling back when it cannot link there. Removing the skill with `agent-skills remove magpie -g` runs `uninstall.sh` first to undo the PATH symlink.
29
+
30
+ Under a Claude Code plugin install, the plugin's `bin/` puts `magpie` on your PATH, and `bin/magpie` installs its runtime dependencies on first run (Bun required). `install.sh` and `MAGPIE_BIN_DIR` apply only to the bunx install.
23
31
 
24
32
  If you cloned this repo and want to run from source, you can also invoke `./install.sh` directly: it does the PATH-link step against the local source tree.
25
33
 
@@ -12,4 +12,21 @@ while [ -L "$SCRIPT" ]; do
12
12
  fi
13
13
  done
14
14
  DIR="$(cd -P "$(dirname "$SCRIPT")" && pwd)"
15
+ ROOT="$(dirname "$DIR")"
16
+
17
+ # A plugin install puts bin/ on PATH without running install.sh, so the
18
+ # first run has to fetch runtime dependencies itself. Progress goes to stderr
19
+ # because callers parse magpie's stdout. The gate is a marker written after
20
+ # success, not node_modules itself, because a failed or killed install leaves
21
+ # node_modules behind and the next run would skip straight to a crash.
22
+ MARKER="$ROOT/node_modules/.magpie-deps-installed"
23
+ if [ -f "$ROOT/package.json" ] && [ ! -f "$MARKER" ]; then
24
+ echo "Installing magpie runtime dependencies..." >&2
25
+ (cd "$ROOT" && bun install --production --silent) >&2 || {
26
+ echo "magpie: bun install failed in $ROOT" >&2
27
+ exit 1
28
+ }
29
+ touch "$MARKER"
30
+ fi
31
+
15
32
  exec bun "$DIR/magpie.ts" "$@"
@@ -18,14 +18,18 @@ chmod +x "$BIN_SOURCE" || true
18
18
 
19
19
  # Magpie has runtime dependencies (shiki, etc.) declared in package.json.
20
20
  # node_modules is not shipped via npm, so we populate it here. Use bun if
21
- # available; fall back to a clear error otherwise.
22
- if [ -f "$SOURCE_DIR/package.json" ] && [ ! -d "$SOURCE_DIR/node_modules" ]; then
21
+ # available; fall back to a clear error otherwise. The gate and marker match
22
+ # bin/magpie's first-run install, so the wrapper does not install again, and a
23
+ # failed install here leaves no marker and is retried on the next run.
24
+ MARKER="$SOURCE_DIR/node_modules/.magpie-deps-installed"
25
+ if [ -f "$SOURCE_DIR/package.json" ] && [ ! -f "$MARKER" ]; then
23
26
  if command -v bun >/dev/null 2>&1; then
24
27
  echo "Installing magpie runtime dependencies..."
25
28
  (cd "$SOURCE_DIR" && bun install --production --silent) || {
26
29
  echo "magpie postinstall: bun install failed in $SOURCE_DIR" >&2
27
30
  exit 1
28
31
  }
32
+ touch "$MARKER"
29
33
  else
30
34
  echo "magpie postinstall: bun not found on PATH; install Bun (https://bun.sh) and rerun this script." >&2
31
35
  exit 1
@@ -56,7 +60,21 @@ install_link() {
56
60
  echo "Installing magpie CLI on PATH:"
57
61
 
58
62
  CLI_INSTALL_DIR=""
59
- if install_link "/usr/local/bin"; then
63
+ # An explicit MAGPIE_BIN_DIR is the only place the caller wants the link, so a
64
+ # failure there is an error rather than a cue to try the default locations.
65
+ if [ -n "${MAGPIE_BIN_DIR:-}" ]; then
66
+ # The recorded path is read by uninstall.sh from any cwd, so it must be absolute.
67
+ case "$MAGPIE_BIN_DIR" in
68
+ /*) ;;
69
+ *) MAGPIE_BIN_DIR="$PWD/$MAGPIE_BIN_DIR" ;;
70
+ esac
71
+ if install_link "$MAGPIE_BIN_DIR"; then
72
+ CLI_INSTALL_DIR="$MAGPIE_BIN_DIR"
73
+ else
74
+ echo "magpie postinstall: could not link magpie into MAGPIE_BIN_DIR ($MAGPIE_BIN_DIR)" >&2
75
+ exit 1
76
+ fi
77
+ elif install_link "/usr/local/bin"; then
60
78
  CLI_INSTALL_DIR="/usr/local/bin"
61
79
  elif install_link "$HOME/.local/bin"; then
62
80
  CLI_INSTALL_DIR="$HOME/.local/bin"
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "magpie",
3
- "version": "0.14.1",
4
- "description": "Interactive PR review pipeline. Runs five parallel specialist subagents (security, bugs, performance, code-smells, architecture) plus a once-per-PR checks pass that finds tests and controls able to pass without proving their purpose, drops findings whose quoted evidence is not in the code, dedupes, runs critic subagents that re-check each finding against the worktree and set its risk label, peer-reviews via codex exec (falling back to a Claude second opinion when codex is unavailable), and serves an interactive HTML report for selecting or dismissing findings to post via gh, recording the outcome per finding as labels. Bundles a Bun CLI installed onto PATH via the skill's postinstall step. Use when the user asks to review a GitHub pull request. Local mode (setup --local) runs the same pipeline on the current branch's diff from its merge base, uncommitted and untracked work included, against the caller's live tree, with no server or posting, and hands the ranked findings back in the terminal for an implementing or orchestrating agent to address. Splits oversized diffs into budgeted shards and rebuilds the diff from the local clone when gh pr diff refuses it.",
3
+ "version": "0.14.5",
4
+ "description": "Interactive PR review pipeline. Runs five parallel specialist subagents (security, bugs, performance, code-smells, architecture) plus a once-per-PR checks pass that finds tests and controls able to pass without proving their purpose, drops findings whose quoted evidence is not in the code, dedupes, runs critic subagents that re-check each finding against the worktree and set its risk label, peer-reviews via codex exec (falling back to a Claude second opinion when codex is unavailable), and serves an interactive HTML report for selecting or dismissing findings to post via gh, recording the outcome per finding as labels. Bundles a Bun CLI put on PATH by the skill's postinstall step under a bunx install, or by the plugin's bin/ under a Claude Code plugin install. Use when the user asks to review a GitHub pull request. Local mode (setup --local) runs the same pipeline on the current branch's diff from its merge base, uncommitted and untracked work included, against the caller's live tree, with no server or posting, and hands the ranked findings back in the terminal for an implementing or orchestrating agent to address. Splits oversized diffs into budgeted shards and rebuilds the diff from the local clone when gh pr diff refuses it.",
5
5
  "author": "iceinvein",
6
6
  "type": "prompt",
7
7
  "tools": [
@@ -185,6 +185,12 @@ writing.
185
185
  bunx @iceinvein/agent-skills install migrate -g
186
186
  ```
187
187
 
188
+ As a Claude Code plugin, after `/plugin marketplace add iceinvein/agent-skills`:
189
+
190
+ ```
191
+ /plugin install migrate@iceinvein-skills
192
+ ```
193
+
188
194
  This skill ships in two parts: the prompt (`SKILL.md`) and a companion Bun
189
195
  CLI (`bin` + `scripts`). The agent-skills installer writes both into
190
196
  `~/.claude/skills/migrate/` and then runs the bundled `install.sh` as a
@@ -7,7 +7,8 @@ description: Source-agnostic legacy migration mapping. Walks a legacy codebase t
7
7
 
8
8
  ## Prerequisites
9
9
 
10
- - `migrate` on `PATH`, put there by this skill's `install.sh`.
10
+ - `migrate` on `PATH`, put there by this skill's `install.sh` under a bunx
11
+ install, or by the plugin's `bin/` under a Claude Code plugin install.
11
12
  - A read-only checkout of the legacy source. `migrate check`'s `citations` and
12
13
  `source` gates read it; nothing here writes to it, and every writer in the CLI
13
14
  refuses a path that resolves inside it.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "migrate",
3
- "version": "0.4.1",
3
+ "version": "0.4.3",
4
4
  "description": "Source-agnostic legacy migration mapping. Walks a legacy codebase through probe, enumerate, seam, extract, parity, queue, adjudicate and handoff, building an auditable requirements ledger with mandatory citations and a `migrate check` gate in place of self-reported completeness. Use when the user asks to migrate, re-specify, replatform, or map a legacy system onto a new stack, to hand mapped requirements to a delivery team, or to resume, check, report, or forecast a mapping run already under way.",
5
5
  "author": "iceinvein",
6
6
  "type": "prompt",
@@ -37,7 +37,7 @@ It updates the requirements, re-runs the scheduler and every page that quotes
37
37
  a figure.
38
38
 
39
39
  **Scheduler on its own:**
40
- `bun .claude/skills/scope-estimate/scripts/schedule.ts build-plan/requirements build-plan/plan.json --json build-plan/schedule.json`
40
+ `bun <skill-dir>/scripts/schedule.ts build-plan/requirements build-plan/plan.json --json build-plan/schedule.json`
41
41
  (`--help` lists the `plan.json` fields).
42
42
 
43
43
  **Say when presenting:** status is from reading code, not running it; rates
@@ -52,6 +52,12 @@ bunx @iceinvein/agent-skills install scope-estimate # this project
52
52
  bunx @iceinvein/agent-skills install scope-estimate -g # every project
53
53
  ```
54
54
 
55
+ As a Claude Code plugin, after `/plugin marketplace add iceinvein/agent-skills`:
56
+
57
+ ```
58
+ /plugin install scope-estimate@iceinvein-skills
59
+ ```
60
+
55
61
  Needs [Bun](https://bun.sh) for the scheduler. Claude Code only.
56
62
 
57
63
  ## Starting a run
@@ -118,15 +124,16 @@ stays a local file until you say otherwise.
118
124
  ## The scheduler
119
125
 
120
126
  ```
121
- bun .claude/skills/scope-estimate/scripts/schedule.ts <requirements-dir> <plan.json> [--json <out.json>]
127
+ bun <skill-dir>/scripts/schedule.ts <requirements-dir> <plan.json> [--json <out.json>]
122
128
  ```
123
129
 
124
- Use `~/.claude/skills/...` for a global install. It prints totals, gated
125
- totals, per-slice requirements and planning days, the engineers x worktrees
126
- matrix at planning rates, the headline fast, planning and floor weeks, the
127
- longest dependency chain, packages sized by assumption and dependencies that
128
- resolved to nothing. `--json` adds the full report, including the lane order
129
- for every team combination.
130
+ `<skill-dir>` is the skill's install directory (e.g.
131
+ `.claude/skills/scope-estimate` for a bunx project install). It prints
132
+ totals, gated totals, per-slice requirements and planning days, the
133
+ engineers x worktrees matrix at planning rates, the headline fast, planning
134
+ and floor weeks, the longest dependency chain, packages sized by assumption
135
+ and dependencies that resolved to nothing. `--json` adds the full report,
136
+ including the lane order for every team combination.
130
137
 
131
138
  It refuses a plan that misses or double-counts a requirement, reuses a
132
139
  package id, names an unknown package or undeclared layer, has a dependency
@@ -131,19 +131,20 @@ Complete `build-plan/plan.json` (slices, foundation packages, any split or
131
131
  ordered product packages) and run:
132
132
 
133
133
  ```
134
- bun .claude/skills/scope-estimate/scripts/schedule.ts build-plan/requirements build-plan/plan.json --json build-plan/schedule.json
134
+ bun <skill-dir>/scripts/schedule.ts build-plan/requirements build-plan/plan.json --json build-plan/schedule.json
135
135
  ```
136
136
 
137
- (Use `~/.claude/skills/...` if the skill was installed globally.) It refuses a
138
- plan that does not account for every requirement exactly once, reuses a
139
- package id, names an unknown package or undeclared layer, lets scheduled work
140
- wait on gated work, or has a dependency cycle (naming the requirement behind
141
- each edge). It prints totals, gated totals, per-slice counts and planning
142
- days, the engineers x worktrees matrix at planning rates, the headline
143
- fast/planning/floor range, the critical chain, assumed sizes and unresolved
144
- dependencies. Fix the plan, not the output. **Every number in the docs comes
145
- from this output.** Re-run it after any requirement, rate or team change and
146
- update every page that quotes a figure. Reference: `references/schedule.md`.
137
+ (`<skill-dir>` is this skill's base directory, stated when the skill loads.) It
138
+ refuses a plan that does not account for every requirement exactly once,
139
+ reuses a package id, names an unknown package or undeclared layer, lets
140
+ scheduled work wait on gated work, or has a dependency cycle (naming the
141
+ requirement behind each edge). It prints totals, gated totals, per-slice
142
+ counts and planning days, the engineers x worktrees matrix at planning rates,
143
+ the headline fast/planning/floor range, the critical chain, assumed sizes and
144
+ unresolved dependencies. Fix the plan, not the output. **Every number in the
145
+ docs comes from this output.** Re-run it after any requirement, rate or team
146
+ change and update every page that quotes a figure. Reference:
147
+ `references/schedule.md`.
147
148
 
148
149
  ### 5. Architecture and proposal checks
149
150
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scope-estimate",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "Scopes and estimates a rebuild or respec of an existing system for a proposal: read-only discovery agents write cited inventories, a capability register gives every capability a status and a proposed disposition, area agents turn it into sized functional requirements, and a bundled Bun scheduler groups them into slices and contracts-first work packages and schedules them across engineers x worktrees at fast, planning and floor rates. The scheduler refuses a plan that misses or double-counts a requirement or has a dependency cycle, so every figure in the docs comes from one reproducible run. Also covers current and target architecture pages, proposal review, and a two-week calibration before committing to a date. Lighter than migrate: no store and no phase gates.",
5
5
  "author": "iceinvein",
6
6
  "type": "prompt",
@@ -431,7 +431,8 @@ reads the run state rather than the plan: it sees what has actually landed.
431
431
  task with no `Effort` line goes out on this session's model and effort, and a
432
432
  downshift that was neither marked nor ratified is a saving your partner never
433
433
  agreed to. A task ratified at `Effort: low` goes out with
434
- `subagent_type: sluice-implementer-low`, the agent sluice installs with
434
+ `subagent_type: sluice-implementer-low` (`sluice:sluice-implementer-low`
435
+ under a Claude Code plugin install), the agent sluice installs with
435
436
  `effort: low` and `model: inherit`: the Agent tool takes a model and no
436
437
  effort, so an agent definition is the one place a dispatch's effort can be
437
438
  set. Reviewers are the
@@ -533,7 +534,8 @@ something stronger would be promising what is already in use. What a tier buys
533
534
  is a dispatch. The one effort rule that holds across all of them is that a
534
535
  review never runs below the effort that built the task, so tiers 1 and 2 over
535
536
  an `Effort: low` task go out at this session's effort rather than on
536
- `sluice-implementer-low`.
537
+ `sluice-implementer-low` (`sluice:sluice-implementer-low` under a Claude Code
538
+ plugin install).
537
539
 
538
540
  **Where the repo carries review tiering of its own, this table governs.** It is
539
541
  the more specific of the two: wired into `Flips`, the `Effort` marks and the
@@ -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` from the JSON the harness sends on stdin.
396
- Substitute the install path: `${CLAUDE_CONFIG_DIR:-$HOME/.claude}` where a
397
- session may set one, or `$cwd/.claude/skills/sluice/scripts/statusline.sh` for a
398
- project-local install.
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 from the JSON the harness sends on stdin. Those
104
- two lines carry no knowledge of where a run lives, so they stay correct as this
105
- skill changes; every install updates the script behind them.
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
@@ -155,8 +206,18 @@ main_tree() { # <dir>
155
206
  ORIG_DIR="$DIR"
156
207
  if [ "$SUB" != "init" ] && [ ! -f "$DIR/.sluice/run.json" ] \
157
208
  && [ "$(git -C "$DIR" rev-parse --is-inside-work-tree 2>/dev/null)" = "true" ]; then
209
+ # The tree's own run lives at its top, not at whatever subdirectory the
210
+ # session is sitting in. Testing only $DIR sent a session in a worktree's
211
+ # package straight past that worktree's run to the main tree's.
212
+ OWN_TREE="$(git -C "$DIR" rev-parse --show-toplevel 2>/dev/null)"
158
213
  MAIN_TREE="$(main_tree "$DIR")"
159
- if [ -n "${MAIN_TREE:-}" ] && [ -d "$MAIN_TREE" ]; then
214
+ # The session's own run outranks the set's, which may be anybody's.
215
+ SESSION_TREE="$(session_run "${MAIN_TREE:-}")"
216
+ if [ -n "${OWN_TREE:-}" ] && [ -f "$OWN_TREE/.sluice/run.json" ]; then
217
+ DIR="$OWN_TREE"
218
+ elif [ -n "$SESSION_TREE" ]; then
219
+ DIR="$SESSION_TREE"
220
+ elif [ -n "${MAIN_TREE:-}" ] && [ -d "$MAIN_TREE" ]; then
160
221
  DIR="$MAIN_TREE"
161
222
 
162
223
  # Then forward, where the main tree's run has moved on into a worktree.
@@ -835,12 +896,20 @@ case "$SUB" in
835
896
  --arg channel "$CHANNEL" \
836
897
  --arg started "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
837
898
  --arg plan "$PLAN" \
838
- --arg record "$RECORD" '
899
+ --arg record "$RECORD" \
900
+ --arg session "$SESSION" '
839
901
  {schema: 1, topic: $topic, channel: $channel, started: $started}
840
902
  + (if $plan == "" then {} else {plan: $plan} end)
841
903
  + (if $record == "" then {} else {record: $record} end)
904
+ + (if $session == "" then {} else {session: $session} end)
842
905
  + {tasks: []}
843
- ' | 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
844
913
  ;;
845
914
 
846
915
  task)
@@ -1257,6 +1326,20 @@ case "$SUB" in
1257
1326
  elif ! leave_note "$NOTE_TREE" "$TO"; then
1258
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"
1259
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
1260
1343
  fi
1261
1344
 
1262
1345
  echo "moved $(jq -r '.topic // "run"' "$DEST_STATE" 2>/dev/null || echo run) to $TO"
@@ -1294,6 +1377,7 @@ case "$SUB" in
1294
1377
  # file aside intact rather than leaving it wedged in the tree.
1295
1378
  started="$(jq -r '.started // empty' "$STATE" 2>/dev/null)"
1296
1379
  topic="$(jq -r '.topic // empty' "$STATE" 2>/dev/null)"
1380
+ owner="$(run_owner "$STATE")"
1297
1381
  stamp="$(printf '%s' "$started" | tr -cd '0-9TZ')"
1298
1382
  slug="$(printf '%s' "${topic:-run}" | tr -cs 'A-Za-z0-9._-' '-')"
1299
1383
  [ -n "$stamp" ] || stamp="unknown"
@@ -1322,6 +1406,13 @@ case "$SUB" in
1322
1406
  IFS= read -r AT <"$NOTE_TREE/.sluice/run.at" 2>/dev/null || true
1323
1407
  [ "$AT" = "$(cd "$DIR" && pwd -P)" ] && rm -f "$NOTE_TREE/.sluice/run.at"
1324
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
1325
1416
  # The summary needs parseable state and close is the one command that
1326
1417
  # does not, so an unreadable run still gets a line naming where it went.
1327
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
- dir="$(printf '%s' "$input" | jq -r '.workspace.current_dir // .cwd // empty' 2>/dev/null || true)"
40
- [ -n "$dir" ] || exit 0
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. Always exits 0 in silence on anything it cannot
9
- # render: a status bar draws on every keystroke and has nowhere to put an error,
10
- # so a message here would be permanent clutter rather than a report anyone acts
11
- # on.
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
- # Five answers end the walk. A state file is a run, wherever it was found. So is
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. A
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
- rendered="$(bash "$STATUS" line --full --dir "$DIR" 2>/dev/null)" || exit 0
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sluice",
3
- "version": "0.25.1",
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",