Merge pull request #1217 from j2h4u/fix/task-count-pattern
fix(milestone): read task count from **Tasks:** field in summary body
This commit is contained in:
@@ -134,9 +134,16 @@ function cmdMilestoneComplete(cwd, version, options, raw) {
|
|||||||
if (fm['one-liner']) {
|
if (fm['one-liner']) {
|
||||||
accomplishments.push(fm['one-liner']);
|
accomplishments.push(fm['one-liner']);
|
||||||
}
|
}
|
||||||
// Count tasks
|
// Count tasks: prefer **Tasks:** N from Performance section,
|
||||||
const taskMatches = content.match(/##\s*Task\s*\d+/gi) || [];
|
// then <task XML tags, then ## Task N markdown headers
|
||||||
totalTasks += taskMatches.length;
|
const tasksFieldMatch = content.match(/\*\*Tasks:\*\*\s*(\d+)/);
|
||||||
|
if (tasksFieldMatch) {
|
||||||
|
totalTasks += parseInt(tasksFieldMatch[1], 10);
|
||||||
|
} else {
|
||||||
|
const xmlTaskMatches = content.match(/<task[\s>]/gi) || [];
|
||||||
|
const mdTaskMatches = content.match(/##\s*Task\s*\d+/gi) || [];
|
||||||
|
totalTasks += xmlTaskMatches.length || mdTaskMatches.length;
|
||||||
|
}
|
||||||
} catch { /* intentionally empty */ }
|
} catch { /* intentionally empty */ }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -424,6 +424,30 @@ describe('milestone complete command', () => {
|
|||||||
assert.strictEqual(output.phases, 2, 'should count only phases 456 and 457');
|
assert.strictEqual(output.phases, 2, 'should count only phases 456 and 457');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('counts tasks from **Tasks:** N in summary body', () => {
|
||||||
|
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 });
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(p1, '01-01-SUMMARY.md'),
|
||||||
|
`---\none-liner: Built the foundation\n---\n\n# Phase 1: Foundation Summary\n\n**Built the foundation**\n\n## Performance\n\n- **Duration:** 28 min\n- **Tasks:** 7\n- **Files modified:** 12\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.strictEqual(output.tasks, 7, 'should count tasks from **Tasks:** N field');
|
||||||
|
});
|
||||||
|
|
||||||
test('handles empty phases directory', () => {
|
test('handles empty phases directory', () => {
|
||||||
fs.writeFileSync(
|
fs.writeFileSync(
|
||||||
path.join(tmpDir, '.planning', 'ROADMAP.md'),
|
path.join(tmpDir, '.planning', 'ROADMAP.md'),
|
||||||
|
|||||||
Reference in New Issue
Block a user