5-phase-workflow 1.8.6 → 1.8.8

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/bin/install.js CHANGED
@@ -4,6 +4,9 @@ const fs = require('fs');
4
4
  const path = require('path');
5
5
  const readline = require('readline');
6
6
 
7
+ // Active runtime ('claude' or 'codex') — set once in main()
8
+ let activeRuntime = 'claude';
9
+
7
10
  // ANSI colors for terminal output
8
11
  const colors = {
9
12
  reset: '\x1b[0m',
@@ -94,7 +97,8 @@ function parseArgs() {
94
97
  uninstall: false,
95
98
  upgrade: false,
96
99
  check: false,
97
- help: false
100
+ help: false,
101
+ runtime: 'claude' // 'claude' or 'codex'
98
102
  };
99
103
 
100
104
  for (const arg of args) {
@@ -104,6 +108,7 @@ function parseArgs() {
104
108
  else if (arg === '--upgrade' || arg === '--force' || arg === '-U') options.upgrade = true;
105
109
  else if (arg === '--check') options.check = true;
106
110
  else if (arg === '--help' || arg === '-h') options.help = true;
111
+ else if (arg === '--codex') options.runtime = 'codex';
107
112
  }
108
113
 
109
114
  // Default to local if neither specified
@@ -124,6 +129,7 @@ Usage: npx 5-phase-workflow [options]
124
129
  Options:
125
130
  --global, -g Install to ~/.claude/ (available across all projects)
126
131
  --local, -l Install to ./.claude/ (project-specific, default)
132
+ --codex Install for Codex CLI (to ~/.codex/ or ./.codex/)
127
133
  --upgrade, -U Upgrade to latest version (auto-update, no prompt)
128
134
  --force Alias for --upgrade
129
135
  --check Check installed version and available updates
@@ -131,26 +137,38 @@ Options:
131
137
  --help, -h Show this help message
132
138
 
133
139
  Examples:
134
- npx 5-phase-workflow # Install locally or prompt for update
135
- npx 5-phase-workflow --global # Install globally
140
+ npx 5-phase-workflow # Install locally for Claude Code
141
+ npx 5-phase-workflow --global # Install globally for Claude Code
142
+ npx 5-phase-workflow --codex # Install locally for Codex CLI
143
+ npx 5-phase-workflow --codex -g # Install globally for Codex CLI
136
144
  npx 5-phase-workflow --upgrade # Auto-update to latest version
137
145
  npx 5-phase-workflow --check # Check version without updating
138
146
  npx 5-phase-workflow --uninstall # Remove from current directory
139
147
  `);
140
148
  }
141
149
 
142
- // Get installation target path (.claude/ directory)
150
+ // Get config directory name for active runtime
151
+ function getRuntimeDirName() {
152
+ if (activeRuntime === 'codex') return '.codex';
153
+ return '.claude';
154
+ }
155
+
156
+ // Get installation target path (.claude/ or .codex/ directory)
143
157
  function getTargetPath(isGlobal) {
158
+ const dirName = getRuntimeDirName();
144
159
  if (isGlobal) {
160
+ if (activeRuntime === 'codex' && process.env.CODEX_HOME) {
161
+ return process.env.CODEX_HOME;
162
+ }
145
163
  const homeDir = process.env.HOME || process.env.USERPROFILE;
146
- return path.join(homeDir, '.claude');
164
+ return path.join(homeDir, dirName);
147
165
  }
148
- return path.join(process.cwd(), '.claude');
166
+ return path.join(process.cwd(), dirName);
149
167
  }
150
168
 
151
169
  // Get data path (.5/ directory for config, version, features)
152
170
  // Local installs: <project>/.5 (project root)
153
- // Global installs: ~/.claude/.5 (alongside global install)
171
+ // Global installs: ~/<runtime-dir>/.5 (alongside global install)
154
172
  function getDataPath(isGlobal) {
155
173
  if (isGlobal) {
156
174
  return path.join(getTargetPath(true), '.5');
@@ -258,7 +276,11 @@ const LEGACY_REMOVED_FILES = [
258
276
  'agents/integration-agent.md',
259
277
  'agents/step-fixer.md',
260
278
  'agents/step-verifier.md',
261
- 'agents/verification-agent.md'
279
+ 'agents/verification-agent.md',
280
+ 'templates/STACK.md',
281
+ 'templates/STRUCTURE.md',
282
+ 'templates/CONVENTIONS.md',
283
+ 'templates/INTEGRATIONS.md'
262
284
  ];
263
285
 
264
286
  // Get list of workflow-owned files/directories (not user-created)
@@ -274,8 +296,6 @@ function getWorkflowManagedFiles() {
274
296
 
275
297
  // Skills: specific skill directories
276
298
  skills: [
277
- 'build-project',
278
- 'run-tests',
279
299
  'configure-project',
280
300
  'generate-readme'
281
301
  ],
@@ -299,10 +319,6 @@ function getWorkflowManagedFiles() {
299
319
  // Project documentation templates
300
320
  'ARCHITECTURE.md',
301
321
  'CONCERNS.md',
302
- 'CONVENTIONS.md',
303
- 'INTEGRATIONS.md',
304
- 'STACK.md',
305
- 'STRUCTURE.md',
306
322
  'TESTING.md',
307
323
  // Workflow output templates
308
324
  'workflow/FEATURE-SPEC.md',
@@ -316,8 +332,10 @@ function getWorkflowManagedFiles() {
316
332
  };
317
333
  }
318
334
 
319
- // Flatten getWorkflowManagedFiles() into a list of relative paths (relative to .claude/)
335
+ // Flatten getWorkflowManagedFiles() into a list of relative paths (relative to target dir)
320
336
  function getFileManifest() {
337
+ if (activeRuntime === 'codex') return getCodexFileManifest();
338
+
321
339
  const managed = getWorkflowManagedFiles();
322
340
  const manifest = [];
323
341
 
@@ -356,6 +374,141 @@ function getFileManifest() {
356
374
  return manifest;
357
375
  }
358
376
 
377
+ // ── Codex conversion functions ──────────────────────────────────────────────
378
+
379
+ // Extract YAML frontmatter and body from a markdown file
380
+ function extractFrontmatterAndBody(content) {
381
+ const match = content.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/);
382
+ if (!match) return { frontmatter: null, body: content };
383
+ return { frontmatter: match[1], body: match[2] };
384
+ }
385
+
386
+ // Extract a single field value from YAML frontmatter (simple key: value)
387
+ function extractFrontmatterField(frontmatter, field) {
388
+ if (!frontmatter) return null;
389
+ const match = frontmatter.match(new RegExp(`^${field}:\\s*(.+)$`, 'm'));
390
+ return match ? match[1].trim() : null;
391
+ }
392
+
393
+ // Convert /5:command-name references to $5-command-name (Codex skill mentions)
394
+ function convertSlashCommandsToCodexMentions(content) {
395
+ return content.replace(/\/5:([a-z0-9-]+)/g, (_, name) => `$5-${name}`);
396
+ }
397
+
398
+ // Convert Claude command markdown to Codex-compatible content
399
+ function convertClaudeToCodexMarkdown(content) {
400
+ let converted = convertSlashCommandsToCodexMentions(content);
401
+ // Replace .claude/ path references with .codex/
402
+ converted = converted.replace(/\.claude\//g, '.codex/');
403
+ return converted;
404
+ }
405
+
406
+ // Generate the adapter header that teaches Codex how to map Claude Code concepts
407
+ function getCodexSkillAdapterHeader(skillName) {
408
+ const invocation = `$${skillName}`;
409
+ return `<codex_skill_adapter>
410
+ ## Skill Invocation
411
+ - This skill is invoked by mentioning \`${invocation}\`.
412
+ - Treat all user text after \`${invocation}\` as the skill argument.
413
+
414
+ ## Tool Mapping (Claude Code → Codex)
415
+ This skill was authored for Claude Code. Map these tool references:
416
+
417
+ | Claude Code | Codex Equivalent |
418
+ |-------------|------------------|
419
+ | \`AskUserQuestion\` | Ask the user directly in conversation |
420
+ | \`Task(subagent_type="Explore")\` | Research the codebase yourself using available tools |
421
+ | \`Task(prompt="...")\` | \`spawn_agent(message="...")\` |
422
+ | \`Read\` | \`read_file\` |
423
+ | \`Write\` | \`write_file\` |
424
+ | \`Edit\` | \`patch\` |
425
+ | \`Bash\` | \`shell\` |
426
+ | \`Glob\` | \`glob\` / \`list_directory\` |
427
+ | \`Grep\` | \`grep\` / \`search\` |
428
+ | \`TaskCreate/TaskUpdate\` | Track progress internally |
429
+ | \`EnterPlanMode\` | Not available — use structured output instead |
430
+
431
+ ## Guard Rules (replaces plan-guard hook)
432
+ During planning phases (plan-feature, plan-implementation):
433
+ - Do NOT write to any file outside \`.5/\`
434
+ - Do NOT write source code — only specifications and plans
435
+ - Do NOT spawn implementation agents — only Explore/research agents
436
+ </codex_skill_adapter>`;
437
+ }
438
+
439
+ // Convert a Claude command .md file into a Codex SKILL.md
440
+ function convertClaudeCommandToCodexSkill(content, skillName) {
441
+ const converted = convertClaudeToCodexMarkdown(content);
442
+ const { frontmatter, body } = extractFrontmatterAndBody(converted);
443
+
444
+ let description = `Run 5-Phase Workflow: ${skillName}`;
445
+ if (frontmatter) {
446
+ const maybeDesc = extractFrontmatterField(frontmatter, 'description');
447
+ if (maybeDesc) description = maybeDesc;
448
+ }
449
+
450
+ // Truncate description for metadata
451
+ const shortDesc = description.length > 180 ? `${description.slice(0, 177)}...` : description;
452
+ const adapter = getCodexSkillAdapterHeader(skillName);
453
+
454
+ return `---
455
+ name: ${skillName}
456
+ description: ${description}
457
+ metadata:
458
+ short-description: ${shortDesc}
459
+ ---
460
+
461
+ ${adapter}
462
+
463
+ ${body.trimStart()}`;
464
+ }
465
+
466
+ // Convert a Claude SKILL.md to Codex-compatible SKILL.md (lighter conversion, no adapter needed for skills)
467
+ function convertClaudeSkillToCodexSkill(content) {
468
+ return convertClaudeToCodexMarkdown(content);
469
+ }
470
+
471
+ // Get file manifest for Codex installs (skills-based structure)
472
+ function getCodexFileManifest() {
473
+ const managed = getWorkflowManagedFiles();
474
+ const manifest = [];
475
+
476
+ // Commands become skills
477
+ // Each command file in commands/5/ becomes skills/5-{name}/SKILL.md
478
+ const commandsSrc = path.join(getSourcePath(), 'commands', '5');
479
+ if (fs.existsSync(commandsSrc)) {
480
+ const files = fs.readdirSync(commandsSrc).filter(f => f.endsWith('.md'));
481
+ for (const file of files) {
482
+ const name = file.replace('.md', '');
483
+ manifest.push(`skills/5-${name}`);
484
+ }
485
+ }
486
+
487
+ // Original skills also become Codex skills
488
+ for (const skill of managed.skills) {
489
+ manifest.push(`skills/${skill}`);
490
+ }
491
+
492
+ // Templates are copied as-is
493
+ for (const template of managed.templates) {
494
+ manifest.push(`templates/${template}`);
495
+ }
496
+
497
+ // References are copied as-is
498
+ if (managed.references) {
499
+ for (const ref of managed.references) {
500
+ manifest.push(`references/${ref}`);
501
+ }
502
+ }
503
+
504
+ // Instructions file
505
+ manifest.push('instructions.md');
506
+
507
+ return manifest;
508
+ }
509
+
510
+ // ── End Codex conversion functions ──────────────────────────────────────────
511
+
359
512
  // Selectively update only workflow-managed files, preserve user content
360
513
  function selectiveUpdate(targetPath, sourcePath) {
361
514
  const managed = getWorkflowManagedFiles();
@@ -611,22 +764,42 @@ function mergeSettings(targetPath, sourcePath) {
611
764
 
612
765
  // Check if installation exists
613
766
  function checkExistingInstallation(targetPath) {
767
+ if (activeRuntime === 'codex') {
768
+ // Codex: commands are installed as skills/5-plan-feature/SKILL.md
769
+ const markerFile = path.join(targetPath, 'skills', '5-plan-feature', 'SKILL.md');
770
+ return fs.existsSync(markerFile);
771
+ }
614
772
  const markerFile = path.join(targetPath, 'commands', '5', 'plan-feature.md');
615
773
  return fs.existsSync(markerFile);
616
774
  }
617
775
 
618
776
  // Helper to show commands
619
777
  function showCommandsHelp(isGlobal) {
620
- log.info('Available commands:');
621
- log.info(' /5:plan-feature - Start feature planning (Phase 1)');
622
- log.info(' /5:plan-implementation - Create implementation plan (Phase 2)');
623
- log.info(' /5:implement-feature - Execute implementation (Phase 3)');
624
- log.info(' /5:verify-implementation - Verify implementation (Phase 4)');
625
- log.info(' /5:review-code - Code review (Phase 5)');
626
- log.info(' /5:address-review-findings - Apply review findings & PR comments');
627
- log.info(' /5:configure - Interactive project setup');
628
- log.info(' /5:reconfigure - Refresh docs/skills (no Q&A)');
629
- log.info(' /5:unlock - Remove planning guard lock');
778
+ if (activeRuntime === 'codex') {
779
+ log.info('Available skills (invoke with $ prefix in Codex):');
780
+ log.info(' $5-plan-feature - Start feature planning (Phase 1)');
781
+ log.info(' $5-plan-implementation - Create implementation plan (Phase 2)');
782
+ log.info(' $5-implement-feature - Execute implementation (Phase 3)');
783
+ log.info(' $5-verify-implementation - Verify implementation (Phase 4)');
784
+ log.info(' $5-review-code - Code review (Phase 5)');
785
+ log.info(' $5-address-review-findings - Apply review findings & PR comments');
786
+ log.info(' $5-configure - Interactive project setup');
787
+ log.info(' $5-reconfigure - Refresh docs/skills (no Q&A)');
788
+ log.info(' $5-eject - Eject from update mechanism');
789
+ log.info(' $5-unlock - Remove planning guard lock');
790
+ } else {
791
+ log.info('Available commands:');
792
+ log.info(' /5:plan-feature - Start feature planning (Phase 1)');
793
+ log.info(' /5:plan-implementation - Create implementation plan (Phase 2)');
794
+ log.info(' /5:implement-feature - Execute implementation (Phase 3)');
795
+ log.info(' /5:verify-implementation - Verify implementation (Phase 4)');
796
+ log.info(' /5:review-code - Code review (Phase 5)');
797
+ log.info(' /5:address-review-findings - Apply review findings & PR comments');
798
+ log.info(' /5:configure - Interactive project setup');
799
+ log.info(' /5:reconfigure - Refresh docs/skills (no Q&A)');
800
+ log.info(' /5:eject - Eject from update mechanism');
801
+ log.info(' /5:unlock - Remove planning guard lock');
802
+ }
630
803
  log.info('');
631
804
  log.info(`Config file: ${path.join(getDataPath(isGlobal), 'config.json')}`);
632
805
  }
@@ -722,26 +895,320 @@ function performUpdate(targetPath, sourcePath, isGlobal, versionInfo) {
722
895
  showCommandsHelp(isGlobal);
723
896
  }
724
897
 
898
+ // ── Codex install/update/uninstall ──────────────────────────────────────────
899
+
900
+ // Generate instructions.md for Codex (replaces hooks + settings.json)
901
+ function generateCodexInstructions(targetPath) {
902
+ const content = `# 5-Phase Workflow — Codex Instructions
903
+
904
+ This file is managed by the 5-Phase Workflow installer. It provides Codex with
905
+ the context it needs to run the workflow skills correctly.
906
+
907
+ ## Workflow Overview
908
+
909
+ The 5-Phase Workflow provides structured feature development:
910
+
911
+ 1. **Plan Feature** (\`$5-plan-feature\`) — Requirements gathering & feature spec
912
+ 2. **Plan Implementation** (\`$5-plan-implementation\`) — Technical planning
913
+ 3. **Implement Feature** (\`$5-implement-feature\`) — Orchestrated implementation
914
+ 4. **Verify Implementation** (\`$5-verify-implementation\`) — Build & test verification
915
+ 5. **Review Code** (\`$5-review-code\`) — Code review
916
+
917
+ ## Data Directory
918
+
919
+ All workflow state lives in \`.5/\` at the project root:
920
+ - \`.5/config.json\` — Project configuration
921
+ - \`.5/features/{name}/feature.md\` — Feature specifications
922
+ - \`.5/features/{name}/plan.md\` — Implementation plans
923
+ - \`.5/features/{name}/state.json\` — Implementation state
924
+
925
+ ## Guard Rules
926
+
927
+ During planning phases (plan-feature, plan-implementation):
928
+ - Do NOT write files outside \`.5/\`
929
+ - Do NOT write source code — only specifications and plans
930
+ - The \`.5/.planning-active\` marker indicates planning is in progress
931
+
932
+ ## Configuration
933
+
934
+ Run \`$5-configure\` after installation to set up your project.
935
+
936
+ ## Templates & References
937
+
938
+ Templates are in \`.codex/templates/\` and references in \`.codex/references/\`.
939
+ Skills reference these paths for output formatting.
940
+ `;
941
+
942
+ fs.writeFileSync(path.join(targetPath, 'instructions.md'), content);
943
+ log.success('Generated instructions.md');
944
+ }
945
+
946
+ // Install commands as Codex skills
947
+ function installCodexSkills(targetPath, sourcePath) {
948
+ const commandsSrc = path.join(sourcePath, 'commands', '5');
949
+ const skillsDest = path.join(targetPath, 'skills');
950
+
951
+ if (!fs.existsSync(commandsSrc)) return;
952
+
953
+ const files = fs.readdirSync(commandsSrc).filter(f => f.endsWith('.md'));
954
+
955
+ for (const file of files) {
956
+ const name = file.replace('.md', '');
957
+ const skillName = `5-${name}`;
958
+ const content = fs.readFileSync(path.join(commandsSrc, file), 'utf8');
959
+ const converted = convertClaudeCommandToCodexSkill(content, skillName);
960
+
961
+ const skillDir = path.join(skillsDest, skillName);
962
+ if (!fs.existsSync(skillDir)) {
963
+ fs.mkdirSync(skillDir, { recursive: true });
964
+ }
965
+ fs.writeFileSync(path.join(skillDir, 'SKILL.md'), converted);
966
+ }
967
+ log.success(`Installed ${files.length} workflow skills`);
968
+
969
+ // Also convert and install original skills (configure-project, generate-readme)
970
+ const managed = getWorkflowManagedFiles();
971
+ for (const skill of managed.skills) {
972
+ const skillSrc = path.join(sourcePath, 'skills', skill);
973
+ if (!fs.existsSync(skillSrc)) continue;
974
+
975
+ const skillDestDir = path.join(skillsDest, skill);
976
+ if (!fs.existsSync(skillDestDir)) {
977
+ fs.mkdirSync(skillDestDir, { recursive: true });
978
+ }
979
+
980
+ // Copy all files in the skill directory, converting .md files
981
+ const skillFiles = fs.readdirSync(skillSrc);
982
+ for (const sf of skillFiles) {
983
+ const srcFile = path.join(skillSrc, sf);
984
+ const destFile = path.join(skillDestDir, sf);
985
+ if (sf.endsWith('.md')) {
986
+ const content = fs.readFileSync(srcFile, 'utf8');
987
+ fs.writeFileSync(destFile, convertClaudeSkillToCodexSkill(content));
988
+ } else {
989
+ fs.copyFileSync(srcFile, destFile);
990
+ }
991
+ }
992
+ }
993
+ log.success('Installed utility skills');
994
+ }
995
+
996
+ // Codex fresh installation
997
+ function performCodexFreshInstall(targetPath, sourcePath, isGlobal) {
998
+ if (!fs.existsSync(targetPath)) {
999
+ fs.mkdirSync(targetPath, { recursive: true });
1000
+ log.success(`Created ${targetPath}`);
1001
+ }
1002
+
1003
+ // Install commands as Codex skills
1004
+ installCodexSkills(targetPath, sourcePath);
1005
+
1006
+ // Copy templates and references as-is (just path conversion)
1007
+ for (const dir of ['templates', 'references']) {
1008
+ const src = path.join(sourcePath, dir);
1009
+ const dest = path.join(targetPath, dir);
1010
+ if (fs.existsSync(src)) {
1011
+ copyDir(src, dest);
1012
+ log.success(`Installed ${dir}/`);
1013
+ }
1014
+ }
1015
+
1016
+ // Generate instructions.md (replaces hooks + settings.json)
1017
+ generateCodexInstructions(targetPath);
1018
+
1019
+ // Initialize version tracking (shared .5/ directory)
1020
+ initializeVersionJson(isGlobal);
1021
+
1022
+ log.header('Codex Installation Complete!');
1023
+ log.info('');
1024
+ log.info('Next step: Configure your project');
1025
+ log.info('Mention: $5-configure');
1026
+ log.info('');
1027
+ log.info('This will:');
1028
+ log.info(' • Detect your project type and build commands');
1029
+ log.info(' • Set up ticket tracking conventions');
1030
+ log.info(' • Generate comprehensive documentation');
1031
+ log.info(' • Create project-specific skills');
1032
+
1033
+ showCommandsHelp(isGlobal);
1034
+ }
1035
+
1036
+ // Codex selective update
1037
+ function codexSelectiveUpdate(targetPath, sourcePath) {
1038
+ // Remove old skills and re-install
1039
+ const managed = getWorkflowManagedFiles();
1040
+ const skillsDest = path.join(targetPath, 'skills');
1041
+
1042
+ // Remove workflow command skills (5-*)
1043
+ const commandsSrc = path.join(sourcePath, 'commands', '5');
1044
+ if (fs.existsSync(commandsSrc)) {
1045
+ const files = fs.readdirSync(commandsSrc).filter(f => f.endsWith('.md'));
1046
+ for (const file of files) {
1047
+ const name = file.replace('.md', '');
1048
+ const skillDir = path.join(skillsDest, `5-${name}`);
1049
+ if (fs.existsSync(skillDir)) removeDir(skillDir);
1050
+ }
1051
+ }
1052
+
1053
+ // Remove utility skills
1054
+ for (const skill of managed.skills) {
1055
+ const skillDir = path.join(skillsDest, skill);
1056
+ if (fs.existsSync(skillDir)) removeDir(skillDir);
1057
+ }
1058
+
1059
+ // Re-install all skills
1060
+ installCodexSkills(targetPath, sourcePath);
1061
+
1062
+ // Update templates and references
1063
+ for (const dir of ['templates', 'references']) {
1064
+ const src = path.join(sourcePath, dir);
1065
+ const dest = path.join(targetPath, dir);
1066
+ if (fs.existsSync(src)) {
1067
+ if (fs.existsSync(dest)) removeDir(dest);
1068
+ copyDir(src, dest);
1069
+ }
1070
+ }
1071
+ log.success('Updated templates and references');
1072
+
1073
+ // Regenerate instructions.md
1074
+ generateCodexInstructions(targetPath);
1075
+ }
1076
+
1077
+ // Codex update
1078
+ function performCodexUpdate(targetPath, sourcePath, isGlobal, versionInfo) {
1079
+ log.header(`Updating Codex install from ${versionInfo.installed || 'legacy'} to ${versionInfo.available}`);
1080
+ log.info('Preserving user-created skills');
1081
+
1082
+ codexSelectiveUpdate(targetPath, sourcePath);
1083
+
1084
+ // Clean up orphaned files
1085
+ const dataDir = getDataPath(isGlobal);
1086
+ cleanupOrphanedFiles(targetPath, dataDir);
1087
+
1088
+ // Update version.json
1089
+ const versionFile = path.join(dataDir, 'version.json');
1090
+ const now = new Date().toISOString();
1091
+ const existing = fs.existsSync(versionFile)
1092
+ ? JSON.parse(fs.readFileSync(versionFile, 'utf8'))
1093
+ : {};
1094
+ const versionData = {
1095
+ packageVersion: versionInfo.available,
1096
+ installedAt: existing.installedAt || now,
1097
+ lastUpdated: now,
1098
+ installationType: existing.installationType || (isGlobal ? 'global' : 'local'),
1099
+ manifest: getFileManifest()
1100
+ };
1101
+
1102
+ if (!fs.existsSync(dataDir)) {
1103
+ fs.mkdirSync(dataDir, { recursive: true });
1104
+ }
1105
+ fs.writeFileSync(versionFile, JSON.stringify(versionData, null, 2));
1106
+ ensureDotFiveGitignore(dataDir);
1107
+
1108
+ const featuresDir = path.join(dataDir, 'features');
1109
+ if (!fs.existsSync(featuresDir)) {
1110
+ fs.mkdirSync(featuresDir, { recursive: true });
1111
+ }
1112
+
1113
+ log.header('Update Complete!');
1114
+ log.success(`Now running version ${versionInfo.available}`);
1115
+ showCommandsHelp(isGlobal);
1116
+ }
1117
+
1118
+ // Codex uninstallation
1119
+ function codexUninstall() {
1120
+ const targetPath = getTargetPath(false);
1121
+
1122
+ log.header('5-Phase Workflow Uninstallation (Codex)');
1123
+ log.info(`Target: ${targetPath}`);
1124
+
1125
+ if (!checkExistingInstallation(targetPath)) {
1126
+ log.warn('No Codex installation found at this location');
1127
+ return;
1128
+ }
1129
+
1130
+ // Remove workflow command skills (5-*)
1131
+ const commandsSrc = path.join(getSourcePath(), 'commands', '5');
1132
+ if (fs.existsSync(commandsSrc)) {
1133
+ const files = fs.readdirSync(commandsSrc).filter(f => f.endsWith('.md'));
1134
+ for (const file of files) {
1135
+ const name = file.replace('.md', '');
1136
+ const skillDir = path.join(targetPath, 'skills', `5-${name}`);
1137
+ if (fs.existsSync(skillDir)) removeDir(skillDir);
1138
+ }
1139
+ log.success('Removed workflow skills');
1140
+ }
1141
+
1142
+ // Remove utility skills
1143
+ const managed = getWorkflowManagedFiles();
1144
+ for (const skill of managed.skills) {
1145
+ const skillDir = path.join(targetPath, 'skills', skill);
1146
+ if (fs.existsSync(skillDir)) removeDir(skillDir);
1147
+ }
1148
+ log.success('Removed utility skills (preserved user-created skills)');
1149
+
1150
+ // Remove templates
1151
+ for (const template of managed.templates) {
1152
+ const templatePath = path.join(targetPath, 'templates', template);
1153
+ if (fs.existsSync(templatePath)) fs.unlinkSync(templatePath);
1154
+ }
1155
+ log.success('Removed workflow templates');
1156
+
1157
+ // Remove references
1158
+ if (managed.references) {
1159
+ for (const ref of managed.references) {
1160
+ const refPath = path.join(targetPath, 'references', ref);
1161
+ if (fs.existsSync(refPath)) fs.unlinkSync(refPath);
1162
+ }
1163
+ log.success('Removed workflow references');
1164
+ }
1165
+
1166
+ // Remove instructions.md
1167
+ const instructionsPath = path.join(targetPath, 'instructions.md');
1168
+ if (fs.existsSync(instructionsPath)) {
1169
+ fs.unlinkSync(instructionsPath);
1170
+ log.success('Removed instructions.md');
1171
+ }
1172
+
1173
+ // Remove data directory (.5/)
1174
+ const dataDir = getDataPath(false);
1175
+ if (fs.existsSync(dataDir)) {
1176
+ removeDir(dataDir);
1177
+ log.success('Removed .5/ data directory');
1178
+ }
1179
+
1180
+ log.header('Uninstallation Complete!');
1181
+ }
1182
+
1183
+ // ── End Codex install/update/uninstall ──────────────────────────────────────
1184
+
725
1185
  // Perform installation
726
1186
  function install(isGlobal, forceUpgrade = false) {
727
1187
  const targetPath = getTargetPath(isGlobal);
728
1188
  const sourcePath = getSourcePath();
729
1189
 
730
- log.header('5-Phase Workflow Installation');
1190
+ const runtimeLabel = activeRuntime === 'codex' ? 'Codex' : 'Claude Code';
1191
+ log.header(`5-Phase Workflow Installation (${runtimeLabel})`);
731
1192
  log.info(`Target: ${targetPath}`);
732
1193
  log.info(`Source: ${sourcePath}`);
733
1194
 
734
- // Migrate data from old .claude/.5/ to new .5/ location
735
- migrateDataDir(isGlobal);
1195
+ // Migrate data from old .claude/.5/ to new .5/ location (Claude only)
1196
+ if (activeRuntime === 'claude') {
1197
+ migrateDataDir(isGlobal);
1198
+ }
736
1199
 
737
1200
  // Check for existing installation and version
738
1201
  const versionInfo = getVersionInfo(targetPath, isGlobal);
739
1202
 
1203
+ // Select the right install/update functions for this runtime
1204
+ const freshInstall = activeRuntime === 'codex' ? performCodexFreshInstall : performFreshInstall;
1205
+ const update = activeRuntime === 'codex' ? performCodexUpdate : performUpdate;
1206
+
740
1207
  if (versionInfo.exists) {
741
1208
  if (versionInfo.legacy) {
742
1209
  log.warn('Detected legacy installation (no version tracking)');
743
1210
  log.info(`Upgrading from legacy install to ${versionInfo.available}`);
744
- performUpdate(targetPath, sourcePath, isGlobal, versionInfo);
1211
+ update(targetPath, sourcePath, isGlobal, versionInfo);
745
1212
  return;
746
1213
  } else if (versionInfo.needsUpdate) {
747
1214
  log.info(`Installed: ${versionInfo.installed}`);
@@ -760,12 +1227,12 @@ function install(isGlobal, forceUpgrade = false) {
760
1227
  log.info('Update cancelled');
761
1228
  return;
762
1229
  }
763
- performUpdate(targetPath, sourcePath, isGlobal, versionInfo);
1230
+ update(targetPath, sourcePath, isGlobal, versionInfo);
764
1231
  });
765
1232
  return; // Wait for user input
766
1233
  }
767
1234
  // Force upgrade, no prompt
768
- performUpdate(targetPath, sourcePath, isGlobal, versionInfo);
1235
+ update(targetPath, sourcePath, isGlobal, versionInfo);
769
1236
  return;
770
1237
  } else {
771
1238
  // Same version
@@ -775,11 +1242,16 @@ function install(isGlobal, forceUpgrade = false) {
775
1242
  }
776
1243
 
777
1244
  // Fresh install (no existing installation)
778
- performFreshInstall(targetPath, sourcePath, isGlobal);
1245
+ freshInstall(targetPath, sourcePath, isGlobal);
779
1246
  }
780
1247
 
781
1248
  // Perform uninstallation
782
1249
  function uninstall() {
1250
+ if (activeRuntime === 'codex') {
1251
+ codexUninstall();
1252
+ return;
1253
+ }
1254
+
783
1255
  const targetPath = getTargetPath(false); // Always local for uninstall
784
1256
 
785
1257
  log.header('5-Phase Workflow Uninstallation');
@@ -876,6 +1348,9 @@ function uninstall() {
876
1348
  function main() {
877
1349
  const options = parseArgs();
878
1350
 
1351
+ // Set module-level runtime before any path resolution
1352
+ activeRuntime = options.runtime;
1353
+
879
1354
  if (options.help) {
880
1355
  showHelp();
881
1356
  return;
@@ -887,16 +1362,17 @@ function main() {
887
1362
  const versionInfo = getVersionInfo(targetPath, options.global);
888
1363
 
889
1364
  if (!versionInfo.exists) {
890
- log.info('Not installed');
1365
+ log.info(`Not installed (${activeRuntime})`);
891
1366
  return;
892
1367
  }
893
1368
 
1369
+ log.info(`Runtime: ${activeRuntime}`);
894
1370
  log.info(`Installed: ${versionInfo.installed || 'legacy (no version)'}`);
895
1371
  log.info(`Available: ${versionInfo.available}`);
896
1372
 
897
1373
  if (versionInfo.needsUpdate) {
898
1374
  log.warn('Update available');
899
- log.info('Run: npx 5-phase-workflow --upgrade');
1375
+ log.info(`Run: npx 5-phase-workflow${activeRuntime === 'codex' ? ' --codex' : ''} --upgrade`);
900
1376
  } else {
901
1377
  log.success('Up to date');
902
1378
  }