@ktpartners/dgs-platform 2.7.5 → 2.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/agents/dgs-executor.md +0 -52
  3. package/deliver-great-systems/bin/dgs-tools.cjs +66 -10
  4. package/deliver-great-systems/bin/lib/commands.cjs +1 -8
  5. package/deliver-great-systems/bin/lib/config.cjs +9 -90
  6. package/deliver-great-systems/bin/lib/context.cjs +2 -2
  7. package/deliver-great-systems/bin/lib/context.test.cjs +100 -100
  8. package/deliver-great-systems/bin/lib/core.cjs +17 -57
  9. package/deliver-great-systems/bin/lib/core.test.cjs +166 -170
  10. package/deliver-great-systems/bin/lib/docs.cjs +3 -3
  11. package/deliver-great-systems/bin/lib/docs.test.cjs +14 -7
  12. package/deliver-great-systems/bin/lib/execution.cjs +2 -2
  13. package/deliver-great-systems/bin/lib/execution.test.cjs +65 -67
  14. package/deliver-great-systems/bin/lib/ideas.cjs +4 -4
  15. package/deliver-great-systems/bin/lib/ideas.test.cjs +45 -44
  16. package/deliver-great-systems/bin/lib/init.cjs +9 -4
  17. package/deliver-great-systems/bin/lib/init.test.cjs +242 -175
  18. package/deliver-great-systems/bin/lib/jobs.cjs +1 -1
  19. package/deliver-great-systems/bin/lib/jobs.test.cjs +203 -202
  20. package/deliver-great-systems/bin/lib/migration.cjs +256 -281
  21. package/deliver-great-systems/bin/lib/migration.test.cjs +385 -440
  22. package/deliver-great-systems/bin/lib/milestone.cjs +1 -1
  23. package/deliver-great-systems/bin/lib/overlap.cjs +4 -4
  24. package/deliver-great-systems/bin/lib/overlap.test.cjs +45 -44
  25. package/deliver-great-systems/bin/lib/path-audit.test.cjs +16 -22
  26. package/deliver-great-systems/bin/lib/paths.cjs +60 -59
  27. package/deliver-great-systems/bin/lib/paths.test.cjs +192 -225
  28. package/deliver-great-systems/bin/lib/phase.cjs +5 -4
  29. package/deliver-great-systems/bin/lib/projects.cjs +8 -8
  30. package/deliver-great-systems/bin/lib/projects.test.cjs +75 -74
  31. package/deliver-great-systems/bin/lib/repos.cjs +94 -230
  32. package/deliver-great-systems/bin/lib/repos.test.cjs +84 -75
  33. package/deliver-great-systems/bin/lib/search.cjs +4 -4
  34. package/deliver-great-systems/bin/lib/specs.cjs +2 -2
  35. package/deliver-great-systems/bin/lib/sync.cjs +1 -1
  36. package/deliver-great-systems/bin/lib/template.cjs +3 -3
  37. package/deliver-great-systems/bin/lib/test-helpers.cjs +59 -162
  38. package/deliver-great-systems/bin/lib/verify.cjs +3 -3
  39. package/deliver-great-systems/references/planning-config.md +7 -8
  40. package/deliver-great-systems/workflows/add-tests.md +1 -1
  41. package/deliver-great-systems/workflows/approve-spec.md +1 -11
  42. package/deliver-great-systems/workflows/complete-milestone.md +2 -2
  43. package/deliver-great-systems/workflows/consolidate-ideas.md +1 -1
  44. package/deliver-great-systems/workflows/create-milestone-job.md +2 -2
  45. package/deliver-great-systems/workflows/discuss-phase.md +2 -2
  46. package/deliver-great-systems/workflows/execute-phase.md +63 -4
  47. package/deliver-great-systems/workflows/execute-plan.md +0 -51
  48. package/deliver-great-systems/workflows/find-related-ideas.md +1 -1
  49. package/deliver-great-systems/workflows/help.md +25 -58
  50. package/deliver-great-systems/workflows/init-product.md +14 -451
  51. package/deliver-great-systems/workflows/map-codebase.md +109 -0
  52. package/deliver-great-systems/workflows/new-project.md +0 -1
  53. package/deliver-great-systems/workflows/quick.md +2 -2
  54. package/deliver-great-systems/workflows/run-job.md +56 -0
  55. package/package.json +1 -1
