@ikunin/sprintpilot 2.3.1 → 2.3.2
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/lib/commands/install.js
CHANGED
|
@@ -517,6 +517,59 @@ async function evictV1SkillsFromToolDirs(projectRoot, { dryRun = false } = {}) {
|
|
|
517
517
|
return removed;
|
|
518
518
|
}
|
|
519
519
|
|
|
520
|
+
// Skills owned by Sprintpilot all live under one of these prefixes. Used
|
|
521
|
+
// by the orphan-prune sweep to scope which skill dirs the installer is
|
|
522
|
+
// allowed to touch. Anything outside these prefixes (BMad's own skills,
|
|
523
|
+
// user-authored skills, other addons) is left strictly alone.
|
|
524
|
+
const SPRINTPILOT_SKILL_PREFIXES = ['sprint-autopilot-', 'sprintpilot-'];
|
|
525
|
+
|
|
526
|
+
// v2.3.2 — orphan prune. After the per-tool deploy loop has installed the
|
|
527
|
+
// current set of skills, walk the tool's skills/ dir and remove any
|
|
528
|
+
// Sprintpilot-namespace skill dirs that fell out of the manifest between
|
|
529
|
+
// releases. Without this, skills removed in a later release (e.g.
|
|
530
|
+
// sprintpilot-code-review and sprintpilot-party-mode dropped in v2.3.1)
|
|
531
|
+
// linger in users' tool dirs forever, polluting the slash-command picker
|
|
532
|
+
// and pointing at stale internal code.
|
|
533
|
+
//
|
|
534
|
+
// Returns the list of orphan names (relative). Backs up before removing,
|
|
535
|
+
// using the same .sprintpilot-backups/ convention as the install loop.
|
|
536
|
+
async function pruneOrphanSkillsFromToolDir(
|
|
537
|
+
skillsDir,
|
|
538
|
+
currentSkills,
|
|
539
|
+
backupDir,
|
|
540
|
+
ts,
|
|
541
|
+
{ dryRun = false } = {},
|
|
542
|
+
) {
|
|
543
|
+
if (!(await fs.pathExists(skillsDir))) return [];
|
|
544
|
+
const orphans = [];
|
|
545
|
+
const currentSet = new Set(currentSkills);
|
|
546
|
+
let entries;
|
|
547
|
+
try {
|
|
548
|
+
entries = await fs.readdir(skillsDir, { withFileTypes: true });
|
|
549
|
+
} catch {
|
|
550
|
+
return [];
|
|
551
|
+
}
|
|
552
|
+
for (const entry of entries) {
|
|
553
|
+
if (!entry.isDirectory()) continue;
|
|
554
|
+
const name = entry.name;
|
|
555
|
+
if (!SPRINTPILOT_SKILL_PREFIXES.some((p) => name.startsWith(p))) continue;
|
|
556
|
+
if (currentSet.has(name)) continue;
|
|
557
|
+
if (dryRun) {
|
|
558
|
+
orphans.push(name);
|
|
559
|
+
continue;
|
|
560
|
+
}
|
|
561
|
+
const target = path.join(skillsDir, name);
|
|
562
|
+
try {
|
|
563
|
+
await backupSkill(target, backupDir, ts);
|
|
564
|
+
await fs.remove(target);
|
|
565
|
+
orphans.push(name);
|
|
566
|
+
} catch {
|
|
567
|
+
// Best-effort: skip on failure rather than aborting the install.
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
return orphans;
|
|
571
|
+
}
|
|
572
|
+
|
|
520
573
|
// Best-effort detection of a lingering global install of the v1 npm
|
|
521
574
|
// package. When `npm ls -g --json` exits non-zero (e.g. ELSPROBLEMS from
|
|
522
575
|
// unrelated peerDep warnings), it still writes valid JSON to stdout, so
|
|
@@ -948,18 +1001,25 @@ async function readExistingComplexityProfile(projectRoot, v1Snapshot) {
|
|
|
948
1001
|
return m[1];
|
|
949
1002
|
}
|
|
950
1003
|
|
|
951
|
-
// v2.3.0 —
|
|
952
|
-
// _Sprintpilot/manifest.yaml `installed_skills` against what
|
|
953
|
-
//
|
|
954
|
-
// classic "added skill to manifest but forgot to ship the
|
|
955
|
-
// at install time rather than at first invocation.
|
|
1004
|
+
// v2.3.0 — packaging-hygiene check. Cross-reference the project's
|
|
1005
|
+
// _Sprintpilot/manifest.yaml `installed_skills` against what the npm
|
|
1006
|
+
// package actually ships under `<bundleDir>/skills/<name>/SKILL.md`.
|
|
1007
|
+
// Catches the classic "added skill to manifest but forgot to ship the
|
|
1008
|
+
// files" bug at install time rather than at first invocation.
|
|
1009
|
+
//
|
|
1010
|
+
// IMPORTANT: this checks the BUNDLE (the npm package's source tree
|
|
1011
|
+
// pointed to by `bundleDir`, default `ADDON_DIR`), not the project's
|
|
1012
|
+
// _Sprintpilot/. The project never gets `skills/` copied to its
|
|
1013
|
+
// _Sprintpilot/ — skills only land in per-tool dirs (.claude/skills/,
|
|
1014
|
+
// .cursor/skills/, ...). Earlier versions wrongly checked the project
|
|
1015
|
+
// path, which caused a false-positive WARN on every install. Fixed in
|
|
1016
|
+
// v2.3.2.
|
|
956
1017
|
//
|
|
957
1018
|
// Returns { missing: string[] } — empty array means everything is
|
|
958
1019
|
// wired correctly. The caller chooses how to surface mismatches
|
|
959
1020
|
// (warning vs fail). We never fail the install on a mismatch; it's
|
|
960
|
-
// hygiene, not correctness
|
|
961
|
-
|
|
962
|
-
async function verifySkillManifest(projectRoot) {
|
|
1021
|
+
// hygiene, not correctness.
|
|
1022
|
+
async function verifySkillManifest(projectRoot, bundleDir = ADDON_DIR) {
|
|
963
1023
|
const manifestPath = path.join(projectRoot, PROJECT_ADDON_DIR_NAME, 'manifest.yaml');
|
|
964
1024
|
if (!(await fs.pathExists(manifestPath))) {
|
|
965
1025
|
return { missing: [] };
|
|
@@ -982,7 +1042,7 @@ async function verifySkillManifest(projectRoot) {
|
|
|
982
1042
|
}
|
|
983
1043
|
const missing = [];
|
|
984
1044
|
for (const name of skillNames) {
|
|
985
|
-
const skillFile = path.join(
|
|
1045
|
+
const skillFile = path.join(bundleDir, 'skills', name, 'SKILL.md');
|
|
986
1046
|
if (!(await fs.pathExists(skillFile))) missing.push(name);
|
|
987
1047
|
}
|
|
988
1048
|
return { missing };
|
|
@@ -1464,6 +1524,16 @@ async function runInstall(options = {}) {
|
|
|
1464
1524
|
totalInstalled += toolInstalled;
|
|
1465
1525
|
}
|
|
1466
1526
|
|
|
1527
|
+
// v2.3.2 — sweep Sprintpilot-namespace skills that fell out of the
|
|
1528
|
+
// manifest between releases. Backed up to .sprintpilot-backups/ before
|
|
1529
|
+
// removal. Skipped on dry-run except for reporting what would happen.
|
|
1530
|
+
const orphans = await pruneOrphanSkillsFromToolDir(skillsDir, allSkills, backupDir, ts, {
|
|
1531
|
+
dryRun,
|
|
1532
|
+
});
|
|
1533
|
+
for (const name of orphans) {
|
|
1534
|
+
console.log(` ${dryRun ? 'Would remove' : 'Removed'} orphan skill: ${name}`);
|
|
1535
|
+
}
|
|
1536
|
+
|
|
1467
1537
|
await installSystemPrompt(tool, projectRoot, ADDON_DIR, ctx, { dryRun });
|
|
1468
1538
|
console.log('');
|
|
1469
1539
|
}
|
|
@@ -1760,5 +1830,7 @@ module.exports = {
|
|
|
1760
1830
|
snapshotUserOwnedFiles,
|
|
1761
1831
|
applyUserOwnedFiles,
|
|
1762
1832
|
verifySkillManifest,
|
|
1833
|
+
pruneOrphanSkillsFromToolDir,
|
|
1834
|
+
SPRINTPILOT_SKILL_PREFIXES,
|
|
1763
1835
|
},
|
|
1764
1836
|
};
|
package/package.json
CHANGED