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/README.md +67 -33
- package/bin/install.js +510 -34
- package/docs/workflow-guide.md +1 -1
- package/package.json +1 -1
- package/src/commands/5/address-review-findings.md +2 -2
- package/src/commands/5/configure.md +39 -27
- package/src/commands/5/eject.md +110 -0
- 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 -17
- package/src/commands/5/update.md +16 -3
- package/src/hooks/statusline.js +80 -41
- package/src/skills/configure-docs-index/SKILL.md +196 -0
- package/src/skills/configure-skills/SKILL.md +314 -0
- package/src/skills/generate-readme/SKILL.md +1 -2
- package/src/templates/ARCHITECTURE.md +16 -47
- package/src/templates/CONCERNS.md +12 -54
- package/src/templates/TESTING.md +12 -95
- package/src/skills/build-project/SKILL.md +0 -151
- package/src/skills/configure-project/SKILL.md +0 -465
- package/src/skills/run-tests/SKILL.md +0 -162
- package/src/templates/CONVENTIONS.md +0 -75
- package/src/templates/INTEGRATIONS.md +0 -65
- package/src/templates/STACK.md +0 -60
- package/src/templates/STRUCTURE.md +0 -60
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');
|
|
@@ -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
|
|
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
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1230
|
+
update(targetPath, sourcePath, isGlobal, versionInfo);
|
|
764
1231
|
});
|
|
765
1232
|
return; // Wait for user input
|
|
766
1233
|
}
|
|
767
1234
|
// Force upgrade, no prompt
|
|
768
|
-
|
|
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
|
-
|
|
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(
|
|
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(
|
|
1375
|
+
log.info(`Run: npx 5-phase-workflow${activeRuntime === 'codex' ? ' --codex' : ''} --upgrade`);
|
|
900
1376
|
} else {
|
|
901
1377
|
log.success('Up to date');
|
|
902
1378
|
}
|