@@ -8,7 +8,8 @@ const assert = require('node:assert');
8
8
  const fs = require('fs');
9
9
  const path = require('path');
10
10
  const { execSync } = require('child_process');
11
- const { createTempDir, cleanupDir, createTempProject } = require('./test-helpers.cjs');
11
+ const { createTempDir, cleanupDir, createTempProject, initGitRepo } = require('./test-helpers.cjs');
12
+ const { resetPaths } = require('./paths.cjs');
12
13
  const { findIdeaFile, parseIdeaFrontmatter, ensureIdeasDirs, cmdIdeasCreate, loadManifest, IDEA_STATES, buildIdeaContent, cmdIdeasConsolidate, cmdIdeasFindRelated, cmdIdeasUndoConsolidation } = require('./ideas.cjs');
13
14
 
14
15
  // ─── Helpers ──────────────────────────────────────────────────────────────────
@@ -26,7 +27,7 @@ function createIdeaInState(cwd, state, id, title) {
26
27
  const paddedId = String(id).padStart(3, '0');
27
28
  const filename = `${paddedId}-${slug}.md`;
28
29
  const content = `---\nid: ${id}\ntitle: "${title}"\n---\n\nIdea body.\n`;
29
- const dir = path.join(cwd, '.planning', 'ideas', state);
30
+ const dir = path.join(cwd, 'ideas', state);
30
31
  fs.mkdirSync(dir, { recursive: true });
31
32
  fs.writeFileSync(path.join(dir, filename), content, 'utf-8');
32
33
  return filename;
@@ -38,11 +39,11 @@ describe('findIdeaFile state detection', () => {
38
39
  let tmpDir;
39
40
 
40
41
  beforeEach(() => {
41
- tmpDir = createTempDir('ideas-test-');
42
+ tmpDir = createTempDir('ideas-test-'); tmpDir = fs.realpathSync(tmpDir); initGitRepo(tmpDir);
42
43
  ensureIdeasDirs(tmpDir);
43
44
  });
44
45
 
45
- afterEach(() => cleanupDir(tmpDir));
46
+ afterEach(() => { resetPaths(); cleanupDir(tmpDir); });
46
47
 
47
48
  it('returns state "pending" for idea in pending directory', () => {
48
49
  createIdeaInState(tmpDir, 'pending', 1, 'Test Pending');
@@ -95,11 +96,11 @@ describe('consolidated state', () => {
95
96
  let tmpDir;
96
97
 
97
98
  beforeEach(() => {
98
- tmpDir = createTempDir('ideas-consolidated-test-');
99
+ tmpDir = createTempDir('ideas-consolidated-test-'); tmpDir = fs.realpathSync(tmpDir); initGitRepo(tmpDir);
99
100
  ensureIdeasDirs(tmpDir);
100
101
  });
101
102
 
102
- afterEach(() => cleanupDir(tmpDir));
103
+ afterEach(() => { resetPaths(); cleanupDir(tmpDir); });
103
104
 
104
105
  it('IDEA_STATES includes consolidated', () => {
105
106
  assert.ok(IDEA_STATES.includes('consolidated'), 'IDEA_STATES should include "consolidated"');
@@ -107,7 +108,7 @@ describe('consolidated state', () => {
107
108
  });
108
109
 
109
110
  it('ensureIdeasDirs creates consolidated/ directory', () => {
110
- const consolidatedDir = path.join(tmpDir, '.planning', 'ideas', 'consolidated');
111
+ const consolidatedDir = path.join(tmpDir, 'ideas', 'consolidated');
111
112
  assert.ok(fs.existsSync(consolidatedDir), 'consolidated/ directory should exist');
112
113
  });
113
114
 
@@ -477,11 +478,11 @@ describe('cmdIdeasReject guard messages', () => {
477
478
  let tmpDir;
478
479
 
479
480
  beforeEach(() => {
480
- tmpDir = createTempDir('ideas-reject-test-');
481
+ tmpDir = createTempDir('ideas-reject-test-'); tmpDir = fs.realpathSync(tmpDir); initGitRepo(tmpDir);
481
482
  ensureIdeasDirs(tmpDir);
482
483
  });
483
484
 
484
- afterEach(() => cleanupDir(tmpDir));
485
+ afterEach(() => { resetPaths(); cleanupDir(tmpDir); });
485
486
 
486
487
  it('errors with "already rejected" when rejecting a rejected idea', () => {
487
488
  createIdeaInState(tmpDir, 'rejected', 1, 'Already Rejected');
@@ -543,8 +544,8 @@ describe('ideas root layout', () => {
543
544
  let fixture;
544
545
  afterEach(() => { if (fixture) fixture.cleanup(); });
545
546
 
546
- it('ensureIdeasDirs creates directories at root (not under .planning/)', () => {
547
- fixture = createTempProject({ layout: 'root' });
547
+ it('ensureIdeasDirs creates directories at root (at root)', () => {
548
+ fixture = createTempProject();
548
549
  ensureIdeasDirs(fixture.cwd);
549
550
  assert.ok(fs.existsSync(path.join(fixture.cwd, 'ideas', 'pending')),
550
551
  'ideas/pending should exist at root');
@@ -554,12 +555,12 @@ describe('ideas root layout', () => {
554
555
  'ideas/rejected should exist at root');
555
556
  assert.ok(fs.existsSync(path.join(fixture.cwd, 'ideas', 'consolidated')),
556
557
  'ideas/consolidated should exist at root');
557
- assert.ok(!fs.existsSync(path.join(fixture.cwd, '.planning', 'ideas')),
558
- '.planning/ideas should NOT exist in root layout');
558
+ assert.ok(!fs.existsSync(path.join(fixture.cwd, 'ideas')),
559
+ 'ideas should NOT exist in root layout');
559
560
  });
560
561
 
561
562
  it('findIdeaFile resolves ideas at root in root layout', () => {
562
- fixture = createTempProject({ layout: 'root' });
563
+ fixture = createTempProject();
563
564
  // Create an idea directly at root/ideas/pending/
564
565
  const ideasDir = path.join(fixture.cwd, 'ideas', 'pending');
565
566
  fs.mkdirSync(ideasDir, { recursive: true });
@@ -573,7 +574,7 @@ describe('ideas root layout', () => {
573
574
  });
574
575
 
575
576
  it('loadManifest reads from root/ideas/manifest.json in root layout', () => {
576
- fixture = createTempProject({ layout: 'root' });
577
+ fixture = createTempProject();
577
578
  const ideasDir = path.join(fixture.cwd, 'ideas');
578
579
  fs.mkdirSync(ideasDir, { recursive: true });
579
580
  fs.writeFileSync(path.join(ideasDir, 'manifest.json'), '{"next_id": 5}\n', 'utf-8');
@@ -598,11 +599,11 @@ describe('cmdIdeasConsolidate', () => {
598
599
  const now = new Date().toISOString();
599
600
  const tagsStr = tags.length > 0 ? `[${tags.map(t => `"${t}"`).join(', ')}]` : '[]';
600
601
  const content = `---\nid: ${id}\ntitle: "${title}"\ntags: ${tagsStr}\ncreated: ${now}\nupdated: ${now}\n---\n\n${title} body.\n`;
601
- const dir2 = path.join(dir, '.planning', 'ideas', 'pending');
602
+ const dir2 = path.join(dir, 'ideas', 'pending');
602
603
  fs.mkdirSync(dir2, { recursive: true });
603
604
  fs.writeFileSync(path.join(dir2, filename), content, 'utf-8');
604
605
  // Update manifest so allocateId returns next ID correctly
605
- const manifestDir = path.join(dir, '.planning', 'ideas');
606
+ const manifestDir = path.join(dir, 'ideas');
606
607
  const manifestPath = path.join(manifestDir, 'manifest.json');
607
608
  let manifest;
608
609
  try {
@@ -626,10 +627,10 @@ describe('cmdIdeasConsolidate', () => {
626
627
  const filename = `${paddedId}-${slug}.md`;
627
628
  const now = new Date().toISOString();
628
629
  const content = `---\nid: ${id}\ntitle: "${title}"\ntags: []\ncreated: ${now}\nupdated: ${now}\n---\n\n${title} body.\n`;
629
- const stateDir = path.join(dir, '.planning', 'ideas', state);
630
+ const stateDir = path.join(dir, 'ideas', state);
630
631
  fs.mkdirSync(stateDir, { recursive: true });
631
632
  fs.writeFileSync(path.join(stateDir, filename), content, 'utf-8');
632
- const manifestDir = path.join(dir, '.planning', 'ideas');
633
+ const manifestDir = path.join(dir, 'ideas');
633
634
  const manifestPath = path.join(manifestDir, 'manifest.json');
634
635
  let manifest;
635
636
  try {
@@ -652,7 +653,7 @@ describe('cmdIdeasConsolidate', () => {
652
653
  execSync('git config user.email "test@test.com"', { cwd: dir, stdio: 'pipe' });
653
654
  execSync('git config user.name "Test"', { cwd: dir, stdio: 'pipe' });
654
655
  for (const state of ['pending', 'done', 'rejected', 'consolidated']) {
655
- const stateDir = path.join(dir, '.planning', 'ideas', state);
656
+ const stateDir = path.join(dir, 'ideas', state);
656
657
  fs.mkdirSync(stateDir, { recursive: true });
657
658
  fs.writeFileSync(path.join(stateDir, '.gitkeep'), '', 'utf-8');
658
659
  }
@@ -660,10 +661,10 @@ describe('cmdIdeasConsolidate', () => {
660
661
  }
661
662
 
662
663
  beforeEach(() => {
663
- tmpDir = createTempDir('ideas-consolidate-test-');
664
+ tmpDir = createTempDir('ideas-consolidate-test-'); tmpDir = fs.realpathSync(tmpDir); initGitRepo(tmpDir);
664
665
  });
665
666
 
666
- afterEach(() => cleanupDir(tmpDir));
667
+ afterEach(() => { resetPaths(); cleanupDir(tmpDir); });
667
668
 
668
669
  it('consolidates 2 pending ideas into new idea', () => {
669
670
  initLocalGitRepo(tmpDir);
@@ -686,8 +687,8 @@ describe('cmdIdeasConsolidate', () => {
686
687
  assert.strictEqual(json.moved_files.length, 2);
687
688
 
688
689
  // Source ideas moved to consolidated/
689
- const consolidatedDir = path.join(tmpDir, '.planning', 'ideas', 'consolidated');
690
- const pendingDir = path.join(tmpDir, '.planning', 'ideas', 'pending');
690
+ const consolidatedDir = path.join(tmpDir, 'ideas', 'consolidated');
691
+ const pendingDir = path.join(tmpDir, 'ideas', 'pending');
691
692
  const consolidatedFiles = fs.readdirSync(consolidatedDir).filter(f => f.endsWith('.md'));
692
693
  assert.strictEqual(consolidatedFiles.length, 2);
693
694
 
@@ -795,7 +796,7 @@ describe('cmdIdeasConsolidate', () => {
795
796
  const json = JSON.parse(result);
796
797
 
797
798
  // Read the new idea file to check tags
798
- const newIdeaPath = path.join(tmpDir, '.planning', 'ideas', 'pending', json.filename);
799
+ const newIdeaPath = path.join(tmpDir, 'ideas', 'pending', json.filename);
799
800
  const content = fs.readFileSync(newIdeaPath, 'utf-8');
800
801
  const parsed = parseIdeaFrontmatter(content);
801
802
  const tags = parsed.frontmatter.tags;
@@ -843,7 +844,7 @@ describe('cmdIdeasConsolidate', () => {
843
844
  );
844
845
  const json = JSON.parse(result);
845
846
 
846
- const newIdeaPath = path.join(tmpDir, '.planning', 'ideas', 'pending', json.filename);
847
+ const newIdeaPath = path.join(tmpDir, 'ideas', 'pending', json.filename);
847
848
  const content = fs.readFileSync(newIdeaPath, 'utf-8');
848
849
  assert.ok(content.includes('## Discussion Log'), 'should have Discussion Log section');
849
850
  assert.ok(content.includes('## Research Log'), 'should have Research Log section');
@@ -862,7 +863,7 @@ describe('cmdIdeasConsolidate', () => {
862
863
  );
863
864
 
864
865
  // Check sources in consolidated/ have consolidation notes
865
- const consolidatedDir = path.join(tmpDir, '.planning', 'ideas', 'consolidated');
866
+ const consolidatedDir = path.join(tmpDir, 'ideas', 'consolidated');
866
867
  const files = fs.readdirSync(consolidatedDir).filter(f => f.endsWith('.md'));
867
868
  for (const f of files) {
868
869
  const content = fs.readFileSync(path.join(consolidatedDir, f), 'utf-8');
@@ -924,7 +925,7 @@ describe('cmdIdeasConsolidate', () => {
924
925
  assert.strictEqual(json.moved_files.length, 3);
925
926
 
926
927
  // Check tags on new idea
927
- const newIdeaPath = path.join(tmpDir, '.planning', 'ideas', 'pending', json.filename);
928
+ const newIdeaPath = path.join(tmpDir, 'ideas', 'pending', json.filename);
928
929
  const content = fs.readFileSync(newIdeaPath, 'utf-8');
929
930
  const parsed = parseIdeaFrontmatter(content);
930
931
  assert.ok(parsed.frontmatter.tags.includes('api'));
@@ -949,11 +950,11 @@ describe('cmdIdeasFindRelated', () => {
949
950
  const now = new Date().toISOString();
950
951
  const tagsStr = tags.length > 0 ? `[${tags.map(t => `"${t}"`).join(', ')}]` : '[]';
951
952
  const content = `---\nid: ${id}\ntitle: "${title}"\ntags: ${tagsStr}\ncreated: ${now}\nupdated: ${now}\n---\n\n${body || title + ' body.'}\n`;
952
- const dir2 = path.join(dir, '.planning', 'ideas', 'pending');
953
+ const dir2 = path.join(dir, 'ideas', 'pending');
953
954
  fs.mkdirSync(dir2, { recursive: true });
954
955
  fs.writeFileSync(path.join(dir2, filename), content, 'utf-8');
955
956
  // Update manifest so allocateId returns next ID correctly
956
- const manifestDir = path.join(dir, '.planning', 'ideas');
957
+ const manifestDir = path.join(dir, 'ideas');
957
958
  const manifestPath = path.join(manifestDir, 'manifest.json');
958
959
  let manifest;
959
960
  try {
@@ -978,10 +979,10 @@ describe('cmdIdeasFindRelated', () => {
978
979
  const now = new Date().toISOString();
979
980
  const tagsStr = tags.length > 0 ? `[${tags.map(t => `"${t}"`).join(', ')}]` : '[]';
980
981
  const content = `---\nid: ${id}\ntitle: "${title}"\ntags: ${tagsStr}\ncreated: ${now}\nupdated: ${now}\n---\n\n${title} body.\n`;
981
- const stateDir = path.join(dir, '.planning', 'ideas', state);
982
+ const stateDir = path.join(dir, 'ideas', state);
982
983
  fs.mkdirSync(stateDir, { recursive: true });
983
984
  fs.writeFileSync(path.join(stateDir, filename), content, 'utf-8');
984
- const manifestDir = path.join(dir, '.planning', 'ideas');
985
+ const manifestDir = path.join(dir, 'ideas');
985
986
  const manifestPath = path.join(manifestDir, 'manifest.json');
986
987
  let manifest;
987
988
  try {
@@ -1014,11 +1015,11 @@ describe('cmdIdeasFindRelated', () => {
1014
1015
  }
1015
1016
 
1016
1017
  beforeEach(() => {
1017
- tmpDir = createTempDir('ideas-find-related-test-');
1018
+ tmpDir = createTempDir('ideas-find-related-test-'); tmpDir = fs.realpathSync(tmpDir); initGitRepo(tmpDir);
1018
1019
  ensureIdeasDirs(tmpDir);
1019
1020
  });
1020
1021
 
1021
- afterEach(() => cleanupDir(tmpDir));
1022
+ afterEach(() => { resetPaths(); cleanupDir(tmpDir); });
1022
1023
 
1023
1024
  it('returns empty results when only the anchor idea exists', () => {
1024
1025
  createPendingIdea(tmpDir, 1, 'Solo idea', ['api', 'reliability']);
@@ -1184,11 +1185,11 @@ describe('cmdIdeasUndoConsolidation', () => {
1184
1185
  const now = new Date().toISOString();
1185
1186
  const tagsStr = tags.length > 0 ? `[${tags.map(t => `"${t}"`).join(', ')}]` : '[]';
1186
1187
  const content = `---\nid: ${id}\ntitle: "${title}"\ntags: ${tagsStr}\ncreated: ${now}\nupdated: ${now}\n---\n\n${title} body.\n`;
1187
- const dir2 = path.join(dir, '.planning', 'ideas', 'pending');
1188
+ const dir2 = path.join(dir, 'ideas', 'pending');
1188
1189
  fs.mkdirSync(dir2, { recursive: true });
1189
1190
  fs.writeFileSync(path.join(dir2, filename), content, 'utf-8');
1190
1191
  // Update manifest so allocateId returns next ID correctly
1191
- const manifestDir = path.join(dir, '.planning', 'ideas');
1192
+ const manifestDir = path.join(dir, 'ideas');
1192
1193
  const manifestPath = path.join(manifestDir, 'manifest.json');
1193
1194
  let manifest;
1194
1195
  try {
@@ -1211,7 +1212,7 @@ describe('cmdIdeasUndoConsolidation', () => {
1211
1212
  execSync('git config user.email "test@test.com"', { cwd: dir, stdio: 'pipe' });
1212
1213
  execSync('git config user.name "Test"', { cwd: dir, stdio: 'pipe' });
1213
1214
  for (const state of ['pending', 'done', 'rejected', 'consolidated']) {
1214
- const stateDir = path.join(dir, '.planning', 'ideas', state);
1215
+ const stateDir = path.join(dir, 'ideas', state);
1215
1216
  fs.mkdirSync(stateDir, { recursive: true });
1216
1217
  fs.writeFileSync(path.join(stateDir, '.gitkeep'), '', 'utf-8');
1217
1218
  }
@@ -1242,10 +1243,10 @@ describe('cmdIdeasUndoConsolidation', () => {
1242
1243
  }
1243
1244
 
1244
1245
  beforeEach(() => {
1245
- tmpDir = createTempDir('ideas-undo-consolidation-test-');
1246
+ tmpDir = createTempDir('ideas-undo-consolidation-test-'); tmpDir = fs.realpathSync(tmpDir); initGitRepo(tmpDir);
1246
1247
  });
1247
1248
 
1248
- afterEach(() => cleanupDir(tmpDir));
1249
+ afterEach(() => { resetPaths(); cleanupDir(tmpDir); });
1249
1250
 
1250
1251
  it('undoes consolidation and restores source ideas to pending', () => {
1251
1252
  initLocalGitRepo(tmpDir);
@@ -1260,7 +1261,7 @@ describe('cmdIdeasUndoConsolidation', () => {
1260
1261
  undoViaSubprocess(tmpDir, '3');
1261
1262
 
1262
1263
  // Verify: idea 3 no longer exists in pending/
1263
- const pendingDir = path.join(tmpDir, '.planning', 'ideas', 'pending');
1264
+ const pendingDir = path.join(tmpDir, 'ideas', 'pending');
1264
1265
  const pendingFiles = fs.readdirSync(pendingDir).filter(f => f.endsWith('.md'));
1265
1266
  assert.ok(!pendingFiles.some(f => f.startsWith('003')), 'Consolidated idea 3 should be deleted from pending');
1266
1267
 
@@ -1269,7 +1270,7 @@ describe('cmdIdeasUndoConsolidation', () => {
1269
1270
  assert.ok(pendingFiles.some(f => f.startsWith('002')), 'Idea 2 should be restored to pending');
1270
1271
 
1271
1272
  // Verify: consolidated/ has no idea files
1272
- const consolidatedDir = path.join(tmpDir, '.planning', 'ideas', 'consolidated');
1273
+ const consolidatedDir = path.join(tmpDir, 'ideas', 'consolidated');
1273
1274
  const consolidatedFiles = fs.readdirSync(consolidatedDir).filter(f => f.endsWith('.md'));
1274
1275
  assert.strictEqual(consolidatedFiles.length, 0, 'consolidated/ should have no idea files');
1275
1276
  });
@@ -1284,7 +1285,7 @@ describe('cmdIdeasUndoConsolidation', () => {
1284
1285
  undoViaSubprocess(tmpDir, '3');
1285
1286
 
1286
1287
  // Read each restored source idea and check consolidated_into is gone
1287
- const pendingDir = path.join(tmpDir, '.planning', 'ideas', 'pending');
1288
+ const pendingDir = path.join(tmpDir, 'ideas', 'pending');
1288
1289
  const files = fs.readdirSync(pendingDir).filter(f => f.endsWith('.md'));
1289
1290
  for (const f of files) {
1290
1291
  const content = fs.readFileSync(path.join(pendingDir, f), 'utf-8');
@@ -1304,7 +1305,7 @@ describe('cmdIdeasUndoConsolidation', () => {
1304
1305
  undoViaSubprocess(tmpDir, '3');
1305
1306
 
1306
1307
  // Restored source ideas should NOT have consolidated_from (they never had it)
1307
- const pendingDir = path.join(tmpDir, '.planning', 'ideas', 'pending');
1308
+ const pendingDir = path.join(tmpDir, 'ideas', 'pending');
1308
1309
  const files = fs.readdirSync(pendingDir).filter(f => f.endsWith('.md'));
1309
1310
  for (const f of files) {
1310
1311
  const content = fs.readFileSync(path.join(pendingDir, f), 'utf-8');
@@ -1401,7 +1402,7 @@ describe('cmdIdeasUndoConsolidation', () => {
1401
1402
  const json = JSON.parse(result);
1402
1403
 
1403
1404
  // All 3 source ideas should be restored to pending
1404
- const pendingDir = path.join(tmpDir, '.planning', 'ideas', 'pending');
1405
+ const pendingDir = path.join(tmpDir, 'ideas', 'pending');
1405
1406
  const pendingFiles = fs.readdirSync(pendingDir).filter(f => f.endsWith('.md'));
1406
1407
  assert.ok(pendingFiles.some(f => f.startsWith('001')), 'Idea 1 should be restored');
1407
1408
  assert.ok(pendingFiles.some(f => f.startsWith('002')), 'Idea 2 should be restored');
@@ -190,7 +190,8 @@ function cmdInitExecutePhase(cwd, phase, raw) {
190
190
 
191
191
  const config = loadConfig(cwd);
192
192
  const ctx = resolveProjectContext(cwd);
193
- const phaseInfo = findPhaseInternal(cwd, phase);
193
+ let phaseInfo = findPhaseInternal(cwd, phase);
194
+ if (phaseInfo?.archived) phaseInfo = null;
194
195
  const milestone = getMilestoneInfo(cwd);
195
196
  const planRootRel = path.relative(cwd, getPlanningRoot(cwd)) || '.';
196
197
 
@@ -304,7 +305,8 @@ function cmdInitPlanPhase(cwd, phase, raw) {
304
305
 
305
306
  const config = loadConfig(cwd);
306
307
  const ctx = resolveProjectContext(cwd);
307
- const phaseInfo = findPhaseInternal(cwd, phase);
308
+ let phaseInfo = findPhaseInternal(cwd, phase);
309
+ if (phaseInfo?.archived) phaseInfo = null;
308
310
  const planRootRel = path.relative(cwd, getPlanningRoot(cwd)) || '.';
309
311
 
310
312
  const roadmapPhase = getRoadmapPhaseInternal(cwd, phase);
@@ -651,7 +653,8 @@ function cmdInitVerifyWork(cwd, phase, raw) {
651
653
 
652
654
  const config = loadConfig(cwd);
653
655
  const ctx = resolveProjectContext(cwd);
654
- const phaseInfo = findPhaseInternal(cwd, phase);
656
+ let phaseInfo = findPhaseInternal(cwd, phase);
657
+ if (phaseInfo?.archived) phaseInfo = null;
655
658
 
656
659
  const result = {
657
660
  // Models
@@ -695,7 +698,8 @@ function cmdInitAuditPhase(cwd, phase, raw) {
695
698
 
696
699
  const config = loadConfig(cwd);
697
700
  const ctx = resolveProjectContext(cwd);
698
- const phaseInfo = findPhaseInternal(cwd, phase);
701
+ let phaseInfo = findPhaseInternal(cwd, phase);
702
+ if (phaseInfo?.archived) phaseInfo = null;
699
703
 
700
704
  const result = {
701
705
  // Models
@@ -737,6 +741,7 @@ function cmdInitPhaseOp(cwd, phase, raw, workflow) {
737
741
  const ctx = resolveProjectContext(cwd);
738
742
  const planRootRel = path.relative(cwd, getPlanningRoot(cwd)) || '.';
739
743
  let phaseInfo = findPhaseInternal(cwd, phase);
744
+ if (phaseInfo?.archived) phaseInfo = null;
740
745
  const cadence = getCadence(workflow || 'plan-phase');
741
746
 
742
747
  // Fallback to ROADMAP.md if no directory exists (e.g., Plans: TBD)