diff --git a/dsh/sync/sync-from-upstream.mjs b/dsh/sync/sync-from-upstream.mjs index 44b5350..b4cdd8b 100644 --- a/dsh/sync/sync-from-upstream.mjs +++ b/dsh/sync/sync-from-upstream.mjs @@ -29,7 +29,10 @@ * skill registry addresses skills by bare name. * * A missing anchor fails the run loudly: upstream rewrote text this port - * depends on, so a human must decide what the DSH wording becomes. + * depends on, so a human must decide what the DSH wording becomes. The one + * exception is `optional: true` — a transform whose upstream text v7.0.1 has + * already dropped: it is reported as a warning, not a failure, so the port + * keeps the transform ready for the release that brings the text back. * * Usage: * node dsh/sync/sync-from-upstream.mjs [--out ] [--ref ] @@ -80,12 +83,15 @@ const SKIP = [ 'using-superpowers/references/pi-tools.md' ] -/** Optional manifest of a reduced checkout (test fixtures); absent upstream. */ -const FIXTURE_MANIFEST = '.sync-fixture.json' +const DSH_HOME = '$DSH_HOME' /** * Explicit rewrites applied before the namespace strip. Each entry must match - * exactly `expect` times, or the sync aborts. + * exactly `expect` times, or the sync aborts — unless it is `optional: true`, + * in which case an absent anchor is reported as a warning. Optional entries + * exist for upstream deletions: v7.0.1 dropped the subagent hook example, but + * an upstream release may bring it back, and the adaptation must then return + * automatically instead of being silently absent from the transform list. */ const TRANSFORMS = [ { @@ -124,6 +130,142 @@ const TRANSFORMS = [ ' `subagent-driven-development`, `dispatching-parallel-agents`, and the rest.', '- `using-superpowers` itself is discoverable the same way.' ].join('\n') + }, + { + name: 'brainstorming: DSH platform note for the visual companion', + file: 'brainstorming/visual-companion.md', + find: 'Browser-based visual brainstorming companion for showing mockups, diagrams, and options.\n', + replace: [ + 'Browser-based visual brainstorming companion for showing mockups, diagrams, and options.', + '', + '## Platform notes (DeepSeek Harness port)', + '', + 'The companion server is `scripts/server.cjs` (plain Node.js, runs anywhere', + 'Node runs, including Windows). The `start-server.sh` / `stop-server.sh`', + 'helpers are bash-only: on Windows, start the server directly with', + '`node scripts/server.cjs --project-dir --open` and stop it by killing', + 'the process. All other guidance below is platform-neutral.', + '' + ].join('\n') + }, + { + name: 'executing-plans: DSH availability of subagents', + file: 'executing-plans/SKILL.md', + find: [ + '- You have a plan from superpowers:writing-plans and your human partner', + ' chose inline execution at the handoff.', + '- Your harness has no subagent tool (see the per-platform references in', + ' `../using-superpowers/references/`). Never fabricate a dispatch; run', + ' the plan here.' + ].join('\n'), + replace: [ + '- You have a plan from writing-plans and your human partner chose inline', + ' (Native) execution at the handoff.', + '- No subagent tool is available in this session, or your human partner', + ' chose Native. On the DeepSeek Harness `subagent` / `subagent_fork`', + ' normally ARE available (see `../using-superpowers/references/dsh-tools.md`),', + ' so prefer subagent-driven-development; run the plan here when the work', + ' must stay in one context. Never fabricate a dispatch.' + ].join('\n') + }, + { + name: 'executing-plans: invoke sdd-workspace through bash', + file: 'executing-plans/SKILL.md', + find: '`../subagent-driven-development/scripts/sdd-workspace PLAN_FILE`', + replace: '`bash ../subagent-driven-development/scripts/sdd-workspace PLAN_FILE`' + }, + { + name: 'executing-plans: invoke task-start through bash', + file: 'executing-plans/SKILL.md', + find: "Run this skill's `scripts/task-start PLAN_FILE N`.", + replace: "Run this skill's `bash scripts/task-start PLAN_FILE N`." + }, + { + name: 'executing-plans: invoke task-done through bash', + file: 'executing-plans/SKILL.md', + find: "Run this skill's `scripts/task-done PLAN_FILE N BASE -- `", + replace: "Run this skill's `bash scripts/task-done PLAN_FILE N BASE -- `" + }, + { + name: 'executing-plans: invoke review-package through bash', + file: 'executing-plans/SKILL.md', + find: 'Run `../subagent-driven-development/scripts/review-package PLAN_FILE MERGE_BASE HEAD`', + replace: 'Run `bash ../subagent-driven-development/scripts/review-package PLAN_FILE MERGE_BASE HEAD`' + }, + { + name: 'executing-plans: task-start calls task-brief through bash', + file: 'executing-plans/scripts/task-start', + find: 'out=$("$sdd/task-brief" "$plan" "$n")', + replace: 'out=$("${BASH:-bash}" "$sdd/task-brief" "$plan" "$n")' + }, + { + name: 'executing-plans: task-done calls sdd-workspace through bash', + file: 'executing-plans/scripts/task-done', + find: 'dir=$("$sdd/sdd-workspace" "$plan")', + replace: 'dir=$("${BASH:-bash}" "$sdd/sdd-workspace" "$plan")' + }, + { + name: 'writing-skills: DSH skills directory replaces the cross-harness paths', + file: 'writing-skills/SKILL.md', + find: "**Personal skills live in your runtime's skills directory** (`~/.claude/skills/` on Claude Code) — see [codex-tools.md](../using-superpowers/references/codex-tools.md) or [gemini-tools.md](../using-superpowers/references/gemini-tools.md) for the path on those runtimes. Codex, Copilot CLI, and Gemini CLI all also recognize `~/.agents/skills/` as a cross-runtime alias.", + replace: "**Personal skills live in your runtime's skills directory**: on the DeepSeek Harness, that is `" + + DSH_HOME + "/skills/` (user level, discovered automatically by `skill-filesystem`), the project `.dsh/skills/` / " + + '`.agents/skills/` roots, or — for packaged skills — the `skills/` directory inside a plugin bundle like this ' + + 'one. See [dsh-tools.md](../using-superpowers/references/dsh-tools.md) for the full mechanism.' + }, + { + // optional: upstream v7.0.1 dropped the hook example this transform rewrites. + name: 'subagent-driven-development: hook example uses the DSH home', + file: 'subagent-driven-development/SKILL.md', + find: 'You: "User level (~/.config/superpowers/hooks/)"', + replace: 'You: "Install the hook at the user level (' + DSH_HOME + ')"', + optional: true + }, + { + name: 'diagnosing-superpowers: case workspace lives under the DSH home', + file: 'diagnosing-superpowers/SKILL.md', + find: '`~/.superpowers/diagnosing-superpowers//`', + replace: '`' + DSH_HOME + '/diagnosing-superpowers//`' + }, + { + name: 'diagnosing-superpowers: case template uses the DSH home', + file: 'diagnosing-superpowers/templates/case.md', + find: 'Workspace: ~/.superpowers/diagnosing-superpowers//', + replace: 'Workspace: ' + DSH_HOME + '/diagnosing-superpowers//' + }, + { + name: 'diagnosing-superpowers: report template uses the DSH home', + file: 'diagnosing-superpowers/templates/report.md', + find: 'Report path: ~/.superpowers/diagnosing-superpowers//report.md', + replace: 'Report path: ' + DSH_HOME + '/diagnosing-superpowers//report.md' + }, + { + name: 'diagnosing-superpowers: DSH session discovery facts', + file: 'diagnosing-superpowers/references/session-discovery.md', + find: 'limitation and ask for the missing path, export, or identifying detail.', + replace: [ + 'limitation and ask for the missing path, export, or identifying detail.', + '', + '## DeepSeek Harness', + '', + 'Verified in this environment; confirm it against the records you actually read.', + '', + '- Transcripts: `' + DSH_HOME + '/sessions//session-/session*.jsonl.zstd`', + ' (`' + DSH_HOME + '` defaults to `~/.dsh`). The slug is the working directory with', + ' separators and non-alphanumeric characters folded to `-`, wrapped in `--`.', + '- Child sessions — work dispatched through `subagent` / `subagent_fork` — are', + ' peer directories under the same slug whose header record carries', + ' `delegationDepth: 1`.', + '- The schema version is in the filename (`session.v4.jsonl.zstd`; older files', + ' are `session.v3.jsonl.zstd` or `session.jsonl.zstd`). Read the header', + " record's `version` instead of guessing from the name.", + '- Each file is a concatenation of independent zstd frames, and the `zstd` CLI', + ' is usually absent. See `../using-superpowers/references/dsh-tools.md` for the', + ' frame-by-frame Node decompression command.', + '- Records are JSON lines carrying `type`, `seq`, `time`, and `data`. Establish', + ' what each type means from the records themselves — never import a meaning', + ' from another harness.' + ].join('\n') } ] @@ -198,13 +340,22 @@ function isTextFile(rel) { return TEXT_EXTENSIONS.has(rel.slice(dot)) } -/** Apply every transform whose `file` matches, asserting the anchor exists. */ -function applyTransforms(rel, text, applied) { +/** + * Apply every transform whose `file` matches, asserting the anchor exists. + * Optional transforms whose anchor is gone are collected for a warning. + */ +function applyTransforms(rel, text, applied, optionalMisses) { for (const t of TRANSFORMS) { if (t.file !== rel) continue if (t.find === t.replace) continue const count = text.split(t.find).length - 1 - if (count === 0) fail(`anchor missing in ${rel} for "${t.name}"\n find: ${JSON.stringify(t.find.slice(0, 120))}`) + if (count === 0) { + if (t.optional) { + optionalMisses.push(t) + continue + } + fail(`anchor missing in ${rel} for "${t.name}"\n find: ${JSON.stringify(t.find.slice(0, 120))}`) + } if (count > 1) fail(`anchor matched ${count} times in ${rel} for "${t.name}"`) text = text.replace(t.find, t.replace) applied.add(t.name) @@ -212,25 +363,6 @@ function applyTransforms(rel, text, applied) { return text } -/** - * A real obra/superpowers checkout ships the 15 skills above; the minimal test - * fixture ships two and declares them in `.sync-fixture.json`. The skill-set - * assertion runs in both cases, against the declared set when one exists. - * - * @returns {Promise<{expected: string[], source: string}>} - */ -async function expectedSkillNames(upstreamRoot) { - try { - const manifest = JSON.parse(await readFile(join(upstreamRoot, FIXTURE_MANIFEST), 'utf8')) - if (Array.isArray(manifest.expectedSkills) && manifest.expectedSkills.length > 0) { - return { expected: manifest.expectedSkills, source: FIXTURE_MANIFEST } - } - } catch { - /* real checkouts have no fixture manifest */ - } - return { expected: UPSTREAM_SKILLS, source: 'UPSTREAM_SKILLS' } -} - async function main() { const args = parseArgs(process.argv.slice(2)) const upstreamRoot = args.upstream @@ -244,17 +376,16 @@ async function main() { // A new (or removed) upstream skill must be a deliberate decision: it // changes the skill catalog, the README tables, and possibly the DSH // adaptations. - const { expected, source } = await expectedSkillNames(upstreamRoot) const upstreamSkillDirs = (await readdir(upstreamSkills, { withFileTypes: true })) .filter((entry) => entry.isDirectory()) .map((entry) => entry.name) .sort() - const expectedSorted = [...expected].sort() + const expectedSorted = [...UPSTREAM_SKILLS].sort() if (upstreamSkillDirs.join('\n') !== expectedSorted.join('\n')) { const added = upstreamSkillDirs.filter((name) => !expectedSorted.includes(name)) const removed = expectedSorted.filter((name) => !upstreamSkillDirs.includes(name)) fail( - `the upstream skill set changed (expected ${expectedSorted.length} skills from ${source}) — decide before syncing:\n` + + `the upstream skill set changed (expected ${expectedSorted.length} skills from UPSTREAM_SKILLS) — decide before syncing:\n` + (added.length > 0 ? ` added upstream: ${added.join(', ')}\n` : '') + (removed.length > 0 ? ` removed upstream: ${removed.join(', ')}\n` : '') + ' Update UPSTREAM_SKILLS in this script and the README skill tables.' @@ -280,6 +411,7 @@ async function main() { await mkdir(staging, { recursive: true }) const applied = new Set() + const optionalMisses = [] let copied = 0 let stripped = 0 @@ -296,7 +428,7 @@ async function main() { } let text = await readFile(sourceFile, 'utf8') - text = applyTransforms(rel, text, applied) + text = applyTransforms(rel, text, applied, optionalMisses) const namespaced = text.split('superpowers:').length - 1 if (namespaced > 0) { @@ -314,12 +446,15 @@ async function main() { await cp(dshToolsOverride, overrideTarget) copied++ - // Every transform that names a file must have fired; one that silently - // stopped matching would drop a DSH adaptation from the next release's sync. - const unfired = TRANSFORMS.filter((t) => t.find !== t.replace && !applied.has(t.name)) + // Every required transform must have fired; one that silently stopped + // matching would drop a DSH adaptation from the next release's sync. + const unfired = TRANSFORMS.filter((t) => t.find !== t.replace && !t.optional && !applied.has(t.name)) if (unfired.length > 0) { fail(`transform(s) never matched a copied file: ${unfired.map((t) => `${t.name} (${t.file})`).join('; ')}`) } + for (const t of optionalMisses) { + console.warn(`[warn] optional transform did not match, anchor gone upstream: "${t.name}" (${t.file})`) + } await writeFile( join(staging, '.sync-meta.json'), diff --git a/dsh/tests/fixtures/superpowers-min/.sync-fixture.json b/dsh/tests/fixtures/superpowers-min/.sync-fixture.json deleted file mode 100644 index 97b5eb4..0000000 --- a/dsh/tests/fixtures/superpowers-min/.sync-fixture.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "comment": "Reduced upstream checkout for tests. The skill-set assertion still runs, against this declared set instead of the 15 skills of a real obra/superpowers tree.", - "expectedSkills": ["brainstorming", "using-superpowers"] -} diff --git a/dsh/tests/fixtures/superpowers-min/skills/brainstorming/visual-companion.md b/dsh/tests/fixtures/superpowers-min/skills/brainstorming/visual-companion.md new file mode 100644 index 0000000..eb0ef06 --- /dev/null +++ b/dsh/tests/fixtures/superpowers-min/skills/brainstorming/visual-companion.md @@ -0,0 +1,5 @@ +# Visual companion + +Browser-based visual brainstorming companion for showing mockups, diagrams, and options. + +Minimal upstream stub for sync tests. diff --git a/dsh/tests/fixtures/superpowers-min/skills/diagnosing-superpowers/SKILL.md b/dsh/tests/fixtures/superpowers-min/skills/diagnosing-superpowers/SKILL.md new file mode 100644 index 0000000..8e9530e --- /dev/null +++ b/dsh/tests/fixtures/superpowers-min/skills/diagnosing-superpowers/SKILL.md @@ -0,0 +1,10 @@ +--- +name: diagnosing-superpowers +description: Upstream stub for diagnosing-superpowers used by sync tests. +--- + +# Diagnosing superpowers + +Case workspace: `~/.superpowers/diagnosing-superpowers//` + +Minimal upstream stub for sync tests. diff --git a/dsh/tests/fixtures/superpowers-min/skills/diagnosing-superpowers/references/session-discovery.md b/dsh/tests/fixtures/superpowers-min/skills/diagnosing-superpowers/references/session-discovery.md new file mode 100644 index 0000000..f507c55 --- /dev/null +++ b/dsh/tests/fixtures/superpowers-min/skills/diagnosing-superpowers/references/session-discovery.md @@ -0,0 +1,3 @@ +# Session discovery + +When a record is missing, state the limitation and ask for the missing path, export, or identifying detail. diff --git a/dsh/tests/fixtures/superpowers-min/skills/diagnosing-superpowers/templates/case.md b/dsh/tests/fixtures/superpowers-min/skills/diagnosing-superpowers/templates/case.md new file mode 100644 index 0000000..97a4894 --- /dev/null +++ b/dsh/tests/fixtures/superpowers-min/skills/diagnosing-superpowers/templates/case.md @@ -0,0 +1,3 @@ +# Case + +Workspace: ~/.superpowers/diagnosing-superpowers// diff --git a/dsh/tests/fixtures/superpowers-min/skills/diagnosing-superpowers/templates/report.md b/dsh/tests/fixtures/superpowers-min/skills/diagnosing-superpowers/templates/report.md new file mode 100644 index 0000000..582d66b --- /dev/null +++ b/dsh/tests/fixtures/superpowers-min/skills/diagnosing-superpowers/templates/report.md @@ -0,0 +1,3 @@ +# Report + +Report path: ~/.superpowers/diagnosing-superpowers//report.md diff --git a/dsh/tests/fixtures/superpowers-min/skills/dispatching-parallel-agents/SKILL.md b/dsh/tests/fixtures/superpowers-min/skills/dispatching-parallel-agents/SKILL.md new file mode 100644 index 0000000..d6ea5d3 --- /dev/null +++ b/dsh/tests/fixtures/superpowers-min/skills/dispatching-parallel-agents/SKILL.md @@ -0,0 +1,8 @@ +--- +name: dispatching-parallel-agents +description: Upstream stub for dispatching-parallel-agents used by sync tests. +--- + +# Dispatching parallel agents + +Minimal upstream stub for sync tests. No DSH-specific text is rewritten here. diff --git a/dsh/tests/fixtures/superpowers-min/skills/executing-plans/SKILL.md b/dsh/tests/fixtures/superpowers-min/skills/executing-plans/SKILL.md new file mode 100644 index 0000000..7138cee --- /dev/null +++ b/dsh/tests/fixtures/superpowers-min/skills/executing-plans/SKILL.md @@ -0,0 +1,20 @@ +--- +name: executing-plans +description: Upstream stub for executing-plans used by sync tests. +--- + +# Executing plans + +- You have a plan from superpowers:writing-plans and your human partner + chose inline execution at the handoff. +- Your harness has no subagent tool (see the per-platform references in + `../using-superpowers/references/`). Never fabricate a dispatch; run + the plan here. + +Run this skill's `scripts/task-start PLAN_FILE N`. + +Run this skill's `scripts/task-done PLAN_FILE N BASE -- `. + +Use `../subagent-driven-development/scripts/sdd-workspace PLAN_FILE`. + +Run `../subagent-driven-development/scripts/review-package PLAN_FILE MERGE_BASE HEAD`. diff --git a/dsh/tests/fixtures/superpowers-min/skills/executing-plans/scripts/task-done b/dsh/tests/fixtures/superpowers-min/skills/executing-plans/scripts/task-done new file mode 100644 index 0000000..9fbee67 --- /dev/null +++ b/dsh/tests/fixtures/superpowers-min/skills/executing-plans/scripts/task-done @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +set -euo pipefail +sdd=$(dirname "$0") +plan=$1 +dir=$("$sdd/sdd-workspace" "$plan") +printf '%s\n' "$dir" diff --git a/dsh/tests/fixtures/superpowers-min/skills/executing-plans/scripts/task-start b/dsh/tests/fixtures/superpowers-min/skills/executing-plans/scripts/task-start new file mode 100644 index 0000000..60bb69b --- /dev/null +++ b/dsh/tests/fixtures/superpowers-min/skills/executing-plans/scripts/task-start @@ -0,0 +1,7 @@ +#!/usr/bin/env bash +set -euo pipefail +sdd=$(dirname "$0") +plan=$1 +n=$2 +out=$("$sdd/task-brief" "$plan" "$n") +printf '%s\n' "$out" diff --git a/dsh/tests/fixtures/superpowers-min/skills/finishing-a-development-branch/SKILL.md b/dsh/tests/fixtures/superpowers-min/skills/finishing-a-development-branch/SKILL.md new file mode 100644 index 0000000..91d95d3 --- /dev/null +++ b/dsh/tests/fixtures/superpowers-min/skills/finishing-a-development-branch/SKILL.md @@ -0,0 +1,8 @@ +--- +name: finishing-a-development-branch +description: Upstream stub for finishing-a-development-branch used by sync tests. +--- + +# Finishing a development branch + +Minimal upstream stub for sync tests. No DSH-specific text is rewritten here. diff --git a/dsh/tests/fixtures/superpowers-min/skills/receiving-code-review/SKILL.md b/dsh/tests/fixtures/superpowers-min/skills/receiving-code-review/SKILL.md new file mode 100644 index 0000000..0c309be --- /dev/null +++ b/dsh/tests/fixtures/superpowers-min/skills/receiving-code-review/SKILL.md @@ -0,0 +1,8 @@ +--- +name: receiving-code-review +description: Upstream stub for receiving-code-review used by sync tests. +--- + +# Receiving code review + +Minimal upstream stub for sync tests. No DSH-specific text is rewritten here. diff --git a/dsh/tests/fixtures/superpowers-min/skills/requesting-code-review/SKILL.md b/dsh/tests/fixtures/superpowers-min/skills/requesting-code-review/SKILL.md new file mode 100644 index 0000000..f0b8fef --- /dev/null +++ b/dsh/tests/fixtures/superpowers-min/skills/requesting-code-review/SKILL.md @@ -0,0 +1,8 @@ +--- +name: requesting-code-review +description: Upstream stub for requesting-code-review used by sync tests. +--- + +# Requesting code review + +Minimal upstream stub for sync tests. No DSH-specific text is rewritten here. diff --git a/dsh/tests/fixtures/superpowers-min/skills/subagent-driven-development/SKILL.md b/dsh/tests/fixtures/superpowers-min/skills/subagent-driven-development/SKILL.md new file mode 100644 index 0000000..416402b --- /dev/null +++ b/dsh/tests/fixtures/superpowers-min/skills/subagent-driven-development/SKILL.md @@ -0,0 +1,14 @@ +--- +name: subagent-driven-development +description: Upstream stub for subagent-driven-development used by sync tests. +--- + +# Subagent-driven development + +Setup example (upstream v7.0.0 wording, kept so the port exercises its +home-dir transform; v7.0.1 dropped the example): + +You: "User level (~/.config/superpowers/hooks/)" +Human: "Project level" + +Minimal upstream stub for sync tests. diff --git a/dsh/tests/fixtures/superpowers-min/skills/systematic-debugging/SKILL.md b/dsh/tests/fixtures/superpowers-min/skills/systematic-debugging/SKILL.md new file mode 100644 index 0000000..1a5d47c --- /dev/null +++ b/dsh/tests/fixtures/superpowers-min/skills/systematic-debugging/SKILL.md @@ -0,0 +1,8 @@ +--- +name: systematic-debugging +description: Upstream stub for systematic-debugging used by sync tests. +--- + +# Systematic debugging + +Minimal upstream stub for sync tests. No DSH-specific text is rewritten here. diff --git a/dsh/tests/fixtures/superpowers-min/skills/test-driven-development/SKILL.md b/dsh/tests/fixtures/superpowers-min/skills/test-driven-development/SKILL.md new file mode 100644 index 0000000..5353be4 --- /dev/null +++ b/dsh/tests/fixtures/superpowers-min/skills/test-driven-development/SKILL.md @@ -0,0 +1,8 @@ +--- +name: test-driven-development +description: Upstream stub for test-driven-development used by sync tests. +--- + +# Test-driven development + +Minimal upstream stub for sync tests. No DSH-specific text is rewritten here. diff --git a/dsh/tests/fixtures/superpowers-min/skills/using-git-worktrees/SKILL.md b/dsh/tests/fixtures/superpowers-min/skills/using-git-worktrees/SKILL.md new file mode 100644 index 0000000..fdfc7ec --- /dev/null +++ b/dsh/tests/fixtures/superpowers-min/skills/using-git-worktrees/SKILL.md @@ -0,0 +1,8 @@ +--- +name: using-git-worktrees +description: Upstream stub for using-git-worktrees used by sync tests. +--- + +# Using git worktrees + +Minimal upstream stub for sync tests. No DSH-specific text is rewritten here. diff --git a/dsh/tests/fixtures/superpowers-min/skills/verification-before-completion/SKILL.md b/dsh/tests/fixtures/superpowers-min/skills/verification-before-completion/SKILL.md new file mode 100644 index 0000000..f074eca --- /dev/null +++ b/dsh/tests/fixtures/superpowers-min/skills/verification-before-completion/SKILL.md @@ -0,0 +1,8 @@ +--- +name: verification-before-completion +description: Upstream stub for verification-before-completion used by sync tests. +--- + +# Verification before completion + +Minimal upstream stub for sync tests. No DSH-specific text is rewritten here. diff --git a/dsh/tests/fixtures/superpowers-min/skills/writing-plans/SKILL.md b/dsh/tests/fixtures/superpowers-min/skills/writing-plans/SKILL.md new file mode 100644 index 0000000..5be50ea --- /dev/null +++ b/dsh/tests/fixtures/superpowers-min/skills/writing-plans/SKILL.md @@ -0,0 +1,8 @@ +--- +name: writing-plans +description: Upstream stub for writing-plans used by sync tests. +--- + +# Writing plans + +Minimal upstream stub for sync tests. No DSH-specific text is rewritten here. diff --git a/dsh/tests/fixtures/superpowers-min/skills/writing-skills/SKILL.md b/dsh/tests/fixtures/superpowers-min/skills/writing-skills/SKILL.md new file mode 100644 index 0000000..2627130 --- /dev/null +++ b/dsh/tests/fixtures/superpowers-min/skills/writing-skills/SKILL.md @@ -0,0 +1,10 @@ +--- +name: writing-skills +description: Upstream stub for writing-skills used by sync tests. +--- + +# Writing skills + +**Personal skills live in your runtime's skills directory** (`~/.claude/skills/` on Claude Code) — see [codex-tools.md](../using-superpowers/references/codex-tools.md) or [gemini-tools.md](../using-superpowers/references/gemini-tools.md) for the path on those runtimes. Codex, Copilot CLI, and Gemini CLI all also recognize `~/.agents/skills/` as a cross-runtime alias. + +Minimal upstream stub for sync tests. diff --git a/dsh/tests/sync.test.mjs b/dsh/tests/sync.test.mjs index c3f976e..bd75ec0 100644 --- a/dsh/tests/sync.test.mjs +++ b/dsh/tests/sync.test.mjs @@ -32,6 +32,10 @@ test('sync happy path: transforms, namespace strip, override, meta', () => { assert.equal(result.status, 0, `stderr: ${result.stderr}`) assert.match(result.stdout, /synced \d+ files/) + assert.match(result.stdout, /transforms applied: 16/) + + const skillDirs = readdirSync(out).filter((name) => name !== '.sync-meta.json') + assert.equal(skillDirs.length, 15, `expected all 15 upstream skills, got ${skillDirs.join(', ')}`) const using = readFileSync(join(out, 'using-superpowers', 'SKILL.md'), 'utf8') assert.match(using, /references\/dsh-tools\.md/) @@ -83,6 +87,26 @@ test('sync fails loud on a broken transform anchor and leaves --out untouched', } }) +test('sync fails loud on a broken anchor of another transform', () => { + const work = tempDir('dsh-sync-anchor-executing-plans-') + try { + const upstream = fixtureCopy(work) + const skillPath = join(upstream, 'skills', 'executing-plans', 'SKILL.md') + const anchor = 'Run `../subagent-driven-development/scripts/review-package PLAN_FILE MERGE_BASE HEAD`' + const original = readFileSync(skillPath, 'utf8') + assert.ok(original.includes(anchor), 'fixture must carry the review-package anchor verbatim') + write(skillPath, original.replace(anchor, 'Run `../subagent-driven-development/scripts/review-package PLAN_FILE HEAD`')) + + const result = runSync([upstream, '--out', join(work, 'out'), '--ref', 'v7.0.1']) + + assert.equal(result.status, 1) + assert.match(result.stderr, /invoke review-package through bash/) + assert.equal(existsSync(join(work, 'out')), false) + } finally { + cleanup(work) + } +}) + test('sync fails on a changed skill set', () => { const work = tempDir('dsh-sync-skill-set-') try {