5-phase-workflow 3.0.0 → 3.1.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 +31 -101
- package/bin/install.js +168 -176
- package/bin/sync-agents.js +59 -54
- package/docs/workflow-guide.md +35 -79
- package/package.json +2 -2
- package/src/agents/step-executor-agent.md +5 -4
- package/src/agents/step-orchestrator-agent.md +12 -12
- package/src/agents/verification-agent.md +7 -8
- package/src/codex-commands/plan.md +65 -0
- package/src/commands/5/address-review-findings.md +43 -52
- package/src/commands/5/analyze-feature.md +1 -1
- package/src/commands/5/configure.md +75 -179
- package/src/commands/5/eject.md +0 -1
- package/src/commands/5/implement.md +78 -68
- package/src/commands/5/lean-check.md +75 -0
- package/src/commands/5/reconfigure.md +48 -129
- package/src/commands/5/review.md +42 -14
- package/src/commands/5/synchronize-agents.md +1 -1
- package/src/commands/5/update.md +2 -2
- package/src/hooks/dependency-guard.js +150 -0
- package/src/hooks/statusline.js +5 -3
- package/src/references/configure-tables.md +0 -33
- package/src/settings.json +3 -4
- package/src/skills/baseline-conventions/SKILL.md +37 -0
- package/src/skills/cognitive-load/SKILL.md +52 -0
- package/src/skills/configure-agents-md/SKILL.md +72 -0
- package/src/skills/configure-rules/SKILL.md +146 -0
- package/src/skills/documentation-style/SKILL.md +32 -0
- package/src/skills/fowler-code-smells/SKILL.md +25 -0
- package/src/skills/review-style/SKILL.md +142 -0
- package/src/templates/AGENTS.md +31 -31
- package/src/templates/workflow/REVIEW-FINDINGS.md +2 -9
- package/src/workflows/5-implement.js +67 -92
- package/docs/findings.md +0 -361
- package/docs/progress.md +0 -102
- package/src/commands/5/apply-review-findings.md +0 -66
- package/src/commands/5/commit.md +0 -43
- package/src/commands/5/plan.md +0 -189
- package/src/commands/5/reply-pr-comments.md +0 -48
- package/src/commands/5/split.md +0 -86
- package/src/commands/5/triage-pr-comments.md +0 -70
- package/src/hooks/config-guard.js +0 -30
- package/src/skills/configure-docs-index/SKILL.md +0 -243
- package/src/skills/configure-skills/SKILL.md +0 -311
- package/src/skills/generate-readme/EXAMPLES.md +0 -168
- package/src/skills/generate-readme/SKILL.md +0 -122
- package/src/skills/generate-readme/TEMPLATE.md +0 -141
- package/src/skills/use-index/SKILL.md +0 -49
- package/src/templates/ARCHITECTURE.md +0 -33
- package/src/templates/CONCERNS.md +0 -33
- package/src/templates/CONVENTIONS.md +0 -74
- package/src/templates/TESTING.md +0 -24
- package/src/templates/workflow/FIX-PLAN.md +0 -55
- package/src/templates/workflow/PLAN-COMPACT.md +0 -42
- package/src/templates/workflow/PLAN.md +0 -76
- package/src/templates/workflow/REVIEW-SUMMARY.md +0 -53
package/bin/install.js
CHANGED
|
@@ -323,27 +323,15 @@ function copyWorkflowHelperBins(targetPath) {
|
|
|
323
323
|
}
|
|
324
324
|
}
|
|
325
325
|
|
|
326
|
-
// Remove directory recursively
|
|
326
|
+
// Remove a directory recursively. fs.rmSync removes a symlink itself and never follows it,
|
|
327
|
+
// so a link planted in a checkout cannot redirect the deletion outside the project.
|
|
327
328
|
function removeDir(dir) {
|
|
328
|
-
|
|
329
|
-
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
330
|
-
|
|
331
|
-
for (const entry of entries) {
|
|
332
|
-
const fullPath = path.join(dir, entry.name);
|
|
333
|
-
|
|
334
|
-
if (entry.isDirectory()) {
|
|
335
|
-
removeDir(fullPath);
|
|
336
|
-
} else {
|
|
337
|
-
fs.unlinkSync(fullPath);
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
fs.rmdirSync(dir);
|
|
342
|
-
}
|
|
329
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
343
330
|
}
|
|
344
331
|
|
|
345
|
-
// Files removed from the package
|
|
346
|
-
//
|
|
332
|
+
// Files removed from the package over time. Upgrades from installs with a manifest find
|
|
333
|
+
// removals by manifest diffing; this list covers installs that predate the manifest, and
|
|
334
|
+
// uninstall uses it to clear leftovers.
|
|
347
335
|
const LEGACY_REMOVED_FILES = [
|
|
348
336
|
'agents/feature-planner.md',
|
|
349
337
|
'agents/implementation-planner.md',
|
|
@@ -379,7 +367,35 @@ const LEGACY_REMOVED_FILES = [
|
|
|
379
367
|
'commands/5/discuss-feature.md',
|
|
380
368
|
'templates/workflow/STATE.json',
|
|
381
369
|
'skills/5-unlock',
|
|
382
|
-
'skills/5-discuss-feature'
|
|
370
|
+
'skills/5-discuss-feature',
|
|
371
|
+
// Plans live in .5/planning/; no codebase index, scan cache, generated docs, or split
|
|
372
|
+
'commands/5/split.md',
|
|
373
|
+
'skills/5-split',
|
|
374
|
+
'skills/use-index',
|
|
375
|
+
'skills/configure-docs-index',
|
|
376
|
+
'templates/ARCHITECTURE.md',
|
|
377
|
+
'templates/CONCERNS.md',
|
|
378
|
+
'templates/CONVENTIONS.md',
|
|
379
|
+
'templates/TESTING.md',
|
|
380
|
+
// Lean core: Claude Code covers commits, READMEs, PR replies, and per-pattern skills natively
|
|
381
|
+
'commands/5/commit.md',
|
|
382
|
+
// Claude Code plans in native plan mode under the AGENTS.md Planning rules; $5-plan stays for Codex
|
|
383
|
+
'commands/5/plan.md',
|
|
384
|
+
'commands/5/apply-review-findings.md',
|
|
385
|
+
'commands/5/triage-pr-comments.md',
|
|
386
|
+
'commands/5/reply-pr-comments.md',
|
|
387
|
+
'skills/5-commit',
|
|
388
|
+
'skills/5-apply-review-findings',
|
|
389
|
+
'skills/5-triage-pr-comments',
|
|
390
|
+
'skills/5-reply-pr-comments',
|
|
391
|
+
'skills/generate-readme',
|
|
392
|
+
'skills/configure-skills',
|
|
393
|
+
'hooks/config-guard.js',
|
|
394
|
+
'templates/workflow/FIX-PLAN.md',
|
|
395
|
+
// Plans have no fixed shape; plan mode and $5-plan decide it
|
|
396
|
+
'templates/workflow/PLAN.md',
|
|
397
|
+
'templates/workflow/PLAN-COMPACT.md',
|
|
398
|
+
'templates/workflow/REVIEW-SUMMARY.md'
|
|
383
399
|
];
|
|
384
400
|
|
|
385
401
|
// Get list of workflow-owned files/directories (not user-created)
|
|
@@ -388,6 +404,9 @@ function getWorkflowManagedFiles() {
|
|
|
388
404
|
// Commands: only the 5/ namespace
|
|
389
405
|
commands: ['5'],
|
|
390
406
|
|
|
407
|
+
// Codex-only commands, installed as skills/5-{name}: Claude Code covers them natively
|
|
408
|
+
codexCommands: ['plan.md'],
|
|
409
|
+
|
|
391
410
|
// Agents: separate agent files referenced by commands via agent: frontmatter
|
|
392
411
|
agents: [
|
|
393
412
|
'step-executor-agent.md',
|
|
@@ -402,10 +421,14 @@ function getWorkflowManagedFiles() {
|
|
|
402
421
|
|
|
403
422
|
// Skills: specific skill directories
|
|
404
423
|
skills: [
|
|
405
|
-
'configure-
|
|
406
|
-
'configure-
|
|
407
|
-
|
|
408
|
-
'
|
|
424
|
+
'configure-agents-md',
|
|
425
|
+
'configure-rules',
|
|
426
|
+
// Review styles: review-style runs one reviewer per style listed in its SKILL.md
|
|
427
|
+
'review-style',
|
|
428
|
+
'fowler-code-smells',
|
|
429
|
+
'cognitive-load',
|
|
430
|
+
'documentation-style',
|
|
431
|
+
'baseline-conventions'
|
|
409
432
|
],
|
|
410
433
|
|
|
411
434
|
// Hooks: specific hook files
|
|
@@ -413,7 +436,7 @@ function getWorkflowManagedFiles() {
|
|
|
413
436
|
'statusline.js',
|
|
414
437
|
'check-updates.js',
|
|
415
438
|
'check-reconfig.js',
|
|
416
|
-
'
|
|
439
|
+
'dependency-guard.js'
|
|
417
440
|
],
|
|
418
441
|
|
|
419
442
|
// References: lookup tables and schemas read on-demand by commands
|
|
@@ -428,18 +451,10 @@ function getWorkflowManagedFiles() {
|
|
|
428
451
|
|
|
429
452
|
// Templates: specific template files
|
|
430
453
|
templates: [
|
|
431
|
-
// Project
|
|
454
|
+
// Project instructions template
|
|
432
455
|
'AGENTS.md',
|
|
433
|
-
'ARCHITECTURE.md',
|
|
434
|
-
'CONCERNS.md',
|
|
435
|
-
'CONVENTIONS.md',
|
|
436
|
-
'TESTING.md',
|
|
437
456
|
// Workflow output templates
|
|
438
|
-
'workflow/
|
|
439
|
-
'workflow/PLAN-COMPACT.md',
|
|
440
|
-
'workflow/REVIEW-FINDINGS.md',
|
|
441
|
-
'workflow/REVIEW-SUMMARY.md',
|
|
442
|
-
'workflow/FIX-PLAN.md'
|
|
457
|
+
'workflow/REVIEW-FINDINGS.md'
|
|
443
458
|
]
|
|
444
459
|
};
|
|
445
460
|
}
|
|
@@ -478,7 +493,7 @@ function getFileManifest() {
|
|
|
478
493
|
manifest.push(`hooks/${hook}`);
|
|
479
494
|
}
|
|
480
495
|
|
|
481
|
-
// Templates are files (may include nested paths like workflow/
|
|
496
|
+
// Templates are files (may include nested paths like workflow/REVIEW-FINDINGS.md)
|
|
482
497
|
for (const template of managed.templates) {
|
|
483
498
|
manifest.push(`templates/${template}`);
|
|
484
499
|
}
|
|
@@ -538,14 +553,6 @@ function convertClaudeSetupCommandsToCodexNotes(content) {
|
|
|
538
553
|
.replace(
|
|
539
554
|
/- If user selects "Install now": execute the install command/g,
|
|
540
555
|
'- 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'
|
|
541
|
-
)
|
|
542
|
-
.replace(
|
|
543
|
-
/1\. "Install now \(recommended\)" — run `claude plugin install skill-creator@claude-plugins-official` via Bash/g,
|
|
544
|
-
'1. "Install now" — not supported automatically in Codex; install an equivalent skill authoring workflow outside this command, then rerun configuration'
|
|
545
|
-
)
|
|
546
|
-
.replace(
|
|
547
|
-
/- If user selects "Install now": execute the install command, then set `tools\.skillCreator\.available = true` in the config/g,
|
|
548
|
-
'- 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'
|
|
549
556
|
);
|
|
550
557
|
}
|
|
551
558
|
|
|
@@ -566,6 +573,7 @@ This skill was authored for Claude Code. Map these tool references:
|
|
|
566
573
|
| \`Agent(subagent_type="Explore")\` | \`spawn_agent(agent_type="explorer", model="gpt-5.4-mini", reasoning_effort="low", message="...")\` |
|
|
567
574
|
| \`Agent(prompt="...", model="haiku")\` | \`spawn_agent(model="gpt-5.4-mini", reasoning_effort="low", message="...")\` |
|
|
568
575
|
| \`Agent(prompt="...", model="sonnet")\` | \`spawn_agent(model="gpt-5.4", reasoning_effort="medium", message="...")\` |
|
|
576
|
+
| \`Agent(prompt="...", model="opus", effort="medium")\` | \`spawn_agent(model="gpt-5.4", reasoning_effort="high", message="...")\` |
|
|
569
577
|
| \`Agent(prompt="...")\` | \`spawn_agent(model="gpt-5.4-mini", reasoning_effort="low", message="...")\` unless the prompt explicitly requires complex reasoning |
|
|
570
578
|
| \`Read\` | \`read_file\` |
|
|
571
579
|
| \`Write\` | \`write_file\` |
|
|
@@ -573,9 +581,10 @@ This skill was authored for Claude Code. Map these tool references:
|
|
|
573
581
|
| \`Bash\` | \`shell\` |
|
|
574
582
|
| \`Glob\` | \`glob\` / \`list_directory\` |
|
|
575
583
|
| \`Grep\` | \`grep\` / \`search\` |
|
|
576
|
-
| \`TaskCreate/TaskUpdate\` | Track progress internally |
|
|
577
584
|
| \`Workflow\` | Not available — run the command's prose fallback loop instead |
|
|
578
585
|
| \`Skill(skill="code-review")\` | Not available — run the command's prose fallback loop instead |
|
|
586
|
+
| \`Skill(skill="security-review")\` | Not available — skip it; the prose review fallback of \`$5-review\` covers security |
|
|
587
|
+
| \`Skill(skill="review-style", args="...")\` | Read \`.codex/skills/review-style/SKILL.md\` and follow it with the same arguments |
|
|
579
588
|
| \`EnterPlanMode\` | Not available — run the command's prose fallback loop instead |
|
|
580
589
|
|
|
581
590
|
## Model Mapping (single source of truth)
|
|
@@ -585,28 +594,30 @@ When a skill, plan, or execution-graph component names a model, map it to Codex
|
|
|
585
594
|
|-------------|-------------|------------------|
|
|
586
595
|
| \`haiku\` (or unset) | \`gpt-5.4-mini\` | \`low\` |
|
|
587
596
|
| \`sonnet\` | \`gpt-5.4\` | \`medium\` |
|
|
597
|
+
| \`opus\` | \`gpt-5.4\` | \`high\` |
|
|
588
598
|
| Explore / explorer agent | \`gpt-5.4-mini\` (\`agent_type: explorer\`) | \`low\` |
|
|
589
599
|
|
|
590
600
|
This table is authoritative — skill bodies do not repeat per-call model mappings.
|
|
591
601
|
|
|
592
602
|
## Codex Token Budget
|
|
593
603
|
- Default to \`gpt-5.4-mini\` with \`reasoning_effort: low\` for exploration, orchestration, simple implementation, and mechanical file edits.
|
|
594
|
-
- Use \`gpt-5.4\` with \`reasoning_effort: medium\`
|
|
604
|
+
- Use \`gpt-5.4\` with \`reasoning_effort: medium\` for ordinary logic and final verification with meaningful logic review.
|
|
605
|
+
- Use \`gpt-5.4\` with \`reasoning_effort: high\` only for complex logic, cross-module behavior, security-sensitive changes, data migrations, public API contracts, or retries after failure.
|
|
595
606
|
- Use stronger models only when a previous cheaper attempt failed for reasoning reasons.
|
|
596
607
|
- 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.
|
|
597
608
|
|
|
598
609
|
## Guard Rules (Codex has no plan mode)
|
|
599
610
|
Claude Code enforces these at the harness level via native plan mode. Codex has no equivalent, so honour them yourself while running \`$5-plan\`, **until the user approves the plan**:
|
|
600
|
-
- Do NOT write
|
|
601
|
-
- Do NOT write source code
|
|
611
|
+
- Do NOT write any file — the plan is written only after approval
|
|
612
|
+
- Do NOT write source code
|
|
602
613
|
- Do NOT spawn implementation agents — only Explore/research agents
|
|
603
614
|
|
|
604
|
-
After approval, \`$5-plan\`
|
|
615
|
+
After approval, \`$5-plan\` saves the plan in \`.5/planning/\` and hands it to \`$5-implement\`.
|
|
605
616
|
|
|
606
617
|
## Update & Migration Notices (replaces statusline hooks)
|
|
607
618
|
At the very start of this skill, before doing anything else, read these two files if they exist:
|
|
608
619
|
- \`.5/.update-cache.json\` — if \`latestAvailableVersion\` is set, compare it to \`packageVersion\` in \`.5/version.json\`. If a newer version is available, tell the user: "foifi update available: <version> — run \`npx foifi --codex --upgrade\` to update."
|
|
609
|
-
- \`.5/.migration-
|
|
620
|
+
- \`.5/.migration-v*\` — if a file such as \`.5/.migration-v4\` exists, tell the user: "You upgraded foifi to a new major version. Run \`$5-reconfigure\` to update your project configuration."
|
|
610
621
|
|
|
611
622
|
Show each applicable notice once at the top of your response, then continue with the skill normally. Do not abort the skill because of a notice.
|
|
612
623
|
</codex_skill_adapter>`;
|
|
@@ -644,20 +655,29 @@ function convertClaudeSkillToCodexSkill(content) {
|
|
|
644
655
|
return convertClaudeToCodexMarkdown(content);
|
|
645
656
|
}
|
|
646
657
|
|
|
658
|
+
// Command files that become Codex skills: the shared commands/5/ plus the Codex-only codex-commands/
|
|
659
|
+
// (Claude Code covers those natively, e.g. plan mode replaces /5:plan).
|
|
660
|
+
function getCodexCommandFiles(sourcePath) {
|
|
661
|
+
const toEntry = (dir, file) => ({ name: file.replace('.md', ''), file: path.join(dir, file) });
|
|
662
|
+
const sharedDir = path.join(sourcePath, 'commands', '5');
|
|
663
|
+
const shared = fs.existsSync(sharedDir)
|
|
664
|
+
? fs.readdirSync(sharedDir).filter(f => f.endsWith('.md')).map(f => toEntry(sharedDir, f))
|
|
665
|
+
: [];
|
|
666
|
+
const codexDir = path.join(sourcePath, 'codex-commands');
|
|
667
|
+
const codexOnly = getWorkflowManagedFiles().codexCommands
|
|
668
|
+
.filter(f => fs.existsSync(path.join(codexDir, f)))
|
|
669
|
+
.map(f => toEntry(codexDir, f));
|
|
670
|
+
return [...shared, ...codexOnly];
|
|
671
|
+
}
|
|
672
|
+
|
|
647
673
|
// Get file manifest for Codex installs (skills-based structure)
|
|
648
674
|
function getCodexFileManifest() {
|
|
649
675
|
const managed = getWorkflowManagedFiles();
|
|
650
676
|
const manifest = [];
|
|
651
677
|
|
|
652
|
-
// Commands become skills
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
if (fs.existsSync(commandsSrc)) {
|
|
656
|
-
const files = fs.readdirSync(commandsSrc).filter(f => f.endsWith('.md'));
|
|
657
|
-
for (const file of files) {
|
|
658
|
-
const name = file.replace('.md', '');
|
|
659
|
-
manifest.push(`skills/5-${name}`);
|
|
660
|
-
}
|
|
678
|
+
// Commands become skills: each command file becomes skills/5-{name}/SKILL.md
|
|
679
|
+
for (const { name } of getCodexCommandFiles(getSourcePath())) {
|
|
680
|
+
manifest.push(`skills/5-${name}`);
|
|
661
681
|
}
|
|
662
682
|
|
|
663
683
|
// Original skills also become Codex skills
|
|
@@ -688,9 +708,6 @@ function getCodexFileManifest() {
|
|
|
688
708
|
}
|
|
689
709
|
}
|
|
690
710
|
|
|
691
|
-
// Instructions file
|
|
692
|
-
manifest.push('instructions.md');
|
|
693
|
-
|
|
694
711
|
return manifest;
|
|
695
712
|
}
|
|
696
713
|
|
|
@@ -790,7 +807,7 @@ function selectiveUpdate(targetPath, sourcePath) {
|
|
|
790
807
|
const src = path.join(templatesSrc, template);
|
|
791
808
|
const dest = path.join(templatesDest, template);
|
|
792
809
|
if (fs.existsSync(src)) {
|
|
793
|
-
// Ensure parent directory exists for nested paths (e.g., workflow/
|
|
810
|
+
// Ensure parent directory exists for nested paths (e.g., workflow/REVIEW-FINDINGS.md)
|
|
794
811
|
const destDir = path.dirname(dest);
|
|
795
812
|
if (!fs.existsSync(destDir)) {
|
|
796
813
|
fs.mkdirSync(destDir, { recursive: true });
|
|
@@ -856,9 +873,12 @@ function cleanupOrphanedFiles(targetPath, dataDir) {
|
|
|
856
873
|
|
|
857
874
|
if (oldManifest) {
|
|
858
875
|
// Manifest exists: diff old vs current, delete orphans
|
|
876
|
+
const installRoot = path.resolve(targetPath) + path.sep;
|
|
859
877
|
for (const entry of oldManifest) {
|
|
860
878
|
if (!currentSet.has(entry)) {
|
|
861
|
-
const fullPath = path.
|
|
879
|
+
const fullPath = path.resolve(targetPath, entry);
|
|
880
|
+
// version.json may come from the checkout, so an entry must stay inside the install dir
|
|
881
|
+
if (!fullPath.startsWith(installRoot)) continue;
|
|
862
882
|
if (fs.existsSync(fullPath)) {
|
|
863
883
|
const stat = fs.statSync(fullPath);
|
|
864
884
|
if (stat.isDirectory()) {
|
|
@@ -890,7 +910,7 @@ function cleanupOrphanedFiles(targetPath, dataDir) {
|
|
|
890
910
|
// Ensure .5/.gitignore exists and contains transient runtime files
|
|
891
911
|
function ensureDotFiveGitignore(dataDir) {
|
|
892
912
|
const gitignorePath = path.join(dataDir, '.gitignore');
|
|
893
|
-
const entries = ['.update-cache.json', '.migration-v*', '.reconfig-reminder'];
|
|
913
|
+
const entries = ['.update-cache.json', '.migration-v*', '.reconfig-reminder', '.dependency-guard.json'];
|
|
894
914
|
if (fs.existsSync(gitignorePath)) {
|
|
895
915
|
let content = fs.readFileSync(gitignorePath, 'utf8');
|
|
896
916
|
for (const entry of entries) {
|
|
@@ -1009,7 +1029,7 @@ function mergeHookArrays(targetArr, sourceArr) {
|
|
|
1009
1029
|
// Hook event keys that contain arrays of hook entries
|
|
1010
1030
|
const HOOK_ARRAY_KEYS = new Set([
|
|
1011
1031
|
'PreToolUse', 'PostToolUse', 'SessionStart', 'SessionEnd',
|
|
1012
|
-
'PreCompact', 'PostCompact'
|
|
1032
|
+
'PreCompact', 'PostCompact', 'Stop', 'SubagentStop', 'UserPromptSubmit'
|
|
1013
1033
|
]);
|
|
1014
1034
|
|
|
1015
1035
|
// Deep merge for settings.json
|
|
@@ -1047,7 +1067,7 @@ function mergeSettings(targetPath, sourcePath) {
|
|
|
1047
1067
|
|
|
1048
1068
|
if (fs.existsSync(targetSettings)) {
|
|
1049
1069
|
const existingSettings = JSON.parse(fs.readFileSync(targetSettings, 'utf8'));
|
|
1050
|
-
const merged = deepMerge(
|
|
1070
|
+
const merged = deepMerge(existingSettings, newSettings);
|
|
1051
1071
|
fs.writeFileSync(targetSettings, JSON.stringify(merged, null, 2));
|
|
1052
1072
|
log.info('Merged settings with existing configuration');
|
|
1053
1073
|
} else {
|
|
@@ -1064,35 +1084,35 @@ function checkExistingInstallation(targetPath) {
|
|
|
1064
1084
|
const legacyMarkerFile = path.join(targetPath, 'skills', '5-plan-feature', 'SKILL.md');
|
|
1065
1085
|
return fs.existsSync(markerFile) || fs.existsSync(legacyMarkerFile);
|
|
1066
1086
|
}
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1087
|
+
// plan.md and plan-feature.md mark installs from before plan mode replaced /5:plan
|
|
1088
|
+
return ['implement.md', 'plan.md', 'plan-feature.md']
|
|
1089
|
+
.some(file => fs.existsSync(path.join(targetPath, 'commands', '5', file)));
|
|
1070
1090
|
}
|
|
1071
1091
|
|
|
1072
1092
|
// Helper to show commands
|
|
1073
1093
|
function showCommandsHelp(isGlobal) {
|
|
1074
1094
|
if (activeRuntime === 'codex') {
|
|
1075
1095
|
log.info('Available skills (invoke with $ prefix in Codex):');
|
|
1076
|
-
log.info(' $5-plan - Create or refine a
|
|
1077
|
-
log.info(' $5-split - Split plan into smaller plans');
|
|
1096
|
+
log.info(' $5-plan - Create or refine a plan');
|
|
1078
1097
|
log.info(' $5-implement - Execute implementation + verification');
|
|
1079
|
-
log.info(' $5-review - Code review');
|
|
1080
|
-
log.info(' $5-
|
|
1081
|
-
log.info(' $5-address-review-findings - Decide review findings
|
|
1098
|
+
log.info(' $5-review - Code and standards review');
|
|
1099
|
+
log.info(' $5-lean-check - Check a diff for scope drift/over-engineering');
|
|
1100
|
+
log.info(' $5-address-review-findings - Decide on review findings, apply the fixes');
|
|
1101
|
+
log.info(' $5-analyze-feature - Document a feature with mermaid diagrams');
|
|
1082
1102
|
log.info(' $5-configure - Interactive project setup');
|
|
1083
|
-
log.info(' $5-reconfigure - Refresh
|
|
1103
|
+
log.info(' $5-reconfigure - Refresh the AGENTS.md block and rules (no Q&A)');
|
|
1084
1104
|
log.info(' $5-eject - Eject from update mechanism');
|
|
1085
1105
|
log.info(' $5-synchronize-agents - Sync user content between runtimes');
|
|
1086
1106
|
} else {
|
|
1107
|
+
log.info('Plan in native plan mode (shift+tab); AGENTS.md shapes the plan.');
|
|
1087
1108
|
log.info('Available commands:');
|
|
1088
|
-
log.info(' /5:plan - Create or refine a unified plan');
|
|
1089
|
-
log.info(' /5:split - Split plan into smaller plans');
|
|
1090
1109
|
log.info(' /5:implement - Execute implementation + verification');
|
|
1091
|
-
log.info(' /5:review - Code review');
|
|
1092
|
-
log.info(' /5:
|
|
1093
|
-
log.info(' /5:address-review-findings - Decide review findings
|
|
1110
|
+
log.info(' /5:review - Code and standards review');
|
|
1111
|
+
log.info(' /5:lean-check - Check a diff for scope drift/over-engineering');
|
|
1112
|
+
log.info(' /5:address-review-findings - Decide on review findings, apply the fixes');
|
|
1113
|
+
log.info(' /5:analyze-feature - Document a feature with mermaid diagrams');
|
|
1094
1114
|
log.info(' /5:configure - Interactive project setup');
|
|
1095
|
-
log.info(' /5:reconfigure - Refresh
|
|
1115
|
+
log.info(' /5:reconfigure - Refresh the AGENTS.md block and rules (no Q&A)');
|
|
1096
1116
|
log.info(' /5:eject - Eject from update mechanism');
|
|
1097
1117
|
log.info(' /5:synchronize-agents - Sync user content between runtimes');
|
|
1098
1118
|
}
|
|
@@ -1138,10 +1158,8 @@ function performFreshInstall(targetPath, sourcePath, isGlobal) {
|
|
|
1138
1158
|
log.info('This will:');
|
|
1139
1159
|
log.info(' • Detect your project type and build commands');
|
|
1140
1160
|
log.info(' • Set up ticket tracking conventions');
|
|
1141
|
-
log.info(' •
|
|
1142
|
-
log.info(' • Create
|
|
1143
|
-
log.info('');
|
|
1144
|
-
log.info('Tip: Configure will offer to install helpful plugins like skill-creator');
|
|
1161
|
+
log.info(' • Add the workflow rules to AGENTS.md as one marked block, linking your repository standards');
|
|
1162
|
+
log.info(' • Create file-scoped rules from your conventions');
|
|
1145
1163
|
|
|
1146
1164
|
showCommandsHelp(isGlobal);
|
|
1147
1165
|
}
|
|
@@ -1192,24 +1210,31 @@ function removeContextForkFromSkills(targetPath) {
|
|
|
1192
1210
|
} catch (e) {}
|
|
1193
1211
|
}
|
|
1194
1212
|
|
|
1195
|
-
// Remove
|
|
1196
|
-
//
|
|
1197
|
-
//
|
|
1198
|
-
|
|
1213
|
+
// Remove retired PreToolUse hook entries from an existing settings.json. Native plan mode
|
|
1214
|
+
// replaced plan-guard; config-guard blocked every Agent call in sessions outside the workflow.
|
|
1215
|
+
// Claude Code only — Codex never had a settings.json.
|
|
1216
|
+
const RETIRED_HOOKS = ['plan-guard.js', 'config-guard.js'];
|
|
1217
|
+
|
|
1218
|
+
function removeRetiredHooks(targetPath) {
|
|
1199
1219
|
const settingsFile = path.join(targetPath, 'settings.json');
|
|
1200
1220
|
if (!fs.existsSync(settingsFile)) return;
|
|
1201
1221
|
try {
|
|
1202
1222
|
const settings = JSON.parse(fs.readFileSync(settingsFile, 'utf8'));
|
|
1203
1223
|
const pre = settings.hooks && settings.hooks.PreToolUse;
|
|
1204
1224
|
if (!Array.isArray(pre)) return;
|
|
1205
|
-
const
|
|
1206
|
-
|
|
1207
|
-
|
|
1225
|
+
const isRetired = h => typeof h.command === 'string' && RETIRED_HOOKS.some(hook => h.command.includes(hook));
|
|
1226
|
+
if (!pre.some(entry => (entry.hooks || []).some(isRetired))) return;
|
|
1227
|
+
// Drop only the retired hooks; an entry survives while it still holds the user's own hooks
|
|
1228
|
+
const kept = pre
|
|
1229
|
+
.map(entry => (entry.hooks || []).some(isRetired)
|
|
1230
|
+
? { ...entry, hooks: entry.hooks.filter(h => !isRetired(h)) }
|
|
1231
|
+
: entry)
|
|
1232
|
+
.filter(entry => !Array.isArray(entry.hooks) || entry.hooks.length);
|
|
1208
1233
|
if (kept.length) settings.hooks.PreToolUse = kept;
|
|
1209
1234
|
else delete settings.hooks.PreToolUse;
|
|
1210
1235
|
if (settings.hooks && !Object.keys(settings.hooks).length) delete settings.hooks;
|
|
1211
1236
|
fs.writeFileSync(settingsFile, JSON.stringify(settings, null, 2));
|
|
1212
|
-
log.info('Removed
|
|
1237
|
+
log.info('Removed retired guard hooks from settings.json');
|
|
1213
1238
|
} catch (e) {}
|
|
1214
1239
|
}
|
|
1215
1240
|
|
|
@@ -1224,6 +1249,25 @@ function removePlanningActiveMarker(dataDir) {
|
|
|
1224
1249
|
} catch (e) {}
|
|
1225
1250
|
}
|
|
1226
1251
|
|
|
1252
|
+
// Move .5/features/{name}/plan.md to .5/planning/{name}.md. Plans now live where native plan
|
|
1253
|
+
// mode saves them, and /5:implement finds them by their `feature` frontmatter. The .5/ data
|
|
1254
|
+
// is shared, so performUpdate() and performCodexUpdate() both run this.
|
|
1255
|
+
function movePlansToPlanningDir(dataDir) {
|
|
1256
|
+
const featuresDir = path.join(dataDir, 'features');
|
|
1257
|
+
if (!fs.existsSync(featuresDir)) return;
|
|
1258
|
+
const planningDir = path.join(dataDir, 'planning');
|
|
1259
|
+
try {
|
|
1260
|
+
for (const feature of fs.readdirSync(featuresDir)) {
|
|
1261
|
+
const planFile = path.join(featuresDir, feature, 'plan.md');
|
|
1262
|
+
const target = path.join(planningDir, `${feature}.md`);
|
|
1263
|
+
if (!fs.existsSync(planFile) || fs.existsSync(target)) continue;
|
|
1264
|
+
fs.mkdirSync(planningDir, { recursive: true });
|
|
1265
|
+
fs.renameSync(planFile, target);
|
|
1266
|
+
log.info(`Moved plan: features/${feature}/plan.md → planning/${feature}.md`);
|
|
1267
|
+
}
|
|
1268
|
+
} catch (e) {}
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1227
1271
|
// Perform update (preserves user-created files, updates .5/ data directory)
|
|
1228
1272
|
function performUpdate(targetPath, sourcePath, isGlobal, versionInfo) {
|
|
1229
1273
|
log.header(`Updating from ${versionInfo.installed || 'legacy'} to ${versionInfo.available}`);
|
|
@@ -1245,10 +1289,13 @@ function performUpdate(targetPath, sourcePath, isGlobal, versionInfo) {
|
|
|
1245
1289
|
// Strip `context: fork` from generated skills — caused infinite loops
|
|
1246
1290
|
removeContextForkFromSkills(targetPath);
|
|
1247
1291
|
|
|
1248
|
-
// Native plan mode replaces the plan-guard hook and its marker file
|
|
1249
|
-
|
|
1292
|
+
// Native plan mode replaces the plan-guard hook and its marker file; config-guard is retired
|
|
1293
|
+
removeRetiredHooks(targetPath);
|
|
1250
1294
|
removePlanningActiveMarker(dataDir);
|
|
1251
1295
|
|
|
1296
|
+
// Plans moved from .5/features/{name}/plan.md to .5/planning/{name}.md
|
|
1297
|
+
movePlansToPlanningDir(dataDir);
|
|
1298
|
+
|
|
1252
1299
|
// Flag v1 → v2 major upgrade so statusline can prompt for reconfigure
|
|
1253
1300
|
const prevMajor = versionInfo.installed ? parseInt(versionInfo.installed.split('.')[0], 10) : 0;
|
|
1254
1301
|
const newMajor = versionInfo.available ? parseInt(versionInfo.available.split('.')[0], 10) : 0;
|
|
@@ -1278,65 +1325,15 @@ function performUpdate(targetPath, sourcePath, isGlobal, versionInfo) {
|
|
|
1278
1325
|
|
|
1279
1326
|
// ── Codex install/update/uninstall ──────────────────────────────────────────
|
|
1280
1327
|
|
|
1281
|
-
// Generate instructions.md for Codex (replaces hooks + settings.json)
|
|
1282
|
-
function generateCodexInstructions(targetPath) {
|
|
1283
|
-
const content = `# dev-workflow — Codex Instructions
|
|
1284
|
-
|
|
1285
|
-
This file is managed by the dev-workflow installer. It provides Codex with
|
|
1286
|
-
the context it needs to run the workflow skills correctly.
|
|
1287
|
-
|
|
1288
|
-
## Workflow Overview
|
|
1289
|
-
|
|
1290
|
-
The workflow provides structured feature development:
|
|
1291
|
-
|
|
1292
|
-
1. **Plan** (\`$5-plan\`) — Requirements, discovery, and unified plan
|
|
1293
|
-
2. **Implement** (\`$5-implement\`) — Orchestrated implementation with inline verification
|
|
1294
|
-
3. **Review** (\`$5-review\`) — Code review
|
|
1295
|
-
|
|
1296
|
-
## Data Directory
|
|
1297
|
-
|
|
1298
|
-
All workflow state lives in \`.5/\` at the project root:
|
|
1299
|
-
- \`.5/config.json\` — Project configuration
|
|
1300
|
-
- \`.5/features/{name}/plan.md\` — Unified plan
|
|
1301
|
-
- \`.5/features/{name}/state.json\` — Implementation resume state
|
|
1302
|
-
|
|
1303
|
-
## Guard Rules
|
|
1304
|
-
|
|
1305
|
-
During the planning phase ($5-plan), until the user approves the plan:
|
|
1306
|
-
- Do NOT write files outside \`.5/\`
|
|
1307
|
-
- Do NOT write source code — only the unified plan and scan cache
|
|
1308
|
-
|
|
1309
|
-
## Configuration
|
|
1310
|
-
|
|
1311
|
-
Run \`$5-configure\` after installation to set up your project.
|
|
1312
|
-
|
|
1313
|
-
## Codex Token Budget
|
|
1314
|
-
|
|
1315
|
-
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.
|
|
1316
|
-
|
|
1317
|
-
## Templates & References
|
|
1318
|
-
|
|
1319
|
-
Templates are in \`.codex/templates/\` and references in \`.codex/references/\`.
|
|
1320
|
-
Skills reference these paths for output formatting.
|
|
1321
|
-
`;
|
|
1322
|
-
|
|
1323
|
-
fs.writeFileSync(path.join(targetPath, 'instructions.md'), content);
|
|
1324
|
-
log.success('Generated instructions.md');
|
|
1325
|
-
}
|
|
1326
|
-
|
|
1327
1328
|
// Install commands as Codex skills
|
|
1328
1329
|
function installCodexSkills(targetPath, sourcePath) {
|
|
1329
|
-
const commandsSrc = path.join(sourcePath, 'commands', '5');
|
|
1330
1330
|
const skillsDest = path.join(targetPath, 'skills');
|
|
1331
|
+
const files = getCodexCommandFiles(sourcePath);
|
|
1332
|
+
if (files.length === 0) return;
|
|
1331
1333
|
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
const files = fs.readdirSync(commandsSrc).filter(f => f.endsWith('.md'));
|
|
1335
|
-
|
|
1336
|
-
for (const file of files) {
|
|
1337
|
-
const name = file.replace('.md', '');
|
|
1334
|
+
for (const { name, file } of files) {
|
|
1338
1335
|
const skillName = `5-${name}`;
|
|
1339
|
-
const content = fs.readFileSync(
|
|
1336
|
+
const content = fs.readFileSync(file, 'utf8');
|
|
1340
1337
|
const converted = convertClaudeCommandToCodexSkill(content, skillName);
|
|
1341
1338
|
|
|
1342
1339
|
const skillDir = path.join(skillsDest, skillName);
|
|
@@ -1347,7 +1344,7 @@ function installCodexSkills(targetPath, sourcePath) {
|
|
|
1347
1344
|
}
|
|
1348
1345
|
log.success(`Installed ${files.length} workflow skills`);
|
|
1349
1346
|
|
|
1350
|
-
// Also convert and install
|
|
1347
|
+
// Also convert and install the workflow's own skills (configure-*, review styles)
|
|
1351
1348
|
const managed = getWorkflowManagedFiles();
|
|
1352
1349
|
for (const skill of managed.skills) {
|
|
1353
1350
|
const skillSrc = path.join(sourcePath, 'skills', skill);
|
|
@@ -1421,9 +1418,6 @@ function performCodexFreshInstall(targetPath, sourcePath, isGlobal) {
|
|
|
1421
1418
|
copyWorkflowHelperBins(targetPath);
|
|
1422
1419
|
log.success('Installed bin/ (workflow helper binaries)');
|
|
1423
1420
|
|
|
1424
|
-
// Generate instructions.md (replaces hooks + settings.json)
|
|
1425
|
-
generateCodexInstructions(targetPath);
|
|
1426
|
-
|
|
1427
1421
|
// Initialize version tracking (shared .5/ directory)
|
|
1428
1422
|
initializeVersionJson(isGlobal);
|
|
1429
1423
|
|
|
@@ -1435,8 +1429,7 @@ function performCodexFreshInstall(targetPath, sourcePath, isGlobal) {
|
|
|
1435
1429
|
log.info('This will:');
|
|
1436
1430
|
log.info(' • Detect your project type and build commands');
|
|
1437
1431
|
log.info(' • Set up ticket tracking conventions');
|
|
1438
|
-
log.info(' •
|
|
1439
|
-
log.info(' • Create project-specific skills');
|
|
1432
|
+
log.info(' • Add the workflow rules to AGENTS.md as one marked block, linking your repository standards');
|
|
1440
1433
|
|
|
1441
1434
|
showCommandsHelp(isGlobal);
|
|
1442
1435
|
}
|
|
@@ -1448,14 +1441,9 @@ function codexSelectiveUpdate(targetPath, sourcePath) {
|
|
|
1448
1441
|
const skillsDest = path.join(targetPath, 'skills');
|
|
1449
1442
|
|
|
1450
1443
|
// Remove workflow command skills (5-*)
|
|
1451
|
-
const
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
for (const file of files) {
|
|
1455
|
-
const name = file.replace('.md', '');
|
|
1456
|
-
const skillDir = path.join(skillsDest, `5-${name}`);
|
|
1457
|
-
if (fs.existsSync(skillDir)) removeDir(skillDir);
|
|
1458
|
-
}
|
|
1444
|
+
for (const { name } of getCodexCommandFiles(sourcePath)) {
|
|
1445
|
+
const skillDir = path.join(skillsDest, `5-${name}`);
|
|
1446
|
+
if (fs.existsSync(skillDir)) removeDir(skillDir);
|
|
1459
1447
|
}
|
|
1460
1448
|
|
|
1461
1449
|
// Remove utility skills
|
|
@@ -1482,9 +1470,6 @@ function codexSelectiveUpdate(targetPath, sourcePath) {
|
|
|
1482
1470
|
// Update helper binaries used by runtime commands
|
|
1483
1471
|
copyWorkflowHelperBins(targetPath);
|
|
1484
1472
|
log.success('Updated bin/ helper binaries');
|
|
1485
|
-
|
|
1486
|
-
// Regenerate instructions.md
|
|
1487
|
-
generateCodexInstructions(targetPath);
|
|
1488
1473
|
}
|
|
1489
1474
|
|
|
1490
1475
|
// Codex update
|
|
@@ -1504,6 +1489,9 @@ function performCodexUpdate(targetPath, sourcePath, isGlobal, versionInfo) {
|
|
|
1504
1489
|
// The plan-guard marker is shared .5/ state; Codex has no settings.json hook to remove
|
|
1505
1490
|
removePlanningActiveMarker(dataDir);
|
|
1506
1491
|
|
|
1492
|
+
// Plans moved from .5/features/{name}/plan.md to .5/planning/{name}.md
|
|
1493
|
+
movePlansToPlanningDir(dataDir);
|
|
1494
|
+
|
|
1507
1495
|
// Flag v1 → v2 major upgrade so skills can prompt for reconfigure
|
|
1508
1496
|
const prevMajor = versionInfo.installed ? parseInt(versionInfo.installed.split('.')[0], 10) : 0;
|
|
1509
1497
|
const newMajor = versionInfo.available ? parseInt(versionInfo.available.split('.')[0], 10) : 0;
|
|
@@ -1540,17 +1528,21 @@ function codexUninstall() {
|
|
|
1540
1528
|
return;
|
|
1541
1529
|
}
|
|
1542
1530
|
|
|
1531
|
+
// Remove workflow files retired from the package, which the current source no longer lists
|
|
1532
|
+
for (const entry of LEGACY_REMOVED_FILES) {
|
|
1533
|
+
const fullPath = path.join(targetPath, entry);
|
|
1534
|
+
if (!fs.existsSync(fullPath)) continue;
|
|
1535
|
+
if (fs.statSync(fullPath).isDirectory()) removeDir(fullPath);
|
|
1536
|
+
else fs.unlinkSync(fullPath);
|
|
1537
|
+
log.info(`Removed legacy orphan: ${entry}`);
|
|
1538
|
+
}
|
|
1539
|
+
|
|
1543
1540
|
// Remove workflow command skills (5-*)
|
|
1544
|
-
const
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
for (const file of files) {
|
|
1548
|
-
const name = file.replace('.md', '');
|
|
1549
|
-
const skillDir = path.join(targetPath, 'skills', `5-${name}`);
|
|
1550
|
-
if (fs.existsSync(skillDir)) removeDir(skillDir);
|
|
1551
|
-
}
|
|
1552
|
-
log.success('Removed workflow skills');
|
|
1541
|
+
for (const { name } of getCodexCommandFiles(getSourcePath())) {
|
|
1542
|
+
const skillDir = path.join(targetPath, 'skills', `5-${name}`);
|
|
1543
|
+
if (fs.existsSync(skillDir)) removeDir(skillDir);
|
|
1553
1544
|
}
|
|
1545
|
+
log.success('Removed workflow skills');
|
|
1554
1546
|
|
|
1555
1547
|
// Remove utility skills
|
|
1556
1548
|
const managed = getWorkflowManagedFiles();
|
|
@@ -1596,7 +1588,7 @@ function codexUninstall() {
|
|
|
1596
1588
|
log.success('Removed workflow helper binaries');
|
|
1597
1589
|
}
|
|
1598
1590
|
|
|
1599
|
-
// Remove instructions.md
|
|
1591
|
+
// Remove instructions.md left by earlier versions (Codex never read it; AGENTS.md carries the rules)
|
|
1600
1592
|
const instructionsPath = path.join(targetPath, 'instructions.md');
|
|
1601
1593
|
if (fs.existsSync(instructionsPath)) {
|
|
1602
1594
|
fs.unlinkSync(instructionsPath);
|