@howlil/ez-agents 3.1.0 → 3.4.1
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/README.md +295 -714
- package/bin/install.js +387 -62
- package/commands/ez/auth.md +87 -0
- package/commands/ez/join-discord.md +1 -1
- package/ez-agents/bin/ez-tools.cjs +120 -2
- package/ez-agents/bin/lib/assistant-adapter.cjs +62 -3
- package/ez-agents/bin/lib/audit-exec.cjs +20 -8
- package/ez-agents/bin/lib/auth.cjs +2 -1
- package/ez-agents/bin/lib/circuit-breaker.cjs +1 -1
- package/ez-agents/bin/lib/commands.cjs +42 -23
- package/ez-agents/bin/lib/config.cjs +18 -11
- package/ez-agents/bin/lib/core.cjs +42 -25
- package/ez-agents/bin/lib/file-lock.cjs +3 -3
- package/ez-agents/bin/lib/fs-utils.cjs +1 -1
- package/ez-agents/bin/lib/git-utils.cjs +1 -1
- package/ez-agents/bin/lib/health-check.cjs +2 -3
- package/ez-agents/bin/lib/index.cjs +1 -1
- package/ez-agents/bin/lib/init.cjs +70 -23
- package/ez-agents/bin/lib/logger.cjs +11 -4
- package/ez-agents/bin/lib/model-provider.cjs +124 -29
- package/ez-agents/bin/lib/phase.cjs +39 -22
- package/ez-agents/bin/lib/planning-write.cjs +107 -0
- package/ez-agents/bin/lib/retry.cjs +1 -1
- package/ez-agents/bin/lib/roadmap.cjs +3 -2
- package/ez-agents/bin/lib/safe-exec.cjs +1 -1
- package/ez-agents/bin/lib/safe-path.cjs +1 -1
- package/ez-agents/bin/lib/state.cjs +24 -9
- package/ez-agents/bin/lib/temp-file.cjs +1 -1
- package/ez-agents/bin/lib/template.cjs +2 -1
- package/ez-agents/bin/lib/test-file-lock.cjs +1 -1
- package/ez-agents/bin/lib/test-graceful.cjs +2 -2
- package/ez-agents/bin/lib/test-logger.cjs +2 -2
- package/ez-agents/bin/lib/test-temp-file.cjs +1 -1
- package/ez-agents/bin/lib/timeout-exec.cjs +4 -3
- package/ez-agents/bin/lib/verify.cjs +54 -25
- package/ez-agents/references/continuation-format.md +1 -1
- package/ez-agents/workflows/add-tests.md +2 -2
- package/ez-agents/workflows/add-todo.md +1 -1
- package/ez-agents/workflows/autonomous.md +15 -15
- package/ez-agents/workflows/diagnose-issues.md +1 -1
- package/ez-agents/workflows/discuss-phase.md +3 -3
- package/ez-agents/workflows/execute-phase.md +2 -2
- package/ez-agents/workflows/health.md +1 -1
- package/ez-agents/workflows/help.md +2 -2
- package/ez-agents/workflows/map-codebase.md +1 -1
- package/ez-agents/workflows/new-milestone.md +5 -5
- package/ez-agents/workflows/new-project.md +12 -10
- package/ez-agents/workflows/plan-phase.md +8 -8
- package/ez-agents/workflows/progress.md +1 -1
- package/ez-agents/workflows/set-profile.md +1 -1
- package/ez-agents/workflows/settings.md +9 -9
- package/ez-agents/workflows/stats.md +1 -1
- package/ez-agents/workflows/ui-phase.md +3 -3
- package/ez-agents/workflows/ui-review.md +2 -2
- package/ez-agents/workflows/update.md +1 -1
- package/ez-agents/workflows/validate-phase.md +3 -3
- package/ez-agents/workflows/verify-work.md +3 -3
- package/package.json +1 -1
- package/scripts/build-hooks.js +1 -1
- package/scripts/fix-qwen-installation.js +144 -0
- package/README.zh-CN.md +0 -702
|
@@ -6,6 +6,7 @@ const fs = require('fs');
|
|
|
6
6
|
const path = require('path');
|
|
7
7
|
const { normalizePhaseName, findPhaseInternal, generateSlugInternal, toPosixPath, output, error } = require('./core.cjs');
|
|
8
8
|
const { reconstructFrontmatter } = require('./frontmatter.cjs');
|
|
9
|
+
const { safePlanningWriteSync } = require('./planning-write.cjs');
|
|
9
10
|
|
|
10
11
|
function cmdTemplateSelect(cwd, planPath, raw) {
|
|
11
12
|
if (!planPath) {
|
|
@@ -214,7 +215,7 @@ function cmdTemplateFill(cwd, templateType, options, raw) {
|
|
|
214
215
|
return;
|
|
215
216
|
}
|
|
216
217
|
|
|
217
|
-
|
|
218
|
+
safePlanningWriteSync(outPath, fullContent);
|
|
218
219
|
const relPath = toPosixPath(path.relative(cwd, outPath));
|
|
219
220
|
output({ created: true, path: relPath, template: templateType }, raw, relPath);
|
|
220
221
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Test script for
|
|
4
|
+
* Test script for EZ Health Check and Timeout Exec
|
|
5
5
|
* Run: node ez-agents/bin/lib/test-graceful.cjs
|
|
6
6
|
*/
|
|
7
7
|
|
|
@@ -9,7 +9,7 @@ const HealthCheck = require('./health-check.cjs');
|
|
|
9
9
|
const { execWithTimeout } = require('./timeout-exec.cjs');
|
|
10
10
|
|
|
11
11
|
console.log('='.repeat(50));
|
|
12
|
-
console.log('Testing
|
|
12
|
+
console.log('Testing EZ Graceful Degradation...');
|
|
13
13
|
console.log('='.repeat(50));
|
|
14
14
|
|
|
15
15
|
// Test 1: Health Check
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Test script for
|
|
4
|
+
* Test script for EZ Logger
|
|
5
5
|
* Run: node ez-agents/bin/lib/test-logger.cjs
|
|
6
6
|
*/
|
|
7
7
|
|
|
@@ -9,7 +9,7 @@ const Logger = require('./logger.cjs');
|
|
|
9
9
|
const path = require('path');
|
|
10
10
|
|
|
11
11
|
console.log('='.repeat(50));
|
|
12
|
-
console.log('Testing
|
|
12
|
+
console.log('Testing EZ Logger...');
|
|
13
13
|
console.log('='.repeat(50));
|
|
14
14
|
|
|
15
15
|
const logger = new Logger();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
4
|
+
* EZ Timeout Exec — Command execution with timeout and fallback
|
|
5
5
|
*
|
|
6
6
|
* Provides safe command execution with configurable timeout
|
|
7
7
|
* Logs errors and supports fallback values
|
|
@@ -30,11 +30,12 @@ const DEFAULT_TIMEOUT = 5000; // 5 seconds
|
|
|
30
30
|
*/
|
|
31
31
|
async function execWithTimeout(cmd, args, options = {}) {
|
|
32
32
|
const { timeout = DEFAULT_TIMEOUT, fallback = null } = options;
|
|
33
|
+
const hasFallback = Object.prototype.hasOwnProperty.call(options, 'fallback');
|
|
33
34
|
|
|
34
35
|
return new Promise(async (resolve, reject) => {
|
|
35
36
|
const timeoutId = setTimeout(() => {
|
|
36
37
|
logger.error(`Command timed out: ${cmd} ${args.join(' ')}`, { timeout });
|
|
37
|
-
if (
|
|
38
|
+
if (hasFallback) {
|
|
38
39
|
logger.info('Using fallback value', { fallback });
|
|
39
40
|
resolve(fallback);
|
|
40
41
|
} else {
|
|
@@ -49,7 +50,7 @@ async function execWithTimeout(cmd, args, options = {}) {
|
|
|
49
50
|
} catch (err) {
|
|
50
51
|
clearTimeout(timeoutId);
|
|
51
52
|
logger.error(`Command failed: ${cmd}`, { error: err.message, args });
|
|
52
|
-
if (
|
|
53
|
+
if (hasFallback) {
|
|
53
54
|
logger.info('Using fallback value', { fallback });
|
|
54
55
|
resolve(fallback);
|
|
55
56
|
} else {
|
|
@@ -7,8 +7,9 @@ const path = require('path');
|
|
|
7
7
|
const { safeReadFile, normalizePhaseName, execGit, findPhaseInternal, getMilestoneInfo, output, error } = require('./core.cjs');
|
|
8
8
|
const { extractFrontmatter, parseMustHavesBlock } = require('./frontmatter.cjs');
|
|
9
9
|
const { writeStateMd } = require('./state.cjs');
|
|
10
|
+
const { defaultLogger: logger } = require('./logger.cjs');
|
|
10
11
|
|
|
11
|
-
function cmdVerifySummary(cwd, summaryPath, checkFileCount, raw) {
|
|
12
|
+
async function cmdVerifySummary(cwd, summaryPath, checkFileCount, raw) {
|
|
12
13
|
if (!summaryPath) {
|
|
13
14
|
error('summary-path required');
|
|
14
15
|
}
|
|
@@ -66,7 +67,7 @@ function cmdVerifySummary(cwd, summaryPath, checkFileCount, raw) {
|
|
|
66
67
|
let commitsExist = false;
|
|
67
68
|
if (hashes.length > 0) {
|
|
68
69
|
for (const hash of hashes.slice(0, 3)) {
|
|
69
|
-
const result = execGit(cwd, ['cat-file', '-t', hash]);
|
|
70
|
+
const result = await execGit(cwd, ['cat-file', '-t', hash]);
|
|
70
71
|
if (result.exitCode === 0 && result.stdout === 'commit') {
|
|
71
72
|
commitsExist = true;
|
|
72
73
|
break;
|
|
@@ -179,7 +180,13 @@ function cmdVerifyPhaseCompleteness(cwd, phase, raw) {
|
|
|
179
180
|
|
|
180
181
|
// List plans and summaries
|
|
181
182
|
let files;
|
|
182
|
-
try {
|
|
183
|
+
try {
|
|
184
|
+
files = fs.readdirSync(phaseDir);
|
|
185
|
+
} catch (err) {
|
|
186
|
+
logger.warn('Failed to read phase directory in cmdVerifyPhaseCompleteness', { phaseDir, error: err.message });
|
|
187
|
+
output({ error: 'Cannot read phase directory' }, raw);
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
183
190
|
|
|
184
191
|
const plans = files.filter(f => f.match(/-PLAN\.md$/i));
|
|
185
192
|
const summaries = files.filter(f => f.match(/-SUMMARY\.md$/i));
|
|
@@ -257,13 +264,13 @@ function cmdVerifyReferences(cwd, filePath, raw) {
|
|
|
257
264
|
}, raw, missing.length === 0 ? 'valid' : 'invalid');
|
|
258
265
|
}
|
|
259
266
|
|
|
260
|
-
function cmdVerifyCommits(cwd, hashes, raw) {
|
|
267
|
+
async function cmdVerifyCommits(cwd, hashes, raw) {
|
|
261
268
|
if (!hashes || hashes.length === 0) { error('At least one commit hash required'); }
|
|
262
269
|
|
|
263
270
|
const valid = [];
|
|
264
271
|
const invalid = [];
|
|
265
272
|
for (const hash of hashes) {
|
|
266
|
-
const result = execGit(cwd, ['cat-file', '-t', hash]);
|
|
273
|
+
const result = await execGit(cwd, ['cat-file', '-t', hash]);
|
|
267
274
|
if (result.exitCode === 0 && result.stdout.trim() === 'commit') {
|
|
268
275
|
valid.push(hash);
|
|
269
276
|
} else {
|
|
@@ -369,7 +376,8 @@ function cmdVerifyKeyLinks(cwd, planFilePath, raw) {
|
|
|
369
376
|
check.detail = `Pattern "${link.pattern}" not found in source or target`;
|
|
370
377
|
}
|
|
371
378
|
}
|
|
372
|
-
} catch {
|
|
379
|
+
} catch (err) {
|
|
380
|
+
logger.warn('Invalid regex while verifying key links', { pattern: link.pattern, error: err.message });
|
|
373
381
|
check.detail = `Invalid regex pattern: ${link.pattern}`;
|
|
374
382
|
}
|
|
375
383
|
} else {
|
|
@@ -426,7 +434,9 @@ function cmdValidateConsistency(cwd, raw) {
|
|
|
426
434
|
const dm = dir.match(/^(\d+[A-Z]?(?:\.\d+)*)/i);
|
|
427
435
|
if (dm) diskPhases.add(dm[1]);
|
|
428
436
|
}
|
|
429
|
-
} catch {
|
|
437
|
+
} catch (err) {
|
|
438
|
+
logger.warn('Failed to enumerate phase directories while validating consistency', { phasesDir, error: err.message });
|
|
439
|
+
}
|
|
430
440
|
|
|
431
441
|
// Check: phases in ROADMAP but not on disk
|
|
432
442
|
for (const p of roadmapPhases) {
|
|
@@ -488,7 +498,9 @@ function cmdValidateConsistency(cwd, raw) {
|
|
|
488
498
|
}
|
|
489
499
|
}
|
|
490
500
|
}
|
|
491
|
-
} catch {
|
|
501
|
+
} catch (err) {
|
|
502
|
+
logger.warn('Failed to validate plan numbering while validating consistency', { phasesDir, error: err.message });
|
|
503
|
+
}
|
|
492
504
|
|
|
493
505
|
// Check: frontmatter in plans has required fields
|
|
494
506
|
try {
|
|
@@ -508,7 +520,9 @@ function cmdValidateConsistency(cwd, raw) {
|
|
|
508
520
|
}
|
|
509
521
|
}
|
|
510
522
|
}
|
|
511
|
-
} catch {
|
|
523
|
+
} catch (err) {
|
|
524
|
+
logger.warn('Failed to validate plan frontmatter while validating consistency', { phasesDir, error: err.message });
|
|
525
|
+
}
|
|
512
526
|
|
|
513
527
|
const passed = errors.length === 0;
|
|
514
528
|
output({ passed, errors, warnings, warning_count: warnings.length }, raw, passed ? 'passed' : 'failed');
|
|
@@ -537,7 +551,7 @@ function cmdValidateHealth(cwd, options, raw) {
|
|
|
537
551
|
|
|
538
552
|
// ─── Check 1: .planning/ exists ───────────────────────────────────────────
|
|
539
553
|
if (!fs.existsSync(planningDir)) {
|
|
540
|
-
addIssue('error', 'E001', '.planning/ directory not found', 'Run /
|
|
554
|
+
addIssue('error', 'E001', '.planning/ directory not found', 'Run /ez:new-project to initialize');
|
|
541
555
|
output({
|
|
542
556
|
status: 'broken',
|
|
543
557
|
errors,
|
|
@@ -550,7 +564,7 @@ function cmdValidateHealth(cwd, options, raw) {
|
|
|
550
564
|
|
|
551
565
|
// ─── Check 2: PROJECT.md exists and has required sections ─────────────────
|
|
552
566
|
if (!fs.existsSync(projectPath)) {
|
|
553
|
-
addIssue('error', 'E002', 'PROJECT.md not found', 'Run /
|
|
567
|
+
addIssue('error', 'E002', 'PROJECT.md not found', 'Run /ez:new-project to create');
|
|
554
568
|
} else {
|
|
555
569
|
const content = fs.readFileSync(projectPath, 'utf-8');
|
|
556
570
|
const requiredSections = ['## What This Is', '## Core Value', '## Requirements'];
|
|
@@ -563,12 +577,12 @@ function cmdValidateHealth(cwd, options, raw) {
|
|
|
563
577
|
|
|
564
578
|
// ─── Check 3: ROADMAP.md exists ───────────────────────────────────────────
|
|
565
579
|
if (!fs.existsSync(roadmapPath)) {
|
|
566
|
-
addIssue('error', 'E003', 'ROADMAP.md not found', 'Run /
|
|
580
|
+
addIssue('error', 'E003', 'ROADMAP.md not found', 'Run /ez:new-milestone to create roadmap');
|
|
567
581
|
}
|
|
568
582
|
|
|
569
583
|
// ─── Check 4: STATE.md exists and references valid phases ─────────────────
|
|
570
584
|
if (!fs.existsSync(statePath)) {
|
|
571
|
-
addIssue('error', 'E004', 'STATE.md not found', 'Run /
|
|
585
|
+
addIssue('error', 'E004', 'STATE.md not found', 'Run /ez:health --repair to regenerate', true);
|
|
572
586
|
repairs.push('regenerateState');
|
|
573
587
|
} else {
|
|
574
588
|
const stateContent = fs.readFileSync(statePath, 'utf-8');
|
|
@@ -584,14 +598,16 @@ function cmdValidateHealth(cwd, options, raw) {
|
|
|
584
598
|
if (m) diskPhases.add(m[1]);
|
|
585
599
|
}
|
|
586
600
|
}
|
|
587
|
-
} catch {
|
|
601
|
+
} catch (err) {
|
|
602
|
+
logger.warn('Failed to read phase directories while validating STATE references', { phasesDir, error: err.message });
|
|
603
|
+
}
|
|
588
604
|
// Check for invalid references
|
|
589
605
|
for (const ref of phaseRefs) {
|
|
590
606
|
const normalizedRef = String(parseInt(ref, 10)).padStart(2, '0');
|
|
591
607
|
if (!diskPhases.has(ref) && !diskPhases.has(normalizedRef) && !diskPhases.has(String(parseInt(ref, 10)))) {
|
|
592
608
|
// Only warn if phases dir has any content (not just an empty project)
|
|
593
609
|
if (diskPhases.size > 0) {
|
|
594
|
-
addIssue('warning', 'W002', `STATE.md references phase ${ref}, but only phases ${[...diskPhases].sort().join(', ')} exist`, 'Run /
|
|
610
|
+
addIssue('warning', 'W002', `STATE.md references phase ${ref}, but only phases ${[...diskPhases].sort().join(', ')} exist`, 'Run /ez:health --repair to regenerate STATE.md', true);
|
|
595
611
|
if (!repairs.includes('regenerateState')) repairs.push('regenerateState');
|
|
596
612
|
}
|
|
597
613
|
}
|
|
@@ -600,7 +616,7 @@ function cmdValidateHealth(cwd, options, raw) {
|
|
|
600
616
|
|
|
601
617
|
// ─── Check 5: config.json valid JSON + valid schema ───────────────────────
|
|
602
618
|
if (!fs.existsSync(configPath)) {
|
|
603
|
-
addIssue('warning', 'W003', 'config.json not found', 'Run /
|
|
619
|
+
addIssue('warning', 'W003', 'config.json not found', 'Run /ez:health --repair to create with defaults', true);
|
|
604
620
|
repairs.push('createConfig');
|
|
605
621
|
} else {
|
|
606
622
|
try {
|
|
@@ -612,7 +628,8 @@ function cmdValidateHealth(cwd, options, raw) {
|
|
|
612
628
|
addIssue('warning', 'W004', `config.json: invalid model_profile "${parsed.model_profile}"`, `Valid values: ${validProfiles.join(', ')}`);
|
|
613
629
|
}
|
|
614
630
|
} catch (err) {
|
|
615
|
-
|
|
631
|
+
logger.warn('Failed to parse config.json in cmdValidateHealth', { configPath, error: err.message });
|
|
632
|
+
addIssue('error', 'E005', `config.json: JSON parse error - ${err.message}`, 'Run /ez:health --repair to reset to defaults', true);
|
|
616
633
|
repairs.push('resetConfig');
|
|
617
634
|
}
|
|
618
635
|
}
|
|
@@ -623,10 +640,12 @@ function cmdValidateHealth(cwd, options, raw) {
|
|
|
623
640
|
const configRaw = fs.readFileSync(configPath, 'utf-8');
|
|
624
641
|
const configParsed = JSON.parse(configRaw);
|
|
625
642
|
if (configParsed.workflow && configParsed.workflow.nyquist_validation === undefined) {
|
|
626
|
-
addIssue('warning', 'W008', 'config.json: workflow.nyquist_validation absent (defaults to enabled but agents may skip)', 'Run /
|
|
643
|
+
addIssue('warning', 'W008', 'config.json: workflow.nyquist_validation absent (defaults to enabled but agents may skip)', 'Run /ez:health --repair to add key', true);
|
|
627
644
|
if (!repairs.includes('addNyquistKey')) repairs.push('addNyquistKey');
|
|
628
645
|
}
|
|
629
|
-
} catch {
|
|
646
|
+
} catch (err) {
|
|
647
|
+
logger.warn('Failed to parse config for nyquist key check', { configPath, error: err.message });
|
|
648
|
+
}
|
|
630
649
|
}
|
|
631
650
|
|
|
632
651
|
// ─── Check 6: Phase directory naming (NN-name format) ─────────────────────
|
|
@@ -637,7 +656,9 @@ function cmdValidateHealth(cwd, options, raw) {
|
|
|
637
656
|
addIssue('warning', 'W005', `Phase directory "${e.name}" doesn't follow NN-name format`, 'Rename to match pattern (e.g., 01-setup)');
|
|
638
657
|
}
|
|
639
658
|
}
|
|
640
|
-
} catch {
|
|
659
|
+
} catch (err) {
|
|
660
|
+
logger.warn('Failed to inspect phase directory naming in health validation', { phasesDir, error: err.message });
|
|
661
|
+
}
|
|
641
662
|
|
|
642
663
|
// ─── Check 7: Orphaned plans (PLAN without SUMMARY) ───────────────────────
|
|
643
664
|
try {
|
|
@@ -656,7 +677,9 @@ function cmdValidateHealth(cwd, options, raw) {
|
|
|
656
677
|
}
|
|
657
678
|
}
|
|
658
679
|
}
|
|
659
|
-
} catch {
|
|
680
|
+
} catch (err) {
|
|
681
|
+
logger.warn('Failed to inspect orphaned plans in health validation', { phasesDir, error: err.message });
|
|
682
|
+
}
|
|
660
683
|
|
|
661
684
|
// ─── Check 7b: Nyquist VALIDATION.md consistency ────────────────────────
|
|
662
685
|
try {
|
|
@@ -670,11 +693,13 @@ function cmdValidateHealth(cwd, options, raw) {
|
|
|
670
693
|
const researchFile = phaseFiles.find(f => f.endsWith('-RESEARCH.md'));
|
|
671
694
|
const researchContent = fs.readFileSync(path.join(phasesDir, e.name, researchFile), 'utf-8');
|
|
672
695
|
if (researchContent.includes('## Validation Architecture')) {
|
|
673
|
-
addIssue('warning', 'W009', `Phase ${e.name}: has Validation Architecture in RESEARCH.md but no VALIDATION.md`, 'Re-run /
|
|
696
|
+
addIssue('warning', 'W009', `Phase ${e.name}: has Validation Architecture in RESEARCH.md but no VALIDATION.md`, 'Re-run /ez-plan-phase with --research to regenerate');
|
|
674
697
|
}
|
|
675
698
|
}
|
|
676
699
|
}
|
|
677
|
-
} catch {
|
|
700
|
+
} catch (err) {
|
|
701
|
+
logger.warn('Failed to inspect validation architecture consistency in health validation', { phasesDir, error: err.message });
|
|
702
|
+
}
|
|
678
703
|
|
|
679
704
|
// ─── Check 8: Run existing consistency checks ─────────────────────────────
|
|
680
705
|
// Inline subset of cmdValidateConsistency
|
|
@@ -696,7 +721,9 @@ function cmdValidateHealth(cwd, options, raw) {
|
|
|
696
721
|
if (dm) diskPhases.add(dm[1]);
|
|
697
722
|
}
|
|
698
723
|
}
|
|
699
|
-
} catch {
|
|
724
|
+
} catch (err) {
|
|
725
|
+
logger.warn('Failed to run roadmap/disk consistency checks in health validation', { roadmapPath, phasesDir, error: err.message });
|
|
726
|
+
}
|
|
700
727
|
|
|
701
728
|
// Phases in ROADMAP but not on disk
|
|
702
729
|
for (const p of roadmapPhases) {
|
|
@@ -755,7 +782,7 @@ function cmdValidateHealth(cwd, options, raw) {
|
|
|
755
782
|
stateContent += `**Current phase:** (determining...)\n`;
|
|
756
783
|
stateContent += `**Status:** Resuming\n\n`;
|
|
757
784
|
stateContent += `## Session Log\n\n`;
|
|
758
|
-
stateContent += `- ${new Date().toISOString().split('T')[0]}: STATE.md regenerated by /
|
|
785
|
+
stateContent += `- ${new Date().toISOString().split('T')[0]}: STATE.md regenerated by /ez:health --repair\n`;
|
|
759
786
|
writeStateMd(statePath, stateContent, cwd);
|
|
760
787
|
repairActions.push({ action: repair, success: true, path: 'STATE.md' });
|
|
761
788
|
break;
|
|
@@ -772,6 +799,7 @@ function cmdValidateHealth(cwd, options, raw) {
|
|
|
772
799
|
}
|
|
773
800
|
repairActions.push({ action: repair, success: true, path: 'config.json' });
|
|
774
801
|
} catch (err) {
|
|
802
|
+
logger.error('Failed to repair nyquist key', { error: err.message });
|
|
775
803
|
repairActions.push({ action: repair, success: false, error: err.message });
|
|
776
804
|
}
|
|
777
805
|
}
|
|
@@ -779,6 +807,7 @@ function cmdValidateHealth(cwd, options, raw) {
|
|
|
779
807
|
}
|
|
780
808
|
}
|
|
781
809
|
} catch (err) {
|
|
810
|
+
logger.error('Failed to perform repair action', { action: repair, error: err.message });
|
|
782
811
|
repairActions.push({ action: repair, success: false, error: err.message });
|
|
783
812
|
}
|
|
784
813
|
}
|
|
@@ -61,7 +61,7 @@ Exit.
|
|
|
61
61
|
Present banner:
|
|
62
62
|
```
|
|
63
63
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
64
|
-
|
|
64
|
+
EZ ► ADD TESTS — Phase ${phase_number}: ${phase_name}
|
|
65
65
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
66
66
|
```
|
|
67
67
|
</step>
|
|
@@ -275,7 +275,7 @@ Create a test coverage report and present to user:
|
|
|
275
275
|
|
|
276
276
|
```
|
|
277
277
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
278
|
-
|
|
278
|
+
EZ ► TEST GENERATION COMPLETE
|
|
279
279
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
280
280
|
|
|
281
281
|
## Results
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<purpose>
|
|
2
|
-
Capture an idea, task, or issue that surfaces during
|
|
2
|
+
Capture an idea, task, or issue that surfaces during an EZ session as a structured todo for later work. Enables "thought → capture → continue" flow without losing context.
|
|
3
3
|
</purpose>
|
|
4
4
|
|
|
5
5
|
<required_reading>
|
|
@@ -40,7 +40,7 @@ Display startup banner:
|
|
|
40
40
|
|
|
41
41
|
```
|
|
42
42
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
43
|
-
|
|
43
|
+
EZ ► AUTONOMOUS
|
|
44
44
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
45
45
|
|
|
46
46
|
Milestone: {milestone_version} — {milestone_name}
|
|
@@ -73,7 +73,7 @@ Parse the JSON `phases` array.
|
|
|
73
73
|
|
|
74
74
|
```
|
|
75
75
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
76
|
-
|
|
76
|
+
EZ ► AUTONOMOUS ▸ COMPLETE 🎉
|
|
77
77
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
78
78
|
|
|
79
79
|
All phases complete! Nothing left to do.
|
|
@@ -112,7 +112,7 @@ For the current phase, display the progress banner:
|
|
|
112
112
|
|
|
113
113
|
```
|
|
114
114
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
115
|
-
|
|
115
|
+
EZ ► AUTONOMOUS ▸ Phase {N}/{T}: {Name} [████░░░░] {P}%
|
|
116
116
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
117
117
|
```
|
|
118
118
|
|
|
@@ -149,7 +149,7 @@ Check `has_context`. If false → go to handle_blocker: "Smart discuss for phase
|
|
|
149
149
|
**3b. Plan**
|
|
150
150
|
|
|
151
151
|
```
|
|
152
|
-
Skill(skill="
|
|
152
|
+
Skill(skill="ez:plan-phase", args="${PHASE_NUM}")
|
|
153
153
|
```
|
|
154
154
|
|
|
155
155
|
Verify plan produced output — re-run `init phase-op` and check `has_plans`. If false → go to handle_blocker: "Plan phase ${PHASE_NUM} did not produce any plans."
|
|
@@ -157,7 +157,7 @@ Verify plan produced output — re-run `init phase-op` and check `has_plans`. If
|
|
|
157
157
|
**3c. Execute**
|
|
158
158
|
|
|
159
159
|
```
|
|
160
|
-
Skill(skill="
|
|
160
|
+
Skill(skill="ez:execute-phase", args="${PHASE_NUM} --no-transition")
|
|
161
161
|
```
|
|
162
162
|
|
|
163
163
|
**3d. Post-Execution Routing**
|
|
@@ -222,14 +222,14 @@ Ask user via AskUserQuestion:
|
|
|
222
222
|
On **"Run gap closure"**: Execute gap closure cycle (limit: 1 attempt):
|
|
223
223
|
|
|
224
224
|
```
|
|
225
|
-
Skill(skill="
|
|
225
|
+
Skill(skill="ez:plan-phase", args="${PHASE_NUM} --gaps")
|
|
226
226
|
```
|
|
227
227
|
|
|
228
228
|
Verify gap plans were created — re-run `init phase-op ${PHASE_NUM}` and check `has_plans`. If no new gap plans → go to handle_blocker: "Gap closure planning for phase ${PHASE_NUM} did not produce plans."
|
|
229
229
|
|
|
230
230
|
Re-execute:
|
|
231
231
|
```
|
|
232
|
-
Skill(skill="
|
|
232
|
+
Skill(skill="ez:execute-phase", args="${PHASE_NUM} --no-transition")
|
|
233
233
|
```
|
|
234
234
|
|
|
235
235
|
Re-read verification status:
|
|
@@ -260,7 +260,7 @@ On **"Stop autonomous mode"**: Go to handle_blocker with "User stopped — gaps
|
|
|
260
260
|
|
|
261
261
|
Run smart discuss for the current phase. Proposes grey area answers in batch tables — the user accepts or overrides per area. Produces identical CONTEXT.md output to regular discuss-phase.
|
|
262
262
|
|
|
263
|
-
> **Note:** Smart discuss is an autonomous-optimized variant of the `
|
|
263
|
+
> **Note:** Smart discuss is an autonomous-optimized variant of the `ez:discuss-phase` skill. It produces identical CONTEXT.md output but uses batch table proposals instead of sequential questioning. The original `discuss-phase` skill remains unchanged (per CTRL-03). Future milestones may extract this to a separate skill file.
|
|
264
264
|
|
|
265
265
|
**Inputs:** `PHASE_NUM` from execute_phase. Run init to get phase paths:
|
|
266
266
|
|
|
@@ -577,7 +577,7 @@ Display lifecycle transition banner:
|
|
|
577
577
|
|
|
578
578
|
```
|
|
579
579
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
580
|
-
|
|
580
|
+
EZ ► AUTONOMOUS ▸ LIFECYCLE
|
|
581
581
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
582
582
|
|
|
583
583
|
All phases complete → Starting lifecycle: audit → complete → cleanup
|
|
@@ -587,7 +587,7 @@ Display lifecycle transition banner:
|
|
|
587
587
|
**5a. Audit**
|
|
588
588
|
|
|
589
589
|
```
|
|
590
|
-
Skill(skill="
|
|
590
|
+
Skill(skill="ez:audit-milestone")
|
|
591
591
|
```
|
|
592
592
|
|
|
593
593
|
After audit completes, detect the result:
|
|
@@ -643,7 +643,7 @@ On **"Stop"**: Go to handle_blocker with "User stopped — tech debt to address.
|
|
|
643
643
|
**5b. Complete Milestone**
|
|
644
644
|
|
|
645
645
|
```
|
|
646
|
-
Skill(skill="
|
|
646
|
+
Skill(skill="ez:complete-milestone", args="${milestone_version}")
|
|
647
647
|
```
|
|
648
648
|
|
|
649
649
|
After complete-milestone returns, verify it produced output:
|
|
@@ -657,7 +657,7 @@ If the archive file does not exist, go to handle_blocker: "Complete milestone di
|
|
|
657
657
|
**5c. Cleanup**
|
|
658
658
|
|
|
659
659
|
```
|
|
660
|
-
Skill(skill="
|
|
660
|
+
Skill(skill="ez:cleanup")
|
|
661
661
|
```
|
|
662
662
|
|
|
663
663
|
Cleanup shows its own dry-run and asks user for approval internally — this is an acceptable pause per CTRL-01 since it's an explicit decision about file deletion.
|
|
@@ -668,7 +668,7 @@ Display final completion banner:
|
|
|
668
668
|
|
|
669
669
|
```
|
|
670
670
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
671
|
-
|
|
671
|
+
EZ ► AUTONOMOUS ▸ COMPLETE 🎉
|
|
672
672
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
673
673
|
|
|
674
674
|
Milestone: {milestone_version} — {milestone_name}
|
|
@@ -701,7 +701,7 @@ When any phase operation fails or a blocker is detected, present 3 options via A
|
|
|
701
701
|
|
|
702
702
|
```
|
|
703
703
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
704
|
-
|
|
704
|
+
EZ ► AUTONOMOUS ▸ STOPPED
|
|
705
705
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
706
706
|
|
|
707
707
|
Completed: {list of completed phases}
|
|
@@ -732,7 +732,7 @@ When any phase operation fails or a blocker is detected, present 3 options via A
|
|
|
732
732
|
- [ ] Final completion or stop summary displayed
|
|
733
733
|
- [ ] After all phases complete, lifecycle step is invoked (not manual suggestion)
|
|
734
734
|
- [ ] Lifecycle transition banner displayed before audit
|
|
735
|
-
- [ ] Audit invoked via Skill(skill="
|
|
735
|
+
- [ ] Audit invoked via Skill(skill="ez:audit-milestone")
|
|
736
736
|
- [ ] Audit result routing: passed → auto-continue, gaps_found → user decides, tech_debt → user decides
|
|
737
737
|
- [ ] Audit technical failure (no file/no status) routes to handle_blocker
|
|
738
738
|
- [ ] Complete-milestone invoked via Skill() with ${milestone_version} arg
|
|
@@ -168,7 +168,7 @@ node "$HOME/.claude/ez-agents/bin/ez-tools.cjs" commit "docs({phase_num}): add r
|
|
|
168
168
|
Display:
|
|
169
169
|
```
|
|
170
170
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
171
|
-
|
|
171
|
+
EZ ► DIAGNOSIS COMPLETE
|
|
172
172
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
173
173
|
|
|
174
174
|
| Gap (Truth) | Root Cause | Files |
|
|
@@ -697,7 +697,7 @@ node "$HOME/.claude/ez-agents/bin/ez-tools.cjs" config-set workflow._auto_chain_
|
|
|
697
697
|
Display banner:
|
|
698
698
|
```
|
|
699
699
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
700
|
-
|
|
700
|
+
EZ ► AUTO-ADVANCING TO PLAN
|
|
701
701
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
702
702
|
|
|
703
703
|
Context captured. Launching plan-phase...
|
|
@@ -705,7 +705,7 @@ Context captured. Launching plan-phase...
|
|
|
705
705
|
|
|
706
706
|
Launch plan-phase using the Skill tool to avoid nested Task sessions (which cause runtime freezes due to deep agent nesting — see #686):
|
|
707
707
|
```
|
|
708
|
-
Skill(skill="
|
|
708
|
+
Skill(skill="ez:plan-phase", args="${PHASE} --auto")
|
|
709
709
|
```
|
|
710
710
|
|
|
711
711
|
This keeps the auto-advance chain flat — discuss, plan, and execute all run at the same nesting level rather than spawning increasingly deep Task agents.
|
|
@@ -714,7 +714,7 @@ This keeps the auto-advance chain flat — discuss, plan, and execute all run at
|
|
|
714
714
|
- **PHASE COMPLETE** → Full chain succeeded. Display:
|
|
715
715
|
```
|
|
716
716
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
717
|
-
|
|
717
|
+
EZ ► PHASE ${PHASE} COMPLETE
|
|
718
718
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
719
719
|
|
|
720
720
|
Auto-advance pipeline finished: discuss → plan → execute
|
|
@@ -174,7 +174,7 @@ Execute each wave in sequence. Within a wave: parallel if `PARALLELIZATION=true`
|
|
|
174
174
|
|
|
175
175
|
5. **Handle failures:**
|
|
176
176
|
|
|
177
|
-
**Known Claude Code bug (classifyHandoffIfNeeded):** If an agent reports "failed" with error containing `classifyHandoffIfNeeded is not defined`, this is a Claude Code runtime bug — not
|
|
177
|
+
**Known Claude Code bug (classifyHandoffIfNeeded):** If an agent reports "failed" with error containing `classifyHandoffIfNeeded is not defined`, this is a Claude Code runtime bug — not an EZ or agent issue. The error fires in the completion handler AFTER all tool calls finish. In this case: run the same spot-checks as step 4 (SUMMARY.md exists, git commits present, no Self-Check: FAILED). If spot-checks PASS → treat as **successful**. If spot-checks FAIL → treat as real failure below.
|
|
178
178
|
|
|
179
179
|
For real failures: report which plan failed → ask "Continue?" or "Stop?" → if continue, dependent plans may also fail. If stop, partial completion report.
|
|
180
180
|
|
|
@@ -454,7 +454,7 @@ Orchestrator: ~10-15% context. Subagents: fresh 200k each. No polling (Task bloc
|
|
|
454
454
|
</context_efficiency>
|
|
455
455
|
|
|
456
456
|
<failure_handling>
|
|
457
|
-
- **classifyHandoffIfNeeded false failure:** Agent reports "failed" but error is `classifyHandoffIfNeeded is not defined` → Claude Code bug, not
|
|
457
|
+
- **classifyHandoffIfNeeded false failure:** Agent reports "failed" but error is `classifyHandoffIfNeeded is not defined` → Claude Code bug, not EZ. Spot-check (SUMMARY exists, commits present) → if pass, treat as success
|
|
458
458
|
- **Agent fails mid-plan:** Missing SUMMARY.md → report, ask user how to proceed
|
|
459
459
|
- **Dependency chain breaks:** Wave 1 fails → Wave 2 dependents likely fail → user chooses attempt or skip
|
|
460
460
|
- **All agents in wave fail:** Systemic issue → stop, report for investigation
|
|
@@ -119,7 +119,7 @@ Usage: `/ez:execute-phase 5`
|
|
|
119
119
|
### Quick Mode
|
|
120
120
|
|
|
121
121
|
**`/ez:quick`**
|
|
122
|
-
Execute small, ad-hoc tasks with
|
|
122
|
+
Execute small, ad-hoc tasks with EZ guarantees but skip optional agents.
|
|
123
123
|
|
|
124
124
|
Quick mode uses the same system with a shorter path:
|
|
125
125
|
- Spawns planner + executor (skips researcher, checker, verifier)
|
|
@@ -307,7 +307,7 @@ Configure workflow toggles and model profile interactively.
|
|
|
307
307
|
Usage: `/ez:settings`
|
|
308
308
|
|
|
309
309
|
**`/ez:set-profile <profile>`**
|
|
310
|
-
Quick switch model profile for
|
|
310
|
+
Quick switch model profile for EZ agents.
|
|
311
311
|
|
|
312
312
|
- `quality` — Opus everywhere except verification
|
|
313
313
|
- `balanced` — Opus for planning, Sonnet for execution (default)
|
|
@@ -312,5 +312,5 @@ End workflow.
|
|
|
312
312
|
- Read agent output files to collect confirmations
|
|
313
313
|
- All 7 codebase documents exist
|
|
314
314
|
- Clear completion summary with line counts
|
|
315
|
-
- User offered clear next steps in
|
|
315
|
+
- User offered clear next steps in EZ style
|
|
316
316
|
</success_criteria>
|