@ikunin/sprintpilot 2.3.1 → 2.3.3
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/_Sprintpilot/manifest.yaml +1 -1
- package/lib/commands/install.js +118 -9
- package/package.json +1 -1
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 };
|
|
@@ -1350,6 +1410,17 @@ async function runInstall(options = {}) {
|
|
|
1350
1410
|
}
|
|
1351
1411
|
}
|
|
1352
1412
|
|
|
1413
|
+
// v2.3.3 — Sprintpilot bundles js-yaml into _Sprintpilot/node_modules/
|
|
1414
|
+
// at install time (see step 6b below). Guard against users committing it.
|
|
1415
|
+
const nodeModulesResult = await addIgnoreEntry(
|
|
1416
|
+
ignore.path,
|
|
1417
|
+
'_Sprintpilot/node_modules/',
|
|
1418
|
+
{ dryRun },
|
|
1419
|
+
);
|
|
1420
|
+
if (nodeModulesResult.added && !dryRun) {
|
|
1421
|
+
console.log(`Added '_Sprintpilot/node_modules/' to ${path.basename(ignore.path)}`);
|
|
1422
|
+
}
|
|
1423
|
+
|
|
1353
1424
|
// 5. Install skills per tool
|
|
1354
1425
|
let totalInstalled = 0;
|
|
1355
1426
|
const allSkills = await listSkills();
|
|
@@ -1464,6 +1535,16 @@ async function runInstall(options = {}) {
|
|
|
1464
1535
|
totalInstalled += toolInstalled;
|
|
1465
1536
|
}
|
|
1466
1537
|
|
|
1538
|
+
// v2.3.2 — sweep Sprintpilot-namespace skills that fell out of the
|
|
1539
|
+
// manifest between releases. Backed up to .sprintpilot-backups/ before
|
|
1540
|
+
// removal. Skipped on dry-run except for reporting what would happen.
|
|
1541
|
+
const orphans = await pruneOrphanSkillsFromToolDir(skillsDir, allSkills, backupDir, ts, {
|
|
1542
|
+
dryRun,
|
|
1543
|
+
});
|
|
1544
|
+
for (const name of orphans) {
|
|
1545
|
+
console.log(` ${dryRun ? 'Would remove' : 'Removed'} orphan skill: ${name}`);
|
|
1546
|
+
}
|
|
1547
|
+
|
|
1467
1548
|
await installSystemPrompt(tool, projectRoot, ADDON_DIR, ctx, { dryRun });
|
|
1468
1549
|
console.log('');
|
|
1469
1550
|
}
|
|
@@ -1497,6 +1578,32 @@ async function runInstall(options = {}) {
|
|
|
1497
1578
|
}
|
|
1498
1579
|
console.log('Runtime resources installed to _Sprintpilot/');
|
|
1499
1580
|
|
|
1581
|
+
// 6b. v2.3.3 — bundle js-yaml into _Sprintpilot/node_modules/.
|
|
1582
|
+
// Two runtime scripts (sprint-plan.js, infer-dependencies.js)
|
|
1583
|
+
// require('js-yaml') for full YAML parse/dump support that the
|
|
1584
|
+
// in-tree yaml-lite intentionally doesn't cover. Without this,
|
|
1585
|
+
// `/sprintpilot-plan-sprint` and dependency inference crash with
|
|
1586
|
+
// MODULE_NOT_FOUND on every invocation in the consumer project.
|
|
1587
|
+
//
|
|
1588
|
+
// We resolve js-yaml from Sprintpilot's own node_modules (where
|
|
1589
|
+
// npm placed it when the user ran `npx @ikunin/sprintpilot`) and
|
|
1590
|
+
// copy the whole package into <projectRoot>/_Sprintpilot/node_modules/js-yaml/.
|
|
1591
|
+
// Node's require walk finds it from _Sprintpilot/scripts/ at runtime.
|
|
1592
|
+
try {
|
|
1593
|
+
const jsYamlPkgJson = require.resolve('js-yaml/package.json');
|
|
1594
|
+
const jsYamlSrc = path.dirname(jsYamlPkgJson);
|
|
1595
|
+
const jsYamlDest = path.join(targetAddonDir, 'node_modules', 'js-yaml');
|
|
1596
|
+
await fs.remove(jsYamlDest);
|
|
1597
|
+
await fs.copy(jsYamlSrc, jsYamlDest, { overwrite: true });
|
|
1598
|
+
console.log(' Bundled js-yaml → _Sprintpilot/node_modules/js-yaml/');
|
|
1599
|
+
} catch (err) {
|
|
1600
|
+
console.warn(
|
|
1601
|
+
pc.yellow(
|
|
1602
|
+
` WARN: failed to bundle js-yaml (${err.message || err}). sprint-plan and dependency inference will fail at runtime.`,
|
|
1603
|
+
),
|
|
1604
|
+
);
|
|
1605
|
+
}
|
|
1606
|
+
|
|
1500
1607
|
// 6a. Re-apply v1 module-config snapshot (if any) — MUST happen after
|
|
1501
1608
|
// step 6 because step 6 wrote pristine bundled configs that would
|
|
1502
1609
|
// otherwise clobber the user's values. On failure, persist the
|
|
@@ -1760,5 +1867,7 @@ module.exports = {
|
|
|
1760
1867
|
snapshotUserOwnedFiles,
|
|
1761
1868
|
applyUserOwnedFiles,
|
|
1762
1869
|
verifySkillManifest,
|
|
1870
|
+
pruneOrphanSkillsFromToolDir,
|
|
1871
|
+
SPRINTPILOT_SKILL_PREFIXES,
|
|
1763
1872
|
},
|
|
1764
1873
|
};
|
package/package.json
CHANGED