@ktpartners/dgs-platform 2.7.4 → 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.
- package/CHANGELOG.md +25 -0
- package/agents/dgs-executor.md +0 -52
- package/deliver-great-systems/bin/dgs-tools.cjs +66 -10
- package/deliver-great-systems/bin/lib/commands.cjs +1 -8
- package/deliver-great-systems/bin/lib/config.cjs +9 -90
- package/deliver-great-systems/bin/lib/context.cjs +2 -2
- package/deliver-great-systems/bin/lib/context.test.cjs +100 -100
- package/deliver-great-systems/bin/lib/core.cjs +17 -57
- package/deliver-great-systems/bin/lib/core.test.cjs +166 -170
- package/deliver-great-systems/bin/lib/docs.cjs +3 -3
- package/deliver-great-systems/bin/lib/docs.test.cjs +14 -7
- package/deliver-great-systems/bin/lib/execution.cjs +2 -2
- package/deliver-great-systems/bin/lib/execution.test.cjs +65 -67
- package/deliver-great-systems/bin/lib/ideas.cjs +4 -4
- package/deliver-great-systems/bin/lib/ideas.test.cjs +45 -44
- package/deliver-great-systems/bin/lib/init.cjs +9 -4
- package/deliver-great-systems/bin/lib/init.test.cjs +242 -175
- package/deliver-great-systems/bin/lib/jobs.cjs +1 -1
- package/deliver-great-systems/bin/lib/jobs.test.cjs +203 -202
- package/deliver-great-systems/bin/lib/migration.cjs +256 -281
- package/deliver-great-systems/bin/lib/migration.test.cjs +385 -440
- package/deliver-great-systems/bin/lib/milestone.cjs +1 -1
- package/deliver-great-systems/bin/lib/overlap.cjs +4 -4
- package/deliver-great-systems/bin/lib/overlap.test.cjs +45 -44
- package/deliver-great-systems/bin/lib/path-audit.test.cjs +16 -22
- package/deliver-great-systems/bin/lib/paths.cjs +60 -59
- package/deliver-great-systems/bin/lib/paths.test.cjs +192 -225
- package/deliver-great-systems/bin/lib/phase.cjs +5 -4
- package/deliver-great-systems/bin/lib/projects.cjs +8 -8
- package/deliver-great-systems/bin/lib/projects.test.cjs +75 -74
- package/deliver-great-systems/bin/lib/repos.cjs +94 -230
- package/deliver-great-systems/bin/lib/repos.test.cjs +84 -75
- package/deliver-great-systems/bin/lib/search.cjs +4 -4
- package/deliver-great-systems/bin/lib/specs.cjs +2 -2
- package/deliver-great-systems/bin/lib/sync.cjs +1 -1
- package/deliver-great-systems/bin/lib/template.cjs +3 -3
- package/deliver-great-systems/bin/lib/test-helpers.cjs +59 -162
- package/deliver-great-systems/bin/lib/verify.cjs +3 -3
- package/deliver-great-systems/references/planning-config.md +7 -8
- package/deliver-great-systems/workflows/add-tests.md +1 -1
- package/deliver-great-systems/workflows/approve-spec.md +1 -11
- package/deliver-great-systems/workflows/complete-milestone.md +2 -2
- package/deliver-great-systems/workflows/consolidate-ideas.md +1 -1
- package/deliver-great-systems/workflows/create-milestone-job.md +2 -2
- package/deliver-great-systems/workflows/discuss-phase.md +2 -2
- package/deliver-great-systems/workflows/execute-phase.md +63 -4
- package/deliver-great-systems/workflows/execute-plan.md +0 -51
- package/deliver-great-systems/workflows/find-related-ideas.md +1 -1
- package/deliver-great-systems/workflows/help.md +25 -58
- package/deliver-great-systems/workflows/init-product.md +14 -451
- package/deliver-great-systems/workflows/map-codebase.md +109 -0
- package/deliver-great-systems/workflows/new-project.md +0 -1
- package/deliver-great-systems/workflows/quick.md +6 -7
- package/deliver-great-systems/workflows/run-job.md +56 -0
- package/deliver-great-systems/workflows/settings.md +30 -0
- package/package.json +5 -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, '
|
|
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, '
|
|
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 (
|
|
547
|
-
fixture = createTempProject(
|
|
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, '
|
|
558
|
-
'
|
|
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(
|
|
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(
|
|
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, '
|
|
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, '
|
|
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, '
|
|
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, '
|
|
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, '
|
|
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, '
|
|
690
|
-
const pendingDir = path.join(tmpDir, '
|
|
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, '
|
|
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, '
|
|
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, '
|
|
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, '
|
|
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, '
|
|
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, '
|
|
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, '
|
|
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, '
|
|
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, '
|
|
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, '
|
|
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, '
|
|
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, '
|
|
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, '
|
|
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, '
|
|
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, '
|
|
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, '
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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)
|