fix(lifecycle): extract one-liner from summary body when not in frontmatter
The summary template puts the one-liner as a `**bold**` line after the `# Phase N` heading, but `cmdSummaryExtract` and `cmdMilestoneComplete` only checked frontmatter `one-liner` field — which is often empty. Adds `extractOneLinerFromBody()` to core.cjs as a fallback that parses the first `**...**` line after the heading. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -363,6 +363,37 @@ requirements-completed:
|
||||
assert.strictEqual(output.decisions, undefined, 'decisions excluded');
|
||||
});
|
||||
|
||||
test('extracts one-liner from body when not in frontmatter', () => {
|
||||
const phaseDir = path.join(tmpDir, '.planning', 'phases', '01-foundation');
|
||||
fs.mkdirSync(phaseDir, { recursive: true });
|
||||
|
||||
fs.writeFileSync(
|
||||
path.join(phaseDir, '01-01-SUMMARY.md'),
|
||||
`---
|
||||
phase: "01"
|
||||
key-files:
|
||||
- src/lib/db.ts
|
||||
---
|
||||
|
||||
# Phase 1: Foundation Summary
|
||||
|
||||
**JWT auth with refresh rotation using jose library**
|
||||
|
||||
## Performance
|
||||
|
||||
- **Duration:** 28 min
|
||||
- **Tasks:** 5
|
||||
`
|
||||
);
|
||||
|
||||
const result = runGsdTools('summary-extract .planning/phases/01-foundation/01-01-SUMMARY.md', tmpDir);
|
||||
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||
|
||||
const output = JSON.parse(result.output);
|
||||
assert.strictEqual(output.one_liner, 'JWT auth with refresh rotation using jose library',
|
||||
'one-liner should be extracted from body **bold** line');
|
||||
});
|
||||
|
||||
test('handles missing frontmatter fields gracefully', () => {
|
||||
const phaseDir = path.join(tmpDir, '.planning', 'phases', '01-foundation');
|
||||
fs.mkdirSync(phaseDir, { recursive: true });
|
||||
|
||||
@@ -448,6 +448,34 @@ describe('milestone complete command', () => {
|
||||
assert.strictEqual(output.tasks, 7, 'should count tasks from **Tasks:** N field');
|
||||
});
|
||||
|
||||
test('extracts one-liner from body when not in frontmatter', () => {
|
||||
fs.writeFileSync(
|
||||
path.join(tmpDir, '.planning', 'ROADMAP.md'),
|
||||
`# Roadmap v1.0\n\n### Phase 1: Foundation\n**Goal:** Setup\n`
|
||||
);
|
||||
fs.writeFileSync(
|
||||
path.join(tmpDir, '.planning', 'STATE.md'),
|
||||
`# State\n\n**Status:** In progress\n**Last Activity:** 2025-01-01\n**Last Activity Description:** Working\n`
|
||||
);
|
||||
|
||||
const p1 = path.join(tmpDir, '.planning', 'phases', '01-foundation');
|
||||
fs.mkdirSync(p1, { recursive: true });
|
||||
// No one-liner in frontmatter, but present in body as bold line
|
||||
fs.writeFileSync(
|
||||
path.join(p1, '01-01-SUMMARY.md'),
|
||||
`---\nphase: "01"\n---\n\n# Phase 1: Foundation Summary\n\n**JWT auth with refresh rotation using jose library**\n\n## Performance\n`
|
||||
);
|
||||
|
||||
const result = runGsdTools('milestone complete v1.0 --name MVP', tmpDir);
|
||||
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||
|
||||
const output = JSON.parse(result.output);
|
||||
assert.ok(
|
||||
output.accomplishments.includes('JWT auth with refresh rotation using jose library'),
|
||||
'should extract one-liner from body bold line'
|
||||
);
|
||||
});
|
||||
|
||||
test('handles empty phases directory', () => {
|
||||
fs.writeFileSync(
|
||||
path.join(tmpDir, '.planning', 'ROADMAP.md'),
|
||||
|
||||
Reference in New Issue
Block a user