5-phase-workflow 1.8.7 → 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/README.md +52 -35
- package/bin/install.js +505 -28
- package/package.json +1 -1
- package/src/commands/5/configure.md +24 -3
- package/src/commands/5/eject.md +29 -10
- package/src/commands/5/plan-feature.md +0 -1
- package/src/commands/5/plan-implementation.md +0 -1
- package/src/commands/5/quick-implement.md +0 -1
- package/src/commands/5/reconfigure.md +20 -19
- package/src/commands/5/update.md +16 -3
- package/src/skills/configure-docs-index/SKILL.md +196 -0
- package/src/skills/{configure-project → configure-skills}/SKILL.md +33 -126
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
|
|
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
|
|
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,
|
|
164
|
+
return path.join(homeDir, dirName);
|
|
147
165
|
}
|
|
148
|
-
return path.join(process.cwd(),
|
|
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:
|
|
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');
|
|
@@ -314,8 +332,10 @@ function getWorkflowManagedFiles() {
|
|
|
314
332
|
};
|
|
315
333
|
}
|
|
316
334
|
|
|
317
|
-
// Flatten getWorkflowManagedFiles() into a list of relative paths (relative to
|
|
335
|
+
// Flatten getWorkflowManagedFiles() into a list of relative paths (relative to target dir)
|
|
318
336
|
function getFileManifest() {
|
|
337
|
+
if (activeRuntime === 'codex') return getCodexFileManifest();
|
|
338
|
+
|
|
319
339
|
const managed = getWorkflowManagedFiles();
|
|
320
340
|
const manifest = [];
|
|
321
341
|
|
|
@@ -354,6 +374,141 @@ function getFileManifest() {
|
|
|
354
374
|
return manifest;
|
|
355
375
|
}
|
|
356
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
|
+
|
|
357
512
|
// Selectively update only workflow-managed files, preserve user content
|
|
358
513
|
function selectiveUpdate(targetPath, sourcePath) {
|
|
359
514
|
const managed = getWorkflowManagedFiles();
|
|
@@ -609,23 +764,42 @@ function mergeSettings(targetPath, sourcePath) {
|
|
|
609
764
|
|
|
610
765
|
// Check if installation exists
|
|
611
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
|
+
}
|
|
612
772
|
const markerFile = path.join(targetPath, 'commands', '5', 'plan-feature.md');
|
|
613
773
|
return fs.existsSync(markerFile);
|
|
614
774
|
}
|
|
615
775
|
|
|
616
776
|
// Helper to show commands
|
|
617
777
|
function showCommandsHelp(isGlobal) {
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
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
|
+
}
|
|
629
803
|
log.info('');
|
|
630
804
|
log.info(`Config file: ${path.join(getDataPath(isGlobal), 'config.json')}`);
|
|
631
805
|
}
|
|
@@ -721,26 +895,320 @@ function performUpdate(targetPath, sourcePath, isGlobal, versionInfo) {
|
|
|
721
895
|
showCommandsHelp(isGlobal);
|
|
722
896
|
}
|
|
723
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
|
+
|
|
724
1185
|
// Perform installation
|
|
725
1186
|
function install(isGlobal, forceUpgrade = false) {
|
|
726
1187
|
const targetPath = getTargetPath(isGlobal);
|
|
727
1188
|
const sourcePath = getSourcePath();
|
|
728
1189
|
|
|
729
|
-
|
|
1190
|
+
const runtimeLabel = activeRuntime === 'codex' ? 'Codex' : 'Claude Code';
|
|
1191
|
+
log.header(`5-Phase Workflow Installation (${runtimeLabel})`);
|
|
730
1192
|
log.info(`Target: ${targetPath}`);
|
|
731
1193
|
log.info(`Source: ${sourcePath}`);
|
|
732
1194
|
|
|
733
|
-
// Migrate data from old .claude/.5/ to new .5/ location
|
|
734
|
-
|
|
1195
|
+
// Migrate data from old .claude/.5/ to new .5/ location (Claude only)
|
|
1196
|
+
if (activeRuntime === 'claude') {
|
|
1197
|
+
migrateDataDir(isGlobal);
|
|
1198
|
+
}
|
|
735
1199
|
|
|
736
1200
|
// Check for existing installation and version
|
|
737
1201
|
const versionInfo = getVersionInfo(targetPath, isGlobal);
|
|
738
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
|
+
|
|
739
1207
|
if (versionInfo.exists) {
|
|
740
1208
|
if (versionInfo.legacy) {
|
|
741
1209
|
log.warn('Detected legacy installation (no version tracking)');
|
|
742
1210
|
log.info(`Upgrading from legacy install to ${versionInfo.available}`);
|
|
743
|
-
|
|
1211
|
+
update(targetPath, sourcePath, isGlobal, versionInfo);
|
|
744
1212
|
return;
|
|
745
1213
|
} else if (versionInfo.needsUpdate) {
|
|
746
1214
|
log.info(`Installed: ${versionInfo.installed}`);
|
|
@@ -759,12 +1227,12 @@ function install(isGlobal, forceUpgrade = false) {
|
|
|
759
1227
|
log.info('Update cancelled');
|
|
760
1228
|
return;
|
|
761
1229
|
}
|
|
762
|
-
|
|
1230
|
+
update(targetPath, sourcePath, isGlobal, versionInfo);
|
|
763
1231
|
});
|
|
764
1232
|
return; // Wait for user input
|
|
765
1233
|
}
|
|
766
1234
|
// Force upgrade, no prompt
|
|
767
|
-
|
|
1235
|
+
update(targetPath, sourcePath, isGlobal, versionInfo);
|
|
768
1236
|
return;
|
|
769
1237
|
} else {
|
|
770
1238
|
// Same version
|
|
@@ -774,11 +1242,16 @@ function install(isGlobal, forceUpgrade = false) {
|
|
|
774
1242
|
}
|
|
775
1243
|
|
|
776
1244
|
// Fresh install (no existing installation)
|
|
777
|
-
|
|
1245
|
+
freshInstall(targetPath, sourcePath, isGlobal);
|
|
778
1246
|
}
|
|
779
1247
|
|
|
780
1248
|
// Perform uninstallation
|
|
781
1249
|
function uninstall() {
|
|
1250
|
+
if (activeRuntime === 'codex') {
|
|
1251
|
+
codexUninstall();
|
|
1252
|
+
return;
|
|
1253
|
+
}
|
|
1254
|
+
|
|
782
1255
|
const targetPath = getTargetPath(false); // Always local for uninstall
|
|
783
1256
|
|
|
784
1257
|
log.header('5-Phase Workflow Uninstallation');
|
|
@@ -875,6 +1348,9 @@ function uninstall() {
|
|
|
875
1348
|
function main() {
|
|
876
1349
|
const options = parseArgs();
|
|
877
1350
|
|
|
1351
|
+
// Set module-level runtime before any path resolution
|
|
1352
|
+
activeRuntime = options.runtime;
|
|
1353
|
+
|
|
878
1354
|
if (options.help) {
|
|
879
1355
|
showHelp();
|
|
880
1356
|
return;
|
|
@@ -886,16 +1362,17 @@ function main() {
|
|
|
886
1362
|
const versionInfo = getVersionInfo(targetPath, options.global);
|
|
887
1363
|
|
|
888
1364
|
if (!versionInfo.exists) {
|
|
889
|
-
log.info(
|
|
1365
|
+
log.info(`Not installed (${activeRuntime})`);
|
|
890
1366
|
return;
|
|
891
1367
|
}
|
|
892
1368
|
|
|
1369
|
+
log.info(`Runtime: ${activeRuntime}`);
|
|
893
1370
|
log.info(`Installed: ${versionInfo.installed || 'legacy (no version)'}`);
|
|
894
1371
|
log.info(`Available: ${versionInfo.available}`);
|
|
895
1372
|
|
|
896
1373
|
if (versionInfo.needsUpdate) {
|
|
897
1374
|
log.warn('Update available');
|
|
898
|
-
log.info(
|
|
1375
|
+
log.info(`Run: npx 5-phase-workflow${activeRuntime === 'codex' ? ' --codex' : ''} --upgrade`);
|
|
899
1376
|
} else {
|
|
900
1377
|
log.success('Up to date');
|
|
901
1378
|
}
|