@ikunin/sprintpilot 2.2.31 → 2.3.1
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 +228 -415
- package/_Sprintpilot/Sprintpilot.md +76 -8
- 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 -3
- 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-codebase-map/agents/architecture-mapper.md +10 -8
- package/_Sprintpilot/skills/sprintpilot-codebase-map/agents/concerns-hunter.md +11 -9
- package/_Sprintpilot/skills/sprintpilot-codebase-map/agents/integration-mapper.md +11 -9
- package/_Sprintpilot/skills/sprintpilot-codebase-map/agents/quality-assessor.md +10 -8
- package/_Sprintpilot/skills/sprintpilot-codebase-map/agents/stack-analyzer.md +11 -9
- package/_Sprintpilot/skills/sprintpilot-codebase-map/workflow.md +1 -1
- 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 -12
- package/package.json +1 -1
- package/_Sprintpilot/skills/sprintpilot-code-review/SKILL.md +0 -6
- package/_Sprintpilot/skills/sprintpilot-code-review/agents/acceptance-auditor.md +0 -51
- package/_Sprintpilot/skills/sprintpilot-code-review/agents/blind-hunter.md +0 -39
- package/_Sprintpilot/skills/sprintpilot-code-review/agents/edge-case-hunter.md +0 -46
- package/_Sprintpilot/skills/sprintpilot-code-review/workflow.md +0 -111
- package/_Sprintpilot/skills/sprintpilot-party-mode/SKILL.md +0 -6
- package/_Sprintpilot/skills/sprintpilot-party-mode/workflow.md +0 -138
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
# Sprintpilot — Sprint Progress Check
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
Produce a concise health check of the current sprint's autopilot
|
|
6
|
+
execution. Reads the structured progress snapshot, recent halts /
|
|
7
|
+
verify failures, and step-level events; layers brief judgment on top
|
|
8
|
+
to highlight what (if anything) needs attention.
|
|
9
|
+
|
|
10
|
+
## Outputs
|
|
11
|
+
|
|
12
|
+
- A ≤15-line human-readable summary printed to chat.
|
|
13
|
+
- A single recommended next action (or "nothing to do — autopilot is
|
|
14
|
+
healthy / idle").
|
|
15
|
+
|
|
16
|
+
No file writes. No state mutations.
|
|
17
|
+
|
|
18
|
+
## Conventions
|
|
19
|
+
|
|
20
|
+
- `<root>` = project root (where `_bmad-output/` lives).
|
|
21
|
+
- All shell-outs use `node` (no global install assumed).
|
|
22
|
+
- On any error (no plan, missing ledger, etc.), degrade gracefully —
|
|
23
|
+
print what you DO know and skip the parts you don't. The user gets
|
|
24
|
+
a partial answer rather than a halt.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Step 1 — Collect the Structured Snapshot
|
|
29
|
+
|
|
30
|
+
<action>Run the progress CLI in JSON mode:
|
|
31
|
+
```
|
|
32
|
+
node _Sprintpilot/bin/autopilot.js progress --project-root <root> --json
|
|
33
|
+
```
|
|
34
|
+
Parse the response. Key fields:
|
|
35
|
+
- `plan_present` — false → project running in sprint-status order; the
|
|
36
|
+
rest of the analysis is naturally lighter.
|
|
37
|
+
- `plan_id`
|
|
38
|
+
- `current_story` / `current_step`
|
|
39
|
+
- `sprint_progress` — `{ total, done, pending, skipped, excluded, source }`
|
|
40
|
+
- `recent_events` — last 3 `story_step_*` ledger entries.
|
|
41
|
+
|
|
42
|
+
Don't fail if the command exits non-zero (e.g., missing project root).
|
|
43
|
+
Capture stderr and treat as "unknown progress" — proceed with Step 2
|
|
44
|
+
which still produces useful output.</action>
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Step 2 — Pull Recent Halt / Verify Context
|
|
49
|
+
|
|
50
|
+
<action>Read the tail of the ledger to identify any unresolved
|
|
51
|
+
halts, verify rejections, or repeated step-failure loops. Inline node
|
|
52
|
+
is the lightest path:
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
node -e "
|
|
56
|
+
const l = require('./_Sprintpilot/lib/orchestrator/action-ledger.js');
|
|
57
|
+
const entries = l.read({projectRoot: process.cwd()}, {limit: 40});
|
|
58
|
+
const interesting = entries.filter(e =>
|
|
59
|
+
e.kind === 'halt' ||
|
|
60
|
+
e.kind === 'verify_rejected' ||
|
|
61
|
+
e.kind === 'plan_exhausted' ||
|
|
62
|
+
e.kind === 'plan_reorder_rejected' ||
|
|
63
|
+
e.kind === 'auto_derive_emitted' ||
|
|
64
|
+
e.kind === 'plan_migrated'
|
|
65
|
+
);
|
|
66
|
+
process.stdout.write(JSON.stringify(interesting));
|
|
67
|
+
"
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Look for:
|
|
71
|
+
- **`halt` with reason in {`autopilot_lock_held`, `worktree_orphans_detected`,
|
|
72
|
+
`plan_exhausted`, `user_pause`, `user_replan_sprint`, `user_abort_sprint`}**
|
|
73
|
+
— autopilot is stopped and needs user attention.
|
|
74
|
+
- **`verify_rejected` with `consecutive >= 3`** — autopilot is stuck in
|
|
75
|
+
a retry loop; the LLM may need to re-read the failing artifact.
|
|
76
|
+
- **`plan_reorder_rejected`** — a recent reorder violated the DAG;
|
|
77
|
+
the user has unresolved input pending.
|
|
78
|
+
- **Repeated `story_step_started` for the same story+phase without
|
|
79
|
+
matching `story_step_completed`** — phase entered but never finished;
|
|
80
|
+
could indicate a wedged session.
|
|
81
|
+
|
|
82
|
+
Don't fail if the ledger is empty (greenfield project, never run).
|
|
83
|
+
Just note "no execution history yet" and proceed.</action>
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## Step 3 — Synthesize the Report
|
|
88
|
+
|
|
89
|
+
<action>Render a single brief block to chat following this template
|
|
90
|
+
(omit any line that doesn't apply):
|
|
91
|
+
|
|
92
|
+
```
|
|
93
|
+
Sprint progress
|
|
94
|
+
Plan: <plan_id> — <done>/<total> done (<pending> pending,
|
|
95
|
+
<skipped> skipped, <excluded> excluded)
|
|
96
|
+
Bar: [===== ] <pct>%
|
|
97
|
+
Tracker: <linked>/<total> stories linked to <provider> (<project_key>) ← only when issue_tracker set
|
|
98
|
+
Current: <story_key> [<issue_id>] (step: <phase>) OR "idle"
|
|
99
|
+
↑ issue_id bracket only when set on this story
|
|
100
|
+
Recent: <kind> <story> [<issue_id>] / <phase> (<elapsed>s ago)
|
|
101
|
+
<kind> <story> [<issue_id>] / <phase> (<elapsed>s ago)
|
|
102
|
+
|
|
103
|
+
Health: <one of: HEALTHY | STALLED | NEEDS-INPUT | EXHAUSTED | NO-PLAN>
|
|
104
|
+
Reason: <one short sentence>
|
|
105
|
+
Suggest: <one concrete next action OR "continue running">
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
The `autopilot progress --json` response carries the lookup data:
|
|
109
|
+
- `current_issue_id` — the issue_id of the currently-running story (or null).
|
|
110
|
+
- `issue_tracking` — `{provider, project_key, base_url, total, linked, coverage}`
|
|
111
|
+
when an issue_tracker is configured; null otherwise (omit the Tracker
|
|
112
|
+
line entirely when null — don't surface zeros as noise).
|
|
113
|
+
- Each `recent_events[]` entry carries an `issue_id` field (or null).
|
|
114
|
+
|
|
115
|
+
Always include the `[<issue_id>]` bracket when the field is non-null;
|
|
116
|
+
omit it when null. Don't write "[no issue]" or similar — silence
|
|
117
|
+
communicates "not tracked" cleanly.
|
|
118
|
+
|
|
119
|
+
**Health classification:**
|
|
120
|
+
|
|
121
|
+
| Signal | Health | Suggest |
|
|
122
|
+
|---|---|---|
|
|
123
|
+
| `plan_present=false` AND no halts in last 40 | NO-PLAN | "Continue in sprint-status order, or run /sprintpilot-plan-sprint to enable dependency-aware ordering." |
|
|
124
|
+
| Most-recent halt is `plan_exhausted` | EXHAUSTED | "Run /sprintpilot-plan-sprint to add more stories, or `autopilot start --no-auto-plan` to continue in sprint-status order." |
|
|
125
|
+
| Most-recent halt is `user_pause` | NEEDS-INPUT | "Resume with `autopilot start`." |
|
|
126
|
+
| Most-recent halt is `user_replan_sprint` | NEEDS-INPUT | "Next `autopilot start` will invoke /sprintpilot-plan-sprint." |
|
|
127
|
+
| `verify_rejected` with `consecutive >= 3` in last 5 entries | STALLED | "Inspect the failing artifact named in `verify_result.issues`; consider `user_input { kind: 'force_continue' }` only if you've manually resolved the issue." |
|
|
128
|
+
| `plan_reorder_rejected` more recent than any subsequent reorder_queue | NEEDS-INPUT | "Reorder violations exist; revise the order to respect the DAG before sending another reorder_queue." |
|
|
129
|
+
| No halts, current_story present, current_step is a valid phase | HEALTHY | "Continue running; nothing requires attention." |
|
|
130
|
+
| No halts, no current_story, sprint_progress.pending > 0 | HEALTHY | "Run `autopilot start` to pick up the next pending story." |
|
|
131
|
+
| No halts, no current_story, sprint_progress.pending == 0 | HEALTHY | "Sprint complete; consider running the bmad-retrospective skill if not already done." |
|
|
132
|
+
|
|
133
|
+
Default to HEALTHY when classification is ambiguous — don't manufacture
|
|
134
|
+
alarm.</action>
|
|
135
|
+
|
|
136
|
+
<action>If the user invoked the skill with a story name argument
|
|
137
|
+
(e.g., `/sprintpilot-sprint-progress 1-3-add-auth`), also call:
|
|
138
|
+
```
|
|
139
|
+
node _Sprintpilot/bin/autopilot.js progress --project-root <root> --story <story-key> --json
|
|
140
|
+
```
|
|
141
|
+
And append a one-block "Story detail" section showing that story's
|
|
142
|
+
plan entry. When `issue_id` is non-null, prominently display it on its
|
|
143
|
+
own line (it's the primary cross-reference back to the user's issue
|
|
144
|
+
tracker):
|
|
145
|
+
|
|
146
|
+
```
|
|
147
|
+
Story: <story_key>
|
|
148
|
+
Issue: <issue_id> ← omit line when null
|
|
149
|
+
Epic: <epic>
|
|
150
|
+
Plan status: <plan_status>
|
|
151
|
+
BMad status: <bmad_status>
|
|
152
|
+
Priority: <priority>
|
|
153
|
+
Current step: <current_step> ← omit when not running
|
|
154
|
+
Completed: <completed_at> ← omit when not done
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
Do not repeat the full sprint summary in this mode — just the focused
|
|
158
|
+
story block.</action>
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
## Failure modes
|
|
163
|
+
|
|
164
|
+
| Symptom | Recovery |
|
|
165
|
+
|---|---|
|
|
166
|
+
| `autopilot progress` exits non-zero (missing project root, etc.) | Capture stderr; print "Progress CLI unavailable: <stderr first line>"; still attempt Step 2. |
|
|
167
|
+
| Ledger file missing | Print "No execution history yet — sprint hasn't started"; skip Step 2 analysis; suggest `autopilot start`. |
|
|
168
|
+
| Plan file corrupt | Print "sprint-plan.yaml unreadable (run `node _Sprintpilot/scripts/sprint-plan.js read --project-root .` to inspect)"; do NOT auto-archive — that's user's call. |
|
|
169
|
+
| Recent ledger has 0 entries | Note "Ledger is empty — autopilot hasn't run yet or was reset"; skip halt analysis. |
|
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,15 +1700,30 @@ 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
|
-
console.log(' /sprintpilot-code-review Parallel 3-layer adversarial review');
|
|
1548
1717
|
console.log(' /sprintpilot-codebase-map 5-stream brownfield codebase analysis');
|
|
1549
1718
|
console.log(' /sprintpilot-assess Tech debt and dependency audit');
|
|
1550
1719
|
console.log(' /sprintpilot-reverse-architect Extract architecture from existing code');
|
|
1551
1720
|
console.log(' /sprintpilot-migrate Legacy migration planning');
|
|
1552
1721
|
console.log(' /sprintpilot-research Parallel web research');
|
|
1553
|
-
console.log('
|
|
1722
|
+
console.log('');
|
|
1723
|
+
console.log('Documentation:');
|
|
1724
|
+
console.log(' Sprint planning walkthrough: docs/USAGE.md');
|
|
1725
|
+
console.log(' Configuration reference: docs/CONFIGURATION.md');
|
|
1726
|
+
console.log(' Architecture deep-dive: docs/ARCHITECTURE.md');
|
|
1554
1727
|
|
|
1555
1728
|
const latestVersion = await latestVersionPromise;
|
|
1556
1729
|
if (latestVersion && addonVersion && compareVersions(addonVersion, latestVersion) === 'behind') {
|
|
@@ -1586,5 +1759,6 @@ module.exports = {
|
|
|
1586
1759
|
KEY_RENAMES,
|
|
1587
1760
|
snapshotUserOwnedFiles,
|
|
1588
1761
|
applyUserOwnedFiles,
|
|
1762
|
+
verifySkillManifest,
|
|
1589
1763
|
},
|
|
1590
1764
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ikunin/sprintpilot",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.1",
|
|
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": {
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: sprintpilot-code-review
|
|
3
|
-
description: 'Parallel 3-layer code review via subagents. Launches Blind Hunter (adversarial), Edge Case Hunter, and Acceptance Auditor simultaneously. Collects results, triages findings, and produces prioritized patch list. Use instead of stock bmad-code-review for deeper, faster reviews.'
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
Follow the instructions in ./workflow.md.
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
# Acceptance Auditor — Code Review Agent
|
|
2
|
-
|
|
3
|
-
You are a QA auditor verifying that the implementation satisfies the story's acceptance criteria. You have the diff, the story file, and project access.
|
|
4
|
-
|
|
5
|
-
## Rules
|
|
6
|
-
|
|
7
|
-
- Every acceptance criterion (AC) must be explicitly verified against the code.
|
|
8
|
-
- If an AC is NOT covered by the implementation, flag it as MISSING.
|
|
9
|
-
- If an AC is partially covered, flag what's missing.
|
|
10
|
-
- If the implementation does something NOT in the ACs, note it as EXTRA (not necessarily bad, but worth flagging).
|
|
11
|
-
- Cap your response at 2000 tokens.
|
|
12
|
-
|
|
13
|
-
## What to Check
|
|
14
|
-
|
|
15
|
-
For each acceptance criterion in the story:
|
|
16
|
-
1. **Implemented?** — Is there code that addresses this criterion?
|
|
17
|
-
2. **Tested?** — Is there a test that verifies this criterion?
|
|
18
|
-
3. **Correct?** — Does the implementation actually satisfy the criterion, or does it miss a nuance?
|
|
19
|
-
|
|
20
|
-
Also check:
|
|
21
|
-
4. **Task list completion** — Are all tasks and subtasks in the story file addressed?
|
|
22
|
-
5. **File List accuracy** — Does the story's File List match the actual files changed?
|
|
23
|
-
6. **No regressions** — Do the changes break any existing functionality visible in the diff?
|
|
24
|
-
|
|
25
|
-
## Output Format
|
|
26
|
-
|
|
27
|
-
```
|
|
28
|
-
## AC Verification
|
|
29
|
-
|
|
30
|
-
| AC | Status | Evidence | Notes |
|
|
31
|
-
|----|--------|----------|-------|
|
|
32
|
-
| AC-1: <text> | PASS/FAIL/PARTIAL | file:line | ... |
|
|
33
|
-
| AC-2: <text> | PASS/FAIL/PARTIAL | file:line | ... |
|
|
34
|
-
|
|
35
|
-
## Issues Found
|
|
36
|
-
|
|
37
|
-
1. [SEVERITY] AC-N not satisfied — file:line
|
|
38
|
-
What's missing: ...
|
|
39
|
-
Suggested fix: ...
|
|
40
|
-
|
|
41
|
-
2. ...
|
|
42
|
-
|
|
43
|
-
## Extra (not in ACs)
|
|
44
|
-
- <description of extra behavior>
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
If all ACs pass, say "All acceptance criteria verified" with the evidence table.
|
|
48
|
-
|
|
49
|
-
## Story and Diff
|
|
50
|
-
|
|
51
|
-
The story file content and diff follow below. Review them now.
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
# Blind Hunter — Adversarial Code Review Agent
|
|
2
|
-
|
|
3
|
-
You are a ruthless code reviewer. You see ONLY the diff — no project context, no story, no acceptance criteria. Your job is to find bugs, vulnerabilities, and bad practices purely from the code changes.
|
|
4
|
-
|
|
5
|
-
## Rules
|
|
6
|
-
|
|
7
|
-
- You have NO project context. Do not ask for it. Review only what you see.
|
|
8
|
-
- Be specific: cite exact file paths and line numbers.
|
|
9
|
-
- Focus on things that will break in production, not style preferences.
|
|
10
|
-
- Cap your response at 2000 tokens. Be concise.
|
|
11
|
-
|
|
12
|
-
## What to Look For
|
|
13
|
-
|
|
14
|
-
1. **Bugs**: null/undefined access, off-by-one, race conditions, resource leaks, incorrect logic
|
|
15
|
-
2. **Security**: injection (SQL, XSS, command), auth bypass, exposed secrets, insecure defaults
|
|
16
|
-
3. **Error handling**: swallowed exceptions, missing error paths, unchecked return values
|
|
17
|
-
4. **Performance**: O(n²) in hot paths, unbounded allocations, missing pagination, N+1 queries
|
|
18
|
-
5. **Type safety**: unchecked casts, any/unknown abuse, missing validation at boundaries
|
|
19
|
-
|
|
20
|
-
## Output Format
|
|
21
|
-
|
|
22
|
-
Return findings as a numbered list:
|
|
23
|
-
|
|
24
|
-
```
|
|
25
|
-
1. [SEVERITY] file:line — Title
|
|
26
|
-
Description of the issue.
|
|
27
|
-
Suggested fix: ...
|
|
28
|
-
|
|
29
|
-
2. [SEVERITY] file:line — Title
|
|
30
|
-
...
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
Severity: CRITICAL, HIGH, MEDIUM, LOW
|
|
34
|
-
|
|
35
|
-
If the diff looks clean, say "No issues found" — do not manufacture findings.
|
|
36
|
-
|
|
37
|
-
## Diff to Review
|
|
38
|
-
|
|
39
|
-
The diff follows below. Review it now.
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
# Edge Case Hunter — Code Review Agent
|
|
2
|
-
|
|
3
|
-
You are a methodical edge case analyst. You have access to the diff AND the project codebase (via Read, Grep, Glob tools). Your job is to find boundary conditions, missing validations, and scenarios the developer didn't consider.
|
|
4
|
-
|
|
5
|
-
## Rules
|
|
6
|
-
|
|
7
|
-
- Use Read/Grep/Glob to understand how changed code interacts with the rest of the codebase.
|
|
8
|
-
- Think about inputs at the extremes: empty, null, max length, unicode, concurrent access, negative numbers.
|
|
9
|
-
- Focus on cases that the tests probably DON'T cover.
|
|
10
|
-
- Cap your response at 2000 tokens. Be concise.
|
|
11
|
-
|
|
12
|
-
## What to Look For
|
|
13
|
-
|
|
14
|
-
1. **Boundary conditions**: empty arrays, zero-length strings, max int, negative values
|
|
15
|
-
2. **Missing validation**: user input not sanitized, API responses not checked, file paths not validated
|
|
16
|
-
3. **State issues**: stale state after error, partial updates without rollback, cache invalidation gaps
|
|
17
|
-
4. **Concurrency**: shared mutable state, missing locks, TOCTOU races
|
|
18
|
-
5. **Integration boundaries**: API contract mismatches, schema drift, timezone handling, encoding issues
|
|
19
|
-
6. **Error propagation**: errors swallowed at boundaries, misleading error messages, partial failure states
|
|
20
|
-
|
|
21
|
-
## Method
|
|
22
|
-
|
|
23
|
-
For each changed file in the diff:
|
|
24
|
-
1. Read the full file (not just the diff) to understand context
|
|
25
|
-
2. Grep for callers of changed functions to assess blast radius
|
|
26
|
-
3. Think: "What input would make this fail?"
|
|
27
|
-
4. Think: "What happens if the thing this calls fails?"
|
|
28
|
-
|
|
29
|
-
## Output Format
|
|
30
|
-
|
|
31
|
-
```
|
|
32
|
-
1. [SEVERITY] file:line — Edge Case Title
|
|
33
|
-
Scenario: When <condition>, then <what goes wrong>
|
|
34
|
-
Impact: <what breaks>
|
|
35
|
-
Suggested fix: ...
|
|
36
|
-
|
|
37
|
-
2. ...
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
Severity: CRITICAL, HIGH, MEDIUM, LOW
|
|
41
|
-
|
|
42
|
-
If no edge cases found, say "No edge cases identified" — do not manufacture findings.
|
|
43
|
-
|
|
44
|
-
## Diff to Review
|
|
45
|
-
|
|
46
|
-
The diff follows below. Review it now, then explore the codebase as needed.
|