@ikunin/sprintpilot 1.0.0
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/LICENSE +201 -0
- package/README.md +330 -0
- package/_Sprintpilot/.secrets-allowlist +26 -0
- package/_Sprintpilot/Sprintpilot.md +216 -0
- package/_Sprintpilot/lib/runtime/args.js +77 -0
- package/_Sprintpilot/lib/runtime/git.js +24 -0
- package/_Sprintpilot/lib/runtime/http.js +96 -0
- package/_Sprintpilot/lib/runtime/log.js +30 -0
- package/_Sprintpilot/lib/runtime/secrets.js +151 -0
- package/_Sprintpilot/lib/runtime/spawn.js +68 -0
- package/_Sprintpilot/lib/runtime/text.js +26 -0
- package/_Sprintpilot/lib/runtime/yaml-lite.js +160 -0
- package/_Sprintpilot/manifest.yaml +26 -0
- package/_Sprintpilot/modules/autopilot/config.yaml +20 -0
- package/_Sprintpilot/modules/git/branching-and-pr-strategy.md +101 -0
- package/_Sprintpilot/modules/git/config.yaml +83 -0
- package/_Sprintpilot/modules/git/templates/commit-patch.txt +1 -0
- package/_Sprintpilot/modules/git/templates/commit-story.txt +1 -0
- package/_Sprintpilot/modules/git/templates/pr-body.md +20 -0
- package/_Sprintpilot/modules/ma/config.yaml +9 -0
- package/_Sprintpilot/scripts/create-pr.js +284 -0
- package/_Sprintpilot/scripts/detect-platform.js +64 -0
- package/_Sprintpilot/scripts/health-check.js +98 -0
- package/_Sprintpilot/scripts/lint-changed.js +249 -0
- package/_Sprintpilot/scripts/lock.js +195 -0
- package/_Sprintpilot/scripts/sanitize-branch.js +107 -0
- package/_Sprintpilot/scripts/stage-and-commit.js +190 -0
- package/_Sprintpilot/scripts/sync-status.js +141 -0
- package/_Sprintpilot/skills/sprint-autopilot-off/SKILL.md +6 -0
- package/_Sprintpilot/skills/sprint-autopilot-off/workflow.md +154 -0
- package/_Sprintpilot/skills/sprint-autopilot-on/SKILL.md +6 -0
- package/_Sprintpilot/skills/sprint-autopilot-on/workflow.md +1119 -0
- package/_Sprintpilot/skills/sprintpilot-assess/SKILL.md +6 -0
- package/_Sprintpilot/skills/sprintpilot-assess/agents/debt-classifier.md +64 -0
- package/_Sprintpilot/skills/sprintpilot-assess/agents/dependency-auditor.md +57 -0
- package/_Sprintpilot/skills/sprintpilot-assess/agents/migration-analyzer.md +62 -0
- package/_Sprintpilot/skills/sprintpilot-assess/workflow.md +114 -0
- package/_Sprintpilot/skills/sprintpilot-code-review/SKILL.md +6 -0
- package/_Sprintpilot/skills/sprintpilot-code-review/agents/acceptance-auditor.md +51 -0
- package/_Sprintpilot/skills/sprintpilot-code-review/agents/blind-hunter.md +39 -0
- package/_Sprintpilot/skills/sprintpilot-code-review/agents/edge-case-hunter.md +46 -0
- package/_Sprintpilot/skills/sprintpilot-code-review/workflow.md +111 -0
- package/_Sprintpilot/skills/sprintpilot-codebase-map/SKILL.md +6 -0
- package/_Sprintpilot/skills/sprintpilot-codebase-map/agents/architecture-mapper.md +129 -0
- package/_Sprintpilot/skills/sprintpilot-codebase-map/agents/concerns-hunter.md +135 -0
- package/_Sprintpilot/skills/sprintpilot-codebase-map/agents/integration-mapper.md +138 -0
- package/_Sprintpilot/skills/sprintpilot-codebase-map/agents/quality-assessor.md +143 -0
- package/_Sprintpilot/skills/sprintpilot-codebase-map/agents/stack-analyzer.md +133 -0
- package/_Sprintpilot/skills/sprintpilot-codebase-map/workflow.md +120 -0
- package/_Sprintpilot/skills/sprintpilot-migrate/SKILL.md +6 -0
- package/_Sprintpilot/skills/sprintpilot-migrate/agents/dependency-analyzer.md +51 -0
- package/_Sprintpilot/skills/sprintpilot-migrate/agents/risk-assessor.md +55 -0
- package/_Sprintpilot/skills/sprintpilot-migrate/agents/stack-mapper.md +49 -0
- package/_Sprintpilot/skills/sprintpilot-migrate/agents/test-parity-analyzer.md +49 -0
- package/_Sprintpilot/skills/sprintpilot-migrate/resources/coexistence-patterns.md +59 -0
- package/_Sprintpilot/skills/sprintpilot-migrate/resources/strategies.md +43 -0
- package/_Sprintpilot/skills/sprintpilot-migrate/templates/component-card.md +11 -0
- package/_Sprintpilot/skills/sprintpilot-migrate/templates/migration-epics.md +35 -0
- package/_Sprintpilot/skills/sprintpilot-migrate/templates/migration-plan.md +66 -0
- package/_Sprintpilot/skills/sprintpilot-migrate/workflow.md +235 -0
- package/_Sprintpilot/skills/sprintpilot-party-mode/SKILL.md +6 -0
- package/_Sprintpilot/skills/sprintpilot-party-mode/workflow.md +138 -0
- package/_Sprintpilot/skills/sprintpilot-research/SKILL.md +6 -0
- package/_Sprintpilot/skills/sprintpilot-research/workflow.md +128 -0
- package/_Sprintpilot/skills/sprintpilot-reverse-architect/SKILL.md +6 -0
- package/_Sprintpilot/skills/sprintpilot-reverse-architect/agents/component-mapper.md +53 -0
- package/_Sprintpilot/skills/sprintpilot-reverse-architect/agents/data-flow-tracer.md +54 -0
- package/_Sprintpilot/skills/sprintpilot-reverse-architect/agents/pattern-extractor.md +67 -0
- package/_Sprintpilot/skills/sprintpilot-reverse-architect/workflow.md +119 -0
- package/_Sprintpilot/skills/sprintpilot-update/SKILL.md +6 -0
- package/_Sprintpilot/skills/sprintpilot-update/workflow.md +46 -0
- package/_Sprintpilot/templates/agent-rules.md +43 -0
- package/bin/sprintpilot.js +95 -0
- package/lib/commands/check-update.js +54 -0
- package/lib/commands/install.js +876 -0
- package/lib/commands/uninstall.js +218 -0
- package/lib/core/bmad-config.js +113 -0
- package/lib/core/file-ops.js +90 -0
- package/lib/core/gitignore.js +54 -0
- package/lib/core/markers.js +126 -0
- package/lib/core/tool-registry.js +73 -0
- package/lib/core/update-check.js +39 -0
- package/lib/core/v1-detect.js +86 -0
- package/lib/prompts.js +82 -0
- package/lib/substitute.js +39 -0
- package/package.json +49 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { execFile } = require('node:child_process');
|
|
4
|
+
const semver = require('semver');
|
|
5
|
+
|
|
6
|
+
const PACKAGE_NAME = '@ikunin/sprintpilot';
|
|
7
|
+
|
|
8
|
+
function spawnCapture(cmd, args, { timeoutMs = 7000 } = {}) {
|
|
9
|
+
return new Promise((resolve) => {
|
|
10
|
+
const child = execFile(cmd, args, { timeout: timeoutMs, windowsHide: true }, (err, stdout) => {
|
|
11
|
+
if (err) return resolve(null);
|
|
12
|
+
resolve(String(stdout || '').trim());
|
|
13
|
+
});
|
|
14
|
+
child.on('error', () => resolve(null));
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async function fetchLatestVersion() {
|
|
19
|
+
const out = await spawnCapture('npm', ['view', `${PACKAGE_NAME}@latest`, 'version']);
|
|
20
|
+
if (!out) return null;
|
|
21
|
+
const first = out.split(/\r?\n/)[0].trim();
|
|
22
|
+
return semver.valid(first) ? first : null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function compareVersions(currentRaw, latestRaw) {
|
|
26
|
+
if (!currentRaw || !latestRaw) return 'unknown';
|
|
27
|
+
const current = semver.coerce(currentRaw)?.version || currentRaw;
|
|
28
|
+
const latest = semver.coerce(latestRaw)?.version || latestRaw;
|
|
29
|
+
if (!semver.valid(current) || !semver.valid(latest)) return 'unknown';
|
|
30
|
+
if (semver.gt(latest, current)) return 'behind';
|
|
31
|
+
if (semver.lt(latest, current)) return 'ahead';
|
|
32
|
+
return 'current';
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
module.exports = {
|
|
36
|
+
PACKAGE_NAME,
|
|
37
|
+
fetchLatestVersion,
|
|
38
|
+
compareVersions,
|
|
39
|
+
};
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const path = require('node:path');
|
|
4
|
+
const fs = require('fs-extra');
|
|
5
|
+
|
|
6
|
+
const { readYaml } = require('./bmad-config');
|
|
7
|
+
|
|
8
|
+
const V1_ADDON_DIR_NAME = '_bmad-addons';
|
|
9
|
+
const V1_MANIFEST_NAME = 'bmad-ma-git';
|
|
10
|
+
const V1_SKILL_NAMES = [
|
|
11
|
+
'bmad-autopilot-on',
|
|
12
|
+
'bmad-autopilot-off',
|
|
13
|
+
'bmad-addon-update',
|
|
14
|
+
'bmad-ma-code-review',
|
|
15
|
+
'bmad-ma-codebase-map',
|
|
16
|
+
'bmad-ma-assess',
|
|
17
|
+
'bmad-ma-reverse-architect',
|
|
18
|
+
'bmad-ma-migrate',
|
|
19
|
+
'bmad-ma-research',
|
|
20
|
+
'bmad-ma-party-mode',
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
// Returns { v1Dir, v1Manifest, detectedVia, manifestAddonName } if the
|
|
24
|
+
// project at projectRoot looks like a bmad-autopilot-addon v1 install, or
|
|
25
|
+
// null otherwise. A "v1 signature" is either: manifest.yaml with
|
|
26
|
+
// addon.name === 'bmad-ma-git' OR (manifest missing/unreadable/naming a
|
|
27
|
+
// different addon but) a skills/ directory containing at least one
|
|
28
|
+
// v1-named skill dir. A malformed manifest does NOT short-circuit to
|
|
29
|
+
// "not v1" — we fall through to the skill-dir heuristic instead, because
|
|
30
|
+
// partial prior installs frequently leave a corrupt manifest behind.
|
|
31
|
+
//
|
|
32
|
+
// detectedVia distinguishes the trust level:
|
|
33
|
+
// 'manifest' — authoritative (manifest names bmad-ma-git)
|
|
34
|
+
// 'skills-no-manifest' — manifest missing, skill-dir heuristic triggered
|
|
35
|
+
// 'skills-unreadable-manifest' — manifest present but unparseable
|
|
36
|
+
// 'skills-other-addon' — manifest cleanly names a DIFFERENT addon,
|
|
37
|
+
// skill dirs still look v1-ish. Ambiguous —
|
|
38
|
+
// caller should require explicit confirmation.
|
|
39
|
+
async function detectV1Installation(projectRoot) {
|
|
40
|
+
const v1Dir = path.join(projectRoot, V1_ADDON_DIR_NAME);
|
|
41
|
+
if (!(await fs.pathExists(v1Dir))) return null;
|
|
42
|
+
const v1Manifest = path.join(v1Dir, 'manifest.yaml');
|
|
43
|
+
|
|
44
|
+
let isV1 = false;
|
|
45
|
+
let manifestAddonName = null;
|
|
46
|
+
let manifestState = 'missing'; // missing | unreadable | parsed
|
|
47
|
+
if (await fs.pathExists(v1Manifest)) {
|
|
48
|
+
try {
|
|
49
|
+
const data = await readYaml(v1Manifest);
|
|
50
|
+
if (data === null) {
|
|
51
|
+
manifestState = 'unreadable';
|
|
52
|
+
} else {
|
|
53
|
+
manifestState = 'parsed';
|
|
54
|
+
manifestAddonName = (data && data.addon && data.addon.name) || null;
|
|
55
|
+
if (manifestAddonName === V1_MANIFEST_NAME) isV1 = true;
|
|
56
|
+
}
|
|
57
|
+
} catch {
|
|
58
|
+
manifestState = 'unreadable';
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
let detectedVia = isV1 ? 'manifest' : null;
|
|
63
|
+
|
|
64
|
+
if (!isV1) {
|
|
65
|
+
const v1SkillsDir = path.join(v1Dir, 'skills');
|
|
66
|
+
if (await fs.pathExists(v1SkillsDir)) {
|
|
67
|
+
const entries = await fs.readdir(v1SkillsDir, { withFileTypes: true });
|
|
68
|
+
const names = entries.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
69
|
+
if (names.some((n) => V1_SKILL_NAMES.includes(n))) {
|
|
70
|
+
isV1 = true;
|
|
71
|
+
if (manifestState === 'parsed') detectedVia = 'skills-other-addon';
|
|
72
|
+
else if (manifestState === 'unreadable') detectedVia = 'skills-unreadable-manifest';
|
|
73
|
+
else detectedVia = 'skills-no-manifest';
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return isV1 ? { v1Dir, v1Manifest, detectedVia, manifestAddonName } : null;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
module.exports = {
|
|
82
|
+
V1_ADDON_DIR_NAME,
|
|
83
|
+
V1_MANIFEST_NAME,
|
|
84
|
+
V1_SKILL_NAMES,
|
|
85
|
+
detectV1Installation,
|
|
86
|
+
};
|
package/lib/prompts.js
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
let clackPromise;
|
|
4
|
+
|
|
5
|
+
function loadClack() {
|
|
6
|
+
if (!clackPromise) {
|
|
7
|
+
clackPromise = import('@clack/prompts');
|
|
8
|
+
}
|
|
9
|
+
return clackPromise;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
async function multiselect(opts) {
|
|
13
|
+
const clack = await loadClack();
|
|
14
|
+
const result = await clack.multiselect(opts);
|
|
15
|
+
if (clack.isCancel(result)) {
|
|
16
|
+
clack.cancel('Cancelled');
|
|
17
|
+
process.exit(0);
|
|
18
|
+
}
|
|
19
|
+
return result;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async function confirm(opts) {
|
|
23
|
+
const clack = await loadClack();
|
|
24
|
+
const result = await clack.confirm(opts);
|
|
25
|
+
if (clack.isCancel(result)) {
|
|
26
|
+
clack.cancel('Cancelled');
|
|
27
|
+
process.exit(0);
|
|
28
|
+
}
|
|
29
|
+
return result;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async function intro(message) {
|
|
33
|
+
const clack = await loadClack();
|
|
34
|
+
clack.intro(message);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async function outro(message) {
|
|
38
|
+
const clack = await loadClack();
|
|
39
|
+
clack.outro(message);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async function note(message, title) {
|
|
43
|
+
const clack = await loadClack();
|
|
44
|
+
clack.note(message, title);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const log = {
|
|
48
|
+
async info(message) {
|
|
49
|
+
const clack = await loadClack();
|
|
50
|
+
clack.log.info(message);
|
|
51
|
+
},
|
|
52
|
+
async success(message) {
|
|
53
|
+
const clack = await loadClack();
|
|
54
|
+
clack.log.success(message);
|
|
55
|
+
},
|
|
56
|
+
async warn(message) {
|
|
57
|
+
const clack = await loadClack();
|
|
58
|
+
clack.log.warn(message);
|
|
59
|
+
},
|
|
60
|
+
async error(message) {
|
|
61
|
+
const clack = await loadClack();
|
|
62
|
+
clack.log.error(message);
|
|
63
|
+
},
|
|
64
|
+
async step(message) {
|
|
65
|
+
const clack = await loadClack();
|
|
66
|
+
clack.log.step(message);
|
|
67
|
+
},
|
|
68
|
+
async message(message) {
|
|
69
|
+
const clack = await loadClack();
|
|
70
|
+
clack.log.message(message);
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
module.exports = {
|
|
75
|
+
loadClack,
|
|
76
|
+
intro,
|
|
77
|
+
outro,
|
|
78
|
+
note,
|
|
79
|
+
multiselect,
|
|
80
|
+
confirm,
|
|
81
|
+
log,
|
|
82
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const path = require('node:path');
|
|
4
|
+
|
|
5
|
+
const TEXT_EXTENSIONS = new Set([
|
|
6
|
+
'.md', '.yaml', '.yml', '.json', '.sh', '.txt',
|
|
7
|
+
]);
|
|
8
|
+
|
|
9
|
+
function isTextFile(filePath) {
|
|
10
|
+
return TEXT_EXTENSIONS.has(path.extname(filePath).toLowerCase());
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function renderString(text, ctx) {
|
|
14
|
+
if (!text) return text;
|
|
15
|
+
let out = text;
|
|
16
|
+
for (const [key, value] of Object.entries(ctx)) {
|
|
17
|
+
if (value == null) continue;
|
|
18
|
+
const token = `{${key}}`;
|
|
19
|
+
if (out.includes(token)) {
|
|
20
|
+
out = out.split(token).join(value);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return out;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function buildContext({ outputFolder }) {
|
|
27
|
+
const out = outputFolder || '_bmad-output';
|
|
28
|
+
return {
|
|
29
|
+
output_folder: out,
|
|
30
|
+
planning_artifacts: `${out}/planning-artifacts`,
|
|
31
|
+
implementation_artifacts: `${out}/implementation-artifacts`,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
module.exports = {
|
|
36
|
+
isTextFile,
|
|
37
|
+
renderString,
|
|
38
|
+
buildContext,
|
|
39
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ikunin/sprintpilot",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Sprintpilot — autopilot and multi-agent addon for BMad Method v6: git workflow, parallel agents, autonomous story execution",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/ikunin/sprintpilot.git"
|
|
9
|
+
},
|
|
10
|
+
"homepage": "https://github.com/ikunin/sprintpilot",
|
|
11
|
+
"bin": {
|
|
12
|
+
"sprintpilot": "bin/sprintpilot.js"
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"bin/",
|
|
16
|
+
"lib/",
|
|
17
|
+
"_Sprintpilot/"
|
|
18
|
+
],
|
|
19
|
+
"keywords": [
|
|
20
|
+
"sprintpilot",
|
|
21
|
+
"autopilot",
|
|
22
|
+
"orchestrator",
|
|
23
|
+
"ai-development",
|
|
24
|
+
"claude",
|
|
25
|
+
"gemini",
|
|
26
|
+
"cursor",
|
|
27
|
+
"windsurf",
|
|
28
|
+
"cline",
|
|
29
|
+
"git-workflow",
|
|
30
|
+
"multi-agent",
|
|
31
|
+
"bmad-method-addon"
|
|
32
|
+
],
|
|
33
|
+
"engines": {
|
|
34
|
+
"node": ">=18.0.0"
|
|
35
|
+
},
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"@clack/core": "^1.0.0",
|
|
38
|
+
"@clack/prompts": "^1.0.0",
|
|
39
|
+
"commander": "^14.0.0",
|
|
40
|
+
"fs-extra": "^11.3.0",
|
|
41
|
+
"js-yaml": "^4.1.0",
|
|
42
|
+
"picocolors": "^1.1.1",
|
|
43
|
+
"semver": "^7.6.3"
|
|
44
|
+
},
|
|
45
|
+
"publishConfig": {
|
|
46
|
+
"access": "public",
|
|
47
|
+
"provenance": true
|
|
48
|
+
}
|
|
49
|
+
}
|