diff --git a/get-shit-done/bin/lib/core.cjs b/get-shit-done/bin/lib/core.cjs
index 20aab2803..bdd50bb7c 100644
--- a/get-shit-done/bin/lib/core.cjs
+++ b/get-shit-done/bin/lib/core.cjs
@@ -379,11 +379,21 @@ function generateSlugInternal(text) {
function getMilestoneInfo(cwd) {
try {
const roadmap = fs.readFileSync(path.join(cwd, '.planning', 'ROADMAP.md'), 'utf-8');
- const versionMatch = roadmap.match(/v(\d+\.\d+)/);
- const nameMatch = roadmap.match(/## .*v\d+\.\d+[:\s]+([^\n(]+)/);
+ // Strip ... blocks so shipped milestones don't interfere
+ const cleaned = roadmap.replace(/[\s\S]*?<\/details>/gi, '');
+ // Extract version and name from the same ## heading for consistency
+ const headingMatch = cleaned.match(/## .*v(\d+\.\d+)[:\s]+([^\n(]+)/);
+ if (headingMatch) {
+ return {
+ version: 'v' + headingMatch[1],
+ name: headingMatch[2].trim(),
+ };
+ }
+ // Fallback: try bare version match
+ const versionMatch = cleaned.match(/v(\d+\.\d+)/);
return {
version: versionMatch ? versionMatch[0] : 'v1.0',
- name: nameMatch ? nameMatch[1].trim() : 'milestone',
+ name: 'milestone',
};
} catch {
return { version: 'v1.0', name: 'milestone' };
diff --git a/tests/core.test.cjs b/tests/core.test.cjs
index 38a4cf783..d859269c2 100644
--- a/tests/core.test.cjs
+++ b/tests/core.test.cjs
@@ -398,6 +398,80 @@ describe('getMilestoneInfo', () => {
assert.strictEqual(info.version, 'v1.0');
assert.strictEqual(info.name, 'milestone');
});
+
+ test('returns active milestone when shipped milestone is collapsed in details block', () => {
+ const roadmap = [
+ '# Milestones',
+ '',
+ '| Version | Status |',
+ '|---------|--------|',
+ '| v0.1 | Shipped |',
+ '| v0.2 | Active |',
+ '',
+ '',
+ 'v0.1 — Legacy Feature Parity (Shipped)
',
+ '',
+ '## Roadmap v0.1: Legacy Feature Parity',
+ '',
+ '### Phase 1: Core Setup',
+ 'Some content about phase 1',
+ '',
+ ' ',
+ '',
+ '## Roadmap v0.2: Dashboard Overhaul',
+ '',
+ '### Phase 8: New Dashboard Layout',
+ 'Some content about phase 8',
+ ].join('\n');
+ fs.writeFileSync(path.join(tmpDir, '.planning', 'ROADMAP.md'), roadmap);
+ const info = getMilestoneInfo(tmpDir);
+ assert.strictEqual(info.version, 'v0.2');
+ assert.strictEqual(info.name, 'Dashboard Overhaul');
+ });
+
+ test('returns active milestone when multiple shipped milestones exist in details blocks', () => {
+ const roadmap = [
+ '# Milestones',
+ '',
+ '| Version | Status |',
+ '|---------|--------|',
+ '| v0.1 | Shipped |',
+ '| v0.2 | Shipped |',
+ '| v0.3 | Active |',
+ '',
+ '',
+ 'v0.1 — Initial Release (Shipped)
',
+ '',
+ '## Roadmap v0.1: Initial Release',
+ '',
+ ' ',
+ '',
+ '',
+ 'v0.2 — Feature Expansion (Shipped)
',
+ '',
+ '## Roadmap v0.2: Feature Expansion',
+ '',
+ ' ',
+ '',
+ '## Roadmap v0.3: Performance Tuning',
+ '',
+ '### Phase 12: Optimize Queries',
+ ].join('\n');
+ fs.writeFileSync(path.join(tmpDir, '.planning', 'ROADMAP.md'), roadmap);
+ const info = getMilestoneInfo(tmpDir);
+ assert.strictEqual(info.version, 'v0.3');
+ assert.strictEqual(info.name, 'Performance Tuning');
+ });
+
+ test('returns defaults when roadmap has no heading matches', () => {
+ fs.writeFileSync(
+ path.join(tmpDir, '.planning', 'ROADMAP.md'),
+ '# Roadmap\n\nSome content without version headings'
+ );
+ const info = getMilestoneInfo(tmpDir);
+ assert.strictEqual(info.version, 'v1.0');
+ assert.strictEqual(info.name, 'milestone');
+ });
});
// ─── searchPhaseInDir ──────────────────────────────────────────────────────────