Compare commits
No commits in common. "ea63b3da606604a82bd3003bcd3c5299d56b2a16" and "148ca131a7c5b6286189f62ed92fa367bf16bb35" have entirely different histories.
ea63b3da60
...
148ca131a7
@ -372,10 +372,6 @@ check() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
install_global() {
|
install_global() {
|
||||||
if ! command -v python3 >/dev/null 2>&1; then
|
|
||||||
echo "FAIL: python3 is required for --global (AGENTS.md is generated from global/CLAUDE.md)" >&2
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
warn_mixed_dsh_home
|
warn_mixed_dsh_home
|
||||||
mkdir -p "$DSH_HOME"
|
mkdir -p "$DSH_HOME"
|
||||||
local target="$DSH_HOME/AGENTS.md"
|
local target="$DSH_HOME/AGENTS.md"
|
||||||
@ -498,41 +494,16 @@ sync_skills() {
|
|||||||
|
|
||||||
# Copy every <dir>/SKILL.md under $1 into $DSH_HOME/skills (rm -rf dest, cp -R).
|
# Copy every <dir>/SKILL.md under $1 into $DSH_HOME/skills (rm -rf dest, cp -R).
|
||||||
# The optional label names where the skills came from in the report line.
|
# The optional label names where the skills came from in the report line.
|
||||||
# $DSH_HOME/.superpowers-manifest remembers the installed superpowers set (one
|
|
||||||
# name per line, sorted): a skill present in the previous manifest but missing
|
|
||||||
# from the new set is removed from $DSH_HOME/skills. Local skills (role, sdd)
|
|
||||||
# are installed by install_local_skills after this and never enter the manifest.
|
|
||||||
copy_skills_from() {
|
copy_skills_from() {
|
||||||
local src="$1" label="${2:-$1}" count=0 dir name dest line
|
local src="$1" label="${2:-$1}" count=0 dir name dest
|
||||||
local manifest="$DSH_HOME/.superpowers-manifest"
|
|
||||||
local old=() new=()
|
|
||||||
if [ -f "$manifest" ]; then
|
|
||||||
while IFS= read -r line; do [ -n "$line" ] && old+=("$line"); done < "$manifest"
|
|
||||||
fi
|
|
||||||
for dir in "$src"/*; do
|
for dir in "$src"/*; do
|
||||||
[ -f "$dir/SKILL.md" ] || continue
|
[ -f "$dir/SKILL.md" ] || continue
|
||||||
name="$(basename "$dir")"
|
name="$(basename "$dir")"
|
||||||
new+=("$name")
|
|
||||||
dest="$DSH_HOME/skills/$name"
|
dest="$DSH_HOME/skills/$name"
|
||||||
rm -rf "$dest"
|
rm -rf "$dest"
|
||||||
cp -R "$dir" "$dest"
|
cp -R "$dir" "$dest"
|
||||||
count=$((count + 1))
|
count=$((count + 1))
|
||||||
done
|
done
|
||||||
local keep found
|
|
||||||
for name in ${old[@]+"${old[@]}"}; do
|
|
||||||
found=0
|
|
||||||
for keep in ${new[@]+"${new[@]}"}; do
|
|
||||||
if [ "$keep" = "$name" ]; then
|
|
||||||
found=1
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
if [ "$found" -eq 0 ]; then
|
|
||||||
rm -rf "$DSH_HOME/skills/$name"
|
|
||||||
echo "removed $DSH_HOME/skills/$name (no longer in the superpowers source)"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
printf '%s\n' ${new[@]+"${new[@]}"} | sort -u > "$manifest"
|
|
||||||
echo "copied $count superpowers skills into $DSH_HOME/skills (source: $label)"
|
echo "copied $count superpowers skills into $DSH_HOME/skills (source: $label)"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -456,44 +456,6 @@ test('--skills syncs an explicit --superpowers-from source in a clean HOME', ()
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
test('--skills removes a skill that disappeared upstream (M-3)', () => {
|
|
||||||
const work = tempDir()
|
|
||||||
try {
|
|
||||||
const home = join(work, 'home')
|
|
||||||
const src = fixtureSuperpowers(home, null, join(home, 'src', 'superpowers'))
|
|
||||||
const manifest = join(home, '.dsh', '.superpowers-manifest')
|
|
||||||
const installed = join(home, '.dsh', 'skills')
|
|
||||||
|
|
||||||
const first = runInstaller(home, ['--skills', '--superpowers-from', src])
|
|
||||||
assert.equal(first.status, 0, first.stdout + first.stderr)
|
|
||||||
assert.match(first.stdout, /copied 15 superpowers skills/)
|
|
||||||
assert.ok(existsSync(manifest), 'the superpowers manifest is written by --skills')
|
|
||||||
assert.match(readFileSync(manifest, 'utf8'), /using-superpowers/)
|
|
||||||
|
|
||||||
// Simulate a skill the upstream no longer ships: it stays on disk and in
|
|
||||||
// the old manifest, but the new set does not contain it. The sync itself
|
|
||||||
// rejects a changed 15-skill upstream set, so the manifest is the only
|
|
||||||
// place the install remembers the previous set.
|
|
||||||
write(join(installed, 'extra-skill', 'SKILL.md'), '---\nname: extra-skill\n---\nbody\n')
|
|
||||||
write(manifest, `${readFileSync(manifest, 'utf8')}extra-skill\n`)
|
|
||||||
|
|
||||||
const second = runInstaller(home, ['--skills', '--superpowers-from', src])
|
|
||||||
assert.equal(second.status, 0, second.stdout + second.stderr)
|
|
||||||
assert.match(second.stdout, /copied 15 superpowers skills/)
|
|
||||||
assert.ok(
|
|
||||||
second.stdout.includes(`removed ${join(installed, 'extra-skill')} (no longer in the superpowers source)`),
|
|
||||||
second.stdout,
|
|
||||||
)
|
|
||||||
assert.equal(existsSync(join(installed, 'extra-skill')), false, 'the stale skill directory is removed')
|
|
||||||
assert.ok(existsSync(join(installed, 'using-superpowers', 'SKILL.md')))
|
|
||||||
assert.ok(existsSync(join(installed, 'brainstorming', 'SKILL.md')))
|
|
||||||
assert.ok(existsSync(join(installed, 'role', 'SKILL.md')), 'local skills are not touched')
|
|
||||||
assert.doesNotMatch(readFileSync(manifest, 'utf8'), /extra-skill/, 'the manifest reflects the new set')
|
|
||||||
} finally {
|
|
||||||
cleanup(work)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
test('--superpowers-from accepts the skills directory itself', () => {
|
test('--superpowers-from accepts the skills directory itself', () => {
|
||||||
const home = tempDir()
|
const home = tempDir()
|
||||||
try {
|
try {
|
||||||
@ -679,24 +641,6 @@ test('--global installs DSH role paths and explains the instruction format', ()
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
test('--global fails without python3 and writes no truncated AGENTS.md (M-2)', () => {
|
|
||||||
const home = tempDir()
|
|
||||||
try {
|
|
||||||
const emptyBin = join(home, 'empty-bin')
|
|
||||||
mkdirSync(emptyBin, { recursive: true })
|
|
||||||
const run = spawnSync('/bin/bash', [script, '--global'], {
|
|
||||||
env: { ...process.env, HOME: home, DSH_HOME: join(home, '.dsh'), PATH: emptyBin },
|
|
||||||
encoding: 'utf8',
|
|
||||||
})
|
|
||||||
const out = run.stdout + run.stderr
|
|
||||||
assert.equal(run.status, 1, out)
|
|
||||||
assert.match(out, /python3 is required/)
|
|
||||||
assert.equal(existsSync(join(home, '.dsh', 'AGENTS.md')), false)
|
|
||||||
} finally {
|
|
||||||
cleanup(home)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
test('--skills and --roles warn when DSH_HOME exists while HOME is isolated (O-2)', () => {
|
test('--skills and --roles warn when DSH_HOME exists while HOME is isolated (O-2)', () => {
|
||||||
const work = tempDir()
|
const work = tempDir()
|
||||||
try {
|
try {
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
import assert from 'node:assert/strict'
|
import assert from 'node:assert/strict'
|
||||||
import { createHash } from 'node:crypto'
|
import { createHash } from 'node:crypto'
|
||||||
import { homedir } from 'node:os'
|
|
||||||
import { join } from 'node:path'
|
import { join } from 'node:path'
|
||||||
import test from 'node:test'
|
import test from 'node:test'
|
||||||
import { apply } from '../bundle/index.js'
|
import { apply } from '../bundle/index.js'
|
||||||
@ -136,34 +135,6 @@ function recordingLogger(errors) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
test('apply warns once when roles resolve to a legacy Claude Code path (M-1)', () => {
|
|
||||||
const warnings = []
|
|
||||||
const ctx = {
|
|
||||||
tools: { guard() {} },
|
|
||||||
on() {},
|
|
||||||
inject() {},
|
|
||||||
workingDirectory: { get() {} },
|
|
||||||
logger: {
|
|
||||||
info() {},
|
|
||||||
error() {},
|
|
||||||
warn(fmt, ...args) {
|
|
||||||
warnings.push(fmt.replace(/%s/g, () => String(args.shift() ?? '')))
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
apply(ctx, { rolesDir: join(homedir(), '.claude', 'roles') })
|
|
||||||
|
|
||||||
assert.ok(
|
|
||||||
warnings.some(
|
|
||||||
(line) =>
|
|
||||||
line.includes('legacy Claude Code source in use for roles') &&
|
|
||||||
line.includes(join(homedir(), '.claude', 'roles')),
|
|
||||||
),
|
|
||||||
`no legacy-roles warning among: ${warnings.join(' | ')}`,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
test('apply does not throw when ctx.on and ctx.inject are missing', () => {
|
test('apply does not throw when ctx.on and ctx.inject are missing', () => {
|
||||||
const calls = { guard: 0 }
|
const calls = { guard: 0 }
|
||||||
const errors = []
|
const errors = []
|
||||||
|
|||||||
@ -26,7 +26,6 @@ assert_eq "вложенный worktree: git commit в нём" "$(rc "$W" Bash 'g
|
|||||||
assert_eq "вложенный worktree: запись в основной checkout на main" "$(rc "$W" Bash "echo x > $S/n/a.md")" 2
|
assert_eq "вложенный worktree: запись в основной checkout на main" "$(rc "$W" Bash "echo x > $S/n/a.md")" 2
|
||||||
assert_eq "вложенный worktree: Write в основной checkout рядом" "$(rc "$W" Write "$S/n/.claude/x.md")" 2
|
assert_eq "вложенный worktree: Write в основной checkout рядом" "$(rc "$W" Write "$S/n/.claude/x.md")" 2
|
||||||
assert_eq "git worktree add .claude/worktrees/… на main" "$(rc "$S/n" Bash 'git worktree add .claude/worktrees/y -b feat/y origin/main')" 0
|
assert_eq "git worktree add .claude/worktrees/… на main" "$(rc "$S/n" Bash 'git worktree add .claude/worktrees/y -b feat/y origin/main')" 0
|
||||||
assert_eq "worktree add -b chore/x без start-point (O-1 #66)" "$(rc "$S/n" Bash 'git worktree add .claude/worktrees/o1 -b chore/x')" 0
|
|
||||||
# цепочка создания worktree и записи в нём одной командой (#63, O-4): цель под новым путём — по новой ветке
|
# цепочка создания worktree и записи в нём одной командой (#63, O-4): цель под новым путём — по новой ветке
|
||||||
assert_eq "worktree add -b && cd && openspec archive (#63)" "$(rc "$S/n" Bash 'git worktree add .claude/worktrees/z -b feat/z origin/main && cd .claude/worktrees/z && openspec archive y -y')" 0
|
assert_eq "worktree add -b && cd && openspec archive (#63)" "$(rc "$S/n" Bash 'git worktree add .claude/worktrees/z -b feat/z origin/main && cd .claude/worktrees/z && openspec archive y -y')" 0
|
||||||
assert_eq "worktree add -C && cd && openspec archive (#63)" "$(rc "$S/ws" Bash "git -C $S/n worktree add .claude/worktrees/q -b feat/q origin/main && cd $S/n/.claude/worktrees/q && openspec archive y -y")" 0
|
assert_eq "worktree add -C && cd && openspec archive (#63)" "$(rc "$S/ws" Bash "git -C $S/n worktree add .claude/worktrees/q -b feat/q origin/main && cd $S/n/.claude/worktrees/q && openspec archive y -y")" 0
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user