@howlil/ez-agents 3.1.0 → 3.4.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 +21 -21
- package/README.md +288 -718
- package/bin/install.js +438 -71
- package/commands/ez/auth.md +87 -0
- package/commands/ez/join-discord.md +18 -18
- package/ez-agents/bin/ez-tools.cjs +120 -2
- package/ez-agents/bin/lib/assistant-adapter.cjs +264 -205
- package/ez-agents/bin/lib/audit-exec.cjs +26 -9
- package/ez-agents/bin/lib/auth.cjs +2 -1
- package/ez-agents/bin/lib/circuit-breaker.cjs +118 -118
- package/ez-agents/bin/lib/commands.cjs +42 -23
- package/ez-agents/bin/lib/config.cjs +190 -183
- package/ez-agents/bin/lib/core.cjs +42 -25
- package/ez-agents/bin/lib/file-lock.cjs +236 -236
- package/ez-agents/bin/lib/frontmatter.cjs +299 -299
- package/ez-agents/bin/lib/fs-utils.cjs +153 -153
- package/ez-agents/bin/lib/git-utils.cjs +203 -203
- package/ez-agents/bin/lib/health-check.cjs +2 -3
- package/ez-agents/bin/lib/index.cjs +113 -113
- package/ez-agents/bin/lib/init.cjs +757 -710
- package/ez-agents/bin/lib/logger.cjs +52 -15
- package/ez-agents/bin/lib/milestone.cjs +241 -241
- package/ez-agents/bin/lib/model-provider.cjs +241 -146
- package/ez-agents/bin/lib/phase.cjs +925 -908
- package/ez-agents/bin/lib/planning-write.cjs +107 -0
- package/ez-agents/bin/lib/retry.cjs +119 -119
- package/ez-agents/bin/lib/roadmap.cjs +306 -305
- package/ez-agents/bin/lib/safe-exec.cjs +91 -5
- package/ez-agents/bin/lib/safe-path.cjs +130 -130
- package/ez-agents/bin/lib/state.cjs +736 -721
- package/ez-agents/bin/lib/temp-file.cjs +239 -239
- package/ez-agents/bin/lib/template.cjs +223 -222
- package/ez-agents/bin/lib/test-file-lock.cjs +112 -112
- package/ez-agents/bin/lib/test-graceful.cjs +93 -93
- package/ez-agents/bin/lib/test-logger.cjs +60 -60
- package/ez-agents/bin/lib/test-safe-exec.cjs +38 -38
- package/ez-agents/bin/lib/test-safe-path.cjs +33 -33
- package/ez-agents/bin/lib/test-temp-file.cjs +125 -125
- package/ez-agents/bin/lib/timeout-exec.cjs +63 -62
- package/ez-agents/bin/lib/verify.cjs +69 -26
- package/ez-agents/references/checkpoints.md +776 -776
- package/ez-agents/references/continuation-format.md +249 -249
- package/ez-agents/references/questioning.md +162 -162
- package/ez-agents/references/tdd.md +263 -263
- package/ez-agents/templates/codebase/concerns.md +310 -310
- package/ez-agents/templates/codebase/conventions.md +307 -307
- package/ez-agents/templates/codebase/integrations.md +280 -280
- package/ez-agents/templates/codebase/stack.md +186 -186
- package/ez-agents/templates/codebase/testing.md +480 -480
- package/ez-agents/templates/config.json +37 -37
- package/ez-agents/templates/continue-here.md +78 -78
- package/ez-agents/templates/milestone-archive.md +123 -123
- package/ez-agents/templates/milestone.md +115 -115
- package/ez-agents/templates/requirements.md +231 -231
- package/ez-agents/templates/research-project/ARCHITECTURE.md +204 -204
- package/ez-agents/templates/research-project/FEATURES.md +147 -147
- package/ez-agents/templates/research-project/PITFALLS.md +200 -200
- package/ez-agents/templates/research-project/STACK.md +120 -120
- package/ez-agents/templates/research-project/SUMMARY.md +170 -170
- package/ez-agents/templates/retrospective.md +54 -54
- package/ez-agents/templates/roadmap.md +202 -202
- package/ez-agents/templates/summary-minimal.md +41 -41
- package/ez-agents/templates/summary-standard.md +48 -48
- package/ez-agents/templates/summary.md +248 -248
- package/ez-agents/templates/user-setup.md +311 -311
- package/ez-agents/templates/verification-report.md +322 -322
- package/ez-agents/workflows/add-phase.md +112 -112
- package/ez-agents/workflows/add-tests.md +351 -351
- package/ez-agents/workflows/add-todo.md +158 -158
- package/ez-agents/workflows/audit-milestone.md +332 -332
- package/ez-agents/workflows/autonomous.md +743 -743
- package/ez-agents/workflows/check-todos.md +177 -177
- package/ez-agents/workflows/cleanup.md +152 -152
- package/ez-agents/workflows/complete-milestone.md +766 -766
- package/ez-agents/workflows/diagnose-issues.md +219 -219
- package/ez-agents/workflows/discovery-phase.md +289 -289
- package/ez-agents/workflows/discuss-phase.md +762 -762
- package/ez-agents/workflows/execute-phase.md +468 -468
- package/ez-agents/workflows/execute-plan.md +483 -483
- package/ez-agents/workflows/health.md +159 -159
- package/ez-agents/workflows/help.md +492 -492
- package/ez-agents/workflows/insert-phase.md +130 -130
- package/ez-agents/workflows/list-phase-assumptions.md +178 -178
- package/ez-agents/workflows/map-codebase.md +316 -316
- package/ez-agents/workflows/new-milestone.md +384 -384
- package/ez-agents/workflows/new-project.md +1113 -1111
- package/ez-agents/workflows/node-repair.md +92 -92
- package/ez-agents/workflows/pause-work.md +122 -122
- package/ez-agents/workflows/plan-milestone-gaps.md +274 -274
- package/ez-agents/workflows/plan-phase.md +651 -651
- package/ez-agents/workflows/progress.md +382 -382
- package/ez-agents/workflows/quick.md +610 -610
- package/ez-agents/workflows/remove-phase.md +155 -155
- package/ez-agents/workflows/research-phase.md +74 -74
- package/ez-agents/workflows/resume-project.md +307 -307
- package/ez-agents/workflows/set-profile.md +81 -81
- package/ez-agents/workflows/settings.md +242 -242
- package/ez-agents/workflows/stats.md +57 -57
- package/ez-agents/workflows/transition.md +544 -544
- package/ez-agents/workflows/ui-phase.md +290 -290
- package/ez-agents/workflows/ui-review.md +157 -157
- package/ez-agents/workflows/update.md +320 -320
- package/ez-agents/workflows/validate-phase.md +167 -167
- package/ez-agents/workflows/verify-phase.md +243 -243
- package/ez-agents/workflows/verify-work.md +584 -584
- package/package.json +2 -3
- package/scripts/build-hooks.js +43 -43
- package/scripts/fix-qwen-installation.js +144 -0
- package/scripts/run-tests.cjs +29 -29
- package/README.zh-CN.md +0 -702
|
@@ -1,183 +1,190 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Config — Planning config CRUD operations
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
const fs = require('fs');
|
|
6
|
-
const path = require('path');
|
|
7
|
-
const { output, error } = require('./core.cjs');
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
'
|
|
12
|
-
'
|
|
13
|
-
'workflow.
|
|
14
|
-
'workflow.
|
|
15
|
-
'
|
|
16
|
-
'
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
if
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
const
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
},
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Config — Planning config CRUD operations
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
const path = require('path');
|
|
7
|
+
const { output, error } = require('./core.cjs');
|
|
8
|
+
const { safePlanningWriteSync } = require('./planning-write.cjs');
|
|
9
|
+
|
|
10
|
+
const VALID_CONFIG_KEYS = new Set([
|
|
11
|
+
'mode', 'granularity', 'parallelization', 'commit_docs', 'model_profile',
|
|
12
|
+
'search_gitignored', 'brave_search',
|
|
13
|
+
'workflow.research', 'workflow.plan_check', 'workflow.verifier',
|
|
14
|
+
'workflow.nyquist_validation', 'workflow.ui_phase', 'workflow.ui_safety_gate',
|
|
15
|
+
'workflow._auto_chain_active',
|
|
16
|
+
'git.branching_strategy', 'git.phase_branch_template', 'git.milestone_branch_template',
|
|
17
|
+
'planning.commit_docs', 'planning.search_gitignored',
|
|
18
|
+
]);
|
|
19
|
+
|
|
20
|
+
function cmdConfigEnsureSection(cwd, raw) {
|
|
21
|
+
const configPath = path.join(cwd, '.planning', 'config.json');
|
|
22
|
+
const planningDir = path.join(cwd, '.planning');
|
|
23
|
+
|
|
24
|
+
// Ensure .planning directory exists
|
|
25
|
+
try {
|
|
26
|
+
if (!fs.existsSync(planningDir)) {
|
|
27
|
+
fs.mkdirSync(planningDir, { recursive: true });
|
|
28
|
+
}
|
|
29
|
+
} catch (err) {
|
|
30
|
+
error('Failed to create .planning directory: ' + err.message);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Check if config already exists
|
|
34
|
+
if (fs.existsSync(configPath)) {
|
|
35
|
+
const result = { created: false, reason: 'already_exists' };
|
|
36
|
+
output(result, raw, 'exists');
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Detect Brave Search API key availability (prefer ~/.ez)
|
|
41
|
+
const homedir = require('os').homedir();
|
|
42
|
+
const braveKeyCandidates = [
|
|
43
|
+
path.join(homedir, '.ez', 'brave_api_key'),
|
|
44
|
+
];
|
|
45
|
+
const hasBraveSearch = !!(process.env.BRAVE_API_KEY || braveKeyCandidates.some(p => fs.existsSync(p)));
|
|
46
|
+
|
|
47
|
+
// Load user-level defaults from ~/.ez/defaults.json
|
|
48
|
+
const defaultsCandidates = [
|
|
49
|
+
path.join(homedir, '.ez', 'defaults.json'),
|
|
50
|
+
];
|
|
51
|
+
const existingDefaultsPath = defaultsCandidates.find(p => fs.existsSync(p));
|
|
52
|
+
const globalDefaultsPath = existingDefaultsPath || defaultsCandidates[0];
|
|
53
|
+
let userDefaults = {};
|
|
54
|
+
try {
|
|
55
|
+
if (existingDefaultsPath) {
|
|
56
|
+
userDefaults = JSON.parse(fs.readFileSync(globalDefaultsPath, 'utf-8'));
|
|
57
|
+
// Migrate deprecated "depth" key to "granularity"
|
|
58
|
+
if ('depth' in userDefaults && !('granularity' in userDefaults)) {
|
|
59
|
+
const depthToGranularity = { quick: 'coarse', standard: 'standard', comprehensive: 'fine' };
|
|
60
|
+
userDefaults.granularity = depthToGranularity[userDefaults.depth] || userDefaults.depth;
|
|
61
|
+
delete userDefaults.depth;
|
|
62
|
+
try { fs.writeFileSync(globalDefaultsPath, JSON.stringify(userDefaults, null, 2), 'utf-8'); } catch {}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
} catch (err) {
|
|
66
|
+
// Ignore malformed global defaults, fall back to hardcoded
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Create default config (user-level defaults override hardcoded defaults)
|
|
70
|
+
const hardcoded = {
|
|
71
|
+
model_profile: 'balanced',
|
|
72
|
+
commit_docs: true,
|
|
73
|
+
search_gitignored: false,
|
|
74
|
+
branching_strategy: 'none',
|
|
75
|
+
phase_branch_template: 'ez/phase-{phase}-{slug}',
|
|
76
|
+
milestone_branch_template: 'ez/{milestone}-{slug}',
|
|
77
|
+
workflow: {
|
|
78
|
+
research: true,
|
|
79
|
+
plan_check: true,
|
|
80
|
+
verifier: true,
|
|
81
|
+
nyquist_validation: true,
|
|
82
|
+
},
|
|
83
|
+
parallelization: true,
|
|
84
|
+
brave_search: hasBraveSearch,
|
|
85
|
+
};
|
|
86
|
+
const defaults = {
|
|
87
|
+
...hardcoded,
|
|
88
|
+
...userDefaults,
|
|
89
|
+
workflow: { ...hardcoded.workflow, ...(userDefaults.workflow || {}) },
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
try {
|
|
93
|
+
safePlanningWriteSync(configPath, JSON.stringify(defaults, null, 2));
|
|
94
|
+
const result = { created: true, path: '.planning/config.json' };
|
|
95
|
+
output(result, raw, 'created');
|
|
96
|
+
} catch (err) {
|
|
97
|
+
error('Failed to create config.json: ' + err.message);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function cmdConfigSet(cwd, keyPath, value, raw) {
|
|
102
|
+
const configPath = path.join(cwd, '.planning', 'config.json');
|
|
103
|
+
|
|
104
|
+
if (!keyPath) {
|
|
105
|
+
error('Usage: config-set <key.path> <value>');
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (!VALID_CONFIG_KEYS.has(keyPath)) {
|
|
109
|
+
error(`Unknown config key: "${keyPath}". Valid keys: ${[...VALID_CONFIG_KEYS].sort().join(', ')}`);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Parse value (handle booleans and numbers)
|
|
113
|
+
let parsedValue = value;
|
|
114
|
+
if (value === 'true') parsedValue = true;
|
|
115
|
+
else if (value === 'false') parsedValue = false;
|
|
116
|
+
else if (!isNaN(value) && value !== '') parsedValue = Number(value);
|
|
117
|
+
|
|
118
|
+
// Load existing config or start with empty object
|
|
119
|
+
let config = {};
|
|
120
|
+
try {
|
|
121
|
+
if (fs.existsSync(configPath)) {
|
|
122
|
+
config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
123
|
+
}
|
|
124
|
+
} catch (err) {
|
|
125
|
+
error('Failed to read config.json: ' + err.message);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Set nested value using dot notation (e.g., "workflow.research")
|
|
129
|
+
const keys = keyPath.split('.');
|
|
130
|
+
let current = config;
|
|
131
|
+
for (let i = 0; i < keys.length - 1; i++) {
|
|
132
|
+
const key = keys[i];
|
|
133
|
+
if (current[key] === undefined || typeof current[key] !== 'object') {
|
|
134
|
+
current[key] = {};
|
|
135
|
+
}
|
|
136
|
+
current = current[key];
|
|
137
|
+
}
|
|
138
|
+
current[keys[keys.length - 1]] = parsedValue;
|
|
139
|
+
|
|
140
|
+
// Write back
|
|
141
|
+
try {
|
|
142
|
+
safePlanningWriteSync(configPath, JSON.stringify(config, null, 2));
|
|
143
|
+
const result = { updated: true, key: keyPath, value: parsedValue };
|
|
144
|
+
output(result, raw, `${keyPath}=${parsedValue}`);
|
|
145
|
+
} catch (err) {
|
|
146
|
+
error('Failed to write config.json: ' + err.message);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function cmdConfigGet(cwd, keyPath, raw) {
|
|
151
|
+
const configPath = path.join(cwd, '.planning', 'config.json');
|
|
152
|
+
|
|
153
|
+
if (!keyPath) {
|
|
154
|
+
error('Usage: config-get <key.path>');
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
let config = {};
|
|
158
|
+
try {
|
|
159
|
+
if (fs.existsSync(configPath)) {
|
|
160
|
+
config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
161
|
+
} else {
|
|
162
|
+
error('No config.json found at ' + configPath);
|
|
163
|
+
}
|
|
164
|
+
} catch (err) {
|
|
165
|
+
if (err.message.startsWith('No config.json')) throw err;
|
|
166
|
+
error('Failed to read config.json: ' + err.message);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Traverse dot-notation path (e.g., "workflow.auto_advance")
|
|
170
|
+
const keys = keyPath.split('.');
|
|
171
|
+
let current = config;
|
|
172
|
+
for (const key of keys) {
|
|
173
|
+
if (current === undefined || current === null || typeof current !== 'object') {
|
|
174
|
+
error(`Key not found: ${keyPath}`);
|
|
175
|
+
}
|
|
176
|
+
current = current[key];
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (current === undefined) {
|
|
180
|
+
error(`Key not found: ${keyPath}`);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
output(current, raw, String(current));
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
module.exports = {
|
|
187
|
+
cmdConfigEnsureSection,
|
|
188
|
+
cmdConfigSet,
|
|
189
|
+
cmdConfigGet,
|
|
190
|
+
};
|
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
const fs = require('fs');
|
|
6
6
|
const path = require('path');
|
|
7
|
-
const {
|
|
7
|
+
const { safeExec, auditExec } = require('./safe-exec.cjs');
|
|
8
|
+
const { defaultLogger: logger } = require('./logger.cjs');
|
|
8
9
|
|
|
9
10
|
// ─── Path helpers ────────────────────────────────────────────────────────────
|
|
10
11
|
|
|
@@ -63,7 +64,8 @@ function error(message) {
|
|
|
63
64
|
function safeReadFile(filePath) {
|
|
64
65
|
try {
|
|
65
66
|
return fs.readFileSync(filePath, 'utf-8');
|
|
66
|
-
} catch {
|
|
67
|
+
} catch (err) {
|
|
68
|
+
logger.warn('safeReadFile failed', { filePath, error: err.message });
|
|
67
69
|
return null;
|
|
68
70
|
}
|
|
69
71
|
}
|
|
@@ -75,8 +77,8 @@ function loadConfig(cwd) {
|
|
|
75
77
|
commit_docs: true,
|
|
76
78
|
search_gitignored: false,
|
|
77
79
|
branching_strategy: 'none',
|
|
78
|
-
phase_branch_template: '
|
|
79
|
-
milestone_branch_template: '
|
|
80
|
+
phase_branch_template: 'ez/phase-{phase}-{slug}',
|
|
81
|
+
milestone_branch_template: 'ez/{milestone}-{slug}',
|
|
80
82
|
research: true,
|
|
81
83
|
plan_checker: true,
|
|
82
84
|
verifier: true,
|
|
@@ -94,7 +96,11 @@ function loadConfig(cwd) {
|
|
|
94
96
|
const depthToGranularity = { quick: 'coarse', standard: 'standard', comprehensive: 'fine' };
|
|
95
97
|
parsed.granularity = depthToGranularity[parsed.depth] || parsed.depth;
|
|
96
98
|
delete parsed.depth;
|
|
97
|
-
try {
|
|
99
|
+
try {
|
|
100
|
+
fs.writeFileSync(configPath, JSON.stringify(parsed, null, 2), 'utf-8');
|
|
101
|
+
} catch (err) {
|
|
102
|
+
logger.warn('Failed to persist migrated config depth->granularity', { configPath, error: err.message });
|
|
103
|
+
}
|
|
98
104
|
}
|
|
99
105
|
|
|
100
106
|
const get = (key, nested) => {
|
|
@@ -127,42 +133,43 @@ function loadConfig(cwd) {
|
|
|
127
133
|
brave_search: get('brave_search') ?? defaults.brave_search,
|
|
128
134
|
model_overrides: parsed.model_overrides || null,
|
|
129
135
|
};
|
|
130
|
-
} catch {
|
|
136
|
+
} catch (err) {
|
|
137
|
+
logger.warn('Failed to load config, using defaults', { configPath, error: err.message });
|
|
131
138
|
return defaults;
|
|
132
139
|
}
|
|
133
140
|
}
|
|
134
141
|
|
|
135
142
|
// ─── Git utilities ────────────────────────────────────────────────────────────
|
|
136
143
|
|
|
137
|
-
function isGitIgnored(cwd, targetPath) {
|
|
144
|
+
async function isGitIgnored(cwd, targetPath) {
|
|
138
145
|
try {
|
|
139
146
|
// --no-index checks .gitignore rules regardless of whether the file is tracked.
|
|
140
147
|
// Without it, git check-ignore returns "not ignored" for tracked files even when
|
|
141
148
|
// .gitignore explicitly lists them — a common source of confusion when .planning/
|
|
142
149
|
// was committed before being added to .gitignore.
|
|
143
|
-
|
|
150
|
+
const safePath = targetPath.replace(/[^a-zA-Z0-9._\-/]/g, '');
|
|
151
|
+
await auditExec('git', ['check-ignore', '-q', '--no-index', '--', safePath], {
|
|
144
152
|
cwd,
|
|
145
|
-
|
|
153
|
+
context: 'isGitIgnored',
|
|
154
|
+
timeout: 5000
|
|
146
155
|
});
|
|
147
156
|
return true;
|
|
148
|
-
} catch {
|
|
157
|
+
} catch (err) {
|
|
158
|
+
logger.warn('git check-ignore failed, assuming not ignored', { targetPath, error: err.message });
|
|
149
159
|
return false;
|
|
150
160
|
}
|
|
151
161
|
}
|
|
152
162
|
|
|
153
|
-
function execGit(cwd, args) {
|
|
163
|
+
async function execGit(cwd, args) {
|
|
154
164
|
try {
|
|
155
|
-
const
|
|
156
|
-
if (/^[a-zA-Z0-9._\-/=:@]+$/.test(a)) return a;
|
|
157
|
-
return "'" + a.replace(/'/g, "'\\''") + "'";
|
|
158
|
-
});
|
|
159
|
-
const stdout = execSync('git ' + escaped.join(' '), {
|
|
165
|
+
const stdout = await auditExec('git', args, {
|
|
160
166
|
cwd,
|
|
161
|
-
|
|
162
|
-
|
|
167
|
+
context: 'execGit',
|
|
168
|
+
timeout: 30000
|
|
163
169
|
});
|
|
164
170
|
return { exitCode: 0, stdout: stdout.trim(), stderr: '' };
|
|
165
171
|
} catch (err) {
|
|
172
|
+
logger.warn('execGit failed', { args, error: err.message });
|
|
166
173
|
return {
|
|
167
174
|
exitCode: err.status ?? 1,
|
|
168
175
|
stdout: (err.stdout ?? '').toString().trim(),
|
|
@@ -254,7 +261,8 @@ function searchPhaseInDir(baseDir, relBase, normalized) {
|
|
|
254
261
|
has_context: hasContext,
|
|
255
262
|
has_verification: hasVerification,
|
|
256
263
|
};
|
|
257
|
-
} catch {
|
|
264
|
+
} catch (err) {
|
|
265
|
+
logger.warn('Failed to search phase directory', { baseDir, normalized, error: err.message });
|
|
258
266
|
return null;
|
|
259
267
|
}
|
|
260
268
|
}
|
|
@@ -291,7 +299,9 @@ function findPhaseInternal(cwd, phase) {
|
|
|
291
299
|
return result;
|
|
292
300
|
}
|
|
293
301
|
}
|
|
294
|
-
} catch {
|
|
302
|
+
} catch (err) {
|
|
303
|
+
logger.warn('Failed while searching archived milestone phases', { milestonesDir, error: err.message });
|
|
304
|
+
}
|
|
295
305
|
|
|
296
306
|
return null;
|
|
297
307
|
}
|
|
@@ -326,7 +336,9 @@ function getArchivedPhaseDirs(cwd) {
|
|
|
326
336
|
});
|
|
327
337
|
}
|
|
328
338
|
}
|
|
329
|
-
} catch {
|
|
339
|
+
} catch (err) {
|
|
340
|
+
logger.warn('Failed to enumerate archived phase directories', { milestonesDir, error: err.message });
|
|
341
|
+
}
|
|
330
342
|
|
|
331
343
|
return results;
|
|
332
344
|
}
|
|
@@ -362,7 +374,8 @@ function getRoadmapPhaseInternal(cwd, phaseNum) {
|
|
|
362
374
|
goal,
|
|
363
375
|
section,
|
|
364
376
|
};
|
|
365
|
-
} catch {
|
|
377
|
+
} catch (err) {
|
|
378
|
+
logger.warn('Failed to read roadmap phase metadata', { roadmapPath, phaseNum, error: err.message });
|
|
366
379
|
return null;
|
|
367
380
|
}
|
|
368
381
|
}
|
|
@@ -391,7 +404,8 @@ function pathExistsInternal(cwd, targetPath) {
|
|
|
391
404
|
try {
|
|
392
405
|
fs.statSync(fullPath);
|
|
393
406
|
return true;
|
|
394
|
-
} catch {
|
|
407
|
+
} catch (err) {
|
|
408
|
+
logger.warn('Path existence check failed', { fullPath, error: err.message });
|
|
395
409
|
return false;
|
|
396
410
|
}
|
|
397
411
|
}
|
|
@@ -431,7 +445,8 @@ function getMilestoneInfo(cwd) {
|
|
|
431
445
|
version: versionMatch ? versionMatch[0] : 'v1.0',
|
|
432
446
|
name: 'milestone',
|
|
433
447
|
};
|
|
434
|
-
} catch {
|
|
448
|
+
} catch (err) {
|
|
449
|
+
logger.warn('Failed to load milestone info, using fallback', { error: err.message });
|
|
435
450
|
return { version: 'v1.0', name: 'milestone' };
|
|
436
451
|
}
|
|
437
452
|
}
|
|
@@ -450,7 +465,9 @@ function getMilestonePhaseFilter(cwd) {
|
|
|
450
465
|
while ((m = phasePattern.exec(roadmap)) !== null) {
|
|
451
466
|
milestonePhaseNums.add(m[1]);
|
|
452
467
|
}
|
|
453
|
-
} catch {
|
|
468
|
+
} catch (err) {
|
|
469
|
+
logger.warn('Failed to parse milestone phases from roadmap', { error: err.message });
|
|
470
|
+
}
|
|
454
471
|
|
|
455
472
|
if (milestonePhaseNums.size === 0) {
|
|
456
473
|
const passAll = () => true;
|