@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,118 +1,118 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* Implements circuit breaker pattern:
|
|
7
|
-
* - CLOSED: Normal operation
|
|
8
|
-
* - OPEN: Failing, reject requests
|
|
9
|
-
* - HALF_OPEN: Testing if service recovered
|
|
10
|
-
*
|
|
11
|
-
* Usage:
|
|
12
|
-
* const CircuitBreaker = require('./circuit-breaker.cjs');
|
|
13
|
-
* const breaker = new CircuitBreaker({ failureThreshold: 5 });
|
|
14
|
-
* const result = await breaker.execute(() => riskyOperation());
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
const Logger = require('./logger.cjs');
|
|
18
|
-
const logger = new Logger();
|
|
19
|
-
|
|
20
|
-
class CircuitBreaker {
|
|
21
|
-
/**
|
|
22
|
-
* Create circuit breaker
|
|
23
|
-
* @param {Object} options - Configuration
|
|
24
|
-
*/
|
|
25
|
-
constructor(options = {}) {
|
|
26
|
-
this.failureThreshold = options.failureThreshold || 5;
|
|
27
|
-
this.resetTimeout = options.resetTimeout || 60000;
|
|
28
|
-
this.state = 'CLOSED';
|
|
29
|
-
this.failures = 0;
|
|
30
|
-
this.lastFailureTime = null;
|
|
31
|
-
this.successes = 0;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Execute operation with circuit breaker protection
|
|
36
|
-
* @param {Function} operation - Async function to execute
|
|
37
|
-
* @returns {Promise<any>} - Result of operation
|
|
38
|
-
*/
|
|
39
|
-
async execute(operation) {
|
|
40
|
-
// Check if circuit is OPEN
|
|
41
|
-
if (this.state === 'OPEN') {
|
|
42
|
-
const timeSinceFailure = Date.now() - this.lastFailureTime;
|
|
43
|
-
|
|
44
|
-
if (timeSinceFailure > this.resetTimeout) {
|
|
45
|
-
// Try to recover
|
|
46
|
-
this.state = 'HALF_OPEN';
|
|
47
|
-
this.failures = 0;
|
|
48
|
-
logger.info('Circuit breaker HALF_OPEN - testing recovery');
|
|
49
|
-
} else {
|
|
50
|
-
const waitTime = Math.round((this.resetTimeout - timeSinceFailure) / 1000);
|
|
51
|
-
logger.warn('Circuit breaker OPEN - rejecting request', { waitTime });
|
|
52
|
-
throw new Error(`Circuit breaker is OPEN. Try again in ${waitTime}s`);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
try {
|
|
57
|
-
const result = await operation();
|
|
58
|
-
|
|
59
|
-
// Success - reset counters if in HALF_OPEN
|
|
60
|
-
if (this.state === 'HALF_OPEN') {
|
|
61
|
-
this.state = 'CLOSED';
|
|
62
|
-
this.failures = 0;
|
|
63
|
-
logger.info('Circuit breaker CLOSED - service recovered');
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
this.successes++;
|
|
67
|
-
return result;
|
|
68
|
-
} catch (err) {
|
|
69
|
-
this.failures++;
|
|
70
|
-
this.lastFailureTime = Date.now();
|
|
71
|
-
|
|
72
|
-
// Open circuit if threshold reached
|
|
73
|
-
if (this.failures >= this.failureThreshold) {
|
|
74
|
-
this.state = 'OPEN';
|
|
75
|
-
logger.error('Circuit breaker OPEN - failure threshold reached', {
|
|
76
|
-
failures: this.failures
|
|
77
|
-
});
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
throw err;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* Get current state
|
|
86
|
-
* @returns {string} - CLOSED, OPEN, or HALF_OPEN
|
|
87
|
-
*/
|
|
88
|
-
getState() {
|
|
89
|
-
return this.state;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* Get stats
|
|
94
|
-
* @returns {Object} - Statistics
|
|
95
|
-
*/
|
|
96
|
-
getStats() {
|
|
97
|
-
return {
|
|
98
|
-
state: this.state,
|
|
99
|
-
failures: this.failures,
|
|
100
|
-
successes: this.successes,
|
|
101
|
-
failureThreshold: this.failureThreshold,
|
|
102
|
-
lastFailureTime: this.lastFailureTime
|
|
103
|
-
};
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* Reset circuit breaker
|
|
108
|
-
*/
|
|
109
|
-
reset() {
|
|
110
|
-
this.state = 'CLOSED';
|
|
111
|
-
this.failures = 0;
|
|
112
|
-
this.successes = 0;
|
|
113
|
-
this.lastFailureTime = null;
|
|
114
|
-
logger.info('Circuit breaker reset');
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
module.exports = CircuitBreaker;
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* EZ Circuit Breaker — Prevent cascading failures
|
|
5
|
+
*
|
|
6
|
+
* Implements circuit breaker pattern:
|
|
7
|
+
* - CLOSED: Normal operation
|
|
8
|
+
* - OPEN: Failing, reject requests
|
|
9
|
+
* - HALF_OPEN: Testing if service recovered
|
|
10
|
+
*
|
|
11
|
+
* Usage:
|
|
12
|
+
* const CircuitBreaker = require('./circuit-breaker.cjs');
|
|
13
|
+
* const breaker = new CircuitBreaker({ failureThreshold: 5 });
|
|
14
|
+
* const result = await breaker.execute(() => riskyOperation());
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
const Logger = require('./logger.cjs');
|
|
18
|
+
const logger = new Logger();
|
|
19
|
+
|
|
20
|
+
class CircuitBreaker {
|
|
21
|
+
/**
|
|
22
|
+
* Create circuit breaker
|
|
23
|
+
* @param {Object} options - Configuration
|
|
24
|
+
*/
|
|
25
|
+
constructor(options = {}) {
|
|
26
|
+
this.failureThreshold = options.failureThreshold || 5;
|
|
27
|
+
this.resetTimeout = options.resetTimeout || 60000;
|
|
28
|
+
this.state = 'CLOSED';
|
|
29
|
+
this.failures = 0;
|
|
30
|
+
this.lastFailureTime = null;
|
|
31
|
+
this.successes = 0;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Execute operation with circuit breaker protection
|
|
36
|
+
* @param {Function} operation - Async function to execute
|
|
37
|
+
* @returns {Promise<any>} - Result of operation
|
|
38
|
+
*/
|
|
39
|
+
async execute(operation) {
|
|
40
|
+
// Check if circuit is OPEN
|
|
41
|
+
if (this.state === 'OPEN') {
|
|
42
|
+
const timeSinceFailure = Date.now() - this.lastFailureTime;
|
|
43
|
+
|
|
44
|
+
if (timeSinceFailure > this.resetTimeout) {
|
|
45
|
+
// Try to recover
|
|
46
|
+
this.state = 'HALF_OPEN';
|
|
47
|
+
this.failures = 0;
|
|
48
|
+
logger.info('Circuit breaker HALF_OPEN - testing recovery');
|
|
49
|
+
} else {
|
|
50
|
+
const waitTime = Math.round((this.resetTimeout - timeSinceFailure) / 1000);
|
|
51
|
+
logger.warn('Circuit breaker OPEN - rejecting request', { waitTime });
|
|
52
|
+
throw new Error(`Circuit breaker is OPEN. Try again in ${waitTime}s`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
try {
|
|
57
|
+
const result = await operation();
|
|
58
|
+
|
|
59
|
+
// Success - reset counters if in HALF_OPEN
|
|
60
|
+
if (this.state === 'HALF_OPEN') {
|
|
61
|
+
this.state = 'CLOSED';
|
|
62
|
+
this.failures = 0;
|
|
63
|
+
logger.info('Circuit breaker CLOSED - service recovered');
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
this.successes++;
|
|
67
|
+
return result;
|
|
68
|
+
} catch (err) {
|
|
69
|
+
this.failures++;
|
|
70
|
+
this.lastFailureTime = Date.now();
|
|
71
|
+
|
|
72
|
+
// Open circuit if threshold reached
|
|
73
|
+
if (this.failures >= this.failureThreshold) {
|
|
74
|
+
this.state = 'OPEN';
|
|
75
|
+
logger.error('Circuit breaker OPEN - failure threshold reached', {
|
|
76
|
+
failures: this.failures
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
throw err;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Get current state
|
|
86
|
+
* @returns {string} - CLOSED, OPEN, or HALF_OPEN
|
|
87
|
+
*/
|
|
88
|
+
getState() {
|
|
89
|
+
return this.state;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Get stats
|
|
94
|
+
* @returns {Object} - Statistics
|
|
95
|
+
*/
|
|
96
|
+
getStats() {
|
|
97
|
+
return {
|
|
98
|
+
state: this.state,
|
|
99
|
+
failures: this.failures,
|
|
100
|
+
successes: this.successes,
|
|
101
|
+
failureThreshold: this.failureThreshold,
|
|
102
|
+
lastFailureTime: this.lastFailureTime
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Reset circuit breaker
|
|
108
|
+
*/
|
|
109
|
+
reset() {
|
|
110
|
+
this.state = 'CLOSED';
|
|
111
|
+
this.failures = 0;
|
|
112
|
+
this.successes = 0;
|
|
113
|
+
this.lastFailureTime = null;
|
|
114
|
+
logger.info('Circuit breaker reset');
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
module.exports = CircuitBreaker;
|
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
*/
|
|
4
4
|
const fs = require('fs');
|
|
5
5
|
const path = require('path');
|
|
6
|
-
const { execSync } = require('child_process');
|
|
7
6
|
const { safeReadFile, loadConfig, isGitIgnored, execGit, normalizePhaseName, comparePhaseNum, getArchivedPhaseDirs, generateSlugInternal, getMilestoneInfo, resolveModelInternal, MODEL_PROFILES, toPosixPath, output, error, findPhaseInternal } = require('./core.cjs');
|
|
8
7
|
const { extractFrontmatter } = require('./frontmatter.cjs');
|
|
8
|
+
const { defaultLogger: logger } = require('./logger.cjs');
|
|
9
9
|
|
|
10
10
|
function cmdGenerateSlug(text, raw) {
|
|
11
11
|
if (!text) {
|
|
@@ -70,9 +70,13 @@ function cmdListTodos(cwd, area, raw) {
|
|
|
70
70
|
area: todoArea,
|
|
71
71
|
path: toPosixPath(path.join('.planning', 'todos', 'pending', file)),
|
|
72
72
|
});
|
|
73
|
-
} catch {
|
|
73
|
+
} catch (err) {
|
|
74
|
+
logger.warn('Failed to parse todo file in cmdListTodos', { file, error: err.message });
|
|
75
|
+
}
|
|
74
76
|
}
|
|
75
|
-
} catch {
|
|
77
|
+
} catch (err) {
|
|
78
|
+
logger.warn('Failed to list pending todos in cmdListTodos', { pendingDir, error: err.message });
|
|
79
|
+
}
|
|
76
80
|
|
|
77
81
|
const result = { count, todos };
|
|
78
82
|
output(result, raw, count.toString());
|
|
@@ -90,7 +94,8 @@ function cmdVerifyPathExists(cwd, targetPath, raw) {
|
|
|
90
94
|
const type = stats.isDirectory() ? 'directory' : stats.isFile() ? 'file' : 'other';
|
|
91
95
|
const result = { exists: true, type };
|
|
92
96
|
output(result, raw, 'true');
|
|
93
|
-
} catch {
|
|
97
|
+
} catch (err) {
|
|
98
|
+
logger.warn('Path verification failed in cmdVerifyPathExists', { fullPath, error: err.message });
|
|
94
99
|
const result = { exists: false, type: null };
|
|
95
100
|
output(result, raw, 'false');
|
|
96
101
|
}
|
|
@@ -119,7 +124,9 @@ function cmdHistoryDigest(cwd, raw) {
|
|
|
119
124
|
for (const dir of currentDirs) {
|
|
120
125
|
allPhaseDirs.push({ name: dir, fullPath: path.join(phasesDir, dir), milestone: null });
|
|
121
126
|
}
|
|
122
|
-
} catch {
|
|
127
|
+
} catch (err) {
|
|
128
|
+
logger.warn('Failed to enumerate current phase directories in cmdHistoryDigest', { phasesDir, error: err.message });
|
|
129
|
+
}
|
|
123
130
|
}
|
|
124
131
|
|
|
125
132
|
if (allPhaseDirs.length === 0) {
|
|
@@ -177,8 +184,8 @@ function cmdHistoryDigest(cwd, raw) {
|
|
|
177
184
|
fm['tech-stack'].added.forEach(t => digest.tech_stack.add(typeof t === 'string' ? t : t.name));
|
|
178
185
|
}
|
|
179
186
|
|
|
180
|
-
} catch (
|
|
181
|
-
|
|
187
|
+
} catch (err) {
|
|
188
|
+
logger.warn('Skipping malformed summary in cmdHistoryDigest', { summary, dirPath, error: err.message });
|
|
182
189
|
}
|
|
183
190
|
}
|
|
184
191
|
}
|
|
@@ -192,8 +199,9 @@ function cmdHistoryDigest(cwd, raw) {
|
|
|
192
199
|
digest.tech_stack = [...digest.tech_stack];
|
|
193
200
|
|
|
194
201
|
output(digest, raw);
|
|
195
|
-
} catch (
|
|
196
|
-
error('Failed to generate history digest
|
|
202
|
+
} catch (err) {
|
|
203
|
+
logger.error('Failed to generate history digest', { error: err.message });
|
|
204
|
+
error('Failed to generate history digest: ' + err.message);
|
|
197
205
|
}
|
|
198
206
|
}
|
|
199
207
|
|
|
@@ -213,7 +221,7 @@ function cmdResolveModel(cwd, agentType, raw) {
|
|
|
213
221
|
output(result, raw, model);
|
|
214
222
|
}
|
|
215
223
|
|
|
216
|
-
function cmdCommit(cwd, message, files, raw, amend) {
|
|
224
|
+
async function cmdCommit(cwd, message, files, raw, amend) {
|
|
217
225
|
if (!message && !amend) {
|
|
218
226
|
error('commit message required');
|
|
219
227
|
}
|
|
@@ -228,7 +236,7 @@ function cmdCommit(cwd, message, files, raw, amend) {
|
|
|
228
236
|
}
|
|
229
237
|
|
|
230
238
|
// Check if .planning is gitignored
|
|
231
|
-
if (isGitIgnored(cwd, '.planning')) {
|
|
239
|
+
if (await isGitIgnored(cwd, '.planning')) {
|
|
232
240
|
const result = { committed: false, hash: null, reason: 'skipped_gitignored' };
|
|
233
241
|
output(result, raw, 'skipped');
|
|
234
242
|
return;
|
|
@@ -237,12 +245,12 @@ function cmdCommit(cwd, message, files, raw, amend) {
|
|
|
237
245
|
// Stage files
|
|
238
246
|
const filesToStage = files && files.length > 0 ? files : ['.planning/'];
|
|
239
247
|
for (const file of filesToStage) {
|
|
240
|
-
execGit(cwd, ['add', file]);
|
|
248
|
+
await execGit(cwd, ['add', file]);
|
|
241
249
|
}
|
|
242
250
|
|
|
243
251
|
// Commit
|
|
244
252
|
const commitArgs = amend ? ['commit', '--amend', '--no-edit'] : ['commit', '-m', message];
|
|
245
|
-
const commitResult = execGit(cwd, commitArgs);
|
|
253
|
+
const commitResult = await execGit(cwd, commitArgs);
|
|
246
254
|
if (commitResult.exitCode !== 0) {
|
|
247
255
|
if (commitResult.stdout.includes('nothing to commit') || commitResult.stderr.includes('nothing to commit')) {
|
|
248
256
|
const result = { committed: false, hash: null, reason: 'nothing_to_commit' };
|
|
@@ -255,7 +263,7 @@ function cmdCommit(cwd, message, files, raw, amend) {
|
|
|
255
263
|
}
|
|
256
264
|
|
|
257
265
|
// Get short hash
|
|
258
|
-
const hashResult = execGit(cwd, ['rev-parse', '--short', 'HEAD']);
|
|
266
|
+
const hashResult = await execGit(cwd, ['rev-parse', '--short', 'HEAD']);
|
|
259
267
|
const hash = hashResult.exitCode === 0 ? hashResult.stdout : null;
|
|
260
268
|
const result = { committed: true, hash, reason: 'committed' };
|
|
261
269
|
output(result, raw, hash || 'committed');
|
|
@@ -375,6 +383,7 @@ async function cmdWebsearch(query, options, raw) {
|
|
|
375
383
|
results
|
|
376
384
|
}, raw, results.map(r => `${r.title}\n${r.url}\n${r.description}`).join('\n\n'));
|
|
377
385
|
} catch (err) {
|
|
386
|
+
logger.warn('Websearch request failed in cmdWebsearch', { query, error: err.message });
|
|
378
387
|
output({ available: false, error: err.message }, raw, '');
|
|
379
388
|
}
|
|
380
389
|
}
|
|
@@ -411,7 +420,9 @@ function cmdProgressRender(cwd, format, raw) {
|
|
|
411
420
|
|
|
412
421
|
phases.push({ number: phaseNum, name: phaseName, plans, summaries, status });
|
|
413
422
|
}
|
|
414
|
-
} catch {
|
|
423
|
+
} catch (err) {
|
|
424
|
+
logger.warn('Failed to enumerate phase directories in cmdProgressRender', { phasesDir, error: err.message });
|
|
425
|
+
}
|
|
415
426
|
|
|
416
427
|
const percent = totalPlans > 0 ? Math.min(100, Math.round((totalSummaries / totalPlans) * 100)) : 0;
|
|
417
428
|
|
|
@@ -492,7 +503,7 @@ function cmdScaffold(cwd, type, options, raw) {
|
|
|
492
503
|
switch (type) {
|
|
493
504
|
case 'context': {
|
|
494
505
|
filePath = path.join(phaseDir, `${padded}-CONTEXT.md`);
|
|
495
|
-
content = `---\nphase: "${padded}"\nname: "${name || phaseInfo?.phase_name || 'Unnamed'}"\ncreated: ${today}\n---\n\n# Phase ${phase}: ${name || phaseInfo?.phase_name || 'Unnamed'} — Context\n\n## Decisions\n\n_Decisions will be captured during /
|
|
506
|
+
content = `---\nphase: "${padded}"\nname: "${name || phaseInfo?.phase_name || 'Unnamed'}"\ncreated: ${today}\n---\n\n# Phase ${phase}: ${name || phaseInfo?.phase_name || 'Unnamed'} — Context\n\n## Decisions\n\n_Decisions will be captured during /ez-discuss-phase ${phase}_\n\n## Discretion Areas\n\n_Areas where the executor can use judgment_\n\n## Deferred Ideas\n\n_Ideas to consider later_\n`;
|
|
496
507
|
break;
|
|
497
508
|
}
|
|
498
509
|
case 'uat': {
|
|
@@ -532,7 +543,7 @@ function cmdScaffold(cwd, type, options, raw) {
|
|
|
532
543
|
output({ created: true, path: relPath }, raw, relPath);
|
|
533
544
|
}
|
|
534
545
|
|
|
535
|
-
function cmdStats(cwd, format, raw) {
|
|
546
|
+
async function cmdStats(cwd, format, raw) {
|
|
536
547
|
const phasesDir = path.join(cwd, '.planning', 'phases');
|
|
537
548
|
const reqPath = path.join(cwd, '.planning', 'REQUIREMENTS.md');
|
|
538
549
|
const statePath = path.join(cwd, '.planning', 'STATE.md');
|
|
@@ -566,7 +577,9 @@ function cmdStats(cwd, format, raw) {
|
|
|
566
577
|
|
|
567
578
|
phases.push({ number: phaseNum, name: phaseName, plans, summaries, status });
|
|
568
579
|
}
|
|
569
|
-
} catch {
|
|
580
|
+
} catch (err) {
|
|
581
|
+
logger.warn('Failed to enumerate phase directories in cmdStats', { phasesDir, error: err.message });
|
|
582
|
+
}
|
|
570
583
|
|
|
571
584
|
const percent = totalPlans > 0 ? Math.min(100, Math.round((totalSummaries / totalPlans) * 100)) : 0;
|
|
572
585
|
|
|
@@ -581,7 +594,9 @@ function cmdStats(cwd, format, raw) {
|
|
|
581
594
|
requirementsComplete = checked ? checked.length : 0;
|
|
582
595
|
requirementsTotal = requirementsComplete + (unchecked ? unchecked.length : 0);
|
|
583
596
|
}
|
|
584
|
-
} catch {
|
|
597
|
+
} catch (err) {
|
|
598
|
+
logger.warn('Failed to parse REQUIREMENTS.md in cmdStats', { reqPath, error: err.message });
|
|
599
|
+
}
|
|
585
600
|
|
|
586
601
|
// Last activity from STATE.md
|
|
587
602
|
let lastActivity = null;
|
|
@@ -591,17 +606,21 @@ function cmdStats(cwd, format, raw) {
|
|
|
591
606
|
const activityMatch = stateContent.match(/\*\*Last Activity:\*\*\s*(.+)/);
|
|
592
607
|
if (activityMatch) lastActivity = activityMatch[1].trim();
|
|
593
608
|
}
|
|
594
|
-
} catch {
|
|
609
|
+
} catch (err) {
|
|
610
|
+
logger.warn('Failed to read STATE.md in cmdStats', { statePath, error: err.message });
|
|
611
|
+
}
|
|
595
612
|
|
|
596
613
|
// Git stats
|
|
597
614
|
let gitCommits = 0;
|
|
598
615
|
let gitFirstCommitDate = null;
|
|
599
616
|
try {
|
|
600
|
-
const commitCount = execGit(cwd, ['rev-list', '--count', 'HEAD']);
|
|
617
|
+
const commitCount = await execGit(cwd, ['rev-list', '--count', 'HEAD']);
|
|
601
618
|
gitCommits = parseInt(commitCount.trim(), 10) || 0;
|
|
602
|
-
const firstDate = execGit(cwd, ['log', '--reverse', '--format=%as', '--max-count=1']);
|
|
619
|
+
const firstDate = await execGit(cwd, ['log', '--reverse', '--format=%as', '--max-count=1']);
|
|
603
620
|
gitFirstCommitDate = firstDate.trim() || null;
|
|
604
|
-
} catch {
|
|
621
|
+
} catch (err) {
|
|
622
|
+
logger.warn('Failed to compute git stats in cmdStats', { cwd, error: err.message });
|
|
623
|
+
}
|
|
605
624
|
|
|
606
625
|
const completedPhases = phases.filter(p => p.status === 'Complete').length;
|
|
607
626
|
|