@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,417 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Config — Planning config CRUD operations
|
|
3
|
+
*
|
|
4
|
+
* Uses getConfigPath(cwd, mode) for all config file path resolution:
|
|
5
|
+
* - 'read' mode: prefers dgs.config.json, falls back to config.json (dual-read)
|
|
6
|
+
* - 'write' mode: always targets dgs.config.json (write-forward migration)
|
|
7
|
+
*
|
|
8
|
+
* Zero hardcoded .planning/config.json paths — all resolved via getPlanningRoot.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const fs = require('fs');
|
|
12
|
+
const path = require('path');
|
|
13
|
+
const { output, error } = require('./core.cjs');
|
|
14
|
+
const { getPlanningRoot } = require('./paths.cjs');
|
|
15
|
+
|
|
16
|
+
const VALID_CONFIG_KEYS = new Set([
|
|
17
|
+
'mode', 'granularity', 'parallelization', 'commit_docs', 'model_profile',
|
|
18
|
+
'search_gitignored', 'brave_search',
|
|
19
|
+
'workflow.research', 'workflow.plan_check', 'workflow.verifier',
|
|
20
|
+
'workflow.nyquist_validation', 'workflow.ui_phase', 'workflow.ui_safety_gate',
|
|
21
|
+
'workflow._auto_chain_active', 'workflow.discipline',
|
|
22
|
+
'git.base_branch', 'git.branching_strategy', 'git.phase_branch_template', 'git.milestone_branch_template',
|
|
23
|
+
'planning.commit_docs', 'planning.search_gitignored',
|
|
24
|
+
]);
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Resolve the config file path for the given cwd and mode.
|
|
28
|
+
*
|
|
29
|
+
* @param {string} cwd - Working directory
|
|
30
|
+
* @param {'read'|'write'} mode - 'read' for dual-check (dgs.config.json first, config.json fallback),
|
|
31
|
+
* 'write' for always dgs.config.json
|
|
32
|
+
* @returns {string} Absolute path to the config file
|
|
33
|
+
*/
|
|
34
|
+
function getConfigPath(cwd, mode) {
|
|
35
|
+
const root = getPlanningRoot(cwd);
|
|
36
|
+
const newPath = path.join(root, 'dgs.config.json');
|
|
37
|
+
if (mode === 'write') return newPath;
|
|
38
|
+
// Read mode: prefer dgs.config.json, fall back to config.json
|
|
39
|
+
if (fs.existsSync(newPath)) return newPath;
|
|
40
|
+
const legacyPath = path.join(root, 'config.json');
|
|
41
|
+
if (fs.existsSync(legacyPath)) return legacyPath;
|
|
42
|
+
return newPath; // default when neither exists
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function cmdConfigEnsureSection(cwd, raw) {
|
|
46
|
+
const writePath = getConfigPath(cwd, 'write');
|
|
47
|
+
const parentDir = path.dirname(writePath);
|
|
48
|
+
|
|
49
|
+
// Ensure parent directory exists
|
|
50
|
+
try {
|
|
51
|
+
if (!fs.existsSync(parentDir)) {
|
|
52
|
+
fs.mkdirSync(parentDir, { recursive: true });
|
|
53
|
+
}
|
|
54
|
+
} catch (err) {
|
|
55
|
+
error('Failed to create config directory: ' + err.message);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Check if config already exists (either dgs.config.json or legacy config.json)
|
|
59
|
+
const readPath = getConfigPath(cwd, 'read');
|
|
60
|
+
if (fs.existsSync(readPath)) {
|
|
61
|
+
const result = { created: false, reason: 'already_exists' };
|
|
62
|
+
output(result, raw, 'exists');
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Detect Brave Search API key availability
|
|
67
|
+
const homedir = require('os').homedir();
|
|
68
|
+
const braveKeyFile = path.join(homedir, '.dgs', 'brave_api_key');
|
|
69
|
+
const hasBraveSearch = !!(process.env.BRAVE_API_KEY || fs.existsSync(braveKeyFile));
|
|
70
|
+
|
|
71
|
+
// Load user-level defaults from ~/.dgs/defaults.json if available
|
|
72
|
+
const globalDefaultsPath = path.join(homedir, '.dgs', 'defaults.json');
|
|
73
|
+
let userDefaults = {};
|
|
74
|
+
try {
|
|
75
|
+
if (fs.existsSync(globalDefaultsPath)) {
|
|
76
|
+
userDefaults = JSON.parse(fs.readFileSync(globalDefaultsPath, 'utf-8'));
|
|
77
|
+
// Migrate deprecated "depth" key to "granularity"
|
|
78
|
+
if ('depth' in userDefaults && !('granularity' in userDefaults)) {
|
|
79
|
+
const depthToGranularity = { quick: 'coarse', standard: 'standard', comprehensive: 'fine' };
|
|
80
|
+
userDefaults.granularity = depthToGranularity[userDefaults.depth] || userDefaults.depth;
|
|
81
|
+
delete userDefaults.depth;
|
|
82
|
+
try { fs.writeFileSync(globalDefaultsPath, JSON.stringify(userDefaults, null, 2), 'utf-8'); } catch {}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
} catch (err) {
|
|
86
|
+
// Ignore malformed global defaults, fall back to hardcoded
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Create default config (user-level defaults override hardcoded defaults)
|
|
90
|
+
const hardcoded = {
|
|
91
|
+
model_profile: 'balanced',
|
|
92
|
+
commit_docs: true,
|
|
93
|
+
search_gitignored: false,
|
|
94
|
+
branching_strategy: 'none',
|
|
95
|
+
phase_branch_template: 'dgs/{project}/phase-{phase}-{slug}',
|
|
96
|
+
milestone_branch_template: 'dgs/{project}/{milestone}-{slug}',
|
|
97
|
+
base_branch: 'main',
|
|
98
|
+
workflow: {
|
|
99
|
+
research: true,
|
|
100
|
+
plan_check: true,
|
|
101
|
+
verifier: true,
|
|
102
|
+
nyquist_validation: true,
|
|
103
|
+
discipline: true,
|
|
104
|
+
},
|
|
105
|
+
parallelization: true,
|
|
106
|
+
brave_search: hasBraveSearch,
|
|
107
|
+
};
|
|
108
|
+
const defaults = {
|
|
109
|
+
...hardcoded,
|
|
110
|
+
...userDefaults,
|
|
111
|
+
workflow: { ...hardcoded.workflow, ...(userDefaults.workflow || {}) },
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
try {
|
|
115
|
+
fs.writeFileSync(writePath, JSON.stringify(defaults, null, 2), 'utf-8');
|
|
116
|
+
const result = { created: true, path: path.relative(cwd, writePath) || writePath };
|
|
117
|
+
output(result, raw, 'created');
|
|
118
|
+
} catch (err) {
|
|
119
|
+
error('Failed to create dgs.config.json: ' + err.message);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function cmdConfigSet(cwd, keyPath, value, raw) {
|
|
124
|
+
if (!keyPath) {
|
|
125
|
+
error('Usage: config-set <key.path> <value>');
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (!VALID_CONFIG_KEYS.has(keyPath)) {
|
|
129
|
+
error(`Unknown config key: "${keyPath}". Valid keys: ${[...VALID_CONFIG_KEYS].sort().join(', ')}`);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Parse value (handle booleans and numbers)
|
|
133
|
+
let parsedValue = value;
|
|
134
|
+
if (value === 'true') parsedValue = true;
|
|
135
|
+
else if (value === 'false') parsedValue = false;
|
|
136
|
+
else if (!isNaN(value) && value !== '') parsedValue = Number(value);
|
|
137
|
+
|
|
138
|
+
// Load existing config from read path (may be legacy config.json)
|
|
139
|
+
const readPath = getConfigPath(cwd, 'read');
|
|
140
|
+
let config = {};
|
|
141
|
+
try {
|
|
142
|
+
if (fs.existsSync(readPath)) {
|
|
143
|
+
config = JSON.parse(fs.readFileSync(readPath, 'utf-8'));
|
|
144
|
+
}
|
|
145
|
+
} catch (err) {
|
|
146
|
+
error('Failed to read config: ' + err.message);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Set nested value using dot notation (e.g., "workflow.research")
|
|
150
|
+
const keys = keyPath.split('.');
|
|
151
|
+
let current = config;
|
|
152
|
+
for (let i = 0; i < keys.length - 1; i++) {
|
|
153
|
+
const key = keys[i];
|
|
154
|
+
if (current[key] === undefined || typeof current[key] !== 'object') {
|
|
155
|
+
current[key] = {};
|
|
156
|
+
}
|
|
157
|
+
current = current[key];
|
|
158
|
+
}
|
|
159
|
+
current[keys[keys.length - 1]] = parsedValue;
|
|
160
|
+
|
|
161
|
+
// Write to dgs.config.json (write-forward migration)
|
|
162
|
+
const writePath = getConfigPath(cwd, 'write');
|
|
163
|
+
try {
|
|
164
|
+
fs.writeFileSync(writePath, JSON.stringify(config, null, 2), 'utf-8');
|
|
165
|
+
const result = { updated: true, key: keyPath, value: parsedValue };
|
|
166
|
+
output(result, raw, `${keyPath}=${parsedValue}`);
|
|
167
|
+
} catch (err) {
|
|
168
|
+
error('Failed to write dgs.config.json: ' + err.message);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function cmdConfigGet(cwd, keyPath, raw) {
|
|
173
|
+
if (!keyPath) {
|
|
174
|
+
error('Usage: config-get <key.path>');
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const readPath = getConfigPath(cwd, 'read');
|
|
178
|
+
let config = {};
|
|
179
|
+
try {
|
|
180
|
+
if (fs.existsSync(readPath)) {
|
|
181
|
+
config = JSON.parse(fs.readFileSync(readPath, 'utf-8'));
|
|
182
|
+
} else {
|
|
183
|
+
error('No config file found at ' + readPath);
|
|
184
|
+
}
|
|
185
|
+
} catch (err) {
|
|
186
|
+
if (err.message.startsWith('No config file')) throw err;
|
|
187
|
+
error('Failed to read config: ' + err.message);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Traverse dot-notation path (e.g., "workflow.auto_advance")
|
|
191
|
+
const keys = keyPath.split('.');
|
|
192
|
+
let current = config;
|
|
193
|
+
for (const key of keys) {
|
|
194
|
+
if (current === undefined || current === null || typeof current !== 'object') {
|
|
195
|
+
error(`Key not found: ${keyPath}`);
|
|
196
|
+
}
|
|
197
|
+
current = current[key];
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (current === undefined) {
|
|
201
|
+
error(`Key not found: ${keyPath}`);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
output(current, raw, String(current));
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Programmatically set a config field. Uses same dot-notation as cmdConfigSet.
|
|
209
|
+
* Does NOT call output()/process.exit() — returns the updated config.
|
|
210
|
+
*
|
|
211
|
+
* Reads from legacy config.json if it exists, writes to dgs.config.json (auto-migration).
|
|
212
|
+
*
|
|
213
|
+
* @param {string} cwd - Working directory
|
|
214
|
+
* @param {string} keyPath - Dot-notation key (e.g., "product_name" or "workflow.research")
|
|
215
|
+
* @param {*} value - Value to set
|
|
216
|
+
* @returns {object} Updated config object
|
|
217
|
+
*/
|
|
218
|
+
function writeConfigField(cwd, keyPath, value) {
|
|
219
|
+
const readPath = getConfigPath(cwd, 'read');
|
|
220
|
+
let config = {};
|
|
221
|
+
try {
|
|
222
|
+
if (fs.existsSync(readPath)) {
|
|
223
|
+
config = JSON.parse(fs.readFileSync(readPath, 'utf-8'));
|
|
224
|
+
}
|
|
225
|
+
} catch {
|
|
226
|
+
// Start with empty config if parse fails
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const keys = keyPath.split('.');
|
|
230
|
+
let current = config;
|
|
231
|
+
for (let i = 0; i < keys.length - 1; i++) {
|
|
232
|
+
const key = keys[i];
|
|
233
|
+
if (current[key] === undefined || typeof current[key] !== 'object') {
|
|
234
|
+
current[key] = {};
|
|
235
|
+
}
|
|
236
|
+
current = current[key];
|
|
237
|
+
}
|
|
238
|
+
current[keys[keys.length - 1]] = value;
|
|
239
|
+
|
|
240
|
+
const writePath = getConfigPath(cwd, 'write');
|
|
241
|
+
fs.writeFileSync(writePath, JSON.stringify(config, null, 2), 'utf-8');
|
|
242
|
+
return config;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Get the path to review-keys.json in the planning root.
|
|
247
|
+
*
|
|
248
|
+
* @param {string} cwd - Working directory
|
|
249
|
+
* @returns {string} Absolute path to review-keys.json
|
|
250
|
+
*/
|
|
251
|
+
function getReviewKeysPath(cwd) {
|
|
252
|
+
return path.join(getPlanningRoot(cwd), 'review-keys.json');
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Load review configuration from review-keys.json.
|
|
257
|
+
* Reads from review-keys.json in the planning root, applies defaults for missing fields,
|
|
258
|
+
* resolves $ENV_VAR references in api_key values.
|
|
259
|
+
*
|
|
260
|
+
* @param {string} cwd - Working directory
|
|
261
|
+
* @returns {object} { reviewers: { openai: {api_key, model}, gemini: {api_key, model} }, max_rounds, has_any_key }
|
|
262
|
+
*/
|
|
263
|
+
function loadReviewConfig(cwd) {
|
|
264
|
+
const reviewKeysPath = getReviewKeysPath(cwd);
|
|
265
|
+
|
|
266
|
+
const defaults = {
|
|
267
|
+
openai: { api_key: null, model: 'gpt-5-mini' },
|
|
268
|
+
gemini: { api_key: null, model: 'gemini-2.5-flash' },
|
|
269
|
+
max_rounds: 3,
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
let review = {};
|
|
273
|
+
try {
|
|
274
|
+
if (fs.existsSync(reviewKeysPath)) {
|
|
275
|
+
review = JSON.parse(fs.readFileSync(reviewKeysPath, 'utf-8'));
|
|
276
|
+
}
|
|
277
|
+
} catch {
|
|
278
|
+
// If review-keys.json is unreadable/malformed, return defaults
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// Build per-reviewer config with defaults
|
|
282
|
+
const resolveKey = (key) => {
|
|
283
|
+
if (!key) return null;
|
|
284
|
+
if (typeof key === 'string' && key.startsWith('$')) {
|
|
285
|
+
const envVal = process.env[key.slice(1)];
|
|
286
|
+
return envVal || null;
|
|
287
|
+
}
|
|
288
|
+
return key;
|
|
289
|
+
};
|
|
290
|
+
|
|
291
|
+
const openaiSection = review.openai || {};
|
|
292
|
+
const geminiSection = review.gemini || {};
|
|
293
|
+
|
|
294
|
+
const reviewers = {
|
|
295
|
+
openai: {
|
|
296
|
+
api_key: resolveKey(openaiSection.api_key) || defaults.openai.api_key,
|
|
297
|
+
model: openaiSection.model || defaults.openai.model,
|
|
298
|
+
},
|
|
299
|
+
gemini: {
|
|
300
|
+
api_key: resolveKey(geminiSection.api_key) || defaults.gemini.api_key,
|
|
301
|
+
model: geminiSection.model || defaults.gemini.model,
|
|
302
|
+
},
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
const max_rounds = review.max_rounds != null ? review.max_rounds : defaults.max_rounds;
|
|
306
|
+
const has_any_key = !!(reviewers.openai.api_key || reviewers.gemini.api_key);
|
|
307
|
+
|
|
308
|
+
return { reviewers, max_rounds, has_any_key };
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Check if review-keys.json needs .gitignore protection.
|
|
313
|
+
* Returns whether review-keys.json exists with non-empty keys and whether .gitignore covers it.
|
|
314
|
+
*
|
|
315
|
+
* @param {string} cwd - Working directory
|
|
316
|
+
* @returns {object} { needs_gitignore: boolean, has_review_keys: boolean }
|
|
317
|
+
*/
|
|
318
|
+
function checkConfigGitignore(cwd) {
|
|
319
|
+
const reviewKeysPath = getReviewKeysPath(cwd);
|
|
320
|
+
const gitignorePath = path.join(cwd, '.gitignore');
|
|
321
|
+
|
|
322
|
+
// Check if review-keys.json exists with non-empty api_key values
|
|
323
|
+
let has_review_keys = false;
|
|
324
|
+
try {
|
|
325
|
+
if (fs.existsSync(reviewKeysPath)) {
|
|
326
|
+
const review = JSON.parse(fs.readFileSync(reviewKeysPath, 'utf-8'));
|
|
327
|
+
const openaiKey = review.openai && review.openai.api_key;
|
|
328
|
+
const geminiKey = review.gemini && review.gemini.api_key;
|
|
329
|
+
has_review_keys = (typeof openaiKey === 'string' && openaiKey !== '') ||
|
|
330
|
+
(typeof geminiKey === 'string' && geminiKey !== '');
|
|
331
|
+
}
|
|
332
|
+
} catch {
|
|
333
|
+
// If review-keys.json unreadable, treat as no review keys
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
if (!has_review_keys) {
|
|
337
|
+
return { needs_gitignore: false, has_review_keys: false };
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// Check if .gitignore includes review-keys.json
|
|
341
|
+
let gitignoreCoversReviewKeys = false;
|
|
342
|
+
try {
|
|
343
|
+
if (fs.existsSync(gitignorePath)) {
|
|
344
|
+
const content = fs.readFileSync(gitignorePath, 'utf-8');
|
|
345
|
+
const lines = content.split('\n').map((l) => l.trim());
|
|
346
|
+
gitignoreCoversReviewKeys = lines.some(
|
|
347
|
+
(line) =>
|
|
348
|
+
line === 'review-keys.json' ||
|
|
349
|
+
line === '.planning/review-keys.json'
|
|
350
|
+
);
|
|
351
|
+
}
|
|
352
|
+
} catch {
|
|
353
|
+
// If .gitignore unreadable, treat as not covered
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
return {
|
|
357
|
+
needs_gitignore: !gitignoreCoversReviewKeys,
|
|
358
|
+
has_review_keys,
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* Ensure config files and review-keys.json are in .gitignore.
|
|
364
|
+
* Appends entries for dgs.config.json, config.json, and review-keys.json if not already present.
|
|
365
|
+
*
|
|
366
|
+
* @param {string} cwd - Working directory
|
|
367
|
+
* @returns {object} { added: boolean, reason?: string }
|
|
368
|
+
*/
|
|
369
|
+
function ensureConfigGitignored(cwd) {
|
|
370
|
+
const gitignorePath = path.join(cwd, '.gitignore');
|
|
371
|
+
|
|
372
|
+
let content = '';
|
|
373
|
+
try {
|
|
374
|
+
if (fs.existsSync(gitignorePath)) {
|
|
375
|
+
content = fs.readFileSync(gitignorePath, 'utf-8');
|
|
376
|
+
}
|
|
377
|
+
} catch {
|
|
378
|
+
// If unreadable, start fresh
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// Check if already present (either legacy or new name)
|
|
382
|
+
const lines = content.split('\n').map((l) => l.trim());
|
|
383
|
+
const hasLegacy = lines.some((line) => line === '.planning/config.json');
|
|
384
|
+
const hasNew = lines.some((line) => line === '.planning/dgs.config.json' || line === 'dgs.config.json');
|
|
385
|
+
const hasReviewKeys = lines.some((line) => line === 'review-keys.json' || line === '.planning/review-keys.json');
|
|
386
|
+
|
|
387
|
+
if (hasLegacy && hasNew && hasReviewKeys) {
|
|
388
|
+
return { added: false, reason: 'already_present' };
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// Append missing entries
|
|
392
|
+
let entry = '';
|
|
393
|
+
if (!hasNew || !hasLegacy) {
|
|
394
|
+
entry += '\n# DGS config (contains API keys)\n';
|
|
395
|
+
if (!hasNew) entry += 'dgs.config.json\n';
|
|
396
|
+
if (!hasLegacy) entry += '.planning/config.json\n';
|
|
397
|
+
}
|
|
398
|
+
if (!hasReviewKeys) {
|
|
399
|
+
entry += '\n# DGS review keys (contains API keys)\n';
|
|
400
|
+
entry += 'review-keys.json\n';
|
|
401
|
+
entry += '.planning/review-keys.json\n';
|
|
402
|
+
}
|
|
403
|
+
fs.writeFileSync(gitignorePath, content + entry, 'utf-8');
|
|
404
|
+
return { added: true };
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
module.exports = {
|
|
408
|
+
getConfigPath,
|
|
409
|
+
getReviewKeysPath,
|
|
410
|
+
cmdConfigEnsureSection,
|
|
411
|
+
cmdConfigSet,
|
|
412
|
+
cmdConfigGet,
|
|
413
|
+
writeConfigField,
|
|
414
|
+
loadReviewConfig,
|
|
415
|
+
checkConfigGitignore,
|
|
416
|
+
ensureConfigGitignored,
|
|
417
|
+
};
|