@ikunin/sprintpilot 2.2.31 → 2.3.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/README.md +232 -413
- package/_Sprintpilot/Sprintpilot.md +76 -6
- package/_Sprintpilot/bin/autopilot.js +734 -68
- package/_Sprintpilot/lib/orchestrator/action-ledger.js +208 -0
- package/_Sprintpilot/lib/orchestrator/adapt.js +93 -15
- package/_Sprintpilot/lib/orchestrator/profile-rules.js +7 -16
- package/_Sprintpilot/lib/orchestrator/sprint-plan.js +488 -0
- package/_Sprintpilot/lib/orchestrator/state-store.js +9 -5
- package/_Sprintpilot/lib/orchestrator/user-command-applier.js +78 -0
- package/_Sprintpilot/lib/orchestrator/user-commands.js +114 -0
- package/_Sprintpilot/lib/orchestrator/verify.js +10 -17
- package/_Sprintpilot/manifest.yaml +4 -1
- package/_Sprintpilot/modules/autopilot/profiles/_base.yaml +18 -4
- package/_Sprintpilot/modules/git/config.yaml +15 -9
- package/_Sprintpilot/modules/ma/config.yaml +29 -27
- package/_Sprintpilot/scripts/dispatch-layer.js +12 -15
- package/_Sprintpilot/scripts/infer-dependencies.js +706 -254
- package/_Sprintpilot/scripts/log-timing.js +6 -10
- package/_Sprintpilot/scripts/merge-shards.js +21 -23
- package/_Sprintpilot/scripts/post-green-gates.js +3 -1
- package/_Sprintpilot/scripts/resolve-dag.js +452 -280
- package/_Sprintpilot/scripts/sprint-plan.js +1068 -0
- package/_Sprintpilot/scripts/state-shard.js +13 -5
- package/_Sprintpilot/scripts/summarize-timings.js +2 -3
- package/_Sprintpilot/skills/sprint-autopilot-on/SKILL.md +30 -2
- package/_Sprintpilot/skills/sprint-autopilot-on/workflow.orchestrator.md +36 -10
- package/_Sprintpilot/skills/sprintpilot-dependency-graph/SKILL.md +63 -0
- package/_Sprintpilot/skills/sprintpilot-dependency-graph/workflow.md +227 -0
- package/_Sprintpilot/skills/sprintpilot-plan-sprint/SKILL.md +67 -0
- package/_Sprintpilot/skills/sprintpilot-plan-sprint/workflow.md +435 -0
- package/_Sprintpilot/skills/sprintpilot-sprint-progress/SKILL.md +53 -0
- package/_Sprintpilot/skills/sprintpilot-sprint-progress/workflow.md +169 -0
- package/lib/commands/install.js +186 -10
- package/package.json +1 -1
package/lib/commands/install.js
CHANGED
|
@@ -748,7 +748,15 @@ async function evictV1Installation(projectRoot, { dryRun, migrateV1, yes }) {
|
|
|
748
748
|
// Regex-based so we don't add a YAML parser dep for two scalar fields.
|
|
749
749
|
// Unrecognized / unreadable files fall back to bundled defaults.
|
|
750
750
|
async function readExistingAutopilotConfig(projectRoot, v1Snapshot) {
|
|
751
|
-
const out = {
|
|
751
|
+
const out = {
|
|
752
|
+
sessionStoryLimit: null,
|
|
753
|
+
retrospectiveMode: null,
|
|
754
|
+
// v2.3.0 additions. null means "not set in user config" → use the bundled
|
|
755
|
+
// default. autoInferDependencies is read only to surface a deprecation
|
|
756
|
+
// notice on upgrade — we never write it back.
|
|
757
|
+
autoPlanOnStart: null,
|
|
758
|
+
autoInferDependencies: null,
|
|
759
|
+
};
|
|
752
760
|
let raw = null;
|
|
753
761
|
|
|
754
762
|
// Precedence order:
|
|
@@ -809,6 +817,22 @@ async function readExistingAutopilotConfig(projectRoot, v1Snapshot) {
|
|
|
809
817
|
if (modeMatch && RETROSPECTIVE_MODES.includes(modeMatch[1])) {
|
|
810
818
|
out.retrospectiveMode = modeMatch[1];
|
|
811
819
|
}
|
|
820
|
+
// v2.3.0 — `auto_plan_on_start: true|false`. Bool; bundled default is false.
|
|
821
|
+
const planMatch = raw.match(
|
|
822
|
+
new RegExp(`^[ \\t]*auto_plan_on_start:[ \\t]*(true|false)${commentTail}`, 'm'),
|
|
823
|
+
);
|
|
824
|
+
if (planMatch) {
|
|
825
|
+
out.autoPlanOnStart = planMatch[1] === 'true';
|
|
826
|
+
}
|
|
827
|
+
// Legacy `auto_infer_dependencies: true|false` — read so the installer can
|
|
828
|
+
// surface a deprecation notice when the user is upgrading from v2.2.x with
|
|
829
|
+
// the flag set to true (it's now a no-op). Never written back.
|
|
830
|
+
const inferMatch = raw.match(
|
|
831
|
+
new RegExp(`^[ \\t]*auto_infer_dependencies:[ \\t]*(true|false)${commentTail}`, 'm'),
|
|
832
|
+
);
|
|
833
|
+
if (inferMatch) {
|
|
834
|
+
out.autoInferDependencies = inferMatch[1] === 'true';
|
|
835
|
+
}
|
|
812
836
|
return out;
|
|
813
837
|
}
|
|
814
838
|
|
|
@@ -859,7 +883,10 @@ function applyScalar(source, key, value) {
|
|
|
859
883
|
return `${trimmed} ${key}: ${value}\n`;
|
|
860
884
|
}
|
|
861
885
|
|
|
862
|
-
async function patchAutopilotConfig(
|
|
886
|
+
async function patchAutopilotConfig(
|
|
887
|
+
projectRoot,
|
|
888
|
+
{ sessionStoryLimit, retrospectiveMode, autoPlanOnStart },
|
|
889
|
+
) {
|
|
863
890
|
const file = path.join(
|
|
864
891
|
projectRoot,
|
|
865
892
|
PROJECT_ADDON_DIR_NAME,
|
|
@@ -871,6 +898,12 @@ async function patchAutopilotConfig(projectRoot, { sessionStoryLimit, retrospect
|
|
|
871
898
|
const original = await fs.readFile(file, 'utf8');
|
|
872
899
|
let updated = applyScalar(original, 'session_story_limit', sessionStoryLimit);
|
|
873
900
|
updated = applyScalar(updated, 'retrospective_mode', retrospectiveMode);
|
|
901
|
+
// v2.3.0 — auto_plan_on_start is a boolean. applyScalar handles literal
|
|
902
|
+
// values (true/false) the same way as numbers; we just need to pass the
|
|
903
|
+
// unquoted lowercase string for booleans.
|
|
904
|
+
if (autoPlanOnStart !== undefined && autoPlanOnStart !== null) {
|
|
905
|
+
updated = applyScalar(updated, 'auto_plan_on_start', autoPlanOnStart ? 'true' : 'false');
|
|
906
|
+
}
|
|
874
907
|
if (updated !== original) {
|
|
875
908
|
await writeAtomic(file, updated);
|
|
876
909
|
}
|
|
@@ -915,6 +948,46 @@ async function readExistingComplexityProfile(projectRoot, v1Snapshot) {
|
|
|
915
948
|
return m[1];
|
|
916
949
|
}
|
|
917
950
|
|
|
951
|
+
// v2.3.0 — post-install hygiene check. Cross-reference the project's
|
|
952
|
+
// _Sprintpilot/manifest.yaml `installed_skills` against what actually
|
|
953
|
+
// landed under `_Sprintpilot/skills/<name>/SKILL.md`. Catches the
|
|
954
|
+
// classic "added skill to manifest but forgot to ship the files" bug
|
|
955
|
+
// at install time rather than at first invocation.
|
|
956
|
+
//
|
|
957
|
+
// Returns { missing: string[] } — empty array means everything is
|
|
958
|
+
// wired correctly. The caller chooses how to surface mismatches
|
|
959
|
+
// (warning vs fail). We never fail the install on a mismatch; it's
|
|
960
|
+
// hygiene, not correctness — the skill just won't appear under the
|
|
961
|
+
// host tool's /-command if it's missing on disk.
|
|
962
|
+
async function verifySkillManifest(projectRoot) {
|
|
963
|
+
const manifestPath = path.join(projectRoot, PROJECT_ADDON_DIR_NAME, 'manifest.yaml');
|
|
964
|
+
if (!(await fs.pathExists(manifestPath))) {
|
|
965
|
+
return { missing: [] };
|
|
966
|
+
}
|
|
967
|
+
let raw;
|
|
968
|
+
try {
|
|
969
|
+
raw = await fs.readFile(manifestPath, 'utf8');
|
|
970
|
+
} catch {
|
|
971
|
+
return { missing: [] };
|
|
972
|
+
}
|
|
973
|
+
// Parse the YAML list under `installed_skills:` via regex — bullet
|
|
974
|
+
// lines starting with `-` at consistent indent. Cheap; no YAML dep.
|
|
975
|
+
const skillNames = [];
|
|
976
|
+
const installedMatch = raw.match(/^[ \t]*installed_skills:\s*\n((?:[ \t]+- [^\n]+\n?)+)/m);
|
|
977
|
+
if (installedMatch) {
|
|
978
|
+
for (const line of installedMatch[1].split(/\n/)) {
|
|
979
|
+
const m = line.match(/^[ \t]+-\s+([A-Za-z0-9._-]+)/);
|
|
980
|
+
if (m) skillNames.push(m[1]);
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
const missing = [];
|
|
984
|
+
for (const name of skillNames) {
|
|
985
|
+
const skillFile = path.join(projectRoot, PROJECT_ADDON_DIR_NAME, 'skills', name, 'SKILL.md');
|
|
986
|
+
if (!(await fs.pathExists(skillFile))) missing.push(name);
|
|
987
|
+
}
|
|
988
|
+
return { missing };
|
|
989
|
+
}
|
|
990
|
+
|
|
918
991
|
async function patchComplexityProfile(projectRoot, profile) {
|
|
919
992
|
const file = path.join(
|
|
920
993
|
projectRoot,
|
|
@@ -1008,25 +1081,39 @@ async function resolveAutopilotSettings({ projectRoot, yes, dryRun, v1Snapshot }
|
|
|
1008
1081
|
const existing = await readExistingAutopilotConfig(projectRoot, v1Snapshot);
|
|
1009
1082
|
const defaultLimit = existing.sessionStoryLimit ?? DEFAULT_SESSION_STORY_LIMIT;
|
|
1010
1083
|
const defaultMode = existing.retrospectiveMode ?? DEFAULT_RETROSPECTIVE_MODE;
|
|
1084
|
+
// v2.3.0 — opt-in default false; preserve existing user choice on upgrade.
|
|
1085
|
+
const defaultAutoPlan = existing.autoPlanOnStart ?? false;
|
|
1011
1086
|
|
|
1012
1087
|
if (yes) {
|
|
1013
|
-
if (
|
|
1088
|
+
if (
|
|
1089
|
+
existing.sessionStoryLimit != null ||
|
|
1090
|
+
existing.retrospectiveMode != null ||
|
|
1091
|
+
existing.autoPlanOnStart != null
|
|
1092
|
+
) {
|
|
1014
1093
|
console.log(
|
|
1015
1094
|
pc.dim(
|
|
1016
|
-
`Preserving autopilot config: session_story_limit=${defaultLimit}, retrospective_mode=${defaultMode}`,
|
|
1095
|
+
`Preserving autopilot config: session_story_limit=${defaultLimit}, retrospective_mode=${defaultMode}, auto_plan_on_start=${defaultAutoPlan}`,
|
|
1017
1096
|
),
|
|
1018
1097
|
);
|
|
1019
1098
|
}
|
|
1020
|
-
return {
|
|
1099
|
+
return {
|
|
1100
|
+
sessionStoryLimit: defaultLimit,
|
|
1101
|
+
retrospectiveMode: defaultMode,
|
|
1102
|
+
autoPlanOnStart: defaultAutoPlan,
|
|
1103
|
+
};
|
|
1021
1104
|
}
|
|
1022
1105
|
|
|
1023
1106
|
if (dryRun) {
|
|
1024
1107
|
console.log(
|
|
1025
1108
|
pc.dim(
|
|
1026
|
-
`[DRY RUN] Would prompt for autopilot config (current: session_story_limit=${defaultLimit}, retrospective_mode=${defaultMode})`,
|
|
1109
|
+
`[DRY RUN] Would prompt for autopilot config (current: session_story_limit=${defaultLimit}, retrospective_mode=${defaultMode}, auto_plan_on_start=${defaultAutoPlan})`,
|
|
1027
1110
|
),
|
|
1028
1111
|
);
|
|
1029
|
-
return {
|
|
1112
|
+
return {
|
|
1113
|
+
sessionStoryLimit: defaultLimit,
|
|
1114
|
+
retrospectiveMode: defaultMode,
|
|
1115
|
+
autoPlanOnStart: defaultAutoPlan,
|
|
1116
|
+
};
|
|
1030
1117
|
}
|
|
1031
1118
|
|
|
1032
1119
|
const limitRaw = await prompts.text({
|
|
@@ -1065,7 +1152,17 @@ async function resolveAutopilotSettings({ projectRoot, yes, dryRun, v1Snapshot }
|
|
|
1065
1152
|
initialValue: defaultMode,
|
|
1066
1153
|
});
|
|
1067
1154
|
|
|
1068
|
-
|
|
1155
|
+
// v2.3.0 — single yes/no prompt for the new plan workflow. Default false:
|
|
1156
|
+
// `autopilot start` runs in sprint-status order until the user explicitly
|
|
1157
|
+
// invokes /sprintpilot-plan-sprint, which is always available regardless.
|
|
1158
|
+
// Set this true to auto-trigger the planning skill on greenfield projects.
|
|
1159
|
+
const autoPlanOnStart = await prompts.confirm({
|
|
1160
|
+
message:
|
|
1161
|
+
'Auto-build a sprint plan on first `autopilot start`? (v2.3.0; runs /sprintpilot-plan-sprint to infer dependencies. You can always invoke the skill manually regardless of this setting.)',
|
|
1162
|
+
initialValue: defaultAutoPlan,
|
|
1163
|
+
});
|
|
1164
|
+
|
|
1165
|
+
return { sessionStoryLimit, retrospectiveMode, autoPlanOnStart };
|
|
1069
1166
|
}
|
|
1070
1167
|
|
|
1071
1168
|
async function runInteractiveToolPicker(detected) {
|
|
@@ -1194,7 +1291,7 @@ async function runInstall(options = {}) {
|
|
|
1194
1291
|
// runtime copy — they're NOT threaded through `renderString`, because
|
|
1195
1292
|
// workflow.md's `{{session_story_limit}}` / `{{retrospective_mode}}`
|
|
1196
1293
|
// variable references would collide with single-brace token matching.
|
|
1197
|
-
const { sessionStoryLimit, retrospectiveMode } = await resolveAutopilotSettings({
|
|
1294
|
+
const { sessionStoryLimit, retrospectiveMode, autoPlanOnStart } = await resolveAutopilotSettings({
|
|
1198
1295
|
projectRoot,
|
|
1199
1296
|
yes,
|
|
1200
1297
|
dryRun,
|
|
@@ -1439,7 +1536,11 @@ async function runInstall(options = {}) {
|
|
|
1439
1536
|
// wrote the bundled default config) AND after the v1 snapshot
|
|
1440
1537
|
// reapply (which might have restored an older config.yaml without
|
|
1441
1538
|
// `retrospective_mode`). The user's prompted values always win.
|
|
1442
|
-
await patchAutopilotConfig(projectRoot, {
|
|
1539
|
+
await patchAutopilotConfig(projectRoot, {
|
|
1540
|
+
sessionStoryLimit,
|
|
1541
|
+
retrospectiveMode,
|
|
1542
|
+
autoPlanOnStart,
|
|
1543
|
+
});
|
|
1443
1544
|
|
|
1444
1545
|
// 6c. Persist the complexity_profile. Separate from patchAutopilotConfig
|
|
1445
1546
|
// so the existing upgrade test coverage (readExistingAutopilotConfig /
|
|
@@ -1501,6 +1602,56 @@ async function runInstall(options = {}) {
|
|
|
1501
1602
|
console.log(
|
|
1502
1603
|
`Total skills installed: ${totalInstalled} (${skillCount} skills x ${selectedTools.length} tools)`,
|
|
1503
1604
|
);
|
|
1605
|
+
|
|
1606
|
+
// v2.3.0 — post-install hygiene: warn if any skill in manifest.yaml
|
|
1607
|
+
// doesn't have a SKILL.md on disk. Non-blocking; surfaces packaging
|
|
1608
|
+
// bugs without failing the install.
|
|
1609
|
+
try {
|
|
1610
|
+
const verify = await verifySkillManifest(projectRoot);
|
|
1611
|
+
if (verify.missing.length > 0) {
|
|
1612
|
+
console.log('');
|
|
1613
|
+
console.log(
|
|
1614
|
+
pc.yellow(` WARN: manifest references skills missing from disk: ${verify.missing.join(', ')}`),
|
|
1615
|
+
);
|
|
1616
|
+
console.log(pc.yellow(' These won\'t appear under your host tool\'s / menu until the SKILL.md files are present.'));
|
|
1617
|
+
}
|
|
1618
|
+
} catch {
|
|
1619
|
+
// Self-check failure is non-fatal — never block install on hygiene.
|
|
1620
|
+
}
|
|
1621
|
+
|
|
1622
|
+
// v2.3.0 upgrade notes — surfaced only when the relevant signals are
|
|
1623
|
+
// actually present. Greenfield installs see nothing; upgraders from
|
|
1624
|
+
// v2.2.x see migration + deprecation notices.
|
|
1625
|
+
const v23Notes = [];
|
|
1626
|
+
const legacyDepsPath = path.join(projectRoot, '_Sprintpilot', 'sprints', 'dependencies.yaml');
|
|
1627
|
+
if (await fs.pathExists(legacyDepsPath)) {
|
|
1628
|
+
v23Notes.push(
|
|
1629
|
+
'Legacy file detected: _Sprintpilot/sprints/dependencies.yaml',
|
|
1630
|
+
' Auto-migrated to sprint-plan.yaml on the first `autopilot start`.',
|
|
1631
|
+
' Run now: node _Sprintpilot/scripts/infer-dependencies.js migrate',
|
|
1632
|
+
);
|
|
1633
|
+
}
|
|
1634
|
+
// Re-read so we can show the deprecation notice without threading state
|
|
1635
|
+
// through every helper. Cheap (one regex scan); only happens once per install.
|
|
1636
|
+
try {
|
|
1637
|
+
const existingForNotes = await readExistingAutopilotConfig(projectRoot, v1ConfigSnapshot);
|
|
1638
|
+
if (existingForNotes.autoInferDependencies === true) {
|
|
1639
|
+
if (v23Notes.length > 0) v23Notes.push('');
|
|
1640
|
+
v23Notes.push(
|
|
1641
|
+
'Deprecated: autopilot.auto_infer_dependencies = true in your config.',
|
|
1642
|
+
' This flag is a no-op in v2.3.0 — superseded by auto_plan_on_start (default false).',
|
|
1643
|
+
' Safe to remove from config.yaml; the new /sprintpilot-plan-sprint workflow',
|
|
1644
|
+
' handles inference manually or on opt-in auto-trigger.',
|
|
1645
|
+
);
|
|
1646
|
+
}
|
|
1647
|
+
} catch {
|
|
1648
|
+
// Config re-read failure is non-fatal — skip the deprecation notice.
|
|
1649
|
+
}
|
|
1650
|
+
if (v23Notes.length > 0) {
|
|
1651
|
+
console.log('');
|
|
1652
|
+
console.log(pc.cyan('v2.3.0 upgrade notes:'));
|
|
1653
|
+
for (const line of v23Notes) console.log(' ' + line);
|
|
1654
|
+
}
|
|
1504
1655
|
console.log('');
|
|
1505
1656
|
console.log('Skills:');
|
|
1506
1657
|
for (const skill of allSkills) console.log(` - ${skill}`);
|
|
@@ -1513,6 +1664,13 @@ async function runInstall(options = {}) {
|
|
|
1513
1664
|
console.log(' /sprint-autopilot-off Disengage and show status');
|
|
1514
1665
|
console.log(' /bmad-help Orientation and next-step guidance (from BMad Method)');
|
|
1515
1666
|
console.log('');
|
|
1667
|
+
console.log('First steps for a new sprint:');
|
|
1668
|
+
console.log(' 1. BMad sprint planning: /bmad-sprint-planning');
|
|
1669
|
+
console.log(' 2. (optional) Sprint plan: /sprintpilot-plan-sprint');
|
|
1670
|
+
console.log(' 3. (optional) Inspect DAG: /sprintpilot-dependency-graph mermaid');
|
|
1671
|
+
console.log(' 4. Start autopilot: /sprint-autopilot-on');
|
|
1672
|
+
console.log(' 5. Check live progress: /sprintpilot-sprint-progress');
|
|
1673
|
+
console.log('');
|
|
1516
1674
|
console.log('Configuration (edit these files to customize behavior):');
|
|
1517
1675
|
console.log('');
|
|
1518
1676
|
console.log(' _Sprintpilot/modules/git/config.yaml');
|
|
@@ -1542,6 +1700,18 @@ async function runInstall(options = {}) {
|
|
|
1542
1700
|
console.log(
|
|
1543
1701
|
` ${apKey('autopilot.retrospective_mode')}${apVal(retrospectiveMode)} Epic-end retrospective: auto (inline) | stop (pause) | skip (not recommended)`,
|
|
1544
1702
|
);
|
|
1703
|
+
console.log(
|
|
1704
|
+
` ${apKey('autopilot.auto_plan_on_start')}${apVal(String(autoPlanOnStart))} Auto-build sprint plan on first start (v2.3.0; default off)`,
|
|
1705
|
+
);
|
|
1706
|
+
console.log('');
|
|
1707
|
+
console.log('Sprint planning + progress (v2.3.0):');
|
|
1708
|
+
console.log(' /sprintpilot-plan-sprint Build dependency-aware sprint plan');
|
|
1709
|
+
console.log(' /sprintpilot-sprint-progress Concise health-check of autopilot execution');
|
|
1710
|
+
console.log(' /sprintpilot-dependency-graph Render DAG (mermaid / graphviz / text / layers / json)');
|
|
1711
|
+
console.log('');
|
|
1712
|
+
console.log('CLI utilities:');
|
|
1713
|
+
console.log(' autopilot progress Live status (--json / --story <key>)');
|
|
1714
|
+
console.log(' autopilot start --no-auto-plan Skip auto-planning for one session');
|
|
1545
1715
|
console.log('');
|
|
1546
1716
|
console.log('Multi-agent skills — run parallel subagents for faster analysis:');
|
|
1547
1717
|
console.log(' /sprintpilot-code-review Parallel 3-layer adversarial review');
|
|
@@ -1551,6 +1721,11 @@ async function runInstall(options = {}) {
|
|
|
1551
1721
|
console.log(' /sprintpilot-migrate Legacy migration planning');
|
|
1552
1722
|
console.log(' /sprintpilot-research Parallel web research');
|
|
1553
1723
|
console.log(' /sprintpilot-party-mode Multi-persona agent discussions');
|
|
1724
|
+
console.log('');
|
|
1725
|
+
console.log('Documentation:');
|
|
1726
|
+
console.log(' Sprint planning walkthrough: docs/USAGE.md');
|
|
1727
|
+
console.log(' Configuration reference: docs/CONFIGURATION.md');
|
|
1728
|
+
console.log(' Architecture deep-dive: docs/ARCHITECTURE.md');
|
|
1554
1729
|
|
|
1555
1730
|
const latestVersion = await latestVersionPromise;
|
|
1556
1731
|
if (latestVersion && addonVersion && compareVersions(addonVersion, latestVersion) === 'behind') {
|
|
@@ -1586,5 +1761,6 @@ module.exports = {
|
|
|
1586
1761
|
KEY_RENAMES,
|
|
1587
1762
|
snapshotUserOwnedFiles,
|
|
1588
1763
|
applyUserOwnedFiles,
|
|
1764
|
+
verifySkillManifest,
|
|
1589
1765
|
},
|
|
1590
1766
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ikunin/sprintpilot",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.0",
|
|
4
4
|
"description": "Sprintpilot — autopilot and multi-agent addon for BMad Method v6: git workflow, parallel agents, autonomous story execution",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|