Merge pull request #483 from glittercowboy/claude/expand-gsd-automation-XVhXH
Add phase lifecycle and roadmap management commands
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* GSD Tools Tests — Schema validation for history-digest command
|
* GSD Tools Tests
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const { test, describe, beforeEach, afterEach } = require('node:test');
|
const { test, describe, beforeEach, afterEach } = require('node:test');
|
||||||
@@ -1191,3 +1191,843 @@ describe('init commands with --include flag', () => {
|
|||||||
assert.strictEqual(output.roadmap_content, undefined, 'roadmap_content not requested, should be undefined');
|
assert.strictEqual(output.roadmap_content, undefined, 'roadmap_content not requested, should be undefined');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
// roadmap analyze command
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
describe('roadmap analyze command', () => {
|
||||||
|
let tmpDir;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
tmpDir = createTempProject();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
cleanup(tmpDir);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('missing ROADMAP.md returns error', () => {
|
||||||
|
const result = runGsdTools('roadmap analyze', tmpDir);
|
||||||
|
assert.ok(result.success, `Command should succeed: ${result.error}`);
|
||||||
|
|
||||||
|
const output = JSON.parse(result.output);
|
||||||
|
assert.strictEqual(output.error, 'ROADMAP.md not found');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('parses phases with goals and disk status', () => {
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(tmpDir, '.planning', 'ROADMAP.md'),
|
||||||
|
`# Roadmap v1.0
|
||||||
|
|
||||||
|
### Phase 1: Foundation
|
||||||
|
**Goal:** Set up infrastructure
|
||||||
|
|
||||||
|
### Phase 2: Authentication
|
||||||
|
**Goal:** Add user auth
|
||||||
|
|
||||||
|
### Phase 3: Features
|
||||||
|
**Goal:** Build core features
|
||||||
|
`
|
||||||
|
);
|
||||||
|
|
||||||
|
// Create phase dirs with varying completion
|
||||||
|
const p1 = path.join(tmpDir, '.planning', 'phases', '01-foundation');
|
||||||
|
fs.mkdirSync(p1, { recursive: true });
|
||||||
|
fs.writeFileSync(path.join(p1, '01-01-PLAN.md'), '# Plan');
|
||||||
|
fs.writeFileSync(path.join(p1, '01-01-SUMMARY.md'), '# Summary');
|
||||||
|
|
||||||
|
const p2 = path.join(tmpDir, '.planning', 'phases', '02-authentication');
|
||||||
|
fs.mkdirSync(p2, { recursive: true });
|
||||||
|
fs.writeFileSync(path.join(p2, '02-01-PLAN.md'), '# Plan');
|
||||||
|
|
||||||
|
const result = runGsdTools('roadmap analyze', tmpDir);
|
||||||
|
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||||
|
|
||||||
|
const output = JSON.parse(result.output);
|
||||||
|
assert.strictEqual(output.phase_count, 3, 'should find 3 phases');
|
||||||
|
assert.strictEqual(output.phases[0].disk_status, 'complete', 'phase 1 complete');
|
||||||
|
assert.strictEqual(output.phases[1].disk_status, 'planned', 'phase 2 planned');
|
||||||
|
assert.strictEqual(output.phases[2].disk_status, 'no_directory', 'phase 3 no directory');
|
||||||
|
assert.strictEqual(output.completed_phases, 1, '1 phase complete');
|
||||||
|
assert.strictEqual(output.total_plans, 2, '2 total plans');
|
||||||
|
assert.strictEqual(output.total_summaries, 1, '1 total summary');
|
||||||
|
assert.strictEqual(output.progress_percent, 50, '50% complete');
|
||||||
|
assert.strictEqual(output.current_phase, '2', 'current phase is 2');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('extracts goals and dependencies', () => {
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(tmpDir, '.planning', 'ROADMAP.md'),
|
||||||
|
`# Roadmap
|
||||||
|
|
||||||
|
### Phase 1: Setup
|
||||||
|
**Goal:** Initialize project
|
||||||
|
**Depends on:** Nothing
|
||||||
|
|
||||||
|
### Phase 2: Build
|
||||||
|
**Goal:** Build features
|
||||||
|
**Depends on:** Phase 1
|
||||||
|
`
|
||||||
|
);
|
||||||
|
|
||||||
|
const result = runGsdTools('roadmap analyze', tmpDir);
|
||||||
|
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||||
|
|
||||||
|
const output = JSON.parse(result.output);
|
||||||
|
assert.strictEqual(output.phases[0].goal, 'Initialize project');
|
||||||
|
assert.strictEqual(output.phases[0].depends_on, 'Nothing');
|
||||||
|
assert.strictEqual(output.phases[1].goal, 'Build features');
|
||||||
|
assert.strictEqual(output.phases[1].depends_on, 'Phase 1');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
// phase add command
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
describe('phase add command', () => {
|
||||||
|
let tmpDir;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
tmpDir = createTempProject();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
cleanup(tmpDir);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('adds phase after highest existing', () => {
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(tmpDir, '.planning', 'ROADMAP.md'),
|
||||||
|
`# Roadmap v1.0
|
||||||
|
|
||||||
|
### Phase 1: Foundation
|
||||||
|
**Goal:** Setup
|
||||||
|
|
||||||
|
### Phase 2: API
|
||||||
|
**Goal:** Build API
|
||||||
|
|
||||||
|
---
|
||||||
|
`
|
||||||
|
);
|
||||||
|
|
||||||
|
const result = runGsdTools('phase add User Dashboard', tmpDir);
|
||||||
|
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||||
|
|
||||||
|
const output = JSON.parse(result.output);
|
||||||
|
assert.strictEqual(output.phase_number, 3, 'should be phase 3');
|
||||||
|
assert.strictEqual(output.slug, 'user-dashboard');
|
||||||
|
|
||||||
|
// Verify directory created
|
||||||
|
assert.ok(
|
||||||
|
fs.existsSync(path.join(tmpDir, '.planning', 'phases', '03-user-dashboard')),
|
||||||
|
'directory should be created'
|
||||||
|
);
|
||||||
|
|
||||||
|
// Verify ROADMAP updated
|
||||||
|
const roadmap = fs.readFileSync(path.join(tmpDir, '.planning', 'ROADMAP.md'), 'utf-8');
|
||||||
|
assert.ok(roadmap.includes('### Phase 3: User Dashboard'), 'roadmap should include new phase');
|
||||||
|
assert.ok(roadmap.includes('**Depends on:** Phase 2'), 'should depend on previous');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('handles empty roadmap', () => {
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(tmpDir, '.planning', 'ROADMAP.md'),
|
||||||
|
`# Roadmap v1.0\n`
|
||||||
|
);
|
||||||
|
|
||||||
|
const result = runGsdTools('phase add Initial Setup', tmpDir);
|
||||||
|
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||||
|
|
||||||
|
const output = JSON.parse(result.output);
|
||||||
|
assert.strictEqual(output.phase_number, 1, 'should be phase 1');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
// phase insert command
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
describe('phase insert command', () => {
|
||||||
|
let tmpDir;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
tmpDir = createTempProject();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
cleanup(tmpDir);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('inserts decimal phase after target', () => {
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(tmpDir, '.planning', 'ROADMAP.md'),
|
||||||
|
`# Roadmap
|
||||||
|
|
||||||
|
### Phase 1: Foundation
|
||||||
|
**Goal:** Setup
|
||||||
|
|
||||||
|
### Phase 2: API
|
||||||
|
**Goal:** Build API
|
||||||
|
`
|
||||||
|
);
|
||||||
|
fs.mkdirSync(path.join(tmpDir, '.planning', 'phases', '01-foundation'), { recursive: true });
|
||||||
|
|
||||||
|
const result = runGsdTools('phase insert 1 Fix Critical Bug', tmpDir);
|
||||||
|
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||||
|
|
||||||
|
const output = JSON.parse(result.output);
|
||||||
|
assert.strictEqual(output.phase_number, '01.1', 'should be 01.1');
|
||||||
|
assert.strictEqual(output.after_phase, '1');
|
||||||
|
|
||||||
|
// Verify directory
|
||||||
|
assert.ok(
|
||||||
|
fs.existsSync(path.join(tmpDir, '.planning', 'phases', '01.1-fix-critical-bug')),
|
||||||
|
'decimal phase directory should be created'
|
||||||
|
);
|
||||||
|
|
||||||
|
// Verify ROADMAP
|
||||||
|
const roadmap = fs.readFileSync(path.join(tmpDir, '.planning', 'ROADMAP.md'), 'utf-8');
|
||||||
|
assert.ok(roadmap.includes('Phase 01.1: Fix Critical Bug (INSERTED)'), 'roadmap should include inserted phase');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('increments decimal when siblings exist', () => {
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(tmpDir, '.planning', 'ROADMAP.md'),
|
||||||
|
`# Roadmap
|
||||||
|
|
||||||
|
### Phase 1: Foundation
|
||||||
|
**Goal:** Setup
|
||||||
|
|
||||||
|
### Phase 2: API
|
||||||
|
**Goal:** Build API
|
||||||
|
`
|
||||||
|
);
|
||||||
|
fs.mkdirSync(path.join(tmpDir, '.planning', 'phases', '01-foundation'), { recursive: true });
|
||||||
|
fs.mkdirSync(path.join(tmpDir, '.planning', 'phases', '01.1-hotfix'), { recursive: true });
|
||||||
|
|
||||||
|
const result = runGsdTools('phase insert 1 Another Fix', tmpDir);
|
||||||
|
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||||
|
|
||||||
|
const output = JSON.parse(result.output);
|
||||||
|
assert.strictEqual(output.phase_number, '01.2', 'should be 01.2');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('rejects missing phase', () => {
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(tmpDir, '.planning', 'ROADMAP.md'),
|
||||||
|
`# Roadmap\n### Phase 1: Test\n**Goal:** Test\n`
|
||||||
|
);
|
||||||
|
|
||||||
|
const result = runGsdTools('phase insert 99 Fix Something', tmpDir);
|
||||||
|
assert.ok(!result.success, 'should fail for missing phase');
|
||||||
|
assert.ok(result.error.includes('not found'), 'error mentions not found');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
// phase remove command
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
describe('phase remove command', () => {
|
||||||
|
let tmpDir;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
tmpDir = createTempProject();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
cleanup(tmpDir);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('removes phase directory and renumbers subsequent', () => {
|
||||||
|
// Setup 3 phases
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(tmpDir, '.planning', 'ROADMAP.md'),
|
||||||
|
`# Roadmap
|
||||||
|
|
||||||
|
### Phase 1: Foundation
|
||||||
|
**Goal:** Setup
|
||||||
|
**Depends on:** Nothing
|
||||||
|
|
||||||
|
### Phase 2: Auth
|
||||||
|
**Goal:** Authentication
|
||||||
|
**Depends on:** Phase 1
|
||||||
|
|
||||||
|
### Phase 3: Features
|
||||||
|
**Goal:** Core features
|
||||||
|
**Depends on:** Phase 2
|
||||||
|
`
|
||||||
|
);
|
||||||
|
|
||||||
|
fs.mkdirSync(path.join(tmpDir, '.planning', 'phases', '01-foundation'), { recursive: true });
|
||||||
|
const p2 = path.join(tmpDir, '.planning', 'phases', '02-auth');
|
||||||
|
fs.mkdirSync(p2, { recursive: true });
|
||||||
|
fs.writeFileSync(path.join(p2, '02-01-PLAN.md'), '# Plan');
|
||||||
|
const p3 = path.join(tmpDir, '.planning', 'phases', '03-features');
|
||||||
|
fs.mkdirSync(p3, { recursive: true });
|
||||||
|
fs.writeFileSync(path.join(p3, '03-01-PLAN.md'), '# Plan');
|
||||||
|
fs.writeFileSync(path.join(p3, '03-02-PLAN.md'), '# Plan 2');
|
||||||
|
|
||||||
|
// Remove phase 2
|
||||||
|
const result = runGsdTools('phase remove 2', tmpDir);
|
||||||
|
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||||
|
|
||||||
|
const output = JSON.parse(result.output);
|
||||||
|
assert.strictEqual(output.removed, '2');
|
||||||
|
assert.strictEqual(output.directory_deleted, '02-auth');
|
||||||
|
|
||||||
|
// Phase 3 should be renumbered to 02
|
||||||
|
assert.ok(
|
||||||
|
fs.existsSync(path.join(tmpDir, '.planning', 'phases', '02-features')),
|
||||||
|
'phase 3 should be renumbered to 02-features'
|
||||||
|
);
|
||||||
|
assert.ok(
|
||||||
|
!fs.existsSync(path.join(tmpDir, '.planning', 'phases', '03-features')),
|
||||||
|
'old 03-features should not exist'
|
||||||
|
);
|
||||||
|
|
||||||
|
// Files inside should be renamed
|
||||||
|
assert.ok(
|
||||||
|
fs.existsSync(path.join(tmpDir, '.planning', 'phases', '02-features', '02-01-PLAN.md')),
|
||||||
|
'plan file should be renumbered to 02-01'
|
||||||
|
);
|
||||||
|
assert.ok(
|
||||||
|
fs.existsSync(path.join(tmpDir, '.planning', 'phases', '02-features', '02-02-PLAN.md')),
|
||||||
|
'plan 2 should be renumbered to 02-02'
|
||||||
|
);
|
||||||
|
|
||||||
|
// ROADMAP should be updated
|
||||||
|
const roadmap = fs.readFileSync(path.join(tmpDir, '.planning', 'ROADMAP.md'), 'utf-8');
|
||||||
|
assert.ok(!roadmap.includes('Phase 2: Auth'), 'removed phase should not be in roadmap');
|
||||||
|
assert.ok(roadmap.includes('Phase 2: Features'), 'phase 3 should be renumbered to 2');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('rejects removal of phase with summaries unless --force', () => {
|
||||||
|
const p1 = path.join(tmpDir, '.planning', 'phases', '01-test');
|
||||||
|
fs.mkdirSync(p1, { recursive: true });
|
||||||
|
fs.writeFileSync(path.join(p1, '01-01-PLAN.md'), '# Plan');
|
||||||
|
fs.writeFileSync(path.join(p1, '01-01-SUMMARY.md'), '# Summary');
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(tmpDir, '.planning', 'ROADMAP.md'),
|
||||||
|
`# Roadmap\n### Phase 1: Test\n**Goal:** Test\n`
|
||||||
|
);
|
||||||
|
|
||||||
|
// Should fail without --force
|
||||||
|
const result = runGsdTools('phase remove 1', tmpDir);
|
||||||
|
assert.ok(!result.success, 'should fail without --force');
|
||||||
|
assert.ok(result.error.includes('executed plan'), 'error mentions executed plans');
|
||||||
|
|
||||||
|
// Should succeed with --force
|
||||||
|
const forceResult = runGsdTools('phase remove 1 --force', tmpDir);
|
||||||
|
assert.ok(forceResult.success, `Force remove failed: ${forceResult.error}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('removes decimal phase and renumbers siblings', () => {
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(tmpDir, '.planning', 'ROADMAP.md'),
|
||||||
|
`# Roadmap\n### Phase 6: Main\n**Goal:** Main\n### Phase 6.1: Fix A\n**Goal:** Fix A\n### Phase 6.2: Fix B\n**Goal:** Fix B\n### Phase 6.3: Fix C\n**Goal:** Fix C\n`
|
||||||
|
);
|
||||||
|
|
||||||
|
fs.mkdirSync(path.join(tmpDir, '.planning', 'phases', '06-main'), { recursive: true });
|
||||||
|
fs.mkdirSync(path.join(tmpDir, '.planning', 'phases', '06.1-fix-a'), { recursive: true });
|
||||||
|
fs.mkdirSync(path.join(tmpDir, '.planning', 'phases', '06.2-fix-b'), { recursive: true });
|
||||||
|
fs.mkdirSync(path.join(tmpDir, '.planning', 'phases', '06.3-fix-c'), { recursive: true });
|
||||||
|
|
||||||
|
const result = runGsdTools('phase remove 6.2', tmpDir);
|
||||||
|
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||||
|
|
||||||
|
// 06.3 should become 06.2
|
||||||
|
assert.ok(
|
||||||
|
fs.existsSync(path.join(tmpDir, '.planning', 'phases', '06.2-fix-c')),
|
||||||
|
'06.3 should be renumbered to 06.2'
|
||||||
|
);
|
||||||
|
assert.ok(
|
||||||
|
!fs.existsSync(path.join(tmpDir, '.planning', 'phases', '06.3-fix-c')),
|
||||||
|
'old 06.3 should not exist'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('updates STATE.md phase count', () => {
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(tmpDir, '.planning', 'ROADMAP.md'),
|
||||||
|
`# Roadmap\n### Phase 1: A\n**Goal:** A\n### Phase 2: B\n**Goal:** B\n`
|
||||||
|
);
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(tmpDir, '.planning', 'STATE.md'),
|
||||||
|
`# State\n\n**Current Phase:** 1\n**Total Phases:** 2\n`
|
||||||
|
);
|
||||||
|
fs.mkdirSync(path.join(tmpDir, '.planning', 'phases', '01-a'), { recursive: true });
|
||||||
|
fs.mkdirSync(path.join(tmpDir, '.planning', 'phases', '02-b'), { recursive: true });
|
||||||
|
|
||||||
|
runGsdTools('phase remove 2', tmpDir);
|
||||||
|
|
||||||
|
const state = fs.readFileSync(path.join(tmpDir, '.planning', 'STATE.md'), 'utf-8');
|
||||||
|
assert.ok(state.includes('**Total Phases:** 1'), 'total phases should be decremented');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
// phase complete command
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
describe('phase complete command', () => {
|
||||||
|
let tmpDir;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
tmpDir = createTempProject();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
cleanup(tmpDir);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('marks phase complete and transitions to next', () => {
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(tmpDir, '.planning', 'ROADMAP.md'),
|
||||||
|
`# Roadmap
|
||||||
|
|
||||||
|
- [ ] Phase 1: Foundation
|
||||||
|
- [ ] Phase 2: API
|
||||||
|
|
||||||
|
### Phase 1: Foundation
|
||||||
|
**Goal:** Setup
|
||||||
|
**Plans:** 1 plans
|
||||||
|
|
||||||
|
### Phase 2: API
|
||||||
|
**Goal:** Build API
|
||||||
|
`
|
||||||
|
);
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(tmpDir, '.planning', 'STATE.md'),
|
||||||
|
`# State\n\n**Current Phase:** 01\n**Current Phase Name:** Foundation\n**Status:** In progress\n**Current Plan:** 01-01\n**Last Activity:** 2025-01-01\n**Last Activity Description:** Working on phase 1\n`
|
||||||
|
);
|
||||||
|
|
||||||
|
const p1 = path.join(tmpDir, '.planning', 'phases', '01-foundation');
|
||||||
|
fs.mkdirSync(p1, { recursive: true });
|
||||||
|
fs.writeFileSync(path.join(p1, '01-01-PLAN.md'), '# Plan');
|
||||||
|
fs.writeFileSync(path.join(p1, '01-01-SUMMARY.md'), '# Summary');
|
||||||
|
fs.mkdirSync(path.join(tmpDir, '.planning', 'phases', '02-api'), { recursive: true });
|
||||||
|
|
||||||
|
const result = runGsdTools('phase complete 1', tmpDir);
|
||||||
|
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||||
|
|
||||||
|
const output = JSON.parse(result.output);
|
||||||
|
assert.strictEqual(output.completed_phase, '1');
|
||||||
|
assert.strictEqual(output.plans_executed, '1/1');
|
||||||
|
assert.strictEqual(output.next_phase, '02');
|
||||||
|
assert.strictEqual(output.is_last_phase, false);
|
||||||
|
|
||||||
|
// Verify STATE.md updated
|
||||||
|
const state = fs.readFileSync(path.join(tmpDir, '.planning', 'STATE.md'), 'utf-8');
|
||||||
|
assert.ok(state.includes('**Current Phase:** 02'), 'should advance to phase 02');
|
||||||
|
assert.ok(state.includes('**Status:** Ready to plan'), 'status should be ready to plan');
|
||||||
|
assert.ok(state.includes('**Current Plan:** Not started'), 'plan should be reset');
|
||||||
|
|
||||||
|
// Verify ROADMAP checkbox
|
||||||
|
const roadmap = fs.readFileSync(path.join(tmpDir, '.planning', 'ROADMAP.md'), 'utf-8');
|
||||||
|
assert.ok(roadmap.includes('[x]'), 'phase should be checked off');
|
||||||
|
assert.ok(roadmap.includes('completed'), 'completion date should be added');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('detects last phase in milestone', () => {
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(tmpDir, '.planning', 'ROADMAP.md'),
|
||||||
|
`# Roadmap\n### Phase 1: Only Phase\n**Goal:** Everything\n`
|
||||||
|
);
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(tmpDir, '.planning', 'STATE.md'),
|
||||||
|
`# State\n\n**Current Phase:** 01\n**Status:** In progress\n**Current Plan:** 01-01\n**Last Activity:** 2025-01-01\n**Last Activity Description:** Working\n`
|
||||||
|
);
|
||||||
|
|
||||||
|
const p1 = path.join(tmpDir, '.planning', 'phases', '01-only-phase');
|
||||||
|
fs.mkdirSync(p1, { recursive: true });
|
||||||
|
fs.writeFileSync(path.join(p1, '01-01-PLAN.md'), '# Plan');
|
||||||
|
fs.writeFileSync(path.join(p1, '01-01-SUMMARY.md'), '# Summary');
|
||||||
|
|
||||||
|
const result = runGsdTools('phase complete 1', tmpDir);
|
||||||
|
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||||
|
|
||||||
|
const output = JSON.parse(result.output);
|
||||||
|
assert.strictEqual(output.is_last_phase, true, 'should detect last phase');
|
||||||
|
assert.strictEqual(output.next_phase, null, 'no next phase');
|
||||||
|
|
||||||
|
const state = fs.readFileSync(path.join(tmpDir, '.planning', 'STATE.md'), 'utf-8');
|
||||||
|
assert.ok(state.includes('Milestone complete'), 'status should be milestone complete');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
// milestone complete command
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
describe('milestone complete command', () => {
|
||||||
|
let tmpDir;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
tmpDir = createTempProject();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
cleanup(tmpDir);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('archives roadmap, requirements, creates MILESTONES.md', () => {
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(tmpDir, '.planning', 'ROADMAP.md'),
|
||||||
|
`# Roadmap v1.0 MVP\n\n### Phase 1: Foundation\n**Goal:** Setup\n`
|
||||||
|
);
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(tmpDir, '.planning', 'REQUIREMENTS.md'),
|
||||||
|
`# Requirements\n\n- [ ] User auth\n- [ ] Dashboard\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: Set up project infrastructure\n---\n# Summary\n`
|
||||||
|
);
|
||||||
|
|
||||||
|
const result = runGsdTools('milestone complete v1.0 --name MVP Foundation', tmpDir);
|
||||||
|
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||||
|
|
||||||
|
const output = JSON.parse(result.output);
|
||||||
|
assert.strictEqual(output.version, 'v1.0');
|
||||||
|
assert.strictEqual(output.phases, 1);
|
||||||
|
assert.ok(output.archived.roadmap, 'roadmap should be archived');
|
||||||
|
assert.ok(output.archived.requirements, 'requirements should be archived');
|
||||||
|
|
||||||
|
// Verify archive files exist
|
||||||
|
assert.ok(
|
||||||
|
fs.existsSync(path.join(tmpDir, '.planning', 'milestones', 'v1.0-ROADMAP.md')),
|
||||||
|
'archived roadmap should exist'
|
||||||
|
);
|
||||||
|
assert.ok(
|
||||||
|
fs.existsSync(path.join(tmpDir, '.planning', 'milestones', 'v1.0-REQUIREMENTS.md')),
|
||||||
|
'archived requirements should exist'
|
||||||
|
);
|
||||||
|
|
||||||
|
// Verify MILESTONES.md created
|
||||||
|
assert.ok(
|
||||||
|
fs.existsSync(path.join(tmpDir, '.planning', 'MILESTONES.md')),
|
||||||
|
'MILESTONES.md should be created'
|
||||||
|
);
|
||||||
|
const milestones = fs.readFileSync(path.join(tmpDir, '.planning', 'MILESTONES.md'), 'utf-8');
|
||||||
|
assert.ok(milestones.includes('v1.0 MVP Foundation'), 'milestone entry should contain name');
|
||||||
|
assert.ok(milestones.includes('Set up project infrastructure'), 'accomplishments should be listed');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('appends to existing MILESTONES.md', () => {
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(tmpDir, '.planning', 'MILESTONES.md'),
|
||||||
|
`# Milestones\n\n## v0.9 Alpha (Shipped: 2025-01-01)\n\n---\n\n`
|
||||||
|
);
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(tmpDir, '.planning', 'ROADMAP.md'),
|
||||||
|
`# Roadmap v1.0\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 result = runGsdTools('milestone complete v1.0 --name Beta', tmpDir);
|
||||||
|
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||||
|
|
||||||
|
const milestones = fs.readFileSync(path.join(tmpDir, '.planning', 'MILESTONES.md'), 'utf-8');
|
||||||
|
assert.ok(milestones.includes('v0.9 Alpha'), 'existing entry should be preserved');
|
||||||
|
assert.ok(milestones.includes('v1.0 Beta'), 'new entry should be appended');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
// validate consistency command
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
describe('validate consistency command', () => {
|
||||||
|
let tmpDir;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
tmpDir = createTempProject();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
cleanup(tmpDir);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('passes for consistent project', () => {
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(tmpDir, '.planning', 'ROADMAP.md'),
|
||||||
|
`# Roadmap\n### Phase 1: A\n### Phase 2: B\n### Phase 3: C\n`
|
||||||
|
);
|
||||||
|
fs.mkdirSync(path.join(tmpDir, '.planning', 'phases', '01-a'), { recursive: true });
|
||||||
|
fs.mkdirSync(path.join(tmpDir, '.planning', 'phases', '02-b'), { recursive: true });
|
||||||
|
fs.mkdirSync(path.join(tmpDir, '.planning', 'phases', '03-c'), { recursive: true });
|
||||||
|
|
||||||
|
const result = runGsdTools('validate consistency', tmpDir);
|
||||||
|
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||||
|
|
||||||
|
const output = JSON.parse(result.output);
|
||||||
|
assert.strictEqual(output.passed, true, 'should pass');
|
||||||
|
assert.strictEqual(output.warning_count, 0, 'no warnings');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('warns about phase on disk but not in roadmap', () => {
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(tmpDir, '.planning', 'ROADMAP.md'),
|
||||||
|
`# Roadmap\n### Phase 1: A\n`
|
||||||
|
);
|
||||||
|
fs.mkdirSync(path.join(tmpDir, '.planning', 'phases', '01-a'), { recursive: true });
|
||||||
|
fs.mkdirSync(path.join(tmpDir, '.planning', 'phases', '02-orphan'), { recursive: true });
|
||||||
|
|
||||||
|
const result = runGsdTools('validate consistency', tmpDir);
|
||||||
|
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||||
|
|
||||||
|
const output = JSON.parse(result.output);
|
||||||
|
assert.ok(output.warning_count > 0, 'should have warnings');
|
||||||
|
assert.ok(
|
||||||
|
output.warnings.some(w => w.includes('disk but not in ROADMAP')),
|
||||||
|
'should warn about orphan directory'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('warns about gaps in phase numbering', () => {
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(tmpDir, '.planning', 'ROADMAP.md'),
|
||||||
|
`# Roadmap\n### Phase 1: A\n### Phase 3: C\n`
|
||||||
|
);
|
||||||
|
fs.mkdirSync(path.join(tmpDir, '.planning', 'phases', '01-a'), { recursive: true });
|
||||||
|
fs.mkdirSync(path.join(tmpDir, '.planning', 'phases', '03-c'), { recursive: true });
|
||||||
|
|
||||||
|
const result = runGsdTools('validate consistency', tmpDir);
|
||||||
|
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||||
|
|
||||||
|
const output = JSON.parse(result.output);
|
||||||
|
assert.ok(
|
||||||
|
output.warnings.some(w => w.includes('Gap in phase numbering')),
|
||||||
|
'should warn about gap'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
// progress command
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
describe('progress command', () => {
|
||||||
|
let tmpDir;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
tmpDir = createTempProject();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
cleanup(tmpDir);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('renders JSON progress', () => {
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(tmpDir, '.planning', 'ROADMAP.md'),
|
||||||
|
`# Roadmap v1.0 MVP\n`
|
||||||
|
);
|
||||||
|
const p1 = path.join(tmpDir, '.planning', 'phases', '01-foundation');
|
||||||
|
fs.mkdirSync(p1, { recursive: true });
|
||||||
|
fs.writeFileSync(path.join(p1, '01-01-PLAN.md'), '# Plan');
|
||||||
|
fs.writeFileSync(path.join(p1, '01-01-SUMMARY.md'), '# Done');
|
||||||
|
fs.writeFileSync(path.join(p1, '01-02-PLAN.md'), '# Plan 2');
|
||||||
|
|
||||||
|
const result = runGsdTools('progress json', tmpDir);
|
||||||
|
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||||
|
|
||||||
|
const output = JSON.parse(result.output);
|
||||||
|
assert.strictEqual(output.total_plans, 2, '2 total plans');
|
||||||
|
assert.strictEqual(output.total_summaries, 1, '1 summary');
|
||||||
|
assert.strictEqual(output.percent, 50, '50%');
|
||||||
|
assert.strictEqual(output.phases.length, 1, '1 phase');
|
||||||
|
assert.strictEqual(output.phases[0].status, 'In Progress', 'phase in progress');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('renders bar format', () => {
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(tmpDir, '.planning', 'ROADMAP.md'),
|
||||||
|
`# Roadmap v1.0\n`
|
||||||
|
);
|
||||||
|
const p1 = path.join(tmpDir, '.planning', 'phases', '01-test');
|
||||||
|
fs.mkdirSync(p1, { recursive: true });
|
||||||
|
fs.writeFileSync(path.join(p1, '01-01-PLAN.md'), '# Plan');
|
||||||
|
fs.writeFileSync(path.join(p1, '01-01-SUMMARY.md'), '# Done');
|
||||||
|
|
||||||
|
const result = runGsdTools('progress bar --raw', tmpDir);
|
||||||
|
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||||
|
assert.ok(result.output.includes('1/1'), 'should include count');
|
||||||
|
assert.ok(result.output.includes('100%'), 'should include 100%');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('renders table format', () => {
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(tmpDir, '.planning', 'ROADMAP.md'),
|
||||||
|
`# Roadmap v1.0 MVP\n`
|
||||||
|
);
|
||||||
|
const p1 = path.join(tmpDir, '.planning', 'phases', '01-foundation');
|
||||||
|
fs.mkdirSync(p1, { recursive: true });
|
||||||
|
fs.writeFileSync(path.join(p1, '01-01-PLAN.md'), '# Plan');
|
||||||
|
|
||||||
|
const result = runGsdTools('progress table --raw', tmpDir);
|
||||||
|
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||||
|
assert.ok(result.output.includes('Phase'), 'should have table header');
|
||||||
|
assert.ok(result.output.includes('foundation'), 'should include phase name');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
// todo complete command
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
describe('todo complete command', () => {
|
||||||
|
let tmpDir;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
tmpDir = createTempProject();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
cleanup(tmpDir);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('moves todo from pending to completed', () => {
|
||||||
|
const pendingDir = path.join(tmpDir, '.planning', 'todos', 'pending');
|
||||||
|
fs.mkdirSync(pendingDir, { recursive: true });
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(pendingDir, 'add-dark-mode.md'),
|
||||||
|
`title: Add dark mode\narea: ui\ncreated: 2025-01-01\n`
|
||||||
|
);
|
||||||
|
|
||||||
|
const result = runGsdTools('todo complete add-dark-mode.md', tmpDir);
|
||||||
|
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||||
|
|
||||||
|
const output = JSON.parse(result.output);
|
||||||
|
assert.strictEqual(output.completed, true);
|
||||||
|
|
||||||
|
// Verify moved
|
||||||
|
assert.ok(
|
||||||
|
!fs.existsSync(path.join(tmpDir, '.planning', 'todos', 'pending', 'add-dark-mode.md')),
|
||||||
|
'should be removed from pending'
|
||||||
|
);
|
||||||
|
assert.ok(
|
||||||
|
fs.existsSync(path.join(tmpDir, '.planning', 'todos', 'completed', 'add-dark-mode.md')),
|
||||||
|
'should be in completed'
|
||||||
|
);
|
||||||
|
|
||||||
|
// Verify completion timestamp added
|
||||||
|
const content = fs.readFileSync(
|
||||||
|
path.join(tmpDir, '.planning', 'todos', 'completed', 'add-dark-mode.md'),
|
||||||
|
'utf-8'
|
||||||
|
);
|
||||||
|
assert.ok(content.startsWith('completed:'), 'should have completed timestamp');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('fails for nonexistent todo', () => {
|
||||||
|
const result = runGsdTools('todo complete nonexistent.md', tmpDir);
|
||||||
|
assert.ok(!result.success, 'should fail');
|
||||||
|
assert.ok(result.error.includes('not found'), 'error mentions not found');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
// scaffold command
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
describe('scaffold command', () => {
|
||||||
|
let tmpDir;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
tmpDir = createTempProject();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
cleanup(tmpDir);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('scaffolds context file', () => {
|
||||||
|
fs.mkdirSync(path.join(tmpDir, '.planning', 'phases', '03-api'), { recursive: true });
|
||||||
|
|
||||||
|
const result = runGsdTools('scaffold context --phase 3', tmpDir);
|
||||||
|
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||||
|
|
||||||
|
const output = JSON.parse(result.output);
|
||||||
|
assert.strictEqual(output.created, true);
|
||||||
|
|
||||||
|
// Verify file content
|
||||||
|
const content = fs.readFileSync(
|
||||||
|
path.join(tmpDir, '.planning', 'phases', '03-api', '03-CONTEXT.md'),
|
||||||
|
'utf-8'
|
||||||
|
);
|
||||||
|
assert.ok(content.includes('Phase 3'), 'should reference phase number');
|
||||||
|
assert.ok(content.includes('Decisions'), 'should have decisions section');
|
||||||
|
assert.ok(content.includes('Discretion Areas'), 'should have discretion section');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('scaffolds UAT file', () => {
|
||||||
|
fs.mkdirSync(path.join(tmpDir, '.planning', 'phases', '03-api'), { recursive: true });
|
||||||
|
|
||||||
|
const result = runGsdTools('scaffold uat --phase 3', tmpDir);
|
||||||
|
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||||
|
|
||||||
|
const output = JSON.parse(result.output);
|
||||||
|
assert.strictEqual(output.created, true);
|
||||||
|
|
||||||
|
const content = fs.readFileSync(
|
||||||
|
path.join(tmpDir, '.planning', 'phases', '03-api', '03-UAT.md'),
|
||||||
|
'utf-8'
|
||||||
|
);
|
||||||
|
assert.ok(content.includes('User Acceptance Testing'), 'should have UAT heading');
|
||||||
|
assert.ok(content.includes('Test Results'), 'should have test results section');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('scaffolds verification file', () => {
|
||||||
|
fs.mkdirSync(path.join(tmpDir, '.planning', 'phases', '03-api'), { recursive: true });
|
||||||
|
|
||||||
|
const result = runGsdTools('scaffold verification --phase 3', tmpDir);
|
||||||
|
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||||
|
|
||||||
|
const output = JSON.parse(result.output);
|
||||||
|
assert.strictEqual(output.created, true);
|
||||||
|
|
||||||
|
const content = fs.readFileSync(
|
||||||
|
path.join(tmpDir, '.planning', 'phases', '03-api', '03-VERIFICATION.md'),
|
||||||
|
'utf-8'
|
||||||
|
);
|
||||||
|
assert.ok(content.includes('Goal-Backward Verification'), 'should have verification heading');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('scaffolds phase directory', () => {
|
||||||
|
const result = runGsdTools('scaffold phase-dir --phase 5 --name User Dashboard', tmpDir);
|
||||||
|
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||||
|
|
||||||
|
const output = JSON.parse(result.output);
|
||||||
|
assert.strictEqual(output.created, true);
|
||||||
|
assert.ok(
|
||||||
|
fs.existsSync(path.join(tmpDir, '.planning', 'phases', '05-user-dashboard')),
|
||||||
|
'directory should be created'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('does not overwrite existing files', () => {
|
||||||
|
const phaseDir = path.join(tmpDir, '.planning', 'phases', '03-api');
|
||||||
|
fs.mkdirSync(phaseDir, { recursive: true });
|
||||||
|
fs.writeFileSync(path.join(phaseDir, '03-CONTEXT.md'), '# Existing content');
|
||||||
|
|
||||||
|
const result = runGsdTools('scaffold context --phase 3', tmpDir);
|
||||||
|
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||||
|
|
||||||
|
const output = JSON.parse(result.output);
|
||||||
|
assert.strictEqual(output.created, false, 'should not overwrite');
|
||||||
|
assert.strictEqual(output.reason, 'already_exists');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -38,96 +38,30 @@ ERROR: No roadmap found (.planning/ROADMAP.md)
|
|||||||
Run /gsd:new-project to initialize.
|
Run /gsd:new-project to initialize.
|
||||||
```
|
```
|
||||||
Exit.
|
Exit.
|
||||||
|
|
||||||
Read roadmap content for parsing.
|
|
||||||
</step>
|
</step>
|
||||||
|
|
||||||
<step name="find_current_milestone">
|
<step name="add_phase">
|
||||||
Parse the roadmap to find the current milestone section:
|
**Delegate the phase addition to gsd-tools:**
|
||||||
|
|
||||||
1. Locate the "## Current Milestone:" heading
|
|
||||||
2. Extract milestone name and version
|
|
||||||
3. Identify all phases under this milestone (before next "---" separator or next milestone heading)
|
|
||||||
4. Parse existing phase numbers (including decimals if present)
|
|
||||||
|
|
||||||
Example structure:
|
|
||||||
|
|
||||||
```
|
|
||||||
## Current Milestone: v1.0 Foundation
|
|
||||||
|
|
||||||
### Phase 4: Focused Command System
|
|
||||||
### Phase 5: Path Routing & Validation
|
|
||||||
### Phase 6: Documentation & Distribution
|
|
||||||
```
|
|
||||||
</step>
|
|
||||||
|
|
||||||
<step name="calculate_next_phase">
|
|
||||||
Find the highest integer phase number in the current milestone:
|
|
||||||
|
|
||||||
1. Extract all phase numbers from phase headings (### Phase N:)
|
|
||||||
2. Filter to integer phases only (ignore decimals like 4.1, 4.2)
|
|
||||||
3. Find the maximum integer value
|
|
||||||
4. Add 1 to get the next phase number
|
|
||||||
|
|
||||||
Example: If phases are 4, 5, 5.1, 6 → next is 7
|
|
||||||
|
|
||||||
Format as two-digit: `printf "%02d" $next_phase`
|
|
||||||
</step>
|
|
||||||
|
|
||||||
<step name="generate_slug">
|
|
||||||
Convert the phase description to a kebab-case slug.
|
|
||||||
|
|
||||||
Use `init phase-op` which provides `phase_slug` computed from phase name, or call:
|
|
||||||
```bash
|
|
||||||
slug=$(node ~/.claude/get-shit-done/bin/gsd-tools.js generate-slug "$description" --raw)
|
|
||||||
```
|
|
||||||
|
|
||||||
Phase directory name: `{two-digit-phase}-{slug}`
|
|
||||||
Example: `07-add-authentication`
|
|
||||||
</step>
|
|
||||||
|
|
||||||
<step name="create_phase_directory">
|
|
||||||
Create the phase directory structure:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
phase_dir=".planning/phases/${phase_num}-${slug}"
|
RESULT=$(node ~/.claude/get-shit-done/bin/gsd-tools.js phase add "${description}")
|
||||||
mkdir -p "$phase_dir"
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Confirm: "Created directory: $phase_dir"
|
The CLI handles:
|
||||||
</step>
|
- Finding the highest existing integer phase number
|
||||||
|
- Calculating next phase number (max + 1)
|
||||||
|
- Generating slug from description
|
||||||
|
- Creating the phase directory (`.planning/phases/{NN}-{slug}/`)
|
||||||
|
- Inserting the phase entry into ROADMAP.md with Goal, Depends on, and Plans sections
|
||||||
|
|
||||||
<step name="update_roadmap">
|
Extract from result: `phase_number`, `padded`, `name`, `slug`, `directory`.
|
||||||
Add the new phase entry to the roadmap:
|
|
||||||
|
|
||||||
1. Find the insertion point (after last phase in current milestone, before "---" separator)
|
|
||||||
2. Insert new phase heading:
|
|
||||||
|
|
||||||
```
|
|
||||||
### Phase {N}: {Description}
|
|
||||||
|
|
||||||
**Goal:** [To be planned]
|
|
||||||
**Depends on:** Phase {N-1}
|
|
||||||
**Plans:** 0 plans
|
|
||||||
|
|
||||||
Plans:
|
|
||||||
- [ ] TBD (run /gsd:plan-phase {N} to break down)
|
|
||||||
|
|
||||||
**Details:**
|
|
||||||
[To be added during planning]
|
|
||||||
```
|
|
||||||
|
|
||||||
3. Write updated roadmap back to file
|
|
||||||
|
|
||||||
Preserve all other content exactly (formatting, spacing, other phases).
|
|
||||||
</step>
|
</step>
|
||||||
|
|
||||||
<step name="update_project_state">
|
<step name="update_project_state">
|
||||||
Update STATE.md to reflect the new phase:
|
Update STATE.md to reflect the new phase:
|
||||||
|
|
||||||
1. Read `.planning/STATE.md`
|
1. Read `.planning/STATE.md`
|
||||||
2. Under "## Current Position" → "**Next Phase:**" add reference to new phase
|
2. Under "## Accumulated Context" → "### Roadmap Evolution" add entry:
|
||||||
3. Under "## Accumulated Context" → "### Roadmap Evolution" add entry:
|
|
||||||
```
|
```
|
||||||
- Phase {N} added: {description}
|
- Phase {N} added: {description}
|
||||||
```
|
```
|
||||||
@@ -145,7 +79,6 @@ Phase {N} added to current milestone:
|
|||||||
- Status: Not planned yet
|
- Status: Not planned yet
|
||||||
|
|
||||||
Roadmap updated: .planning/ROADMAP.md
|
Roadmap updated: .planning/ROADMAP.md
|
||||||
Project state updated: .planning/STATE.md
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -170,10 +103,9 @@ Project state updated: .planning/STATE.md
|
|||||||
</process>
|
</process>
|
||||||
|
|
||||||
<success_criteria>
|
<success_criteria>
|
||||||
- [ ] Phase directory created: `.planning/phases/{NN}-{slug}/`
|
- [ ] `gsd-tools phase add` executed successfully
|
||||||
|
- [ ] Phase directory created
|
||||||
- [ ] Roadmap updated with new phase entry
|
- [ ] Roadmap updated with new phase entry
|
||||||
- [ ] STATE.md updated with roadmap evolution note
|
- [ ] STATE.md updated with roadmap evolution note
|
||||||
- [ ] New phase appears at end of current milestone
|
|
||||||
- [ ] Next phase number calculated correctly (ignoring decimals)
|
|
||||||
- [ ] User informed of next steps
|
- [ ] User informed of next steps
|
||||||
</success_criteria>
|
</success_criteria>
|
||||||
|
|||||||
@@ -37,18 +37,16 @@ When a milestone completes:
|
|||||||
|
|
||||||
<step name="verify_readiness">
|
<step name="verify_readiness">
|
||||||
|
|
||||||
Check milestone completion:
|
**Use `roadmap analyze` for comprehensive readiness check:**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cat .planning/ROADMAP.md
|
ROADMAP=$(node ~/.claude/get-shit-done/bin/gsd-tools.js roadmap analyze)
|
||||||
ls .planning/phases/*/SUMMARY.md 2>/dev/null | wc -l
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Verify:
|
This returns all phases with plan/summary counts and disk status. Use this to verify:
|
||||||
- Which phases belong to this milestone?
|
- Which phases belong to this milestone?
|
||||||
- All phases complete (all plans have summaries)?
|
- All phases complete (all plans have summaries)? Check `disk_status === 'complete'` for each.
|
||||||
- Work tested/validated?
|
- `progress_percent` should be 100%.
|
||||||
- Ready to ship/tag?
|
|
||||||
|
|
||||||
Present:
|
Present:
|
||||||
|
|
||||||
@@ -61,7 +59,7 @@ Includes:
|
|||||||
- Phase 3: Core Features (3/3 plans complete)
|
- Phase 3: Core Features (3/3 plans complete)
|
||||||
- Phase 4: Polish (1/1 plan complete)
|
- Phase 4: Polish (1/1 plan complete)
|
||||||
|
|
||||||
Total: 4 phases, 8 plans, all complete
|
Total: {phase_count} phases, {total_plans} plans, all complete
|
||||||
```
|
```
|
||||||
|
|
||||||
<config-check>
|
<config-check>
|
||||||
@@ -152,36 +150,9 @@ Key accomplishments for this milestone:
|
|||||||
|
|
||||||
<step name="create_milestone_entry">
|
<step name="create_milestone_entry">
|
||||||
|
|
||||||
Create or update `.planning/MILESTONES.md`.
|
**Note:** MILESTONES.md entry is now created automatically by `gsd-tools milestone complete` in the archive_milestone step. The entry includes version, date, phase/plan/task counts, and accomplishments extracted from SUMMARY.md files.
|
||||||
|
|
||||||
If file doesn't exist, create with project name header. If exists, prepend new entry (reverse chronological).
|
If additional details are needed (e.g., user-provided "Delivered" summary, git range, LOC stats), add them manually after the CLI creates the base entry.
|
||||||
|
|
||||||
Use template from `templates/milestone.md`:
|
|
||||||
|
|
||||||
```markdown
|
|
||||||
## v[Version] [Name] (Shipped: YYYY-MM-DD)
|
|
||||||
|
|
||||||
**Delivered:** [One sentence from user]
|
|
||||||
|
|
||||||
**Phases completed:** [X-Y] ([Z] plans total)
|
|
||||||
|
|
||||||
**Key accomplishments:**
|
|
||||||
|
|
||||||
- [List from previous step]
|
|
||||||
|
|
||||||
**Stats:**
|
|
||||||
|
|
||||||
- [Files] files created/modified
|
|
||||||
- [LOC] lines of [language]
|
|
||||||
- [Phases] phases, [Plans] plans, [Tasks] tasks
|
|
||||||
- [Days] days from [start] to ship
|
|
||||||
|
|
||||||
**Git range:** `feat(XX-XX)` → `feat(YY-YY)`
|
|
||||||
|
|
||||||
**What's next:** [Ask user: what's the next goal?]
|
|
||||||
|
|
||||||
---
|
|
||||||
```
|
|
||||||
|
|
||||||
</step>
|
</step>
|
||||||
|
|
||||||
@@ -370,103 +341,71 @@ Update `.planning/ROADMAP.md` — group completed milestone phases:
|
|||||||
|
|
||||||
<step name="archive_milestone">
|
<step name="archive_milestone">
|
||||||
|
|
||||||
Extract completed milestone details to archive.
|
**Delegate archival to gsd-tools:**
|
||||||
|
|
||||||
1. Create `.planning/milestones/v[X.Y]-ROADMAP.md`
|
```bash
|
||||||
2. Read `~/.claude/get-shit-done/templates/milestone-archive.md` template
|
ARCHIVE=$(node ~/.claude/get-shit-done/bin/gsd-tools.js milestone complete "v[X.Y]" --name "[Milestone Name]")
|
||||||
3. Extract from ROADMAP.md: all phases in milestone (by number range), full phase details, plan lists with checkmarks
|
```
|
||||||
4. Extract from PROJECT.md: key decisions, validated requirements
|
|
||||||
5. Fill template placeholders: {{VERSION}}, {{MILESTONE_NAME}}, {{DATE}}, {{PHASE_START}}, {{PHASE_END}}, {{TOTAL_PLANS}}, {{MILESTONE_DESCRIPTION}}, {{PHASES_SECTION}}, {{DECISIONS_FROM_PROJECT}}, {{ISSUES_RESOLVED_DURING_MILESTONE}}
|
The CLI handles:
|
||||||
6. Write to `.planning/milestones/v[X.Y]-ROADMAP.md`
|
- Creating `.planning/milestones/` directory
|
||||||
7. Delete ROADMAP.md:
|
- Archiving ROADMAP.md to `milestones/v[X.Y]-ROADMAP.md`
|
||||||
```bash
|
- Archiving REQUIREMENTS.md to `milestones/v[X.Y]-REQUIREMENTS.md` with archive header
|
||||||
rm .planning/ROADMAP.md
|
- Moving audit file to milestones if it exists
|
||||||
```
|
- Creating/appending MILESTONES.md entry with accomplishments from SUMMARY.md files
|
||||||
8. Verify:
|
- Updating STATE.md (status, last activity)
|
||||||
```bash
|
|
||||||
ls .planning/milestones/v[X.Y]-ROADMAP.md
|
Extract from result: `version`, `date`, `phases`, `plans`, `tasks`, `accomplishments`, `archived`.
|
||||||
```
|
|
||||||
9. Confirm:
|
Verify: `✅ Milestone archived to .planning/milestones/`
|
||||||
```
|
|
||||||
✅ v[X.Y] roadmap archived to milestones/v[X.Y]-ROADMAP.md
|
|
||||||
✅ ROADMAP.md deleted (fresh one for next milestone)
|
|
||||||
```
|
|
||||||
|
|
||||||
**Note:** Phase directories (`.planning/phases/`) are NOT deleted — they accumulate across milestones as raw execution history. Phase numbering continues (v1.0 phases 1-4, v1.1 phases 5-8, etc.).
|
**Note:** Phase directories (`.planning/phases/`) are NOT deleted — they accumulate across milestones as raw execution history. Phase numbering continues (v1.0 phases 1-4, v1.1 phases 5-8, etc.).
|
||||||
|
|
||||||
</step>
|
After archival, the AI still handles:
|
||||||
|
- Reorganizing ROADMAP.md with milestone grouping (requires judgment)
|
||||||
<step name="archive_requirements">
|
- Full PROJECT.md evolution review (requires understanding)
|
||||||
|
- Deleting original ROADMAP.md and REQUIREMENTS.md
|
||||||
Archive requirements for next milestone.
|
- These are NOT fully delegated because they require AI interpretation of content
|
||||||
|
|
||||||
1. Read REQUIREMENTS.md:
|
|
||||||
```bash
|
|
||||||
cat .planning/REQUIREMENTS.md
|
|
||||||
```
|
|
||||||
|
|
||||||
2. Create `.planning/milestones/v[X.Y]-REQUIREMENTS.md`
|
|
||||||
|
|
||||||
3. Transform: mark all requirements `[x]` complete, add outcome notes, update traceability table to "Complete", add milestone summary (total shipped, scope changes, drops)
|
|
||||||
|
|
||||||
4. Write archive with header:
|
|
||||||
```markdown
|
|
||||||
# Requirements Archive: v[X.Y] [Milestone Name]
|
|
||||||
|
|
||||||
**Archived:** [DATE]
|
|
||||||
**Status:** ✅ SHIPPED
|
|
||||||
|
|
||||||
For current requirements, see `.planning/REQUIREMENTS.md`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
[Full REQUIREMENTS.md content with checkboxes marked complete]
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Milestone Summary
|
|
||||||
|
|
||||||
**Shipped:** [X] of [Y] requirements
|
|
||||||
**Adjusted:** [list any that changed]
|
|
||||||
**Dropped:** [list any removed and why]
|
|
||||||
|
|
||||||
---
|
|
||||||
*Archived: [DATE] as part of v[X.Y] milestone completion*
|
|
||||||
```
|
|
||||||
|
|
||||||
5. Delete original:
|
|
||||||
```bash
|
|
||||||
rm .planning/REQUIREMENTS.md
|
|
||||||
```
|
|
||||||
|
|
||||||
6. Confirm:
|
|
||||||
```
|
|
||||||
✅ Requirements archived to milestones/v[X.Y]-REQUIREMENTS.md
|
|
||||||
✅ REQUIREMENTS.md deleted (fresh one for next milestone)
|
|
||||||
```
|
|
||||||
|
|
||||||
**Note:** Next milestone starts with `/gsd:new-milestone` which includes requirements definition. PROJECT.md's Validated section carries cumulative record across milestones.
|
|
||||||
|
|
||||||
</step>
|
</step>
|
||||||
|
|
||||||
<step name="archive_audit">
|
<step name="reorganize_roadmap_and_delete_originals">
|
||||||
|
|
||||||
Move audit file to archive (if exists). Use init context or check directly:
|
After `milestone complete` has archived, reorganize ROADMAP.md with milestone groupings, then delete originals:
|
||||||
|
|
||||||
```bash
|
**Reorganize ROADMAP.md** — group completed milestone phases:
|
||||||
# Check if audit file exists
|
|
||||||
[ -f ".planning/v[X.Y]-MILESTONE-AUDIT.md" ] && mv .planning/v[X.Y]-MILESTONE-AUDIT.md .planning/milestones/
|
```markdown
|
||||||
|
# Roadmap: [Project Name]
|
||||||
|
|
||||||
|
## Milestones
|
||||||
|
|
||||||
|
- ✅ **v1.0 MVP** — Phases 1-4 (shipped YYYY-MM-DD)
|
||||||
|
- 🚧 **v1.1 Security** — Phases 5-6 (in progress)
|
||||||
|
|
||||||
|
## Phases
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>✅ v1.0 MVP (Phases 1-4) — SHIPPED YYYY-MM-DD</summary>
|
||||||
|
|
||||||
|
- [x] Phase 1: Foundation (2/2 plans) — completed YYYY-MM-DD
|
||||||
|
- [x] Phase 2: Authentication (2/2 plans) — completed YYYY-MM-DD
|
||||||
|
|
||||||
|
</details>
|
||||||
```
|
```
|
||||||
|
|
||||||
Confirm: `✅ Audit archived to milestones/v[X.Y]-MILESTONE-AUDIT.md`
|
**Then delete originals:**
|
||||||
|
|
||||||
Skip silently if no audit file — audit is optional.
|
```bash
|
||||||
|
rm .planning/ROADMAP.md
|
||||||
|
rm .planning/REQUIREMENTS.md
|
||||||
|
```
|
||||||
|
|
||||||
</step>
|
</step>
|
||||||
|
|
||||||
<step name="update_state">
|
<step name="update_state">
|
||||||
|
|
||||||
Update STATE.md for milestone completion.
|
Most STATE.md updates were handled by `milestone complete`, but verify and update remaining fields:
|
||||||
|
|
||||||
**Project Reference:**
|
**Project Reference:**
|
||||||
|
|
||||||
@@ -479,17 +418,6 @@ See: .planning/PROJECT.md (updated [today])
|
|||||||
**Current focus:** [Next milestone or "Planning next milestone"]
|
**Current focus:** [Next milestone or "Planning next milestone"]
|
||||||
```
|
```
|
||||||
|
|
||||||
**Current Position:**
|
|
||||||
|
|
||||||
```markdown
|
|
||||||
Phase: [Next phase] of [Total] ([Phase name])
|
|
||||||
Plan: Not started
|
|
||||||
Status: Ready to plan
|
|
||||||
Last activity: [today] — v[X.Y] milestone complete
|
|
||||||
|
|
||||||
Progress: [updated progress bar]
|
|
||||||
```
|
|
||||||
|
|
||||||
**Accumulated Context:**
|
**Accumulated Context:**
|
||||||
- Clear decisions summary (full log in PROJECT.md)
|
- Clear decisions summary (full log in PROJECT.md)
|
||||||
- Clear resolved blockers
|
- Clear resolved blockers
|
||||||
|
|||||||
@@ -17,31 +17,17 @@ Example: `/gsd:insert-phase 72 Fix critical auth bug`
|
|||||||
-> after = 72
|
-> after = 72
|
||||||
-> description = "Fix critical auth bug"
|
-> description = "Fix critical auth bug"
|
||||||
|
|
||||||
Validation:
|
If arguments missing:
|
||||||
|
|
||||||
```bash
|
```
|
||||||
if [ $# -lt 2 ]; then
|
ERROR: Both phase number and description required
|
||||||
echo "ERROR: Both phase number and description required"
|
Usage: /gsd:insert-phase <after> <description>
|
||||||
echo "Usage: /gsd:insert-phase <after> <description>"
|
Example: /gsd:insert-phase 72 Fix critical auth bug
|
||||||
echo "Example: /gsd:insert-phase 72 Fix critical auth bug"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Parse first argument as integer:
|
Exit.
|
||||||
|
|
||||||
```bash
|
|
||||||
after_phase=$1
|
|
||||||
shift
|
|
||||||
description="$*"
|
|
||||||
|
|
||||||
# Validate after_phase is an integer
|
|
||||||
if ! [[ "$after_phase" =~ ^[0-9]+$ ]]; then
|
|
||||||
echo "ERROR: Phase number must be an integer"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
```
|
|
||||||
|
|
||||||
|
Validate first argument is an integer.
|
||||||
</step>
|
</step>
|
||||||
|
|
||||||
<step name="init_context">
|
<step name="init_context">
|
||||||
@@ -56,111 +42,35 @@ Check `roadmap_exists` from init JSON. If false:
|
|||||||
ERROR: No roadmap found (.planning/ROADMAP.md)
|
ERROR: No roadmap found (.planning/ROADMAP.md)
|
||||||
```
|
```
|
||||||
Exit.
|
Exit.
|
||||||
|
|
||||||
Read roadmap content for parsing.
|
|
||||||
</step>
|
</step>
|
||||||
|
|
||||||
<step name="verify_target_phase">
|
<step name="insert_phase">
|
||||||
Verify that the target phase exists in the roadmap:
|
**Delegate the phase insertion to gsd-tools:**
|
||||||
|
|
||||||
1. Search for "### Phase {after_phase}:" heading
|
|
||||||
2. If not found:
|
|
||||||
|
|
||||||
```
|
|
||||||
ERROR: Phase {after_phase} not found in roadmap
|
|
||||||
Available phases: [list phase numbers]
|
|
||||||
```
|
|
||||||
|
|
||||||
Exit.
|
|
||||||
|
|
||||||
3. Verify phase is in current milestone (not completed/archived)
|
|
||||||
</step>
|
|
||||||
|
|
||||||
<step name="find_existing_decimals">
|
|
||||||
Calculate next decimal phase number:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
DECIMAL_INFO=$(node ~/.claude/get-shit-done/bin/gsd-tools.js phase next-decimal "${after_phase}")
|
RESULT=$(node ~/.claude/get-shit-done/bin/gsd-tools.js phase insert "${after_phase}" "${description}")
|
||||||
```
|
```
|
||||||
|
|
||||||
Extract from JSON:
|
The CLI handles:
|
||||||
- `next`: The next available decimal (e.g., "06.1", "06.3")
|
- Verifying target phase exists in ROADMAP.md
|
||||||
- `existing`: Array of existing decimals (e.g., ["06.1", "06.2"])
|
- Calculating next decimal phase number (checking existing decimals on disk)
|
||||||
- `base_phase`: Normalized base phase (e.g., "06")
|
- Generating slug from description
|
||||||
|
- Creating the phase directory (`.planning/phases/{N.M}-{slug}/`)
|
||||||
|
- Inserting the phase entry into ROADMAP.md after the target phase with (INSERTED) marker
|
||||||
|
|
||||||
Store the result:
|
Extract from result: `phase_number`, `after_phase`, `name`, `slug`, `directory`.
|
||||||
```bash
|
|
||||||
decimal_phase=$(echo "$DECIMAL_INFO" | jq -r '.next')
|
|
||||||
```
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
- Phase 72 with no decimals -> next is 72.1
|
|
||||||
- Phase 72 with 72.1 -> next is 72.2
|
|
||||||
- Phase 72 with 72.1, 72.2 -> next is 72.3
|
|
||||||
</step>
|
|
||||||
|
|
||||||
<step name="generate_slug">
|
|
||||||
Convert the phase description to a kebab-case slug.
|
|
||||||
|
|
||||||
Use `generate-slug` command (init phase-op provides `phase_slug` for existing phase, but this is a new phase):
|
|
||||||
```bash
|
|
||||||
slug=$(node ~/.claude/get-shit-done/bin/gsd-tools.js generate-slug "$description" --raw)
|
|
||||||
```
|
|
||||||
|
|
||||||
Phase directory name: `{decimal-phase}-{slug}`
|
|
||||||
Example: `06.1-fix-critical-auth-bug` (phase 6 insertion)
|
|
||||||
</step>
|
|
||||||
|
|
||||||
<step name="create_phase_directory">
|
|
||||||
Create the phase directory structure:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
phase_dir=".planning/phases/${decimal_phase}-${slug}"
|
|
||||||
mkdir -p "$phase_dir"
|
|
||||||
```
|
|
||||||
|
|
||||||
Confirm: "Created directory: $phase_dir"
|
|
||||||
</step>
|
|
||||||
|
|
||||||
<step name="update_roadmap">
|
|
||||||
Insert the new phase entry into the roadmap:
|
|
||||||
|
|
||||||
1. Find insertion point: immediately after Phase {after_phase}'s content (before next phase heading or "---")
|
|
||||||
2. Insert new phase heading with (INSERTED) marker:
|
|
||||||
|
|
||||||
```
|
|
||||||
### Phase {decimal_phase}: {Description} (INSERTED)
|
|
||||||
|
|
||||||
**Goal:** [Urgent work - to be planned]
|
|
||||||
**Depends on:** Phase {after_phase}
|
|
||||||
**Plans:** 0 plans
|
|
||||||
|
|
||||||
Plans:
|
|
||||||
- [ ] TBD (run /gsd:plan-phase {decimal_phase} to break down)
|
|
||||||
|
|
||||||
**Details:**
|
|
||||||
[To be added during planning]
|
|
||||||
```
|
|
||||||
|
|
||||||
3. Write updated roadmap back to file
|
|
||||||
|
|
||||||
The "(INSERTED)" marker helps identify decimal phases as urgent insertions.
|
|
||||||
|
|
||||||
Preserve all other content exactly (formatting, spacing, other phases).
|
|
||||||
</step>
|
</step>
|
||||||
|
|
||||||
<step name="update_project_state">
|
<step name="update_project_state">
|
||||||
Update STATE.md to reflect the inserted phase:
|
Update STATE.md to reflect the inserted phase:
|
||||||
|
|
||||||
1. Read `.planning/STATE.md`
|
1. Read `.planning/STATE.md`
|
||||||
2. Under "## Accumulated Context" -> "### Roadmap Evolution" add entry:
|
2. Under "## Accumulated Context" → "### Roadmap Evolution" add entry:
|
||||||
```
|
```
|
||||||
- Phase {decimal_phase} inserted after Phase {after_phase}: {description} (URGENT)
|
- Phase {decimal_phase} inserted after Phase {after_phase}: {description} (URGENT)
|
||||||
```
|
```
|
||||||
|
|
||||||
If "Roadmap Evolution" section doesn't exist, create it.
|
If "Roadmap Evolution" section doesn't exist, create it.
|
||||||
|
|
||||||
Add note about insertion reason if appropriate.
|
|
||||||
</step>
|
</step>
|
||||||
|
|
||||||
<step name="completion">
|
<step name="completion">
|
||||||
@@ -173,7 +83,7 @@ Phase {decimal_phase} inserted after Phase {after_phase}:
|
|||||||
- Status: Not planned yet
|
- Status: Not planned yet
|
||||||
- Marker: (INSERTED) - indicates urgent work
|
- Marker: (INSERTED) - indicates urgent work
|
||||||
|
|
||||||
Roadmap updated: {roadmap-path}
|
Roadmap updated: .planning/ROADMAP.md
|
||||||
Project state updated: .planning/STATE.md
|
Project state updated: .planning/STATE.md
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -211,10 +121,9 @@ Project state updated: .planning/STATE.md
|
|||||||
<success_criteria>
|
<success_criteria>
|
||||||
Phase insertion is complete when:
|
Phase insertion is complete when:
|
||||||
|
|
||||||
- [ ] Phase directory created: `.planning/phases/{N.M}-{slug}/`
|
- [ ] `gsd-tools phase insert` executed successfully
|
||||||
|
- [ ] Phase directory created
|
||||||
- [ ] Roadmap updated with new phase entry (includes "(INSERTED)" marker)
|
- [ ] Roadmap updated with new phase entry (includes "(INSERTED)" marker)
|
||||||
- [ ] Phase inserted in correct position (after target phase, before next integer phase)
|
|
||||||
- [ ] STATE.md updated with roadmap evolution note
|
- [ ] STATE.md updated with roadmap evolution note
|
||||||
- [ ] Decimal number calculated correctly (based on existing decimals)
|
|
||||||
- [ ] User informed of next steps and dependency implications
|
- [ ] User informed of next steps and dependency implications
|
||||||
</success_criteria>
|
</success_criteria>
|
||||||
|
|||||||
@@ -50,42 +50,68 @@ All file contents are already loaded via `--include` in init_context step:
|
|||||||
No additional file reads needed.
|
No additional file reads needed.
|
||||||
</step>
|
</step>
|
||||||
|
|
||||||
|
<step name="analyze_roadmap">
|
||||||
|
**Get comprehensive roadmap analysis (replaces manual parsing):**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ROADMAP=$(node ~/.claude/get-shit-done/bin/gsd-tools.js roadmap analyze)
|
||||||
|
```
|
||||||
|
|
||||||
|
This returns structured JSON with:
|
||||||
|
- All phases with disk status (complete/partial/planned/empty/no_directory)
|
||||||
|
- Goal and dependencies per phase
|
||||||
|
- Plan and summary counts per phase
|
||||||
|
- Aggregated stats: total plans, summaries, progress percent
|
||||||
|
- Current and next phase identification
|
||||||
|
|
||||||
|
Use this instead of manually reading/parsing ROADMAP.md.
|
||||||
|
</step>
|
||||||
|
|
||||||
<step name="recent">
|
<step name="recent">
|
||||||
**Gather recent work context:**
|
**Gather recent work context:**
|
||||||
|
|
||||||
- Find the 2-3 most recent SUMMARY.md files
|
- Find the 2-3 most recent SUMMARY.md files
|
||||||
- Extract from each: what was accomplished, key decisions, any issues logged
|
- Use `summary-extract` for efficient parsing:
|
||||||
|
```bash
|
||||||
|
node ~/.claude/get-shit-done/bin/gsd-tools.js summary-extract <path> --fields one_liner
|
||||||
|
```
|
||||||
- This shows "what we've been working on"
|
- This shows "what we've been working on"
|
||||||
</step>
|
</step>
|
||||||
|
|
||||||
<step name="position">
|
<step name="position">
|
||||||
**Parse current position from init context:**
|
**Parse current position from init context and roadmap analysis:**
|
||||||
|
|
||||||
- Use `current_phase` and `next_phase` from init for position
|
- Use `current_phase` and `next_phase` from roadmap analyze
|
||||||
- Use `phases` array for plan counts per phase
|
- Use phase-level `has_context` and `has_research` flags from analyze
|
||||||
- Note `paused_at` if work was paused
|
- Note `paused_at` if work was paused (from init context)
|
||||||
- Check for CONTEXT.md: For phases without PLAN.md files, check if `{phase}-CONTEXT.md` exists in phase directory
|
|
||||||
- Count pending todos: use `init todos` or `list-todos`
|
- Count pending todos: use `init todos` or `list-todos`
|
||||||
- Check for active debug sessions: `ls .planning/debug/*.md 2>/dev/null | grep -v resolved | wc -l`
|
- Check for active debug sessions: `ls .planning/debug/*.md 2>/dev/null | grep -v resolved | wc -l`
|
||||||
</step>
|
</step>
|
||||||
|
|
||||||
<step name="report">
|
<step name="report">
|
||||||
**Present rich status report:**
|
**Generate progress bar from gsd-tools, then present rich status report:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Get formatted progress bar
|
||||||
|
PROGRESS_BAR=$(node ~/.claude/get-shit-done/bin/gsd-tools.js progress bar --raw)
|
||||||
|
```
|
||||||
|
|
||||||
|
Present:
|
||||||
|
|
||||||
```
|
```
|
||||||
# [Project Name]
|
# [Project Name]
|
||||||
|
|
||||||
**Progress:** [████████░░] 8/10 plans complete
|
**Progress:** {PROGRESS_BAR}
|
||||||
**Profile:** [quality/balanced/budget]
|
**Profile:** [quality/balanced/budget]
|
||||||
|
|
||||||
## Recent Work
|
## Recent Work
|
||||||
- [Phase X, Plan Y]: [what was accomplished - 1 line]
|
- [Phase X, Plan Y]: [what was accomplished - 1 line from summary-extract]
|
||||||
- [Phase X, Plan Z]: [what was accomplished - 1 line]
|
- [Phase X, Plan Z]: [what was accomplished - 1 line from summary-extract]
|
||||||
|
|
||||||
## Current Position
|
## Current Position
|
||||||
Phase [N] of [total]: [phase-name]
|
Phase [N] of [total]: [phase-name]
|
||||||
Plan [M] of [phase-total]: [status]
|
Plan [M] of [phase-total]: [status]
|
||||||
CONTEXT: [✓ if CONTEXT.md exists | - if not]
|
CONTEXT: [✓ if has_context | - if not]
|
||||||
|
|
||||||
## Key Decisions Made
|
## Key Decisions Made
|
||||||
- [decision 1 from STATE.md]
|
- [decision 1 from STATE.md]
|
||||||
@@ -102,7 +128,7 @@ CONTEXT: [✓ if CONTEXT.md exists | - if not]
|
|||||||
(Only show this section if count > 0)
|
(Only show this section if count > 0)
|
||||||
|
|
||||||
## What's Next
|
## What's Next
|
||||||
[Next phase/plan objective from ROADMAP]
|
[Next phase/plan objective from roadmap analyze]
|
||||||
```
|
```
|
||||||
|
|
||||||
</step>
|
</step>
|
||||||
|
|||||||
@@ -37,20 +37,6 @@ Extract: `phase_found`, `phase_dir`, `phase_number`, `commit_docs`, `roadmap_exi
|
|||||||
Also read STATE.md and ROADMAP.md content for parsing current position.
|
Also read STATE.md and ROADMAP.md content for parsing current position.
|
||||||
</step>
|
</step>
|
||||||
|
|
||||||
<step name="validate_phase_exists">
|
|
||||||
Verify the target phase exists in ROADMAP.md:
|
|
||||||
|
|
||||||
1. Search for `### Phase {target}:` heading
|
|
||||||
2. If not found:
|
|
||||||
|
|
||||||
```
|
|
||||||
ERROR: Phase {target} not found in roadmap
|
|
||||||
Available phases: [list phase numbers]
|
|
||||||
```
|
|
||||||
|
|
||||||
Exit.
|
|
||||||
</step>
|
|
||||||
|
|
||||||
<step name="validate_future_phase">
|
<step name="validate_future_phase">
|
||||||
Verify the phase is a future phase (not started):
|
Verify the phase is a future phase (not started):
|
||||||
|
|
||||||
@@ -70,46 +56,6 @@ To abandon current work, use /gsd:pause-work instead.
|
|||||||
```
|
```
|
||||||
|
|
||||||
Exit.
|
Exit.
|
||||||
|
|
||||||
3. Check for SUMMARY.md files in phase directory:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
ls .planning/phases/{target}-*/*-SUMMARY.md 2>/dev/null
|
|
||||||
```
|
|
||||||
|
|
||||||
If any SUMMARY.md files exist:
|
|
||||||
|
|
||||||
```
|
|
||||||
ERROR: Phase {target} has completed work
|
|
||||||
|
|
||||||
Found executed plans:
|
|
||||||
- {list of SUMMARY.md files}
|
|
||||||
|
|
||||||
Cannot remove phases with completed work.
|
|
||||||
```
|
|
||||||
|
|
||||||
Exit.
|
|
||||||
</step>
|
|
||||||
|
|
||||||
<step name="gather_phase_info">
|
|
||||||
Collect information about the phase being removed:
|
|
||||||
|
|
||||||
1. Extract phase name from ROADMAP.md heading: `### Phase {target}: {Name}`
|
|
||||||
2. Find phase directory: `.planning/phases/{target}-{slug}/`
|
|
||||||
3. Find all subsequent phases (integer and decimal) that need renumbering
|
|
||||||
|
|
||||||
**Subsequent phase detection:**
|
|
||||||
|
|
||||||
For integer phase removal (e.g., 17):
|
|
||||||
- Find all phases > 17 (integers: 18, 19, 20...)
|
|
||||||
- Find all decimal phases >= 17.0 and < 18.0 (17.1, 17.2...) → these become 16.x
|
|
||||||
- Find all decimal phases for subsequent integers (18.1, 19.1...) → renumber with their parent
|
|
||||||
|
|
||||||
For decimal phase removal (e.g., 17.1):
|
|
||||||
- Find all decimal phases > 17.1 and < 18 (17.2, 17.3...) → renumber down
|
|
||||||
- Integer phases unchanged
|
|
||||||
|
|
||||||
List all phases that will be renumbered.
|
|
||||||
</step>
|
</step>
|
||||||
|
|
||||||
<step name="confirm_removal">
|
<step name="confirm_removal">
|
||||||
@@ -120,11 +66,8 @@ Removing Phase {target}: {Name}
|
|||||||
|
|
||||||
This will:
|
This will:
|
||||||
- Delete: .planning/phases/{target}-{slug}/
|
- Delete: .planning/phases/{target}-{slug}/
|
||||||
- Renumber {N} subsequent phases:
|
- Renumber all subsequent phases
|
||||||
- Phase 18 → Phase 17
|
- Update: ROADMAP.md, STATE.md
|
||||||
- Phase 18.1 → Phase 17.1
|
|
||||||
- Phase 19 → Phase 18
|
|
||||||
[etc.]
|
|
||||||
|
|
||||||
Proceed? (y/n)
|
Proceed? (y/n)
|
||||||
```
|
```
|
||||||
@@ -132,110 +75,27 @@ Proceed? (y/n)
|
|||||||
Wait for confirmation.
|
Wait for confirmation.
|
||||||
</step>
|
</step>
|
||||||
|
|
||||||
<step name="delete_phase_directory">
|
<step name="execute_removal">
|
||||||
Use the init context from earlier. The `phase_dir` from `init phase-op` provides the path.
|
**Delegate the entire removal operation to gsd-tools:**
|
||||||
|
|
||||||
Delete the target phase directory if it exists:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# phase_dir from init phase-op contains the path if found
|
RESULT=$(node ~/.claude/get-shit-done/bin/gsd-tools.js phase remove "${target}")
|
||||||
if [ -n "$phase_dir" ]; then
|
|
||||||
rm -rf "$phase_dir"
|
|
||||||
echo "Deleted: $phase_dir/"
|
|
||||||
fi
|
|
||||||
```
|
```
|
||||||
|
|
||||||
If directory doesn't exist (`phase_found=false` from init), note: "No directory to delete (phase not yet created)"
|
If the phase has executed plans (SUMMARY.md files), gsd-tools will error. Use `--force` only if the user confirms:
|
||||||
</step>
|
|
||||||
|
|
||||||
<step name="renumber_directories">
|
|
||||||
Rename all subsequent phase directories:
|
|
||||||
|
|
||||||
For each phase directory that needs renumbering (in reverse order to avoid conflicts):
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Example: renaming 18-dashboard to 17-dashboard
|
RESULT=$(node ~/.claude/get-shit-done/bin/gsd-tools.js phase remove "${target}" --force)
|
||||||
mv ".planning/phases/18-dashboard" ".planning/phases/17-dashboard"
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Process in descending order (20→19, then 19→18, then 18→17) to avoid overwriting.
|
The CLI handles:
|
||||||
|
- Deleting the phase directory
|
||||||
|
- Renumbering all subsequent directories (in reverse order to avoid conflicts)
|
||||||
|
- Renaming all files inside renumbered directories (PLAN.md, SUMMARY.md, etc.)
|
||||||
|
- Updating ROADMAP.md (removing section, renumbering all phase references, updating dependencies)
|
||||||
|
- Updating STATE.md (decrementing phase count)
|
||||||
|
|
||||||
Also rename decimal phase directories:
|
Extract from result: `removed`, `directory_deleted`, `renamed_directories`, `renamed_files`, `roadmap_updated`, `state_updated`.
|
||||||
- `17.1-fix-bug` → `16.1-fix-bug` (if removing integer 17)
|
|
||||||
- `17.2-hotfix` → `17.1-hotfix` (if removing decimal 17.1)
|
|
||||||
</step>
|
|
||||||
|
|
||||||
<step name="rename_files_in_directories">
|
|
||||||
Rename plan files inside renumbered directories:
|
|
||||||
|
|
||||||
For each renumbered directory, rename files that contain the phase number:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Inside 17-dashboard (was 18-dashboard):
|
|
||||||
mv "18-01-PLAN.md" "17-01-PLAN.md"
|
|
||||||
mv "18-02-PLAN.md" "17-02-PLAN.md"
|
|
||||||
mv "18-01-SUMMARY.md" "17-01-SUMMARY.md" # if exists
|
|
||||||
# etc.
|
|
||||||
```
|
|
||||||
|
|
||||||
Also handle CONTEXT.md and DISCOVERY.md (these don't have phase prefixes, so no rename needed).
|
|
||||||
</step>
|
|
||||||
|
|
||||||
<step name="update_roadmap">
|
|
||||||
Update ROADMAP.md:
|
|
||||||
|
|
||||||
1. **Remove the phase section entirely:**
|
|
||||||
- Delete from `### Phase {target}:` to the next phase heading (or section end)
|
|
||||||
|
|
||||||
2. **Remove from phase list:**
|
|
||||||
- Delete line `- [ ] **Phase {target}: {Name}**` or similar
|
|
||||||
|
|
||||||
3. **Remove from Progress table:**
|
|
||||||
- Delete the row for Phase {target}
|
|
||||||
|
|
||||||
4. **Renumber all subsequent phases:**
|
|
||||||
- `### Phase 18:` → `### Phase 17:`
|
|
||||||
- `- [ ] **Phase 18:` → `- [ ] **Phase 17:`
|
|
||||||
- Table rows: `| 18. Dashboard |` → `| 17. Dashboard |`
|
|
||||||
- Plan references: `18-01:` → `17-01:`
|
|
||||||
|
|
||||||
5. **Update dependency references:**
|
|
||||||
- `**Depends on:** Phase 18` → `**Depends on:** Phase 17`
|
|
||||||
- For the phase that depended on the removed phase:
|
|
||||||
- `**Depends on:** Phase 17` (removed) → `**Depends on:** Phase 16`
|
|
||||||
|
|
||||||
6. **Renumber decimal phases:**
|
|
||||||
- `### Phase 17.1:` → `### Phase 16.1:` (if integer 17 removed)
|
|
||||||
- Update all references consistently
|
|
||||||
|
|
||||||
Write updated ROADMAP.md.
|
|
||||||
</step>
|
|
||||||
|
|
||||||
<step name="update_state">
|
|
||||||
Update STATE.md:
|
|
||||||
|
|
||||||
1. **Update total phase count:**
|
|
||||||
- `Phase: 16 of 20` → `Phase: 16 of 19`
|
|
||||||
|
|
||||||
2. **Recalculate progress percentage:**
|
|
||||||
- New percentage based on completed plans / new total plans
|
|
||||||
|
|
||||||
Do NOT add a "Roadmap Evolution" note - the git commit is the record.
|
|
||||||
|
|
||||||
Write updated STATE.md.
|
|
||||||
</step>
|
|
||||||
|
|
||||||
<step name="update_file_contents">
|
|
||||||
Search for and update phase references inside plan files:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Find files that reference the old phase numbers
|
|
||||||
grep -r "Phase 18" .planning/phases/17-*/ 2>/dev/null
|
|
||||||
grep -r "Phase 19" .planning/phases/18-*/ 2>/dev/null
|
|
||||||
# etc.
|
|
||||||
```
|
|
||||||
|
|
||||||
Update any internal references to reflect new numbering.
|
|
||||||
</step>
|
</step>
|
||||||
|
|
||||||
<step name="commit">
|
<step name="commit">
|
||||||
@@ -256,13 +116,10 @@ Phase {target} ({original-name}) removed.
|
|||||||
|
|
||||||
Changes:
|
Changes:
|
||||||
- Deleted: .planning/phases/{target}-{slug}/
|
- Deleted: .planning/phases/{target}-{slug}/
|
||||||
- Renumbered: Phases {first-renumbered}-{last-old} → {first-renumbered-1}-{last-new}
|
- Renumbered: {N} directories and {M} files
|
||||||
- Updated: ROADMAP.md, STATE.md
|
- Updated: ROADMAP.md, STATE.md
|
||||||
- Committed: chore: remove phase {target} ({original-name})
|
- Committed: chore: remove phase {target} ({original-name})
|
||||||
|
|
||||||
Current roadmap: {total-remaining} phases
|
|
||||||
Current position: Phase {current} of {new-total}
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## What's Next
|
## What's Next
|
||||||
@@ -280,47 +137,18 @@ Would you like to:
|
|||||||
|
|
||||||
<anti_patterns>
|
<anti_patterns>
|
||||||
|
|
||||||
- Don't remove completed phases (have SUMMARY.md files)
|
- Don't remove completed phases (have SUMMARY.md files) without --force
|
||||||
- Don't remove current or past phases
|
- Don't remove current or past phases
|
||||||
- Don't leave gaps in numbering - always renumber
|
- Don't manually renumber — use `gsd-tools phase remove` which handles all renumbering
|
||||||
- Don't add "removed phase" notes to STATE.md - git commit is the record
|
- Don't add "removed phase" notes to STATE.md — git commit is the record
|
||||||
- Don't ask about each decimal phase - just renumber them
|
|
||||||
- Don't modify completed phase directories
|
- Don't modify completed phase directories
|
||||||
</anti_patterns>
|
</anti_patterns>
|
||||||
|
|
||||||
<edge_cases>
|
|
||||||
|
|
||||||
**Removing a decimal phase (e.g., 17.1):**
|
|
||||||
- Only affects other decimals in same series (17.2 → 17.1, 17.3 → 17.2)
|
|
||||||
- Integer phases unchanged
|
|
||||||
- Simpler operation
|
|
||||||
|
|
||||||
**No subsequent phases to renumber:**
|
|
||||||
- Removing the last phase (e.g., Phase 20 when that's the end)
|
|
||||||
- Just delete and update ROADMAP.md, no renumbering needed
|
|
||||||
|
|
||||||
**Phase directory doesn't exist:**
|
|
||||||
- Phase may be in ROADMAP.md but directory not created yet
|
|
||||||
- Skip directory deletion, proceed with ROADMAP.md updates
|
|
||||||
|
|
||||||
**Decimal phases under removed integer:**
|
|
||||||
- Removing Phase 17 when 17.1, 17.2 exist
|
|
||||||
- 17.1 → 16.1, 17.2 → 16.2
|
|
||||||
- They maintain their position in execution order (after current last integer)
|
|
||||||
|
|
||||||
</edge_cases>
|
|
||||||
|
|
||||||
<success_criteria>
|
<success_criteria>
|
||||||
Phase removal is complete when:
|
Phase removal is complete when:
|
||||||
|
|
||||||
- [ ] Target phase validated as future/unstarted
|
- [ ] Target phase validated as future/unstarted
|
||||||
- [ ] Phase directory deleted (if existed)
|
- [ ] `gsd-tools phase remove` executed successfully
|
||||||
- [ ] All subsequent phase directories renumbered
|
|
||||||
- [ ] Files inside directories renamed ({old}-01-PLAN.md → {new}-01-PLAN.md)
|
|
||||||
- [ ] ROADMAP.md updated (section removed, all references renumbered)
|
|
||||||
- [ ] STATE.md updated (phase count, progress percentage)
|
|
||||||
- [ ] Dependency references updated in subsequent phases
|
|
||||||
- [ ] Changes committed with descriptive message
|
- [ ] Changes committed with descriptive message
|
||||||
- [ ] No gaps in phase numbering
|
|
||||||
- [ ] User informed of changes
|
- [ ] User informed of changes
|
||||||
</success_criteria>
|
</success_criteria>
|
||||||
|
|||||||
@@ -118,39 +118,22 @@ If found, delete them — phase is complete, handoffs are stale.
|
|||||||
|
|
||||||
</step>
|
</step>
|
||||||
|
|
||||||
<step name="update_roadmap">
|
<step name="update_roadmap_and_state">
|
||||||
|
|
||||||
Update the roadmap file:
|
**Delegate ROADMAP.md and STATE.md updates to gsd-tools:**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
ROADMAP_FILE=".planning/ROADMAP.md"
|
TRANSITION=$(node ~/.claude/get-shit-done/bin/gsd-tools.js phase complete "${current_phase}")
|
||||||
```
|
```
|
||||||
|
|
||||||
Update the file:
|
The CLI handles:
|
||||||
|
- Marking the phase checkbox as `[x]` complete with today's date
|
||||||
|
- Updating plan count to final (e.g., "3/3 plans complete")
|
||||||
|
- Updating the Progress table (Status → Complete, adding date)
|
||||||
|
- Advancing STATE.md to next phase (Current Phase, Status → Ready to plan, Current Plan → Not started)
|
||||||
|
- Detecting if this is the last phase in the milestone
|
||||||
|
|
||||||
- Mark current phase: `[x] Complete`
|
Extract from result: `completed_phase`, `plans_executed`, `next_phase`, `next_phase_name`, `is_last_phase`.
|
||||||
- Add completion date
|
|
||||||
- Update plan count to final (e.g., "3/3 plans complete")
|
|
||||||
- Update Progress table
|
|
||||||
- Keep next phase as `[ ] Not started`
|
|
||||||
|
|
||||||
**Example:**
|
|
||||||
|
|
||||||
```markdown
|
|
||||||
## Phases
|
|
||||||
|
|
||||||
- [x] Phase 1: Foundation (completed 2025-01-15)
|
|
||||||
- [ ] Phase 2: Authentication ← Next
|
|
||||||
- [ ] Phase 3: Core Features
|
|
||||||
|
|
||||||
## Progress
|
|
||||||
|
|
||||||
| Phase | Plans Complete | Status | Completed |
|
|
||||||
| ----------------- | -------------- | ----------- | ---------- |
|
|
||||||
| 1. Foundation | 3/3 | Complete | 2025-01-15 |
|
|
||||||
| 2. Authentication | 0/2 | Not started | - |
|
|
||||||
| 3. Core Features | 0/1 | Not started | - |
|
|
||||||
```
|
|
||||||
|
|
||||||
</step>
|
</step>
|
||||||
|
|
||||||
@@ -250,61 +233,21 @@ After (Phase 2 shipped JWT auth, discovered rate limiting needed):
|
|||||||
|
|
||||||
<step name="update_current_position_after_transition">
|
<step name="update_current_position_after_transition">
|
||||||
|
|
||||||
Update Current Position section in STATE.md to reflect phase completion and transition.
|
**Note:** Basic position updates (Current Phase, Status, Current Plan, Last Activity) were already handled by `gsd-tools phase complete` in the update_roadmap_and_state step.
|
||||||
|
|
||||||
**Format:**
|
Verify the updates are correct by reading STATE.md. If the progress bar needs updating, use:
|
||||||
|
|
||||||
```markdown
|
```bash
|
||||||
Phase: [next] of [total] ([Next phase name])
|
PROGRESS=$(node ~/.claude/get-shit-done/bin/gsd-tools.js progress bar --raw)
|
||||||
Plan: Not started
|
|
||||||
Status: Ready to plan
|
|
||||||
Last activity: [today] — Phase [X] complete, transitioned to Phase [X+1]
|
|
||||||
|
|
||||||
Progress: [updated progress bar]
|
|
||||||
```
|
```
|
||||||
|
|
||||||
**Instructions:**
|
Update the progress bar line in STATE.md with the result.
|
||||||
|
|
||||||
- Increment phase number to next phase
|
|
||||||
- Reset plan to "Not started"
|
|
||||||
- Set status to "Ready to plan"
|
|
||||||
- Update last activity to describe transition
|
|
||||||
- Recalculate progress bar based on completed plans
|
|
||||||
|
|
||||||
**Example — transitioning from Phase 2 to Phase 3:**
|
|
||||||
|
|
||||||
Before:
|
|
||||||
|
|
||||||
```markdown
|
|
||||||
## Current Position
|
|
||||||
|
|
||||||
Phase: 2 of 4 (Authentication)
|
|
||||||
Plan: 2 of 2 in current phase
|
|
||||||
Status: Phase complete
|
|
||||||
Last activity: 2025-01-20 — Completed 02-02-PLAN.md
|
|
||||||
|
|
||||||
Progress: ███████░░░ 60%
|
|
||||||
```
|
|
||||||
|
|
||||||
After:
|
|
||||||
|
|
||||||
```markdown
|
|
||||||
## Current Position
|
|
||||||
|
|
||||||
Phase: 3 of 4 (Core Features)
|
|
||||||
Plan: Not started
|
|
||||||
Status: Ready to plan
|
|
||||||
Last activity: 2025-01-20 — Phase 2 complete, transitioned to Phase 3
|
|
||||||
|
|
||||||
Progress: ███████░░░ 60%
|
|
||||||
```
|
|
||||||
|
|
||||||
**Step complete when:**
|
**Step complete when:**
|
||||||
|
|
||||||
- [ ] Phase number incremented to next phase
|
- [ ] Phase number incremented to next phase (done by phase complete)
|
||||||
- [ ] Plan status reset to "Not started"
|
- [ ] Plan status reset to "Not started" (done by phase complete)
|
||||||
- [ ] Status shows "Ready to plan"
|
- [ ] Status shows "Ready to plan" (done by phase complete)
|
||||||
- [ ] Last activity describes the transition
|
|
||||||
- [ ] Progress bar reflects total completed plans
|
- [ ] Progress bar reflects total completed plans
|
||||||
|
|
||||||
</step>
|
</step>
|
||||||
@@ -394,26 +337,20 @@ Resume file: None
|
|||||||
|
|
||||||
**MANDATORY: Verify milestone status before presenting next steps.**
|
**MANDATORY: Verify milestone status before presenting next steps.**
|
||||||
|
|
||||||
**Step 1: Read ROADMAP.md and identify phases in current milestone**
|
**Use the transition result from `gsd-tools phase complete`:**
|
||||||
|
|
||||||
Read the ROADMAP.md file and extract:
|
The `is_last_phase` field from the phase complete result tells you directly:
|
||||||
1. Current phase number (the phase just transitioned from)
|
- `is_last_phase: false` → More phases remain → Go to **Route A**
|
||||||
2. All phase numbers in the current milestone section
|
- `is_last_phase: true` → Milestone complete → Go to **Route B**
|
||||||
|
|
||||||
To find phases, look for:
|
The `next_phase` and `next_phase_name` fields give you the next phase details.
|
||||||
- Phase headers: lines starting with `### Phase` or `#### Phase`
|
|
||||||
- Phase list items: lines like `- [ ] **Phase X:` or `- [x] **Phase X:`
|
|
||||||
|
|
||||||
Count total phases and identify the highest phase number in the milestone.
|
If you need additional context, use:
|
||||||
|
```bash
|
||||||
|
ROADMAP=$(node ~/.claude/get-shit-done/bin/gsd-tools.js roadmap analyze)
|
||||||
|
```
|
||||||
|
|
||||||
State: "Current phase is {X}. Milestone has {N} phases (highest: {Y})."
|
This returns all phases with goals, disk status, and completion info.
|
||||||
|
|
||||||
**Step 2: Route based on milestone status**
|
|
||||||
|
|
||||||
| Condition | Meaning | Action |
|
|
||||||
|-----------|---------|--------|
|
|
||||||
| current phase < highest phase | More phases remain | Go to **Route A** |
|
|
||||||
| current phase = highest phase | Milestone complete | Go to **Route B** |
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user