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