@bitkentech/shipsmooth-opencode 0.3.27 → 0.3.28

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bitkentech/shipsmooth-opencode",
3
- "version": "0.3.27",
3
+ "version": "0.3.28",
4
4
  "description": "Agent coding workflow with plan-before-implement discipline, TDD, vertical slices, Linear integration, and immutable git-based plan versioning.",
5
5
  "type": "module",
6
6
  "main": "plugin/index.js",
@@ -1,4 +1,4 @@
1
1
  {
2
- "version" : "0.3.27",
2
+ "version" : "0.3.28",
3
3
  "name" : "shipsmooth"
4
4
  }
package/plugin/index.js CHANGED
@@ -27,10 +27,15 @@
27
27
  // The version + skill name come from the rendered config JSON next to the
28
28
  // compiled module (dist/session-start-config.json), so a version bump re-renders
29
29
  // one file and this source is untouched.
30
- import { existsSync } from "node:fs";
31
- import { moduleDir, readConfig, startCommandId, startCommandTemplate, installerPath, safeLog, } from "./lib/internal.js";
32
- export const ShipsmoothPlugin = async ({ client, $ }) => {
33
- const base = moduleDir(import.meta.url);
30
+ import { existsSync, readFileSync } from "node:fs";
31
+ import { join } from "node:path";
32
+ import { moduleDir, readConfig, skillName, startCommandId, startCommandTemplate, installerPath, installSkill, safeLog, } from "./lib/internal.js";
33
+ export const ShipsmoothPlugin = async ({ client, $, worktree }, options) => {
34
+ // The module dir holds the bundled config/installer/skills. Tests may override it
35
+ // (via `__baseDir`) so they can stage fixtures in a tmp dir instead of polluting
36
+ // the real src/; production always uses the compiled module's own location.
37
+ const override = options?.__baseDir;
38
+ const base = typeof override === "string" ? override : moduleDir(import.meta.url);
34
39
  let cfg;
35
40
  try {
36
41
  cfg = readConfig(base);
@@ -45,6 +50,10 @@ export const ShipsmoothPlugin = async ({ client, $ }) => {
45
50
  if (bootstrapped)
46
51
  return;
47
52
  bootstrapped = true;
53
+ await installRuntime();
54
+ await stageSkill();
55
+ }
56
+ async function installRuntime() {
48
57
  const installer = installerPath(base);
49
58
  if (!existsSync(installer)) {
50
59
  await safeLog(client, "error", `shipsmooth: installer not found at ${installer}`);
@@ -65,6 +74,52 @@ export const ShipsmoothPlugin = async ({ client, $ }) => {
65
74
  await safeLog(client, "error", `shipsmooth: bootstrap failed: ${e.message}`);
66
75
  }
67
76
  }
77
+ // Stage the bundled SKILL.md into an OpenCode-scanned skills/ dir, mirroring the
78
+ // plugin's install scope. Non-fatal: a failure leaves the slash command pointing at
79
+ // an undiscovered skill (degraded), but never crashes the session.
80
+ async function stageSkill() {
81
+ try {
82
+ const path = await client.path.get();
83
+ const configDir = path?.data?.config;
84
+ if (!configDir) {
85
+ await safeLog(client, "error", "shipsmooth: no config dir from path.get(); skipping skill staging");
86
+ return;
87
+ }
88
+ const res = installSkill({
89
+ baseDir: base,
90
+ skill: skillName(cfg.name),
91
+ version: cfg.version,
92
+ pkgName: packageName(base),
93
+ configDir,
94
+ worktree,
95
+ });
96
+ if (res.action === "staged") {
97
+ await safeLog(client, "info", `shipsmooth: skill staged at ${res.dest}`);
98
+ }
99
+ else if (res.action === "noop") {
100
+ await safeLog(client, "error", `shipsmooth: skill not staged (${res.reason})`);
101
+ }
102
+ }
103
+ catch (e) {
104
+ await safeLog(client, "error", `shipsmooth: skill staging failed: ${e.message}`);
105
+ }
106
+ }
107
+ // The npm package name as it appears in opencode.json's `plugin` array — read from
108
+ // the plugin's own package.json (one dir above the compiled module's plugin/ root).
109
+ // Falls back to cfg.name if unreadable (scope inference then defaults to global).
110
+ function packageName(baseDir) {
111
+ for (const candidate of [join(baseDir, "package.json"), join(baseDir, "..", "package.json")]) {
112
+ try {
113
+ const pkg = JSON.parse(readFileSync(candidate, "utf-8"));
114
+ if (pkg.name)
115
+ return pkg.name;
116
+ }
117
+ catch {
118
+ // try the next candidate
119
+ }
120
+ }
121
+ return cfg.name;
122
+ }
68
123
  const hooks = {
69
124
  config: async (config) => {
70
125
  config.command = config.command ?? {};
@@ -9,7 +9,7 @@
9
9
  // "fails to load". Keeping them here (imported by index.ts, never re-exported)
10
10
  // means the entry module exposes only the real factory. The unit suite imports
11
11
  // these directly from this module.
12
- import { readFileSync, existsSync } from "node:fs";
12
+ import { readFileSync, writeFileSync, copyFileSync, mkdirSync, existsSync } from "node:fs";
13
13
  import { dirname, join } from "node:path";
14
14
  import { fileURLToPath } from "node:url";
15
15
  export const CONFIG_FILE = "session-start-config.json";
@@ -36,9 +36,17 @@ export function readConfig(baseDir) {
36
36
  }
37
37
  throw new Error(`shipsmooth: ${CONFIG_FILE} not found near ${baseDir}`);
38
38
  }
39
- /** The skill the start command delegates to: `start` (prod) or `start-dev` (dev). */
39
+ /**
40
+ * The skill the start command delegates to, and the dir name it is staged under:
41
+ * `shipsmooth-start` (prod) or `shipsmooth-start-dev` (dev).
42
+ *
43
+ * Namespaced (not bare `start`) because OpenCode's skills/ namespace is shared
44
+ * host-wide and flat — a bare `start` would collide with any other `start` skill
45
+ * (first discovered wins + warns). This must match the rendered skill dir basename
46
+ * and its frontmatter `name:` (set in the opencode render spec).
47
+ */
40
48
  export function skillName(pluginName) {
41
- return pluginName.endsWith("-dev") ? "start-dev" : "start";
49
+ return pluginName.endsWith("-dev") ? "shipsmooth-start-dev" : "shipsmooth-start";
42
50
  }
43
51
  /** Thin launcher template — points the agent at the canonical skill, no inlined workflow. */
44
52
  export function startCommandTemplate(pluginName) {
@@ -63,3 +71,97 @@ export async function safeLog(client, level, message) {
63
71
  // logging is best-effort
64
72
  }
65
73
  }
74
+ // ── Skill staging (plan-88) ─────────────────────────────────────────────────
75
+ //
76
+ // OpenCode discovers SKILL.md ONLY from filesystem dirs it scans (~/.config/
77
+ // opencode/skills, <project>/.opencode/skills, ~/.claude/skills, .agents/skills,
78
+ // …) — NEVER from inside a node_modules package. So the bundled skills/<name>/
79
+ // SKILL.md the plugin ships is invisible until we copy it into a scanned dir.
80
+ //
81
+ // We stage it ourselves, mirroring how the plugin was installed: a project-scoped
82
+ // install (package listed in <worktree>/opencode.json) → <worktree>/.opencode/
83
+ // skills/; a global install (listed in <config>/opencode.json) → <config>/skills/;
84
+ // and global as the fallback when scope can't be determined. A version marker beside
85
+ // the staged skill makes re-staging idempotent across sessions and fresh across
86
+ // version bumps (OpenCode has no per-version skill dirs).
87
+ /** Marker file (beside the staged SKILL.md) recording the staged plugin version. */
88
+ export const SKILL_VERSION_MARKER = ".shipsmooth-version";
89
+ /**
90
+ * Resolve the bundled source SKILL.md for `skillName`. In the assembled payload the
91
+ * compiled module lives under `plugin/` but the bundled `skills/` sits at the payload
92
+ * ROOT (next to plugin/, where OpenCode itself discovers a dev filesystem load) — so
93
+ * `<base>/../skills` is the real location. We also accept `<base>/skills` (co-located)
94
+ * for layouts/tests that bundle skills beside the module. First existing wins; if
95
+ * neither exists, return the root candidate (installSkill then reports a clear noop).
96
+ */
97
+ export function bundledSkillPath(baseDir, skill) {
98
+ const root = join(baseDir, "..", "skills", skill, "SKILL.md");
99
+ const colocated = join(baseDir, "skills", skill, "SKILL.md");
100
+ if (existsSync(root))
101
+ return root;
102
+ if (existsSync(colocated))
103
+ return colocated;
104
+ return root;
105
+ }
106
+ /** True if `opencode.json` at `configDir` lists `pkgName` in its `plugin` array. */
107
+ function configListsPlugin(configDir, pkgName) {
108
+ const file = join(configDir, "opencode.json");
109
+ if (!existsSync(file))
110
+ return false;
111
+ try {
112
+ const parsed = JSON.parse(readFileSync(file, "utf-8"));
113
+ const arr = Array.isArray(parsed.plugin) ? parsed.plugin : [];
114
+ // Entries are either "name" or ["name", options]; match the name in both forms.
115
+ return arr.some((e) => {
116
+ const name = Array.isArray(e) ? e[0] : e;
117
+ return name === pkgName;
118
+ });
119
+ }
120
+ catch {
121
+ return false; // unreadable/malformed config → treat as "not listed"
122
+ }
123
+ }
124
+ /**
125
+ * Pick the skills/ destination ROOT mirroring install scope:
126
+ * - project: `<worktree>/.opencode/skills` when the project opencode.json lists us
127
+ * - global: `<configDir>/skills` when the global opencode.json lists us
128
+ * - fallback: global, when neither lists us (plugins-dir install, unreadable config)
129
+ */
130
+ export function resolveSkillsRoot(pkgName, configDir, worktree) {
131
+ if (worktree && configListsPlugin(worktree, pkgName)) {
132
+ return join(worktree, ".opencode", "skills");
133
+ }
134
+ // global match OR fallback both land in the global config skills dir.
135
+ return join(configDir, "skills");
136
+ }
137
+ /**
138
+ * Stage the bundled SKILL.md into a scope-appropriate, OpenCode-scanned skills/ dir.
139
+ * Idempotent (skips when the marker already records this version) and pure-ish: all
140
+ * effects are filesystem writes the caller has consented to. Returns what it did.
141
+ * Throws only on genuine IO failure — the caller wraps this non-fatally.
142
+ */
143
+ export function installSkill(args) {
144
+ const { baseDir, skill, version, pkgName, configDir, worktree } = args;
145
+ const src = bundledSkillPath(baseDir, skill);
146
+ if (!existsSync(src)) {
147
+ return { action: "noop", reason: `bundled skill not found at ${src}` };
148
+ }
149
+ const destDir = join(resolveSkillsRoot(pkgName, configDir, worktree), skill);
150
+ const dest = join(destDir, "SKILL.md");
151
+ const marker = join(destDir, SKILL_VERSION_MARKER);
152
+ // Already staged at this version → nothing to do.
153
+ if (existsSync(dest) && existsSync(marker)) {
154
+ try {
155
+ if (readFileSync(marker, "utf-8").trim() === version) {
156
+ return { action: "skipped", dest };
157
+ }
158
+ }
159
+ catch {
160
+ // unreadable marker → fall through and re-stage
161
+ }
162
+ }
163
+ mkdirSync(destDir, { recursive: true });
164
+ copyFileSync(src, dest);
165
+ writeFileSync(marker, version);
166
+ return { action: "staged", dest };
167
+ }
@@ -1,9 +1,9 @@
1
1
  ---
2
- name: start
2
+ name: shipsmooth-start
3
3
  description: Use when starting any task — applies the shipsmooth agent coding workflow.
4
4
  ---
5
5
 
6
- # start — Agent Coding Workflow
6
+ # shipsmooth-start — Agent Coding Workflow
7
7
 
8
8
  ## When to apply this skill
9
9
  Apply this skill whenever you are:
@@ -55,7 +55,7 @@ This workflow supports two task tracking modes. Choose one at the start of each
55
55
 
56
56
  Throughout this skill, instructions marked `[Linear]` apply only in Linear mode; instructions marked `[Local]` apply only in Local mode. Unmarked instructions apply to both.
57
57
 
58
- `[Local]` Script invocations use `${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.27/bin/shipsmooth <subcommand>`. All scripts read/write `.shipsmooth/plans/plan-{N}-tasks.xml` relative to the repo root.
58
+ `[Local]` Script invocations use `${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.28/bin/shipsmooth <subcommand>`. All scripts read/write `.shipsmooth/plans/plan-{N}-tasks.xml` relative to the repo root.
59
59
 
60
60
  ---
61
61
 
@@ -90,19 +90,19 @@ Plans are markdown files. They contain: narrative, design decisions, architectur
90
90
  Every time a plan file is committed, immediately create and push a version tag:
91
91
 
92
92
  ```bash
93
- ${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.27/bin/shipsmooth plan tag --plan {N} --kind version
93
+ ${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.28/bin/shipsmooth plan tag --plan {N} --kind version
94
94
  # prints: git push origin plan-{N}-v{K} — run that line to push
95
95
  ```
96
96
 
97
97
  On clean completion:
98
98
  ```bash
99
- ${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.27/bin/shipsmooth plan tag --plan {N} --kind complete
99
+ ${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.28/bin/shipsmooth plan tag --plan {N} --kind complete
100
100
  # prints: git push origin plan-{N}-complete
101
101
  ```
102
102
 
103
103
  On abandonment:
104
104
  ```bash
105
- ${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.27/bin/shipsmooth plan tag --plan {N} --kind abandoned
105
+ ${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.28/bin/shipsmooth plan tag --plan {N} --kind abandoned
106
106
  # prints: git push origin plan-{N}-abandoned
107
107
  ```
108
108
 
@@ -151,16 +151,16 @@ one:
151
151
 
152
152
  - find the plans directory first — in **external** mode (the default) it is not
153
153
  `.shipsmooth/plans/` but a separate state dir. Ask the CLI:
154
- `${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.27/bin/shipsmooth store info --json` reports `plansDir` (when `status` is `ready`).
154
+ `${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.28/bin/shipsmooth store info --json` reports `plansDir` (when `status` is `ready`).
155
155
  List `plansDir`'s `plan-*-tasks.xml` (the highest plan number is the most likely
156
156
  candidate); if `status` is **not** `ready`, state is not set up yet — run the
157
157
  **first-run handshake** below before going further (there is no active plan to resume).
158
158
  - check that plan's state with
159
- `${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.27/bin/shipsmooth plan resume --plan {N}` — a plan-level status of `active` /
159
+ `${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.28/bin/shipsmooth plan resume --plan {N}` — a plan-level status of `active` /
160
160
  `in-review` with tasks still `pending` / in-progress means work is unfinished.
161
161
 
162
162
 
163
- > **First-run handshake `[Local]`.** When a `${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.27/bin/shipsmooth` command reports that state is
163
+ > **First-run handshake `[Local]`.** When a `${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.28/bin/shipsmooth` command reports that state is
164
164
  > not set up — `store info --json` returns a `status` other than `ready`, or a
165
165
  > state-dependent command exits with a `status:"needs-decision"` / `status:"unresolvable"`
166
166
  > JSON line (exit 10 / 11) — do **not** treat it as a normal error. Run this handshake. The
@@ -180,13 +180,13 @@ one:
180
180
  > option:
181
181
  > ```bash
182
182
  > # external (recommended) — accept the proposed folder:
183
- > ${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.27/bin/shipsmooth store init --choice external --json
183
+ > ${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.28/bin/shipsmooth store init --choice external --json
184
184
  > # external — a different folder the user named:
185
- > ${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.27/bin/shipsmooth store init --choice external --path <user's folder> --json
185
+ > ${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.28/bin/shipsmooth store init --choice external --path <user's folder> --json
186
186
  > # keep it inside this repo:
187
- > ${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.27/bin/shipsmooth store init --choice in-repo --json
187
+ > ${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.28/bin/shipsmooth store init --choice in-repo --json
188
188
  > # a configured external folder went missing — recreate it:
189
- > ${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.27/bin/shipsmooth store init --choice recreate --path <path from the option> --json
189
+ > ${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.28/bin/shipsmooth store init --choice recreate --path <path from the option> --json
190
190
  > ```
191
191
  > `store init` creates the chosen location, writes the config entry, and prints the
192
192
  > `ready` shape — read its `plansDir` for where plan files now live.
@@ -225,7 +225,7 @@ that he will add detail later or work exploratorily. **Do not slow him down.**
225
225
  Run **one** command and hand back:
226
226
 
227
227
  ```bash
228
- ${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.27/bin/shipsmooth plan quick --desc "{short-description}"
228
+ ${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.28/bin/shipsmooth plan quick --desc "{short-description}"
229
229
  # derives the next plan number, creates + checks out t/{N}-{slug},
230
230
  # and writes a stub .shipsmooth/plans/plan-{N}.md.
231
231
  # It does NOT commit — that is intentional.
@@ -259,7 +259,7 @@ fleshed out the stub.
259
259
 
260
260
  Kickoff: *"start a new plan, feature is X"* — no spec, no prior planning.
261
261
 
262
- - ✅ **Target:** run `${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.27/bin/shipsmooth plan quick --desc "X"` → relay its
262
+ - ✅ **Target:** run `${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.28/bin/shipsmooth plan quick --desc "X"` → relay its
263
263
  output (branch + stub created, uncommitted) → **stop**.
264
264
  - ❌ **Anti-target #1:** run several rounds of repo investigation, then fire a
265
265
  multi-part questionnaire asking the user to choose the approach, before
@@ -296,12 +296,12 @@ is already rich, or after the user has fleshed out a Phase 0 stub.
296
296
  ```
297
297
  6. **Verify Preconditions:**
298
298
  ```bash
299
- ${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.27/bin/shipsmooth plan preflight --plan {N}
299
+ ${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.28/bin/shipsmooth plan preflight --plan {N}
300
300
  # Exits 0 (PASS) or 1 (FAIL: dirty tree / missing version tag). Warns on unpushed branch.
301
301
  ```
302
302
  7. **Create Task Tracking Infrastructure:**
303
303
  - `[Linear]` Create the `[agent]` Linear project. Create Linear issues from the **risk-sorted** plan tasks. Each issue description must include the **Risk Level** ($L/M/H$) and the tag-based GitHub URL of the specific plan version that generated it.
304
- - `[Local]` Run `${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.27/bin/shipsmooth plan init --plan {N} --tasks-from .shipsmooth/plans/plan-{N}.md` to generate `.shipsmooth/plans/plan-{N}-tasks.xml`. Commit the XML file immediately after creation. **Never hand-write this XML file — always generate it via the CLI. The format uses child elements, not attributes.** The CLI requires task headings in the form `### Task N: Name [Risk]` where `N` is a positive integer — alphanumeric IDs (e.g. `01-A`) are not supported. To express a dependency between tasks, add a `*Depends-on: P[,Q...]*` line anywhere in the task body before the next heading (e.g. `*Depends-on: 1,3*`). The CLI parses this line and writes `<depends-on>` into the XML automatically.
304
+ - `[Local]` Run `${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.28/bin/shipsmooth plan init --plan {N} --tasks-from .shipsmooth/plans/plan-{N}.md` to generate `.shipsmooth/plans/plan-{N}-tasks.xml`. Commit the XML file immediately after creation. **Never hand-write this XML file — always generate it via the CLI. The format uses child elements, not attributes.** The CLI requires task headings in the form `### Task N: Name [Risk]` where `N` is a positive integer — alphanumeric IDs (e.g. `01-A`) are not supported. To express a dependency between tasks, add a `*Depends-on: P[,Q...]*` line anywhere in the task body before the next heading (e.g. `*Depends-on: 1,3*`). The CLI parses this line and writes `<depends-on>` into the XML automatically.
305
305
  - Organise tasks as **thin vertical slices** in both modes.
306
306
  8. **Final Review & Go-ahead:**
307
307
  - `[Linear]` **Stop.** Post to the Linear project that the risk-sorted plan is ready for review.
@@ -315,7 +315,7 @@ is already rich, or after the user has fleshed out a Phase 0 stub.
315
315
  **Session-resume pre-flight `[Local]`** — If you are picking up a plan that was started in a previous session, run this before doing anything else:
316
316
 
317
317
  ```bash
318
- ${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.27/bin/shipsmooth plan resume --plan {N}
318
+ ${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.28/bin/shipsmooth plan resume --plan {N}
319
319
  # Prints: XML file present check and task state summary.
320
320
  ```
321
321
 
@@ -327,7 +327,7 @@ repo stays untouched and the plan files live in a separate state directory. Ask
327
327
  where to read them — it is the source of truth — rather than guessing:
328
328
 
329
329
  ```bash
330
- ${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.27/bin/shipsmooth store info --json
330
+ ${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.28/bin/shipsmooth store info --json
331
331
  # -> {"status":"ready","mode":"external","stateRoot":"...","plansDir":"<dir>/plans"}
332
332
  # Read plan narratives (plan-{N}.md) and task XML from the reported `plansDir`.
333
333
  # If status is not "ready", state is not set up yet — handle per first-run (Phase 0).
@@ -342,7 +342,7 @@ as you would for an in-repo plan — `mode: in-repo` simply reports the in-repo
342
342
 
343
343
  Create a branch named after the primary issue for this plan:
344
344
  ```bash
345
- ${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.27/bin/shipsmooth plan branch --issue {issue-id} --desc "{short-description}"
345
+ ${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.28/bin/shipsmooth plan branch --issue {issue-id} --desc "{short-description}"
346
346
  # prints: git push -u origin t/{issue-id}-{slug} — run that line to push
347
347
  ```
348
348
  All task commits go on this branch. The `t/` prefix stands for "task". Usernames are omitted — the task identity is what matters long-term.
@@ -352,7 +352,7 @@ All task commits go on this branch. The `t/` prefix stands for "task". Usernames
352
352
 
353
353
  > **Commit-message convention (code commits in the project repo).** How you word a code
354
354
  > commit depends on the resolved storage mode. Check it once per session with
355
- > `${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.27/bin/shipsmooth store info --json` and read `mode`.
355
+ > `${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.28/bin/shipsmooth store info --json` and read `mode`.
356
356
  >
357
357
  > - **`in-repo` mode:** keep the prefixed convention — `task(N): <short description>` and
358
358
  > `draft(N): de-risk <task name>`. The plan/task history is shipsmooth's own and lives
@@ -395,7 +395,7 @@ Core Invariant #6).
395
395
  - Implement just enough to prove the approach works. Focus on the core complexity.
396
396
  - Commit per the **commit-message convention**: `draft(N): de-risk [task name]` in in-repo mode; in standalone mode a plain feature message with no `draft(N)`/`task(N)` reference.
397
397
  - `[Linear]` Post a comment on the Linear issue notifying the human the draft is ready.
398
- - `[Local]` Run `${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.27/bin/shipsmooth task status --plan {N} --task {id} --status de-risked` and `${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.27/bin/shipsmooth task comment --plan {N} --task {id} --message "De-risk draft ready for review"`.
398
+ - `[Local]` Run `${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.28/bin/shipsmooth task status --plan {N} --task {id} --status de-risked` and `${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.28/bin/shipsmooth task comment --plan {N} --task {id} --message "De-risk draft ready for review"`.
399
399
  - **Wait for explicit approval of the approach.**
400
400
 
401
401
  ##### Step B: Hardening (Quality Phase)
@@ -419,7 +419,7 @@ quality conforms to its instructions):
419
419
  - `[Linear]` Mark the Linear issue **Agent Coded**.
420
420
 
421
421
 
422
- - `[Local]` Run `${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.27/bin/shipsmooth task status --plan {N} --task {id} --status agent-coded` and `${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.27/bin/shipsmooth task set-commit --plan {N} --task {id} --commit $(git rev-parse HEAD)`.
422
+ - `[Local]` Run `${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.28/bin/shipsmooth task status --plan {N} --task {id} --status agent-coded` and `${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.28/bin/shipsmooth task set-commit --plan {N} --task {id} --commit $(git rev-parse HEAD)`.
423
423
 
424
424
 
425
425
  #### Low risk tasks — Single-pass (current behavior)
@@ -440,17 +440,17 @@ quality conforms to its instructions):
440
440
  - `[Linear]` Mark the Linear issue **Agent Coded**. No draft review needed.
441
441
 
442
442
 
443
- - `[Local]` Run `${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.27/bin/shipsmooth task status --plan {N} --task {id} --status agent-coded` and `${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.27/bin/shipsmooth task set-commit --plan {N} --task {id} --commit $(git rev-parse HEAD)`. No draft review needed.
443
+ - `[Local]` Run `${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.28/bin/shipsmooth task status --plan {N} --task {id} --status agent-coded` and `${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.28/bin/shipsmooth task set-commit --plan {N} --task {id} --commit $(git rev-parse HEAD)`. No draft review needed.
444
444
 
445
445
 
446
446
  ---
447
447
 
448
448
  - **Minor deviation** (task split, reorder, clarification):
449
449
  - `[Linear]` Update the Linear issue(s), add a deviation comment explaining why, continue.
450
- - `[Local]` Run `${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.27/bin/shipsmooth task deviation --plan {N} --task {id} --type minor --message "..."`, continue.
450
+ - `[Local]` Run `${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.28/bin/shipsmooth task deviation --plan {N} --task {id} --type minor --message "..."`, continue.
451
451
  - **Major deviation** (fundamental plan problem, architecture issue, blocked): Stop immediately.
452
452
  - `[Linear]` Post a Linear project update. Set project health to **"At Risk"**.
453
- - `[Local]` Run `${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.27/bin/shipsmooth plan update --plan {N} --blocked --message "..."`.
453
+ - `[Local]` Run `${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.28/bin/shipsmooth plan update --plan {N} --blocked --message "..."`.
454
454
  - Wait for the human to revise the plan file, commit, push, and give a new go-ahead.
455
455
 
456
456
  Never autonomously modify the `.shipsmooth/plans/` file during execution. If a plan change is needed, surface it and wait.
@@ -465,11 +465,11 @@ git tag plan-07-complete
465
465
  git push origin plan-07-complete
466
466
  ```
467
467
  - `[Linear]` Close all Linear issues in the `[agent]` project. Mark `[agent]` project complete and archive it. Update the permanent backlog feature issue to reflect delivery (link to completing PR, note what was delivered).
468
- - `[Local]` Run `${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.27/bin/shipsmooth plan update --plan {N} --status complete --message "Plan complete."`. Commit the final XML state. Update the permanent backlog feature issue (if tracked externally) or note delivery in the plan file.
468
+ - `[Local]` Run `${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.28/bin/shipsmooth plan update --plan {N} --status complete --message "Plan complete."`. Commit the final XML state. Update the permanent backlog feature issue (if tracked externally) or note delivery in the plan file.
469
469
 
470
470
  ### Completion with Loose Ends
471
471
  - `[Linear]` Label unresolved issues `needs-triage`. Set `[agent]` project to **"In Review"**. Post a project update listing each open issue and why it's unresolved. Wait for human to review: they will promote worthy issues to the permanent backlog or discard them. Human marks the project complete and archives it.
472
- - `[Local]` Run `${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.27/bin/shipsmooth task status --plan {N} --task {id} --status needs-triage` for each unresolved task. Run `${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.27/bin/shipsmooth plan update --plan {N} --status in-review --message "..."`. Commit the XML. Wait for human to review.
472
+ - `[Local]` Run `${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.28/bin/shipsmooth task status --plan {N} --task {id} --status needs-triage` for each unresolved task. Run `${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.28/bin/shipsmooth plan update --plan {N} --status in-review --message "..."`. Commit the XML. Wait for human to review.
473
473
 
474
474
  ### Abandonment
475
475
  - Human commits a plan file deletion with a commit message referencing the superseding plan number
@@ -480,7 +480,7 @@ git push origin plan-07-complete
480
480
  ```
481
481
  - **Do not delete any earlier tags** (`plan-07-v1`, `plan-07-v2`, etc.) — they are the audit trail
482
482
  - `[Linear]` Surface all open tasks for human triage. Migrate worthy tasks to the permanent backlog with a note: "Partial delivery — see plan-07-abandoned, superseded by plan-{M}". Archive the `[agent]` project with a closing note referencing the deletion commit hash and the superseding plan.
483
- - `[Local]` Run `${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.27/bin/shipsmooth plan update --plan {N} --status abandoned --message "Superseded by plan-{M}."`. Commit the final XML state.
483
+ - `[Local]` Run `${XDG_CACHE_HOME:-~/.cache}/shipsmooth/0.3.28/bin/shipsmooth plan update --plan {N} --status abandoned --message "Superseded by plan-{M}."`. Commit the final XML state.
484
484
 
485
485
  ---
486
486