5-phase-workflow 1.9.5 → 2.0.0
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 +68 -420
- package/bin/install.js +294 -64
- package/bin/sync-agents.js +50 -11
- package/docs/findings.md +3 -3
- package/docs/workflow-guide.md +110 -1046
- package/package.json +6 -5
- package/src/agents/step-executor-agent.md +49 -0
- package/src/agents/step-orchestrator-agent.md +111 -0
- package/src/agents/verification-agent.md +78 -0
- package/src/commands/5/address-review-findings.md +69 -403
- package/src/commands/5/apply-review-findings.md +66 -0
- package/src/commands/5/configure.md +110 -76
- package/src/commands/5/discuss-feature.md +47 -57
- package/src/commands/5/eject.md +7 -6
- package/src/commands/5/implement.md +202 -0
- package/src/commands/5/plan.md +164 -0
- package/src/commands/5/reconfigure.md +30 -29
- package/src/commands/5/reply-pr-comments.md +46 -0
- package/src/commands/5/review.md +95 -0
- package/src/commands/5/split.md +190 -0
- package/src/commands/5/synchronize-agents.md +4 -4
- package/src/commands/5/triage-pr-comments.md +70 -0
- package/src/commands/5/update.md +8 -8
- package/src/hooks/check-updates.js +50 -7
- package/src/hooks/plan-guard.js +28 -22
- package/src/hooks/statusline.js +50 -4
- package/src/skills/configure-docs-index/SKILL.md +16 -20
- package/src/skills/configure-skills/SKILL.md +12 -12
- package/src/templates/AGENTS.md +94 -0
- package/src/templates/workflow/FIX-PLAN.md +1 -1
- package/src/templates/workflow/PLAN-COMPACT.md +42 -0
- package/src/templates/workflow/PLAN.md +58 -34
- package/src/templates/workflow/REVIEW-FINDINGS.md +7 -16
- package/src/templates/workflow/REVIEW-SUMMARY.md +5 -0
- package/src/templates/workflow/STATE.json +32 -3
- package/src/agents/component-executor.md +0 -57
- package/src/commands/5/implement-feature.md +0 -381
- package/src/commands/5/plan-feature.md +0 -293
- package/src/commands/5/plan-implementation.md +0 -333
- package/src/commands/5/quick-implement.md +0 -375
- package/src/commands/5/review-code.md +0 -212
- package/src/commands/5/verify-implementation.md +0 -277
- package/src/templates/workflow/FEATURE-SPEC.md +0 -100
- package/src/templates/workflow/VERIFICATION-REPORT.md +0 -103
package/bin/install.js
CHANGED
|
@@ -25,15 +25,60 @@ const log = {
|
|
|
25
25
|
header: (msg) => console.log(`\n${colors.bright}${msg}${colors.reset}\n`)
|
|
26
26
|
};
|
|
27
27
|
|
|
28
|
-
// Version comparison (semver)
|
|
29
|
-
// Uses parseInt to handle pre-release tags (e.g., "2-beta" → 2)
|
|
28
|
+
// Version comparison (semver, including pre-release ordering)
|
|
30
29
|
function compareVersions(v1, v2) {
|
|
31
|
-
const
|
|
32
|
-
const
|
|
30
|
+
const parsed1 = parseSemver(v1);
|
|
31
|
+
const parsed2 = parseSemver(v2);
|
|
32
|
+
|
|
33
33
|
for (let i = 0; i < 3; i++) {
|
|
34
|
-
if (
|
|
35
|
-
if (
|
|
34
|
+
if (parsed1.core[i] > parsed2.core[i]) return 1;
|
|
35
|
+
if (parsed1.core[i] < parsed2.core[i]) return -1;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return comparePrerelease(parsed1.prerelease, parsed2.prerelease);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function parseSemver(version) {
|
|
42
|
+
const normalized = String(version || '').trim().replace(/^v/, '').split('+')[0];
|
|
43
|
+
const prereleaseIndex = normalized.indexOf('-');
|
|
44
|
+
const corePart = prereleaseIndex === -1 ? normalized : normalized.slice(0, prereleaseIndex);
|
|
45
|
+
const prereleasePart = prereleaseIndex === -1 ? '' : normalized.slice(prereleaseIndex + 1);
|
|
46
|
+
const core = corePart.split('.').slice(0, 3).map(part => parseInt(part, 10) || 0);
|
|
47
|
+
while (core.length < 3) core.push(0);
|
|
48
|
+
|
|
49
|
+
return {
|
|
50
|
+
core,
|
|
51
|
+
prerelease: prereleasePart ? prereleasePart.split(/[.-]/) : []
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function comparePrerelease(pre1, pre2) {
|
|
56
|
+
if (pre1.length === 0 && pre2.length === 0) return 0;
|
|
57
|
+
if (pre1.length === 0) return 1;
|
|
58
|
+
if (pre2.length === 0) return -1;
|
|
59
|
+
|
|
60
|
+
const length = Math.max(pre1.length, pre2.length);
|
|
61
|
+
for (let i = 0; i < length; i++) {
|
|
62
|
+
const id1 = pre1[i];
|
|
63
|
+
const id2 = pre2[i];
|
|
64
|
+
if (id1 === undefined) return -1;
|
|
65
|
+
if (id2 === undefined) return 1;
|
|
66
|
+
if (id1 === id2) continue;
|
|
67
|
+
|
|
68
|
+
const id1Numeric = /^[0-9]+$/.test(id1);
|
|
69
|
+
const id2Numeric = /^[0-9]+$/.test(id2);
|
|
70
|
+
if (id1Numeric && id2Numeric) {
|
|
71
|
+
const n1 = parseInt(id1, 10);
|
|
72
|
+
const n2 = parseInt(id2, 10);
|
|
73
|
+
if (n1 > n2) return 1;
|
|
74
|
+
if (n1 < n2) return -1;
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
if (id1Numeric) return -1;
|
|
78
|
+
if (id2Numeric) return 1;
|
|
79
|
+
return id1 > id2 ? 1 : -1;
|
|
36
80
|
}
|
|
81
|
+
|
|
37
82
|
return 0;
|
|
38
83
|
}
|
|
39
84
|
|
|
@@ -56,11 +101,15 @@ function getInstalledVersion(isGlobal) {
|
|
|
56
101
|
|
|
57
102
|
// Get package version from package.json
|
|
58
103
|
function getPackageVersion() {
|
|
59
|
-
const pkgPath = path.join(
|
|
104
|
+
const pkgPath = path.join(getPackageRoot(), 'package.json');
|
|
60
105
|
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
61
106
|
return pkg.version;
|
|
62
107
|
}
|
|
63
108
|
|
|
109
|
+
function getPackageRoot() {
|
|
110
|
+
return path.join(__dirname, '..');
|
|
111
|
+
}
|
|
112
|
+
|
|
64
113
|
// Get full version info
|
|
65
114
|
function getVersionInfo(targetPath, isGlobal) {
|
|
66
115
|
const exists = checkExistingInstallation(targetPath);
|
|
@@ -126,9 +175,9 @@ function parseArgs() {
|
|
|
126
175
|
// Show help message
|
|
127
176
|
function showHelp() {
|
|
128
177
|
console.log(`
|
|
129
|
-
${colors.bright}
|
|
178
|
+
${colors.bright}dev-workflow Installer${colors.reset}
|
|
130
179
|
|
|
131
|
-
Usage: npx
|
|
180
|
+
Usage: npx foifi [options]
|
|
132
181
|
|
|
133
182
|
Options:
|
|
134
183
|
--global, -g Install to ~/.claude/ (available across all projects)
|
|
@@ -141,13 +190,13 @@ Options:
|
|
|
141
190
|
--help, -h Show this help message
|
|
142
191
|
|
|
143
192
|
Examples:
|
|
144
|
-
npx
|
|
145
|
-
npx
|
|
146
|
-
npx
|
|
147
|
-
npx
|
|
148
|
-
npx
|
|
149
|
-
npx
|
|
150
|
-
npx
|
|
193
|
+
npx foifi # Install locally for Claude Code
|
|
194
|
+
npx foifi --global # Install globally for Claude Code
|
|
195
|
+
npx foifi --codex # Install locally for Codex CLI
|
|
196
|
+
npx foifi --codex -g # Install globally for Codex CLI
|
|
197
|
+
npx foifi --upgrade # Auto-update to latest version
|
|
198
|
+
npx foifi --check # Check version without updating
|
|
199
|
+
npx foifi --uninstall # Remove from current directory
|
|
151
200
|
`);
|
|
152
201
|
}
|
|
153
202
|
|
|
@@ -228,7 +277,11 @@ function copyDirMerge(src, dest) {
|
|
|
228
277
|
function getSourcePath() {
|
|
229
278
|
// When installed via npm, __dirname is <install-location>/bin
|
|
230
279
|
// Source files are in <install-location>/src
|
|
231
|
-
return path.join(
|
|
280
|
+
return path.join(getPackageRoot(), 'src');
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
function getPackageBinPath() {
|
|
284
|
+
return path.join(getPackageRoot(), 'bin');
|
|
232
285
|
}
|
|
233
286
|
|
|
234
287
|
// Copy directory recursively
|
|
@@ -251,6 +304,25 @@ function copyDir(src, dest) {
|
|
|
251
304
|
}
|
|
252
305
|
}
|
|
253
306
|
|
|
307
|
+
function copyWorkflowHelperBins(targetPath) {
|
|
308
|
+
const managed = getWorkflowManagedFiles();
|
|
309
|
+
if (!managed.binHelpers || managed.binHelpers.length === 0) return;
|
|
310
|
+
|
|
311
|
+
const binSrc = getPackageBinPath();
|
|
312
|
+
const binDest = path.join(targetPath, 'bin');
|
|
313
|
+
if (!fs.existsSync(binDest)) {
|
|
314
|
+
fs.mkdirSync(binDest, { recursive: true });
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
for (const binHelper of managed.binHelpers) {
|
|
318
|
+
const src = path.join(binSrc, binHelper);
|
|
319
|
+
const dest = path.join(binDest, binHelper);
|
|
320
|
+
if (fs.existsSync(src)) {
|
|
321
|
+
fs.copyFileSync(src, dest);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
254
326
|
// Remove directory recursively
|
|
255
327
|
function removeDir(dir) {
|
|
256
328
|
if (fs.existsSync(dir)) {
|
|
@@ -280,11 +352,28 @@ const LEGACY_REMOVED_FILES = [
|
|
|
280
352
|
'agents/integration-agent.md',
|
|
281
353
|
'agents/step-fixer.md',
|
|
282
354
|
'agents/step-verifier.md',
|
|
283
|
-
'agents/verification-agent.md',
|
|
284
355
|
'templates/STACK.md',
|
|
285
356
|
'templates/STRUCTURE.md',
|
|
286
357
|
'templates/CONVENTIONS.md',
|
|
287
|
-
'templates/INTEGRATIONS.md'
|
|
358
|
+
'templates/INTEGRATIONS.md',
|
|
359
|
+
'commands/5/plan-feature.md',
|
|
360
|
+
'commands/5/plan-implementation.md',
|
|
361
|
+
'commands/5/implement-feature.md',
|
|
362
|
+
'commands/5/verify.md',
|
|
363
|
+
'commands/5/verify-implementation.md',
|
|
364
|
+
'commands/5/review-code.md',
|
|
365
|
+
'commands/5/quick-implement.md',
|
|
366
|
+
'agents/component-executor.md',
|
|
367
|
+
'templates/workflow/FEATURE-SPEC.md',
|
|
368
|
+
'templates/workflow/VERIFICATION-REPORT.md',
|
|
369
|
+
'skills/5-plan-feature',
|
|
370
|
+
'skills/5-plan-implementation',
|
|
371
|
+
'skills/5-implement-feature',
|
|
372
|
+
'skills/5-verify',
|
|
373
|
+
'skills/5-verify-implementation',
|
|
374
|
+
'skills/5-review-code',
|
|
375
|
+
'skills/5-quick-implement',
|
|
376
|
+
'skills/configure-project'
|
|
288
377
|
];
|
|
289
378
|
|
|
290
379
|
// Get list of workflow-owned files/directories (not user-created)
|
|
@@ -295,13 +384,14 @@ function getWorkflowManagedFiles() {
|
|
|
295
384
|
|
|
296
385
|
// Agents: separate agent files referenced by commands via agent: frontmatter
|
|
297
386
|
agents: [
|
|
298
|
-
'
|
|
387
|
+
'step-executor-agent.md',
|
|
388
|
+
'step-orchestrator-agent.md',
|
|
389
|
+
'verification-agent.md'
|
|
299
390
|
],
|
|
300
391
|
|
|
301
392
|
// Skills: specific skill directories
|
|
302
393
|
skills: [
|
|
303
394
|
'configure-docs-index',
|
|
304
|
-
'configure-project',
|
|
305
395
|
'configure-skills',
|
|
306
396
|
'generate-readme'
|
|
307
397
|
],
|
|
@@ -320,17 +410,22 @@ function getWorkflowManagedFiles() {
|
|
|
320
410
|
'configure-tables.md'
|
|
321
411
|
],
|
|
322
412
|
|
|
413
|
+
// Helper binaries: runtime scripts invoked by commands
|
|
414
|
+
binHelpers: [
|
|
415
|
+
'sync-agents.js'
|
|
416
|
+
],
|
|
417
|
+
|
|
323
418
|
// Templates: specific template files
|
|
324
419
|
templates: [
|
|
325
420
|
// Project documentation templates
|
|
421
|
+
'AGENTS.md',
|
|
326
422
|
'ARCHITECTURE.md',
|
|
327
423
|
'CONCERNS.md',
|
|
328
424
|
'TESTING.md',
|
|
329
425
|
// Workflow output templates
|
|
330
|
-
'workflow/FEATURE-SPEC.md',
|
|
331
426
|
'workflow/PLAN.md',
|
|
427
|
+
'workflow/PLAN-COMPACT.md',
|
|
332
428
|
'workflow/STATE.json',
|
|
333
|
-
'workflow/VERIFICATION-REPORT.md',
|
|
334
429
|
'workflow/REVIEW-FINDINGS.md',
|
|
335
430
|
'workflow/REVIEW-SUMMARY.md',
|
|
336
431
|
'workflow/FIX-PLAN.md'
|
|
@@ -365,7 +460,7 @@ function getFileManifest() {
|
|
|
365
460
|
manifest.push(`hooks/${hook}`);
|
|
366
461
|
}
|
|
367
462
|
|
|
368
|
-
// Templates are files (may include nested paths like workflow/
|
|
463
|
+
// Templates are files (may include nested paths like workflow/PLAN.md)
|
|
369
464
|
for (const template of managed.templates) {
|
|
370
465
|
manifest.push(`templates/${template}`);
|
|
371
466
|
}
|
|
@@ -377,6 +472,12 @@ function getFileManifest() {
|
|
|
377
472
|
}
|
|
378
473
|
}
|
|
379
474
|
|
|
475
|
+
if (managed.binHelpers) {
|
|
476
|
+
for (const binHelper of managed.binHelpers) {
|
|
477
|
+
manifest.push(`bin/${binHelper}`);
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
|
|
380
481
|
return manifest;
|
|
381
482
|
}
|
|
382
483
|
|
|
@@ -406,9 +507,30 @@ function convertClaudeToCodexMarkdown(content) {
|
|
|
406
507
|
let converted = convertSlashCommandsToCodexMentions(content);
|
|
407
508
|
// Replace .claude/ path references with .codex/
|
|
408
509
|
converted = converted.replace(/\.claude\//g, '.codex/');
|
|
510
|
+
converted = convertClaudeSetupCommandsToCodexNotes(converted);
|
|
409
511
|
return converted;
|
|
410
512
|
}
|
|
411
513
|
|
|
514
|
+
function convertClaudeSetupCommandsToCodexNotes(content) {
|
|
515
|
+
return content
|
|
516
|
+
.replace(
|
|
517
|
+
/1\. "Install now \(recommended\)" — run `claude mcp add context7 -- npx -y @anthropic-ai\/claude-code-mcp-server-context7` via Bash/g,
|
|
518
|
+
'1. "Install now" — not supported automatically in Codex; install Context7 using your Codex MCP setup outside this workflow, then rerun configuration'
|
|
519
|
+
)
|
|
520
|
+
.replace(
|
|
521
|
+
/- If user selects "Install now": execute the install command/g,
|
|
522
|
+
'- If user selects "Install now": explain that Codex cannot run the Claude Code installer command; leave `tools.context7.available = false` unless Context7 is already detected'
|
|
523
|
+
)
|
|
524
|
+
.replace(
|
|
525
|
+
/1\. "Install now \(recommended\)" — run `claude plugin install skill-creator@claude-plugins-official` via Bash/g,
|
|
526
|
+
'1. "Install now" — not supported automatically in Codex; install an equivalent skill authoring workflow outside this command, then rerun configuration'
|
|
527
|
+
)
|
|
528
|
+
.replace(
|
|
529
|
+
/- If user selects "Install now": execute the install command, then set `tools\.skillCreator\.available = true` in the config/g,
|
|
530
|
+
'- If user selects "Install now": explain that Codex cannot run the Claude Code plugin installer; leave `tools.skillCreator.available = false` unless an equivalent tool is already detected'
|
|
531
|
+
);
|
|
532
|
+
}
|
|
533
|
+
|
|
412
534
|
// Generate the adapter header that teaches Codex how to map Claude Code concepts
|
|
413
535
|
function getCodexSkillAdapterHeader(skillName) {
|
|
414
536
|
const invocation = `$${skillName}`;
|
|
@@ -423,8 +545,10 @@ This skill was authored for Claude Code. Map these tool references:
|
|
|
423
545
|
| Claude Code | Codex Equivalent |
|
|
424
546
|
|-------------|------------------|
|
|
425
547
|
| \`AskUserQuestion\` | Ask the user directly in conversation |
|
|
426
|
-
| \`Agent(subagent_type="Explore")\` |
|
|
427
|
-
| \`Agent(prompt="...")\` | \`spawn_agent(message="...")\` |
|
|
548
|
+
| \`Agent(subagent_type="Explore")\` | \`spawn_agent(agent_type="explorer", model="gpt-5.4-mini", reasoning_effort="low", message="...")\` |
|
|
549
|
+
| \`Agent(prompt="...", model="haiku")\` | \`spawn_agent(model="gpt-5.4-mini", reasoning_effort="low", message="...")\` |
|
|
550
|
+
| \`Agent(prompt="...", model="sonnet")\` | \`spawn_agent(model="gpt-5.4", reasoning_effort="medium", message="...")\` |
|
|
551
|
+
| \`Agent(prompt="...")\` | \`spawn_agent(model="gpt-5.4-mini", reasoning_effort="low", message="...")\` unless the prompt explicitly requires complex reasoning |
|
|
428
552
|
| \`Read\` | \`read_file\` |
|
|
429
553
|
| \`Write\` | \`write_file\` |
|
|
430
554
|
| \`Edit\` | \`patch\` |
|
|
@@ -434,10 +558,16 @@ This skill was authored for Claude Code. Map these tool references:
|
|
|
434
558
|
| \`TaskCreate/TaskUpdate\` | Track progress internally |
|
|
435
559
|
| \`EnterPlanMode\` | Not available — use structured output instead |
|
|
436
560
|
|
|
561
|
+
## Codex Token Budget
|
|
562
|
+
- Default to \`gpt-5.4-mini\` with \`reasoning_effort: low\` for exploration, orchestration, simple implementation, and mechanical file edits.
|
|
563
|
+
- Use \`gpt-5.4\` with \`reasoning_effort: medium\` only for complex logic, cross-module behavior, security-sensitive changes, data migrations, final verification with meaningful logic review, or retries after failure.
|
|
564
|
+
- Use stronger models only when a previous cheaper attempt failed for reasoning reasons.
|
|
565
|
+
- Keep the parent skill context lean: delegate read-heavy exploration to explorer agents and pass only compact findings, target paths, pattern references, and command summaries between agents.
|
|
566
|
+
|
|
437
567
|
## Guard Rules (replaces plan-guard hook)
|
|
438
|
-
During planning
|
|
568
|
+
During the planning phase ($5-plan):
|
|
439
569
|
- Do NOT write to any file outside \`.5/\`
|
|
440
|
-
- Do NOT write source code — only
|
|
570
|
+
- Do NOT write source code — only the unified plan and scan cache
|
|
441
571
|
- Do NOT spawn implementation agents — only Explore/research agents
|
|
442
572
|
</codex_skill_adapter>`;
|
|
443
573
|
}
|
|
@@ -447,7 +577,7 @@ function convertClaudeCommandToCodexSkill(content, skillName) {
|
|
|
447
577
|
const converted = convertClaudeToCodexMarkdown(content);
|
|
448
578
|
const { frontmatter, body } = extractFrontmatterAndBody(converted);
|
|
449
579
|
|
|
450
|
-
let description = `Run
|
|
580
|
+
let description = `Run dev-workflow: ${skillName}`;
|
|
451
581
|
if (frontmatter) {
|
|
452
582
|
const maybeDesc = extractFrontmatterField(frontmatter, 'description');
|
|
453
583
|
if (maybeDesc) description = maybeDesc;
|
|
@@ -495,6 +625,11 @@ function getCodexFileManifest() {
|
|
|
495
625
|
manifest.push(`skills/${skill}`);
|
|
496
626
|
}
|
|
497
627
|
|
|
628
|
+
// Agents are copied as-is for Codex skills that reference .codex/agents/
|
|
629
|
+
for (const agent of managed.agents) {
|
|
630
|
+
manifest.push(`agents/${agent}`);
|
|
631
|
+
}
|
|
632
|
+
|
|
498
633
|
// Templates are copied as-is
|
|
499
634
|
for (const template of managed.templates) {
|
|
500
635
|
manifest.push(`templates/${template}`);
|
|
@@ -507,6 +642,12 @@ function getCodexFileManifest() {
|
|
|
507
642
|
}
|
|
508
643
|
}
|
|
509
644
|
|
|
645
|
+
if (managed.binHelpers) {
|
|
646
|
+
for (const binHelper of managed.binHelpers) {
|
|
647
|
+
manifest.push(`bin/${binHelper}`);
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
|
|
510
651
|
// Instructions file
|
|
511
652
|
manifest.push('instructions.md');
|
|
512
653
|
|
|
@@ -590,7 +731,7 @@ function selectiveUpdate(targetPath, sourcePath) {
|
|
|
590
731
|
const src = path.join(templatesSrc, template);
|
|
591
732
|
const dest = path.join(templatesDest, template);
|
|
592
733
|
if (fs.existsSync(src)) {
|
|
593
|
-
// Ensure parent directory exists for nested paths (e.g., workflow/
|
|
734
|
+
// Ensure parent directory exists for nested paths (e.g., workflow/PLAN.md)
|
|
594
735
|
const destDir = path.dirname(dest);
|
|
595
736
|
if (!fs.existsSync(destDir)) {
|
|
596
737
|
fs.mkdirSync(destDir, { recursive: true });
|
|
@@ -616,6 +757,23 @@ function selectiveUpdate(targetPath, sourcePath) {
|
|
|
616
757
|
}
|
|
617
758
|
log.success('Updated references/ (workflow files only)');
|
|
618
759
|
}
|
|
760
|
+
|
|
761
|
+
// Update helper binaries used by runtime commands
|
|
762
|
+
if (managed.binHelpers && managed.binHelpers.length > 0) {
|
|
763
|
+
const binSrc = getPackageBinPath();
|
|
764
|
+
const binDest = path.join(targetPath, 'bin');
|
|
765
|
+
if (!fs.existsSync(binDest)) {
|
|
766
|
+
fs.mkdirSync(binDest, { recursive: true });
|
|
767
|
+
}
|
|
768
|
+
for (const binHelper of managed.binHelpers) {
|
|
769
|
+
const src = path.join(binSrc, binHelper);
|
|
770
|
+
const dest = path.join(binDest, binHelper);
|
|
771
|
+
if (fs.existsSync(src)) {
|
|
772
|
+
fs.copyFileSync(src, dest);
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
log.success('Updated bin/ (workflow helper binaries)');
|
|
776
|
+
}
|
|
619
777
|
}
|
|
620
778
|
|
|
621
779
|
// Clean up files that were previously installed but are no longer managed
|
|
@@ -825,24 +983,25 @@ function mergeSettings(targetPath, sourcePath) {
|
|
|
825
983
|
// Check if installation exists
|
|
826
984
|
function checkExistingInstallation(targetPath) {
|
|
827
985
|
if (activeRuntime === 'codex') {
|
|
828
|
-
// Codex: commands are installed as skills/5-plan
|
|
829
|
-
const markerFile = path.join(targetPath, 'skills', '5-plan
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
986
|
+
// Codex: commands are installed as skills/5-plan/SKILL.md
|
|
987
|
+
const markerFile = path.join(targetPath, 'skills', '5-plan', 'SKILL.md');
|
|
988
|
+
const legacyMarkerFile = path.join(targetPath, 'skills', '5-plan-feature', 'SKILL.md');
|
|
989
|
+
return fs.existsSync(markerFile) || fs.existsSync(legacyMarkerFile);
|
|
990
|
+
}
|
|
991
|
+
const markerFile = path.join(targetPath, 'commands', '5', 'plan.md');
|
|
992
|
+
const legacyMarkerFile = path.join(targetPath, 'commands', '5', 'plan-feature.md');
|
|
993
|
+
return fs.existsSync(markerFile) || fs.existsSync(legacyMarkerFile);
|
|
834
994
|
}
|
|
835
995
|
|
|
836
996
|
// Helper to show commands
|
|
837
997
|
function showCommandsHelp(isGlobal) {
|
|
838
998
|
if (activeRuntime === 'codex') {
|
|
839
999
|
log.info('Available skills (invoke with $ prefix in Codex):');
|
|
840
|
-
log.info(' $5-plan-
|
|
841
|
-
log.info(' $5-
|
|
842
|
-
log.info(' $5-implement-
|
|
843
|
-
log.info(' $5-
|
|
844
|
-
log.info(' $5-review-
|
|
845
|
-
log.info(' $5-address-review-findings - Apply review findings & PR comments');
|
|
1000
|
+
log.info(' $5-plan - Create unified plan');
|
|
1001
|
+
log.info(' $5-split - Split plan into smaller plans');
|
|
1002
|
+
log.info(' $5-implement - Execute implementation + verification');
|
|
1003
|
+
log.info(' $5-review - Code review');
|
|
1004
|
+
log.info(' $5-address-review-findings - Decide review findings & PR comments');
|
|
846
1005
|
log.info(' $5-configure - Interactive project setup');
|
|
847
1006
|
log.info(' $5-reconfigure - Refresh docs/skills (no Q&A)');
|
|
848
1007
|
log.info(' $5-eject - Eject from update mechanism');
|
|
@@ -850,12 +1009,11 @@ function showCommandsHelp(isGlobal) {
|
|
|
850
1009
|
log.info(' $5-synchronize-agents - Sync user content between runtimes');
|
|
851
1010
|
} else {
|
|
852
1011
|
log.info('Available commands:');
|
|
853
|
-
log.info(' /5:plan-
|
|
854
|
-
log.info(' /5:
|
|
855
|
-
log.info(' /5:implement-
|
|
856
|
-
log.info(' /5:
|
|
857
|
-
log.info(' /5:review-
|
|
858
|
-
log.info(' /5:address-review-findings - Apply review findings & PR comments');
|
|
1012
|
+
log.info(' /5:plan - Create unified plan');
|
|
1013
|
+
log.info(' /5:split - Split plan into smaller plans');
|
|
1014
|
+
log.info(' /5:implement - Execute implementation + verification');
|
|
1015
|
+
log.info(' /5:review - Code review');
|
|
1016
|
+
log.info(' /5:address-review-findings - Decide review findings & PR comments');
|
|
859
1017
|
log.info(' /5:configure - Interactive project setup');
|
|
860
1018
|
log.info(' /5:reconfigure - Refresh docs/skills (no Q&A)');
|
|
861
1019
|
log.info(' /5:eject - Eject from update mechanism');
|
|
@@ -889,6 +1047,10 @@ function performFreshInstall(targetPath, sourcePath, isGlobal) {
|
|
|
889
1047
|
// Merge settings
|
|
890
1048
|
mergeSettings(targetPath, sourcePath);
|
|
891
1049
|
|
|
1050
|
+
// Install helper binaries used by runtime commands
|
|
1051
|
+
copyWorkflowHelperBins(targetPath);
|
|
1052
|
+
log.success('Installed bin/ (workflow helper binaries)');
|
|
1053
|
+
|
|
892
1054
|
// Initialize version tracking
|
|
893
1055
|
initializeVersionJson(isGlobal);
|
|
894
1056
|
|
|
@@ -944,40 +1106,41 @@ function performUpdate(targetPath, sourcePath, isGlobal, versionInfo) {
|
|
|
944
1106
|
|
|
945
1107
|
// Generate instructions.md for Codex (replaces hooks + settings.json)
|
|
946
1108
|
function generateCodexInstructions(targetPath) {
|
|
947
|
-
const content = `#
|
|
1109
|
+
const content = `# dev-workflow — Codex Instructions
|
|
948
1110
|
|
|
949
|
-
This file is managed by the
|
|
1111
|
+
This file is managed by the dev-workflow installer. It provides Codex with
|
|
950
1112
|
the context it needs to run the workflow skills correctly.
|
|
951
1113
|
|
|
952
1114
|
## Workflow Overview
|
|
953
1115
|
|
|
954
|
-
The
|
|
1116
|
+
The workflow provides structured feature development:
|
|
955
1117
|
|
|
956
|
-
1. **Plan
|
|
957
|
-
2. **
|
|
958
|
-
3. **
|
|
959
|
-
4. **Verify Implementation** (\`$5-verify-implementation\`) — Build & test verification
|
|
960
|
-
5. **Review Code** (\`$5-review-code\`) — Code review
|
|
1118
|
+
1. **Plan** (\`$5-plan\`) — Requirements, discovery, and unified plan
|
|
1119
|
+
2. **Implement** (\`$5-implement\`) — Orchestrated implementation with inline verification
|
|
1120
|
+
3. **Review** (\`$5-review\`) — Code review
|
|
961
1121
|
|
|
962
1122
|
## Data Directory
|
|
963
1123
|
|
|
964
1124
|
All workflow state lives in \`.5/\` at the project root:
|
|
965
1125
|
- \`.5/config.json\` — Project configuration
|
|
966
|
-
- \`.5/features/{name}/
|
|
967
|
-
- \`.5/features/{name}/plan.md\` — Implementation plans
|
|
1126
|
+
- \`.5/features/{name}/plan.md\` — Unified plan
|
|
968
1127
|
- \`.5/features/{name}/state.json\` — Implementation state
|
|
969
1128
|
|
|
970
1129
|
## Guard Rules
|
|
971
1130
|
|
|
972
|
-
During planning
|
|
1131
|
+
During the planning phase ($5-plan):
|
|
973
1132
|
- Do NOT write files outside \`.5/\`
|
|
974
|
-
- Do NOT write source code — only
|
|
1133
|
+
- Do NOT write source code — only the unified plan and scan cache
|
|
975
1134
|
- The \`.5/.planning-active\` marker indicates planning is in progress
|
|
976
1135
|
|
|
977
1136
|
## Configuration
|
|
978
1137
|
|
|
979
1138
|
Run \`$5-configure\` after installation to set up your project.
|
|
980
1139
|
|
|
1140
|
+
## Codex Token Budget
|
|
1141
|
+
|
|
1142
|
+
Workflow skills use \`gpt-5.4-mini\` with low reasoning for exploration, orchestration, and simple executors by default. They escalate to \`gpt-5.4\` with medium reasoning only for complex logic, security-sensitive work, data migrations, public API changes, final verification that needs logic review, or retries after a cheaper attempt fails.
|
|
1143
|
+
|
|
981
1144
|
## Templates & References
|
|
982
1145
|
|
|
983
1146
|
Templates are in \`.codex/templates/\` and references in \`.codex/references/\`.
|
|
@@ -1038,6 +1201,28 @@ function installCodexSkills(targetPath, sourcePath) {
|
|
|
1038
1201
|
log.success('Installed utility skills');
|
|
1039
1202
|
}
|
|
1040
1203
|
|
|
1204
|
+
// Install workflow agents for Codex skills that reference .codex/agents/
|
|
1205
|
+
function installCodexAgents(targetPath, sourcePath) {
|
|
1206
|
+
const managed = getWorkflowManagedFiles();
|
|
1207
|
+
const agentsSrc = path.join(sourcePath, 'agents');
|
|
1208
|
+
const agentsDest = path.join(targetPath, 'agents');
|
|
1209
|
+
|
|
1210
|
+
if (!fs.existsSync(agentsSrc) || managed.agents.length === 0) return;
|
|
1211
|
+
|
|
1212
|
+
if (!fs.existsSync(agentsDest)) {
|
|
1213
|
+
fs.mkdirSync(agentsDest, { recursive: true });
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
for (const agent of managed.agents) {
|
|
1217
|
+
const src = path.join(agentsSrc, agent);
|
|
1218
|
+
const dest = path.join(agentsDest, agent);
|
|
1219
|
+
if (fs.existsSync(src)) {
|
|
1220
|
+
fs.copyFileSync(src, dest);
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1223
|
+
log.success('Installed agents/');
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1041
1226
|
// Codex fresh installation
|
|
1042
1227
|
function performCodexFreshInstall(targetPath, sourcePath, isGlobal) {
|
|
1043
1228
|
if (!fs.existsSync(targetPath)) {
|
|
@@ -1047,6 +1232,7 @@ function performCodexFreshInstall(targetPath, sourcePath, isGlobal) {
|
|
|
1047
1232
|
|
|
1048
1233
|
// Install commands as Codex skills
|
|
1049
1234
|
installCodexSkills(targetPath, sourcePath);
|
|
1235
|
+
installCodexAgents(targetPath, sourcePath);
|
|
1050
1236
|
|
|
1051
1237
|
// Copy templates and references as-is (just path conversion)
|
|
1052
1238
|
for (const dir of ['templates', 'references']) {
|
|
@@ -1058,6 +1244,10 @@ function performCodexFreshInstall(targetPath, sourcePath, isGlobal) {
|
|
|
1058
1244
|
}
|
|
1059
1245
|
}
|
|
1060
1246
|
|
|
1247
|
+
// Install helper binaries used by runtime commands
|
|
1248
|
+
copyWorkflowHelperBins(targetPath);
|
|
1249
|
+
log.success('Installed bin/ (workflow helper binaries)');
|
|
1250
|
+
|
|
1061
1251
|
// Generate instructions.md (replaces hooks + settings.json)
|
|
1062
1252
|
generateCodexInstructions(targetPath);
|
|
1063
1253
|
|
|
@@ -1103,6 +1293,7 @@ function codexSelectiveUpdate(targetPath, sourcePath) {
|
|
|
1103
1293
|
|
|
1104
1294
|
// Re-install all skills
|
|
1105
1295
|
installCodexSkills(targetPath, sourcePath);
|
|
1296
|
+
installCodexAgents(targetPath, sourcePath);
|
|
1106
1297
|
|
|
1107
1298
|
// Update templates and references
|
|
1108
1299
|
for (const dir of ['templates', 'references']) {
|
|
@@ -1115,6 +1306,10 @@ function codexSelectiveUpdate(targetPath, sourcePath) {
|
|
|
1115
1306
|
}
|
|
1116
1307
|
log.success('Updated templates and references');
|
|
1117
1308
|
|
|
1309
|
+
// Update helper binaries used by runtime commands
|
|
1310
|
+
copyWorkflowHelperBins(targetPath);
|
|
1311
|
+
log.success('Updated bin/ helper binaries');
|
|
1312
|
+
|
|
1118
1313
|
// Regenerate instructions.md
|
|
1119
1314
|
generateCodexInstructions(targetPath);
|
|
1120
1315
|
}
|
|
@@ -1148,7 +1343,7 @@ function performCodexUpdate(targetPath, sourcePath, isGlobal, versionInfo) {
|
|
|
1148
1343
|
function codexUninstall() {
|
|
1149
1344
|
const targetPath = getTargetPath(false);
|
|
1150
1345
|
|
|
1151
|
-
log.header('
|
|
1346
|
+
log.header('dev-workflow Uninstallation (Codex)');
|
|
1152
1347
|
log.info(`Target: ${targetPath}`);
|
|
1153
1348
|
|
|
1154
1349
|
if (!checkExistingInstallation(targetPath)) {
|
|
@@ -1176,6 +1371,13 @@ function codexUninstall() {
|
|
|
1176
1371
|
}
|
|
1177
1372
|
log.success('Removed utility skills (preserved user-created skills)');
|
|
1178
1373
|
|
|
1374
|
+
// Remove agents
|
|
1375
|
+
for (const agent of managed.agents) {
|
|
1376
|
+
const agentPath = path.join(targetPath, 'agents', agent);
|
|
1377
|
+
if (fs.existsSync(agentPath)) fs.unlinkSync(agentPath);
|
|
1378
|
+
}
|
|
1379
|
+
log.success('Removed workflow agents');
|
|
1380
|
+
|
|
1179
1381
|
// Remove templates
|
|
1180
1382
|
for (const template of managed.templates) {
|
|
1181
1383
|
const templatePath = path.join(targetPath, 'templates', template);
|
|
@@ -1192,6 +1394,19 @@ function codexUninstall() {
|
|
|
1192
1394
|
log.success('Removed workflow references');
|
|
1193
1395
|
}
|
|
1194
1396
|
|
|
1397
|
+
// Remove helper binaries
|
|
1398
|
+
if (managed.binHelpers) {
|
|
1399
|
+
for (const binHelper of managed.binHelpers) {
|
|
1400
|
+
const binPath = path.join(targetPath, 'bin', binHelper);
|
|
1401
|
+
if (fs.existsSync(binPath)) fs.unlinkSync(binPath);
|
|
1402
|
+
}
|
|
1403
|
+
const binDir = path.join(targetPath, 'bin');
|
|
1404
|
+
if (fs.existsSync(binDir) && fs.readdirSync(binDir).length === 0) {
|
|
1405
|
+
fs.rmdirSync(binDir);
|
|
1406
|
+
}
|
|
1407
|
+
log.success('Removed workflow helper binaries');
|
|
1408
|
+
}
|
|
1409
|
+
|
|
1195
1410
|
// Remove instructions.md
|
|
1196
1411
|
const instructionsPath = path.join(targetPath, 'instructions.md');
|
|
1197
1412
|
if (fs.existsSync(instructionsPath)) {
|
|
@@ -1217,7 +1432,7 @@ function install(isGlobal, forceUpgrade = false) {
|
|
|
1217
1432
|
const sourcePath = getSourcePath();
|
|
1218
1433
|
|
|
1219
1434
|
const runtimeLabel = activeRuntime === 'codex' ? 'Codex' : 'Claude Code';
|
|
1220
|
-
log.header(`
|
|
1435
|
+
log.header(`dev-workflow Installation (${runtimeLabel})`);
|
|
1221
1436
|
log.info(`Target: ${targetPath}`);
|
|
1222
1437
|
log.info(`Source: ${sourcePath}`);
|
|
1223
1438
|
|
|
@@ -1311,7 +1526,7 @@ function uninstall() {
|
|
|
1311
1526
|
|
|
1312
1527
|
const targetPath = getTargetPath(false); // Always local for uninstall
|
|
1313
1528
|
|
|
1314
|
-
log.header('
|
|
1529
|
+
log.header('dev-workflow Uninstallation');
|
|
1315
1530
|
log.info(`Target: ${targetPath}`);
|
|
1316
1531
|
|
|
1317
1532
|
if (!checkExistingInstallation(targetPath)) {
|
|
@@ -1389,6 +1604,21 @@ function uninstall() {
|
|
|
1389
1604
|
log.success('Removed workflow references (preserved user-created references)');
|
|
1390
1605
|
}
|
|
1391
1606
|
|
|
1607
|
+
// Remove only workflow-managed helper binaries
|
|
1608
|
+
if (managed.binHelpers) {
|
|
1609
|
+
for (const binHelper of managed.binHelpers) {
|
|
1610
|
+
const binPath = path.join(targetPath, 'bin', binHelper);
|
|
1611
|
+
if (fs.existsSync(binPath)) {
|
|
1612
|
+
fs.unlinkSync(binPath);
|
|
1613
|
+
}
|
|
1614
|
+
}
|
|
1615
|
+
const binDir = path.join(targetPath, 'bin');
|
|
1616
|
+
if (fs.existsSync(binDir) && fs.readdirSync(binDir).length === 0) {
|
|
1617
|
+
fs.rmdirSync(binDir);
|
|
1618
|
+
}
|
|
1619
|
+
log.success('Removed workflow helper binaries (preserved user-created binaries)');
|
|
1620
|
+
}
|
|
1621
|
+
|
|
1392
1622
|
// Remove data directory (.5/)
|
|
1393
1623
|
const dataDir = getDataPath(false);
|
|
1394
1624
|
if (fs.existsSync(dataDir)) {
|
|
@@ -1457,7 +1687,7 @@ function main() {
|
|
|
1457
1687
|
}
|
|
1458
1688
|
|
|
1459
1689
|
if (anyUpdateAvailable) {
|
|
1460
|
-
log.info('Run: npx
|
|
1690
|
+
log.info('Run: npx foifi --upgrade');
|
|
1461
1691
|
}
|
|
1462
1692
|
|
|
1463
1693
|
activeRuntime = primaryRuntime;
|