fix(dsh): валидировать legacy-кэш и пустой --superpowers-from (#49)
This commit is contained in:
parent
ab458b58d2
commit
4054e13806
@ -28,6 +28,7 @@ DO_SKILLS=0
|
||||
DO_ROLES=0
|
||||
DO_OPENSPEC=0
|
||||
SUPERPOWERS_FROM=""
|
||||
SUPERPOWERS_FROM_SET=0
|
||||
CLONE_HINT="git clone --depth 1 https://github.com/obra/superpowers $DSH_HOME/superpowers"
|
||||
|
||||
usage() {
|
||||
@ -73,15 +74,26 @@ while [ "$#" -gt 0 ]; do
|
||||
exit 2
|
||||
fi
|
||||
SUPERPOWERS_FROM="$2"
|
||||
SUPERPOWERS_FROM_SET=1
|
||||
shift
|
||||
;;
|
||||
--superpowers-from=*) SUPERPOWERS_FROM="${1#*=}" ;;
|
||||
--superpowers-from=*)
|
||||
SUPERPOWERS_FROM="${1#*=}"
|
||||
SUPERPOWERS_FROM_SET=1
|
||||
;;
|
||||
-h|--help) usage; exit 0 ;;
|
||||
*) echo "unknown option: $1" >&2; usage >&2; exit 2 ;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
# An explicit flag with an empty value is a typo, not "use the default source":
|
||||
# falling through to legacy would mask exactly the misconfiguration D-2 forbids.
|
||||
if [ "$SUPERPOWERS_FROM_SET" = 1 ] && [ -z "$SUPERPOWERS_FROM" ]; then
|
||||
echo "option --superpowers-from needs a non-empty directory" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
# Normalize a superpowers source: accept the repo root (with skills/) or the
|
||||
# skills directory itself. Prints the skills directory, returns 1 otherwise.
|
||||
normalize_superpowers_dir() {
|
||||
@ -117,8 +129,11 @@ superpowers_source() {
|
||||
return 0
|
||||
fi
|
||||
legacy="$(ls -d "$HOME"/.claude/plugins/cache/claude-plugins-official/superpowers/*/skills 2>/dev/null | sort -V | tail -1 || true)"
|
||||
if [ -n "$legacy" ] && [ -d "$legacy" ]; then
|
||||
printf '%s\tlegacy\n' "$legacy"
|
||||
# A cache directory without using-superpowers is a broken cache, not a source:
|
||||
# accepting it would copy 0 skills and report success (the silent empty
|
||||
# bootstrap this change removes).
|
||||
if [ -n "$legacy" ] && dir="$(normalize_superpowers_dir "$legacy")"; then
|
||||
printf '%s\tlegacy\n' "$dir"
|
||||
return 0
|
||||
fi
|
||||
if dir="$(normalize_superpowers_dir "$HOME/.claude/skills")"; then
|
||||
|
||||
@ -303,3 +303,44 @@ test('--global installs DSH role paths and explains the instruction format', ()
|
||||
cleanup(home)
|
||||
}
|
||||
})
|
||||
|
||||
test('a legacy cache without using-superpowers is not a source', () => {
|
||||
const home = tempDir()
|
||||
try {
|
||||
const cache = join(
|
||||
home,
|
||||
'.claude',
|
||||
'plugins',
|
||||
'cache',
|
||||
'claude-plugins-official',
|
||||
'superpowers',
|
||||
'7.0.0',
|
||||
'skills',
|
||||
)
|
||||
write(join(cache, 'brainstorming', 'SKILL.md'), '---\nname: brainstorming\n---\nbody\n')
|
||||
const run = runInstaller(home, ['--skills'])
|
||||
const out = run.stdout + run.stderr
|
||||
assert.equal(run.status, 1, out)
|
||||
assert.match(out, /FAIL: no superpowers source/)
|
||||
assert.doesNotMatch(out, /copied 0 superpowers/)
|
||||
assert.equal(existsSync(join(home, '.dsh', 'skills', 'brainstorming')), false)
|
||||
} finally {
|
||||
cleanup(home)
|
||||
}
|
||||
})
|
||||
|
||||
test('an empty --superpowers-from is rejected, not ignored', () => {
|
||||
const home = tempDir()
|
||||
try {
|
||||
makeLegacyCache(home)
|
||||
for (const args of [['--skills', '--superpowers-from='], ['--skills', '--superpowers-from', '']]) {
|
||||
const run = runInstaller(home, args)
|
||||
const out = run.stdout + run.stderr
|
||||
assert.equal(run.status, 2, out)
|
||||
assert.match(out, /needs a non-empty directory/)
|
||||
assert.equal(existsSync(join(home, '.dsh', 'skills', 'using-superpowers')), false)
|
||||
}
|
||||
} finally {
|
||||
cleanup(home)
|
||||
}
|
||||
})
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user