@ktpartners/dgs-platform 2.6.2
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/LICENSE +38 -0
- package/README.md +851 -0
- package/agents/dgs-codebase-cross-analyzer.md +183 -0
- package/agents/dgs-codebase-mapper.md +782 -0
- package/agents/dgs-codebase-synthesizer.md +156 -0
- package/agents/dgs-debugger.md +1256 -0
- package/agents/dgs-executor.md +550 -0
- package/agents/dgs-integration-checker.md +481 -0
- package/agents/dgs-nyquist-auditor.md +178 -0
- package/agents/dgs-phase-researcher.md +563 -0
- package/agents/dgs-phase-verifier.md +450 -0
- package/agents/dgs-plan-checker.md +708 -0
- package/agents/dgs-planner.md +1324 -0
- package/agents/dgs-project-researcher.md +631 -0
- package/agents/dgs-research-synthesizer.md +249 -0
- package/agents/dgs-roadmapper.md +652 -0
- package/agents/dgs-verifier.md +607 -0
- package/bin/install.js +2073 -0
- package/commands/dgs/add-doc.md +45 -0
- package/commands/dgs/add-idea.md +38 -0
- package/commands/dgs/add-phase.md +43 -0
- package/commands/dgs/add-repo.md +54 -0
- package/commands/dgs/add-tests.md +41 -0
- package/commands/dgs/add-todo.md +47 -0
- package/commands/dgs/approve-spec.md +38 -0
- package/commands/dgs/audit-milestone.md +36 -0
- package/commands/dgs/audit-phase.md +37 -0
- package/commands/dgs/cancel-job.md +23 -0
- package/commands/dgs/capture-principle.md +143 -0
- package/commands/dgs/check-todos.md +45 -0
- package/commands/dgs/cleanup.md +18 -0
- package/commands/dgs/complete-milestone.md +136 -0
- package/commands/dgs/complete-project.md +70 -0
- package/commands/dgs/consolidate-ideas.md +50 -0
- package/commands/dgs/create-milestone-job.md +37 -0
- package/commands/dgs/debug.md +164 -0
- package/commands/dgs/develop-idea.md +53 -0
- package/commands/dgs/discuss-idea.md +41 -0
- package/commands/dgs/discuss-phase.md +83 -0
- package/commands/dgs/execute-phase.md +41 -0
- package/commands/dgs/fast.md +38 -0
- package/commands/dgs/find-related-ideas.md +43 -0
- package/commands/dgs/health.md +28 -0
- package/commands/dgs/help.md +22 -0
- package/commands/dgs/import-spec.md +36 -0
- package/commands/dgs/init-product.md +28 -0
- package/commands/dgs/insert-phase.md +32 -0
- package/commands/dgs/join-discord.md +18 -0
- package/commands/dgs/list-docs.md +40 -0
- package/commands/dgs/list-ideas.md +42 -0
- package/commands/dgs/list-jobs.md +22 -0
- package/commands/dgs/list-phase-assumptions.md +46 -0
- package/commands/dgs/list-projects.md +57 -0
- package/commands/dgs/list-specs.md +40 -0
- package/commands/dgs/map-codebase.md +92 -0
- package/commands/dgs/new-milestone.md +44 -0
- package/commands/dgs/new-project.md +42 -0
- package/commands/dgs/node-repair.md +26 -0
- package/commands/dgs/overlap-check.md +20 -0
- package/commands/dgs/pause-work.md +38 -0
- package/commands/dgs/plan-milestone-gaps.md +34 -0
- package/commands/dgs/plan-phase.md +44 -0
- package/commands/dgs/progress.md +24 -0
- package/commands/dgs/quick.md +41 -0
- package/commands/dgs/reactivate-project.md +70 -0
- package/commands/dgs/reapply-patches.md +110 -0
- package/commands/dgs/refine-spec.md +38 -0
- package/commands/dgs/reject-idea.md +43 -0
- package/commands/dgs/remove-doc.md +44 -0
- package/commands/dgs/remove-phase.md +31 -0
- package/commands/dgs/remove-repo.md +69 -0
- package/commands/dgs/research-idea.md +43 -0
- package/commands/dgs/research-phase.md +189 -0
- package/commands/dgs/restore-idea.md +45 -0
- package/commands/dgs/resume-work.md +40 -0
- package/commands/dgs/rollback-job.md +24 -0
- package/commands/dgs/run-job.md +35 -0
- package/commands/dgs/search.md +40 -0
- package/commands/dgs/set-profile.md +34 -0
- package/commands/dgs/settings.md +38 -0
- package/commands/dgs/switch-project.md +58 -0
- package/commands/dgs/undo-consolidation.md +42 -0
- package/commands/dgs/update-idea.md +44 -0
- package/commands/dgs/update.md +37 -0
- package/commands/dgs/validate-phase.md +35 -0
- package/commands/dgs/verify-work.md +39 -0
- package/commands/dgs/write-spec.md +49 -0
- package/deliver-great-systems/.planning/phases/09-backend-wiring-and-error-handling/09-01-SUMMARY.md +84 -0
- package/deliver-great-systems/.planning/phases/09-backend-wiring-and-error-handling/09-02-SUMMARY.md +86 -0
- package/deliver-great-systems/.planning/phases/10-v1-to-v2-migration-flow/10-01-SUMMARY.md +85 -0
- package/deliver-great-systems/bin/dgs-tools.cjs +1444 -0
- package/deliver-great-systems/bin/lib/auto-test.cjs +1365 -0
- package/deliver-great-systems/bin/lib/commands.cjs +570 -0
- package/deliver-great-systems/bin/lib/config.cjs +417 -0
- package/deliver-great-systems/bin/lib/conflict-agent.cjs +1063 -0
- package/deliver-great-systems/bin/lib/conflict-agent.test.cjs +554 -0
- package/deliver-great-systems/bin/lib/context.cjs +929 -0
- package/deliver-great-systems/bin/lib/context.test.cjs +693 -0
- package/deliver-great-systems/bin/lib/core.cjs +744 -0
- package/deliver-great-systems/bin/lib/core.test.cjs +822 -0
- package/deliver-great-systems/bin/lib/docs.cjs +919 -0
- package/deliver-great-systems/bin/lib/docs.test.cjs +211 -0
- package/deliver-great-systems/bin/lib/execution.cjs +705 -0
- package/deliver-great-systems/bin/lib/execution.test.cjs +1472 -0
- package/deliver-great-systems/bin/lib/frontmatter.cjs +324 -0
- package/deliver-great-systems/bin/lib/ideas.cjs +1406 -0
- package/deliver-great-systems/bin/lib/ideas.test.cjs +1417 -0
- package/deliver-great-systems/bin/lib/identity.cjs +125 -0
- package/deliver-great-systems/bin/lib/init.cjs +1114 -0
- package/deliver-great-systems/bin/lib/init.test.cjs +1271 -0
- package/deliver-great-systems/bin/lib/jobs.cjs +2015 -0
- package/deliver-great-systems/bin/lib/jobs.test.cjs +2619 -0
- package/deliver-great-systems/bin/lib/merge-conflicts.cjs +654 -0
- package/deliver-great-systems/bin/lib/merge-conflicts.test.cjs +370 -0
- package/deliver-great-systems/bin/lib/migration.cjs +352 -0
- package/deliver-great-systems/bin/lib/migration.test.cjs +582 -0
- package/deliver-great-systems/bin/lib/milestone.cjs +243 -0
- package/deliver-great-systems/bin/lib/overlap.cjs +437 -0
- package/deliver-great-systems/bin/lib/overlap.test.cjs +747 -0
- package/deliver-great-systems/bin/lib/path-audit.test.cjs +384 -0
- package/deliver-great-systems/bin/lib/paths.cjs +144 -0
- package/deliver-great-systems/bin/lib/paths.test.cjs +486 -0
- package/deliver-great-systems/bin/lib/phase.cjs +910 -0
- package/deliver-great-systems/bin/lib/projects.cjs +691 -0
- package/deliver-great-systems/bin/lib/projects.test.cjs +871 -0
- package/deliver-great-systems/bin/lib/repos.cjs +1432 -0
- package/deliver-great-systems/bin/lib/repos.test.cjs +1882 -0
- package/deliver-great-systems/bin/lib/roadmap.cjs +305 -0
- package/deliver-great-systems/bin/lib/search.cjs +570 -0
- package/deliver-great-systems/bin/lib/specs.cjs +1303 -0
- package/deliver-great-systems/bin/lib/state.cjs +893 -0
- package/deliver-great-systems/bin/lib/template.cjs +228 -0
- package/deliver-great-systems/bin/lib/test-helpers.cjs +291 -0
- package/deliver-great-systems/bin/lib/verify.cjs +796 -0
- package/deliver-great-systems/references/checkpoints.md +776 -0
- package/deliver-great-systems/references/conflict-resolution.md +66 -0
- package/deliver-great-systems/references/context-tiers.md +166 -0
- package/deliver-great-systems/references/continuation-format.md +249 -0
- package/deliver-great-systems/references/decimal-phase-calculation.md +67 -0
- package/deliver-great-systems/references/git-integration.md +250 -0
- package/deliver-great-systems/references/git-planning-commit.md +40 -0
- package/deliver-great-systems/references/model-profile-resolution.md +36 -0
- package/deliver-great-systems/references/model-profiles.md +95 -0
- package/deliver-great-systems/references/phase-argument-parsing.md +61 -0
- package/deliver-great-systems/references/planning-config.md +224 -0
- package/deliver-great-systems/references/questioning.md +162 -0
- package/deliver-great-systems/references/spec-review-loop.md +177 -0
- package/deliver-great-systems/references/tdd.md +265 -0
- package/deliver-great-systems/references/ui-brand.md +160 -0
- package/deliver-great-systems/references/verification-patterns.md +612 -0
- package/deliver-great-systems/templates/DEBUG.md +166 -0
- package/deliver-great-systems/templates/UAT.md +251 -0
- package/deliver-great-systems/templates/VALIDATION.md +95 -0
- package/deliver-great-systems/templates/claude-md.md +74 -0
- package/deliver-great-systems/templates/codebase/architecture.md +257 -0
- package/deliver-great-systems/templates/codebase/concerns.md +312 -0
- package/deliver-great-systems/templates/codebase/conventions.md +309 -0
- package/deliver-great-systems/templates/codebase/integrations.md +282 -0
- package/deliver-great-systems/templates/codebase/stack.md +188 -0
- package/deliver-great-systems/templates/codebase/structure.md +287 -0
- package/deliver-great-systems/templates/codebase/testing.md +482 -0
- package/deliver-great-systems/templates/config.json +38 -0
- package/deliver-great-systems/templates/context.md +354 -0
- package/deliver-great-systems/templates/continue-here.md +80 -0
- package/deliver-great-systems/templates/debug-subagent-prompt.md +93 -0
- package/deliver-great-systems/templates/discovery.md +148 -0
- package/deliver-great-systems/templates/milestone-archive.md +125 -0
- package/deliver-great-systems/templates/milestone.md +117 -0
- package/deliver-great-systems/templates/phase-prompt.md +615 -0
- package/deliver-great-systems/templates/planner-subagent-prompt.md +119 -0
- package/deliver-great-systems/templates/project.md +186 -0
- package/deliver-great-systems/templates/requirements.md +233 -0
- package/deliver-great-systems/templates/research-project/ARCHITECTURE.md +206 -0
- package/deliver-great-systems/templates/research-project/FEATURES.md +149 -0
- package/deliver-great-systems/templates/research-project/PITFALLS.md +202 -0
- package/deliver-great-systems/templates/research-project/STACK.md +122 -0
- package/deliver-great-systems/templates/research-project/SUMMARY.md +172 -0
- package/deliver-great-systems/templates/research.md +554 -0
- package/deliver-great-systems/templates/retrospective.md +54 -0
- package/deliver-great-systems/templates/roadmap.md +204 -0
- package/deliver-great-systems/templates/state.md +178 -0
- package/deliver-great-systems/templates/summary-complex.md +59 -0
- package/deliver-great-systems/templates/summary-minimal.md +41 -0
- package/deliver-great-systems/templates/summary-standard.md +48 -0
- package/deliver-great-systems/templates/summary.md +253 -0
- package/deliver-great-systems/templates/user-setup.md +313 -0
- package/deliver-great-systems/templates/verification-report.md +324 -0
- package/deliver-great-systems/workflows/add-doc.md +151 -0
- package/deliver-great-systems/workflows/add-idea.md +96 -0
- package/deliver-great-systems/workflows/add-phase.md +120 -0
- package/deliver-great-systems/workflows/add-tests.md +359 -0
- package/deliver-great-systems/workflows/add-todo.md +162 -0
- package/deliver-great-systems/workflows/approve-spec.md +194 -0
- package/deliver-great-systems/workflows/audit-milestone.md +364 -0
- package/deliver-great-systems/workflows/audit-phase.md +462 -0
- package/deliver-great-systems/workflows/cancel-job.md +108 -0
- package/deliver-great-systems/workflows/check-todos.md +181 -0
- package/deliver-great-systems/workflows/cleanup.md +247 -0
- package/deliver-great-systems/workflows/codereview.md +526 -0
- package/deliver-great-systems/workflows/complete-milestone.md +1298 -0
- package/deliver-great-systems/workflows/consolidate-ideas.md +365 -0
- package/deliver-great-systems/workflows/create-milestone-job.md +177 -0
- package/deliver-great-systems/workflows/develop-idea.md +544 -0
- package/deliver-great-systems/workflows/diagnose-issues.md +231 -0
- package/deliver-great-systems/workflows/discovery-phase.md +301 -0
- package/deliver-great-systems/workflows/discuss-idea.md +263 -0
- package/deliver-great-systems/workflows/discuss-phase.md +733 -0
- package/deliver-great-systems/workflows/execute-phase.md +571 -0
- package/deliver-great-systems/workflows/execute-plan.md +592 -0
- package/deliver-great-systems/workflows/find-related-ideas.md +271 -0
- package/deliver-great-systems/workflows/health.md +173 -0
- package/deliver-great-systems/workflows/help.md +997 -0
- package/deliver-great-systems/workflows/import-spec.md +381 -0
- package/deliver-great-systems/workflows/init-product.md +767 -0
- package/deliver-great-systems/workflows/insert-phase.md +138 -0
- package/deliver-great-systems/workflows/list-docs.md +119 -0
- package/deliver-great-systems/workflows/list-ideas.md +154 -0
- package/deliver-great-systems/workflows/list-jobs.md +89 -0
- package/deliver-great-systems/workflows/list-phase-assumptions.md +192 -0
- package/deliver-great-systems/workflows/list-specs.md +101 -0
- package/deliver-great-systems/workflows/map-codebase.md +621 -0
- package/deliver-great-systems/workflows/new-milestone.md +591 -0
- package/deliver-great-systems/workflows/new-project.md +1113 -0
- package/deliver-great-systems/workflows/node-repair.md +94 -0
- package/deliver-great-systems/workflows/overlap-check.md +86 -0
- package/deliver-great-systems/workflows/pause-work.md +134 -0
- package/deliver-great-systems/workflows/plan-milestone-gaps.md +306 -0
- package/deliver-great-systems/workflows/plan-phase.md +698 -0
- package/deliver-great-systems/workflows/progress.md +386 -0
- package/deliver-great-systems/workflows/quick.md +845 -0
- package/deliver-great-systems/workflows/refine-spec.md +275 -0
- package/deliver-great-systems/workflows/reject-idea.md +109 -0
- package/deliver-great-systems/workflows/remove-doc.md +117 -0
- package/deliver-great-systems/workflows/remove-phase.md +163 -0
- package/deliver-great-systems/workflows/research-idea.md +325 -0
- package/deliver-great-systems/workflows/research-phase.md +81 -0
- package/deliver-great-systems/workflows/restore-idea.md +101 -0
- package/deliver-great-systems/workflows/resume-project.md +311 -0
- package/deliver-great-systems/workflows/rollback-job.md +130 -0
- package/deliver-great-systems/workflows/run-job.md +498 -0
- package/deliver-great-systems/workflows/search.md +130 -0
- package/deliver-great-systems/workflows/set-profile.md +83 -0
- package/deliver-great-systems/workflows/settings.md +470 -0
- package/deliver-great-systems/workflows/transition.md +563 -0
- package/deliver-great-systems/workflows/undo-consolidation.md +155 -0
- package/deliver-great-systems/workflows/update-idea.md +157 -0
- package/deliver-great-systems/workflows/update.md +242 -0
- package/deliver-great-systems/workflows/validate-phase.md +177 -0
- package/deliver-great-systems/workflows/verify-phase.md +253 -0
- package/deliver-great-systems/workflows/verify-work.md +671 -0
- package/deliver-great-systems/workflows/write-spec.md +450 -0
- package/hooks/dist/dgs-check-update.js +62 -0
- package/hooks/dist/dgs-context-monitor.js +141 -0
- package/hooks/dist/dgs-statusline.js +115 -0
- package/package.json +60 -0
- package/scripts/build-hooks.js +43 -0
|
@@ -0,0 +1,486 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for paths.cjs — Planning root detection and PATHS singleton
|
|
3
|
+
*
|
|
4
|
+
* Uses Node.js built-in test runner (node:test) and assert (node:assert/strict).
|
|
5
|
+
* Each test creates an isolated temp directory fixture and cleans up after.
|
|
6
|
+
*
|
|
7
|
+
* Covers PATH-01 through PATH-05 requirements.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const { describe, it, afterEach } = require('node:test');
|
|
11
|
+
const assert = require('node:assert/strict');
|
|
12
|
+
const fs = require('fs');
|
|
13
|
+
const path = require('path');
|
|
14
|
+
const os = require('os');
|
|
15
|
+
|
|
16
|
+
const { getPlanningRoot, getPaths, initPaths, resetPaths, PROJECTS_DIR } = require('./paths.cjs');
|
|
17
|
+
|
|
18
|
+
// ─── Helpers ────────────────────────────────────────────────────────────────
|
|
19
|
+
|
|
20
|
+
function makeTempDir() {
|
|
21
|
+
return fs.mkdtempSync(path.join(os.tmpdir(), 'dgs-paths-test-'));
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function removeTempDir(dir) {
|
|
25
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// ─── 1. Detection cascade — .planning/ layout (PATH-01, PATH-02) ────────────
|
|
29
|
+
|
|
30
|
+
describe('detection cascade — .planning/ layout', () => {
|
|
31
|
+
let cwd;
|
|
32
|
+
|
|
33
|
+
afterEach(() => {
|
|
34
|
+
resetPaths();
|
|
35
|
+
if (cwd) removeTempDir(cwd);
|
|
36
|
+
cwd = null;
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('detects .planning/ layout from .planning/dgs.config.json', () => {
|
|
40
|
+
cwd = makeTempDir();
|
|
41
|
+
fs.mkdirSync(path.join(cwd, '.planning'), { recursive: true });
|
|
42
|
+
fs.writeFileSync(path.join(cwd, '.planning', 'dgs.config.json'), '{}');
|
|
43
|
+
|
|
44
|
+
const root = getPlanningRoot(cwd);
|
|
45
|
+
assert.equal(root, path.join(cwd, '.planning'));
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('detects .planning/ layout from .planning/config.json', () => {
|
|
49
|
+
cwd = makeTempDir();
|
|
50
|
+
fs.mkdirSync(path.join(cwd, '.planning'), { recursive: true });
|
|
51
|
+
fs.writeFileSync(path.join(cwd, '.planning', 'config.json'), '{}');
|
|
52
|
+
|
|
53
|
+
const root = getPlanningRoot(cwd);
|
|
54
|
+
assert.equal(root, path.join(cwd, '.planning'));
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('detects .planning/ layout when both config files exist', () => {
|
|
58
|
+
cwd = makeTempDir();
|
|
59
|
+
fs.mkdirSync(path.join(cwd, '.planning'), { recursive: true });
|
|
60
|
+
fs.writeFileSync(path.join(cwd, '.planning', 'dgs.config.json'), '{}');
|
|
61
|
+
fs.writeFileSync(path.join(cwd, '.planning', 'config.json'), '{}');
|
|
62
|
+
|
|
63
|
+
const root = getPlanningRoot(cwd);
|
|
64
|
+
assert.equal(root, path.join(cwd, '.planning'));
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// ─── 2. Detection cascade — root layout (PATH-01, PATH-02) ──────────────────
|
|
69
|
+
|
|
70
|
+
describe('detection cascade — root layout', () => {
|
|
71
|
+
let cwd;
|
|
72
|
+
|
|
73
|
+
afterEach(() => {
|
|
74
|
+
resetPaths();
|
|
75
|
+
if (cwd) removeTempDir(cwd);
|
|
76
|
+
cwd = null;
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('detects root layout from dgs.config.json with planningRoot "."', () => {
|
|
80
|
+
cwd = makeTempDir();
|
|
81
|
+
fs.writeFileSync(
|
|
82
|
+
path.join(cwd, 'dgs.config.json'),
|
|
83
|
+
JSON.stringify({ planningRoot: '.' })
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
const root = getPlanningRoot(cwd);
|
|
87
|
+
assert.equal(root, cwd);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('returns .planning/ when root dgs.config.json has no planningRoot field', () => {
|
|
91
|
+
cwd = makeTempDir();
|
|
92
|
+
fs.writeFileSync(
|
|
93
|
+
path.join(cwd, 'dgs.config.json'),
|
|
94
|
+
JSON.stringify({ mode: 'interactive' })
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
const root = getPlanningRoot(cwd);
|
|
98
|
+
assert.equal(root, path.join(cwd, '.planning'));
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('falls through when root dgs.config.json has malformed JSON', () => {
|
|
102
|
+
cwd = makeTempDir();
|
|
103
|
+
fs.writeFileSync(path.join(cwd, 'dgs.config.json'), '{not valid json!!!');
|
|
104
|
+
|
|
105
|
+
// Should NOT crash — falls through to default
|
|
106
|
+
const root = getPlanningRoot(cwd);
|
|
107
|
+
// Malformed config falls through; no PROJECT.md => default .planning/
|
|
108
|
+
assert.equal(root, path.join(cwd, '.planning'));
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it('auto-detects root layout when PROJECT.md exists at root with no .planning/ dir', () => {
|
|
112
|
+
cwd = makeTempDir();
|
|
113
|
+
fs.writeFileSync(path.join(cwd, 'PROJECT.md'), '# Project: Test');
|
|
114
|
+
|
|
115
|
+
const root = getPlanningRoot(cwd);
|
|
116
|
+
assert.equal(root, cwd);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it('returns .planning/ when PROJECT.md exists at root AND .planning/ directory exists', () => {
|
|
120
|
+
cwd = makeTempDir();
|
|
121
|
+
fs.writeFileSync(path.join(cwd, 'PROJECT.md'), '# Project: Test');
|
|
122
|
+
fs.mkdirSync(path.join(cwd, '.planning'), { recursive: true });
|
|
123
|
+
|
|
124
|
+
const root = getPlanningRoot(cwd);
|
|
125
|
+
assert.equal(root, path.join(cwd, '.planning'));
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
// ─── 3. Detection cascade — default (PATH-02) ───────────────────────────────
|
|
130
|
+
|
|
131
|
+
describe('detection cascade — default', () => {
|
|
132
|
+
let cwd;
|
|
133
|
+
|
|
134
|
+
afterEach(() => {
|
|
135
|
+
resetPaths();
|
|
136
|
+
if (cwd) removeTempDir(cwd);
|
|
137
|
+
cwd = null;
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it('defaults to .planning/ when no signals are found', () => {
|
|
141
|
+
cwd = makeTempDir();
|
|
142
|
+
|
|
143
|
+
const root = getPlanningRoot(cwd);
|
|
144
|
+
assert.equal(root, path.join(cwd, '.planning'));
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
// ─── 4. Detection cascade — priority (PATH-02) ──────────────────────────────
|
|
149
|
+
|
|
150
|
+
describe('detection cascade — priority', () => {
|
|
151
|
+
let cwd;
|
|
152
|
+
|
|
153
|
+
afterEach(() => {
|
|
154
|
+
resetPaths();
|
|
155
|
+
if (cwd) removeTempDir(cwd);
|
|
156
|
+
cwd = null;
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it('.planning/config.json takes precedence over root dgs.config.json with planningRoot "."', () => {
|
|
160
|
+
cwd = makeTempDir();
|
|
161
|
+
// .planning/ layout signal
|
|
162
|
+
fs.mkdirSync(path.join(cwd, '.planning'), { recursive: true });
|
|
163
|
+
fs.writeFileSync(path.join(cwd, '.planning', 'config.json'), '{}');
|
|
164
|
+
// Root layout signal
|
|
165
|
+
fs.writeFileSync(
|
|
166
|
+
path.join(cwd, 'dgs.config.json'),
|
|
167
|
+
JSON.stringify({ planningRoot: '.' })
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
const root = getPlanningRoot(cwd);
|
|
171
|
+
assert.equal(root, path.join(cwd, '.planning'));
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
// ─── 5. PATHS object shape (PATH-03) ────────────────────────────────────────
|
|
176
|
+
|
|
177
|
+
describe('PATHS object shape', () => {
|
|
178
|
+
let cwd;
|
|
179
|
+
|
|
180
|
+
afterEach(() => {
|
|
181
|
+
resetPaths();
|
|
182
|
+
if (cwd) removeTempDir(cwd);
|
|
183
|
+
cwd = null;
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
const EXPECTED_KEYS = [
|
|
187
|
+
'ROOT', 'PHASES', 'IDEAS', 'SPECS', 'JOBS', 'DOCS',
|
|
188
|
+
'CODEBASE', 'MILESTONES', 'CONFIG', 'CONFIG_LEGACY',
|
|
189
|
+
'QUICK', 'TODOS', 'RESEARCH', 'DEBUG', 'ARCHIVE', 'PROJECTS', 'LAYOUT',
|
|
190
|
+
];
|
|
191
|
+
|
|
192
|
+
it('returns object with all 17 expected keys', () => {
|
|
193
|
+
cwd = makeTempDir();
|
|
194
|
+
fs.mkdirSync(path.join(cwd, '.planning'), { recursive: true });
|
|
195
|
+
fs.writeFileSync(path.join(cwd, '.planning', 'config.json'), '{}');
|
|
196
|
+
|
|
197
|
+
const paths = getPaths(cwd);
|
|
198
|
+
assert.deepEqual(Object.keys(paths).sort(), EXPECTED_KEYS.slice().sort());
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
it('for .planning/ layout: ROOT ends with .planning and LAYOUT is dotplanning', () => {
|
|
202
|
+
cwd = makeTempDir();
|
|
203
|
+
fs.mkdirSync(path.join(cwd, '.planning'), { recursive: true });
|
|
204
|
+
fs.writeFileSync(path.join(cwd, '.planning', 'config.json'), '{}');
|
|
205
|
+
|
|
206
|
+
const paths = getPaths(cwd);
|
|
207
|
+
assert.ok(paths.ROOT.endsWith('.planning'));
|
|
208
|
+
assert.equal(paths.LAYOUT, 'dotplanning');
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
it('for root layout: ROOT equals cwd and LAYOUT is root', () => {
|
|
212
|
+
cwd = makeTempDir();
|
|
213
|
+
fs.writeFileSync(
|
|
214
|
+
path.join(cwd, 'dgs.config.json'),
|
|
215
|
+
JSON.stringify({ planningRoot: '.' })
|
|
216
|
+
);
|
|
217
|
+
|
|
218
|
+
const paths = getPaths(cwd);
|
|
219
|
+
assert.equal(paths.ROOT, cwd);
|
|
220
|
+
assert.equal(paths.LAYOUT, 'root');
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
it('all path values (except LAYOUT) are absolute', () => {
|
|
224
|
+
cwd = makeTempDir();
|
|
225
|
+
fs.mkdirSync(path.join(cwd, '.planning'), { recursive: true });
|
|
226
|
+
fs.writeFileSync(path.join(cwd, '.planning', 'config.json'), '{}');
|
|
227
|
+
|
|
228
|
+
const paths = getPaths(cwd);
|
|
229
|
+
for (const [key, value] of Object.entries(paths)) {
|
|
230
|
+
if (key === 'LAYOUT') continue;
|
|
231
|
+
assert.ok(
|
|
232
|
+
path.isAbsolute(value),
|
|
233
|
+
`Expected ${key} to be absolute, got: ${value}`
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
it('CONFIG ends with dgs.config.json', () => {
|
|
239
|
+
cwd = makeTempDir();
|
|
240
|
+
fs.mkdirSync(path.join(cwd, '.planning'), { recursive: true });
|
|
241
|
+
fs.writeFileSync(path.join(cwd, '.planning', 'config.json'), '{}');
|
|
242
|
+
|
|
243
|
+
const paths = getPaths(cwd);
|
|
244
|
+
assert.ok(paths.CONFIG.endsWith('dgs.config.json'));
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
it('CONFIG_LEGACY ends with config.json', () => {
|
|
248
|
+
cwd = makeTempDir();
|
|
249
|
+
fs.mkdirSync(path.join(cwd, '.planning'), { recursive: true });
|
|
250
|
+
fs.writeFileSync(path.join(cwd, '.planning', 'config.json'), '{}');
|
|
251
|
+
|
|
252
|
+
const paths = getPaths(cwd);
|
|
253
|
+
assert.ok(paths.CONFIG_LEGACY.endsWith('config.json'));
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
it('subdirectory paths are derived from ROOT', () => {
|
|
257
|
+
cwd = makeTempDir();
|
|
258
|
+
fs.mkdirSync(path.join(cwd, '.planning'), { recursive: true });
|
|
259
|
+
fs.writeFileSync(path.join(cwd, '.planning', 'config.json'), '{}');
|
|
260
|
+
|
|
261
|
+
const paths = getPaths(cwd);
|
|
262
|
+
assert.equal(paths.PHASES, path.join(paths.ROOT, 'phases'));
|
|
263
|
+
assert.equal(paths.IDEAS, path.join(paths.ROOT, 'ideas'));
|
|
264
|
+
assert.equal(paths.SPECS, path.join(paths.ROOT, 'specs'));
|
|
265
|
+
assert.equal(paths.JOBS, path.join(paths.ROOT, 'jobs'));
|
|
266
|
+
assert.equal(paths.DOCS, path.join(paths.ROOT, 'docs'));
|
|
267
|
+
assert.equal(paths.CODEBASE, path.join(paths.ROOT, 'codebase'));
|
|
268
|
+
assert.equal(paths.MILESTONES, path.join(paths.ROOT, 'milestones'));
|
|
269
|
+
assert.equal(paths.QUICK, path.join(paths.ROOT, 'quick'));
|
|
270
|
+
assert.equal(paths.TODOS, path.join(paths.ROOT, 'todos'));
|
|
271
|
+
assert.equal(paths.RESEARCH, path.join(paths.ROOT, 'research'));
|
|
272
|
+
assert.equal(paths.DEBUG, path.join(paths.ROOT, 'debug'));
|
|
273
|
+
assert.equal(paths.ARCHIVE, path.join(paths.ROOT, 'archive'));
|
|
274
|
+
assert.equal(paths.PROJECTS, path.join(paths.ROOT, 'projects'));
|
|
275
|
+
});
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
// ─── 6. Object.freeze (PATH-03) ─────────────────────────────────────────────
|
|
279
|
+
|
|
280
|
+
describe('Object.freeze', () => {
|
|
281
|
+
let cwd;
|
|
282
|
+
|
|
283
|
+
afterEach(() => {
|
|
284
|
+
resetPaths();
|
|
285
|
+
if (cwd) removeTempDir(cwd);
|
|
286
|
+
cwd = null;
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
it('getPaths returns a frozen object', () => {
|
|
290
|
+
cwd = makeTempDir();
|
|
291
|
+
fs.mkdirSync(path.join(cwd, '.planning'), { recursive: true });
|
|
292
|
+
fs.writeFileSync(path.join(cwd, '.planning', 'config.json'), '{}');
|
|
293
|
+
|
|
294
|
+
const paths = getPaths(cwd);
|
|
295
|
+
assert.equal(Object.isFrozen(paths), true);
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
it('attempting to add a property does not modify the object', () => {
|
|
299
|
+
cwd = makeTempDir();
|
|
300
|
+
fs.mkdirSync(path.join(cwd, '.planning'), { recursive: true });
|
|
301
|
+
fs.writeFileSync(path.join(cwd, '.planning', 'config.json'), '{}');
|
|
302
|
+
|
|
303
|
+
const paths = getPaths(cwd);
|
|
304
|
+
try {
|
|
305
|
+
paths.NEW_PROP = 'value';
|
|
306
|
+
} catch {
|
|
307
|
+
// strict mode would throw
|
|
308
|
+
}
|
|
309
|
+
assert.equal(paths.NEW_PROP, undefined);
|
|
310
|
+
});
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
// ─── 7. Caching (PATH-03, PATH-04) ──────────────────────────────────────────
|
|
314
|
+
|
|
315
|
+
describe('caching', () => {
|
|
316
|
+
let cwdA, cwdB;
|
|
317
|
+
|
|
318
|
+
afterEach(() => {
|
|
319
|
+
resetPaths();
|
|
320
|
+
if (cwdA) removeTempDir(cwdA);
|
|
321
|
+
if (cwdB) removeTempDir(cwdB);
|
|
322
|
+
cwdA = null;
|
|
323
|
+
cwdB = null;
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
it('same cwd returns same object (referential equality)', () => {
|
|
327
|
+
cwdA = makeTempDir();
|
|
328
|
+
fs.mkdirSync(path.join(cwdA, '.planning'), { recursive: true });
|
|
329
|
+
fs.writeFileSync(path.join(cwdA, '.planning', 'config.json'), '{}');
|
|
330
|
+
|
|
331
|
+
const first = getPaths(cwdA);
|
|
332
|
+
const second = getPaths(cwdA);
|
|
333
|
+
assert.equal(first === second, true, 'Expected referential equality');
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
it('different cwd returns different object', () => {
|
|
337
|
+
cwdA = makeTempDir();
|
|
338
|
+
fs.mkdirSync(path.join(cwdA, '.planning'), { recursive: true });
|
|
339
|
+
fs.writeFileSync(path.join(cwdA, '.planning', 'config.json'), '{}');
|
|
340
|
+
|
|
341
|
+
cwdB = makeTempDir();
|
|
342
|
+
fs.mkdirSync(path.join(cwdB, '.planning'), { recursive: true });
|
|
343
|
+
fs.writeFileSync(path.join(cwdB, '.planning', 'config.json'), '{}');
|
|
344
|
+
|
|
345
|
+
const pathsA = getPaths(cwdA);
|
|
346
|
+
const pathsB = getPaths(cwdB);
|
|
347
|
+
assert.notEqual(pathsA === pathsB, true, 'Expected different objects');
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
it('resetPaths clears the cache (new reference after reset)', () => {
|
|
351
|
+
cwdA = makeTempDir();
|
|
352
|
+
fs.mkdirSync(path.join(cwdA, '.planning'), { recursive: true });
|
|
353
|
+
fs.writeFileSync(path.join(cwdA, '.planning', 'config.json'), '{}');
|
|
354
|
+
|
|
355
|
+
const before = getPaths(cwdA);
|
|
356
|
+
resetPaths();
|
|
357
|
+
const after = getPaths(cwdA);
|
|
358
|
+
assert.notEqual(before === after, true, 'Expected different reference after reset');
|
|
359
|
+
});
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
// ─── 8. initPaths / resetPaths (PATH-04) ─────────────────────────────────────
|
|
363
|
+
|
|
364
|
+
describe('initPaths / resetPaths', () => {
|
|
365
|
+
let cwd;
|
|
366
|
+
|
|
367
|
+
afterEach(() => {
|
|
368
|
+
resetPaths();
|
|
369
|
+
if (cwd) removeTempDir(cwd);
|
|
370
|
+
cwd = null;
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
it('initPaths returns the same PATHS object as getPaths', () => {
|
|
374
|
+
cwd = makeTempDir();
|
|
375
|
+
fs.mkdirSync(path.join(cwd, '.planning'), { recursive: true });
|
|
376
|
+
fs.writeFileSync(path.join(cwd, '.planning', 'config.json'), '{}');
|
|
377
|
+
|
|
378
|
+
const fromInit = initPaths(cwd);
|
|
379
|
+
const fromGet = getPaths(cwd);
|
|
380
|
+
assert.equal(fromInit === fromGet, true, 'initPaths and getPaths should return same reference');
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
it('resetPaths clears cache (verified by reference comparison)', () => {
|
|
384
|
+
cwd = makeTempDir();
|
|
385
|
+
fs.mkdirSync(path.join(cwd, '.planning'), { recursive: true });
|
|
386
|
+
fs.writeFileSync(path.join(cwd, '.planning', 'config.json'), '{}');
|
|
387
|
+
|
|
388
|
+
const before = initPaths(cwd);
|
|
389
|
+
resetPaths();
|
|
390
|
+
const after = initPaths(cwd);
|
|
391
|
+
assert.notEqual(before === after, true, 'Expected new reference after resetPaths');
|
|
392
|
+
});
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
// ─── 9. Leaf module constraint (PATH-05) ─────────────────────────────────────
|
|
396
|
+
|
|
397
|
+
describe('leaf module constraint', () => {
|
|
398
|
+
it('paths.cjs has no DGS module imports (no require("./") patterns)', () => {
|
|
399
|
+
const source = fs.readFileSync(
|
|
400
|
+
path.join(__dirname, 'paths.cjs'),
|
|
401
|
+
'utf-8'
|
|
402
|
+
);
|
|
403
|
+
// Check for require('./anything') — would indicate DGS module import
|
|
404
|
+
const dgsImports = source.match(/require\s*\(\s*['"]\.\/[^'"]*['"]\s*\)/g);
|
|
405
|
+
assert.equal(dgsImports, null, 'paths.cjs should have zero DGS module imports');
|
|
406
|
+
});
|
|
407
|
+
|
|
408
|
+
it('paths.cjs uses fs and path builtins', () => {
|
|
409
|
+
const source = fs.readFileSync(
|
|
410
|
+
path.join(__dirname, 'paths.cjs'),
|
|
411
|
+
'utf-8'
|
|
412
|
+
);
|
|
413
|
+
assert.ok(
|
|
414
|
+
/require\s*\(\s*['"]fs['"]\s*\)/.test(source),
|
|
415
|
+
'Expected require("fs") in paths.cjs'
|
|
416
|
+
);
|
|
417
|
+
assert.ok(
|
|
418
|
+
/require\s*\(\s*['"]path['"]\s*\)/.test(source),
|
|
419
|
+
'Expected require("path") in paths.cjs'
|
|
420
|
+
);
|
|
421
|
+
});
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
// ─── 10. Edge cases ──────────────────────────────────────────────────────────
|
|
425
|
+
|
|
426
|
+
describe('edge cases', () => {
|
|
427
|
+
let cwd;
|
|
428
|
+
|
|
429
|
+
afterEach(() => {
|
|
430
|
+
resetPaths();
|
|
431
|
+
if (cwd) removeTempDir(cwd);
|
|
432
|
+
cwd = null;
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
it('paths returned even when subdirectories do not exist on disk', () => {
|
|
436
|
+
cwd = makeTempDir();
|
|
437
|
+
// No .planning/ directory created, no config files — default case
|
|
438
|
+
|
|
439
|
+
const paths = getPaths(cwd);
|
|
440
|
+
// Should still return all paths even though none of these directories exist
|
|
441
|
+
assert.ok(paths.PHASES);
|
|
442
|
+
assert.ok(paths.IDEAS);
|
|
443
|
+
assert.ok(paths.SPECS);
|
|
444
|
+
assert.ok(paths.ROOT);
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
it('PROJECTS_DIR constant equals "projects"', () => {
|
|
448
|
+
assert.equal(PROJECTS_DIR, 'projects');
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
it('getPaths().PROJECTS returns absolute path ending in /projects', () => {
|
|
452
|
+
cwd = makeTempDir();
|
|
453
|
+
fs.mkdirSync(path.join(cwd, '.planning'), { recursive: true });
|
|
454
|
+
fs.writeFileSync(path.join(cwd, '.planning', 'config.json'), '{}');
|
|
455
|
+
|
|
456
|
+
const paths = getPaths(cwd);
|
|
457
|
+
assert.ok(path.isAbsolute(paths.PROJECTS), 'Expected PROJECTS to be absolute');
|
|
458
|
+
assert.ok(paths.PROJECTS.endsWith('/projects') || paths.PROJECTS.endsWith('\\projects'),
|
|
459
|
+
'Expected PROJECTS path to end with /projects');
|
|
460
|
+
});
|
|
461
|
+
|
|
462
|
+
it('relative cwd is normalized to absolute path', () => {
|
|
463
|
+
// This test verifies that passing a relative cwd gives the same result
|
|
464
|
+
// as passing the equivalent absolute path
|
|
465
|
+
cwd = makeTempDir();
|
|
466
|
+
fs.mkdirSync(path.join(cwd, '.planning'), { recursive: true });
|
|
467
|
+
fs.writeFileSync(path.join(cwd, '.planning', 'config.json'), '{}');
|
|
468
|
+
|
|
469
|
+
// Get paths with absolute cwd
|
|
470
|
+
const absResult = getPaths(cwd);
|
|
471
|
+
resetPaths();
|
|
472
|
+
|
|
473
|
+
// Calling with '.' should resolve to process.cwd(), which is different from
|
|
474
|
+
// our temp dir, but the key behavior is that relative paths get resolved
|
|
475
|
+
// to absolute via path.resolve()
|
|
476
|
+
const dotResult = getPaths('.');
|
|
477
|
+
// The important assertion: all returned paths are absolute regardless of input
|
|
478
|
+
for (const [key, value] of Object.entries(dotResult)) {
|
|
479
|
+
if (key === 'LAYOUT') continue;
|
|
480
|
+
assert.ok(
|
|
481
|
+
path.isAbsolute(value),
|
|
482
|
+
`Expected ${key} to be absolute even with relative cwd input`
|
|
483
|
+
);
|
|
484
|
+
}
|
|
485
|
+
});
|
|
486
|
+
});
|