prizmkit 1.1.58 → 1.1.61

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.
Files changed (205) hide show
  1. package/bin/create-prizmkit.js +8 -6
  2. package/bundled/VERSION.json +3 -3
  3. package/bundled/adapters/codex/agent-adapter.js +38 -0
  4. package/bundled/adapters/codex/paths.js +27 -0
  5. package/bundled/adapters/codex/rules-adapter.js +30 -0
  6. package/bundled/adapters/codex/settings-adapter.js +27 -0
  7. package/bundled/adapters/codex/skill-adapter.js +65 -0
  8. package/bundled/adapters/codex/team-adapter.js +37 -0
  9. package/bundled/agents/prizm-dev-team-dev.md +6 -5
  10. package/bundled/dev-pipeline/.env.example +2 -1
  11. package/bundled/dev-pipeline/README.md +10 -7
  12. package/bundled/dev-pipeline/lib/common.sh +278 -37
  13. package/bundled/dev-pipeline/run-bugfix.sh +10 -61
  14. package/bundled/dev-pipeline/run-feature.sh +10 -78
  15. package/bundled/dev-pipeline/run-recovery.sh +10 -46
  16. package/bundled/dev-pipeline/run-refactor.sh +10 -61
  17. package/bundled/dev-pipeline/scripts/generate-bootstrap-prompt.py +17 -7
  18. package/bundled/dev-pipeline/scripts/generate-bugfix-prompt.py +9 -3
  19. package/bundled/dev-pipeline/scripts/generate-refactor-prompt.py +9 -3
  20. package/bundled/dev-pipeline/scripts/utils.py +6 -4
  21. package/bundled/dev-pipeline-windows/.env.example +28 -0
  22. package/bundled/dev-pipeline-windows/README.md +30 -0
  23. package/bundled/dev-pipeline-windows/SCHEMA_ANALYSIS.md +525 -0
  24. package/bundled/dev-pipeline-windows/assets/feature-list-example.json +146 -0
  25. package/bundled/dev-pipeline-windows/assets/prizm-dev-team-integration.md +138 -0
  26. package/bundled/dev-pipeline-windows/launch-bugfix-daemon.ps1 +9 -0
  27. package/bundled/dev-pipeline-windows/launch-feature-daemon.ps1 +9 -0
  28. package/bundled/dev-pipeline-windows/launch-refactor-daemon.ps1 +9 -0
  29. package/bundled/dev-pipeline-windows/lib/common.ps1 +432 -0
  30. package/bundled/dev-pipeline-windows/lib/daemon.ps1 +140 -0
  31. package/bundled/dev-pipeline-windows/lib/pipeline.ps1 +446 -0
  32. package/bundled/dev-pipeline-windows/lib/reset.ps1 +87 -0
  33. package/bundled/dev-pipeline-windows/reset-bug.ps1 +9 -0
  34. package/bundled/dev-pipeline-windows/reset-feature.ps1 +9 -0
  35. package/bundled/dev-pipeline-windows/reset-refactor.ps1 +9 -0
  36. package/bundled/dev-pipeline-windows/run-bugfix.ps1 +9 -0
  37. package/bundled/dev-pipeline-windows/run-feature.ps1 +9 -0
  38. package/bundled/dev-pipeline-windows/run-recovery.ps1 +76 -0
  39. package/bundled/dev-pipeline-windows/run-refactor.ps1 +9 -0
  40. package/bundled/dev-pipeline-windows/scripts/check-session-status.py +228 -0
  41. package/bundled/dev-pipeline-windows/scripts/cleanup-logs.py +192 -0
  42. package/bundled/dev-pipeline-windows/scripts/detect-stuck.py +530 -0
  43. package/bundled/dev-pipeline-windows/scripts/generate-bootstrap-prompt.py +1737 -0
  44. package/bundled/dev-pipeline-windows/scripts/generate-bugfix-prompt.py +685 -0
  45. package/bundled/dev-pipeline-windows/scripts/generate-recovery-prompt.py +805 -0
  46. package/bundled/dev-pipeline-windows/scripts/generate-refactor-prompt.py +763 -0
  47. package/bundled/dev-pipeline-windows/scripts/init-bugfix-pipeline.py +316 -0
  48. package/bundled/dev-pipeline-windows/scripts/init-dev-team.py +134 -0
  49. package/bundled/dev-pipeline-windows/scripts/init-pipeline.py +380 -0
  50. package/bundled/dev-pipeline-windows/scripts/init-refactor-pipeline.py +399 -0
  51. package/bundled/dev-pipeline-windows/scripts/parse-stream-progress.py +388 -0
  52. package/bundled/dev-pipeline-windows/scripts/patch-completion-notes.py +191 -0
  53. package/bundled/dev-pipeline-windows/scripts/update-bug-status.py +864 -0
  54. package/bundled/dev-pipeline-windows/scripts/update-checkpoint.py +173 -0
  55. package/bundled/dev-pipeline-windows/scripts/update-feature-status.py +1501 -0
  56. package/bundled/dev-pipeline-windows/scripts/update-refactor-status.py +1073 -0
  57. package/bundled/dev-pipeline-windows/scripts/utils.py +542 -0
  58. package/bundled/dev-pipeline-windows/templates/agent-prompts/critic-plan-challenge.md +7 -0
  59. package/bundled/dev-pipeline-windows/templates/agent-prompts/dev-fix.md +7 -0
  60. package/bundled/dev-pipeline-windows/templates/agent-prompts/dev-implement.md +30 -0
  61. package/bundled/dev-pipeline-windows/templates/agent-prompts/dev-resume.md +5 -0
  62. package/bundled/dev-pipeline-windows/templates/agent-prompts/reviewer-review.md +7 -0
  63. package/bundled/dev-pipeline-windows/templates/bootstrap-prompt.md +46 -0
  64. package/bundled/dev-pipeline-windows/templates/bootstrap-tier1.md +43 -0
  65. package/bundled/dev-pipeline-windows/templates/bootstrap-tier2.md +43 -0
  66. package/bundled/dev-pipeline-windows/templates/bootstrap-tier3.md +43 -0
  67. package/bundled/dev-pipeline-windows/templates/bug-fix-list-schema.json +263 -0
  68. package/bundled/dev-pipeline-windows/templates/bugfix-bootstrap-prompt.md +320 -0
  69. package/bundled/dev-pipeline-windows/templates/feature-list-schema.json +237 -0
  70. package/bundled/dev-pipeline-windows/templates/refactor-bootstrap-prompt.md +331 -0
  71. package/bundled/dev-pipeline-windows/templates/refactor-list-schema.json +270 -0
  72. package/bundled/dev-pipeline-windows/templates/sections/ac-verification-checklist.md +13 -0
  73. package/bundled/dev-pipeline-windows/templates/sections/checkpoint-system.md +91 -0
  74. package/bundled/dev-pipeline-windows/templates/sections/context-budget-rules.md +33 -0
  75. package/bundled/dev-pipeline-windows/templates/sections/critical-paths-agent.md +10 -0
  76. package/bundled/dev-pipeline-windows/templates/sections/critical-paths-full.md +12 -0
  77. package/bundled/dev-pipeline-windows/templates/sections/critical-paths-lite.md +7 -0
  78. package/bundled/dev-pipeline-windows/templates/sections/directory-convention-agent.md +8 -0
  79. package/bundled/dev-pipeline-windows/templates/sections/directory-convention-full.md +9 -0
  80. package/bundled/dev-pipeline-windows/templates/sections/directory-convention-lite.md +6 -0
  81. package/bundled/dev-pipeline-windows/templates/sections/failure-capture.md +21 -0
  82. package/bundled/dev-pipeline-windows/templates/sections/feature-context.md +31 -0
  83. package/bundled/dev-pipeline-windows/templates/sections/phase-browser-verification-auto.md +72 -0
  84. package/bundled/dev-pipeline-windows/templates/sections/phase-browser-verification-opencli.md +63 -0
  85. package/bundled/dev-pipeline-windows/templates/sections/phase-browser-verification.md +62 -0
  86. package/bundled/dev-pipeline-windows/templates/sections/phase-commit-full.md +71 -0
  87. package/bundled/dev-pipeline-windows/templates/sections/phase-commit.md +64 -0
  88. package/bundled/dev-pipeline-windows/templates/sections/phase-context-snapshot-agent-suffix.md +23 -0
  89. package/bundled/dev-pipeline-windows/templates/sections/phase-context-snapshot-base.md +24 -0
  90. package/bundled/dev-pipeline-windows/templates/sections/phase-context-snapshot-lite-suffix.md +12 -0
  91. package/bundled/dev-pipeline-windows/templates/sections/phase-critic-plan-full.md +53 -0
  92. package/bundled/dev-pipeline-windows/templates/sections/phase-critic-plan.md +32 -0
  93. package/bundled/dev-pipeline-windows/templates/sections/phase-implement-agent.md +37 -0
  94. package/bundled/dev-pipeline-windows/templates/sections/phase-implement-full.md +50 -0
  95. package/bundled/dev-pipeline-windows/templates/sections/phase-implement-lite.md +52 -0
  96. package/bundled/dev-pipeline-windows/templates/sections/phase-plan-agent.md +27 -0
  97. package/bundled/dev-pipeline-windows/templates/sections/phase-plan-lite.md +27 -0
  98. package/bundled/dev-pipeline-windows/templates/sections/phase-review-agent.md +27 -0
  99. package/bundled/dev-pipeline-windows/templates/sections/phase-review-full.md +29 -0
  100. package/bundled/dev-pipeline-windows/templates/sections/phase-specify-plan-full.md +77 -0
  101. package/bundled/dev-pipeline-windows/templates/sections/phase0-init.md +13 -0
  102. package/bundled/dev-pipeline-windows/templates/sections/phase0-test-baseline.md +23 -0
  103. package/bundled/dev-pipeline-windows/templates/sections/session-context.md +5 -0
  104. package/bundled/dev-pipeline-windows/templates/sections/subagent-timeout-recovery.md +6 -0
  105. package/bundled/dev-pipeline-windows/templates/sections/test-failure-recovery-agent.md +67 -0
  106. package/bundled/dev-pipeline-windows/templates/sections/test-failure-recovery-lite.md +58 -0
  107. package/bundled/dev-pipeline-windows/templates/session-status-schema.json +83 -0
  108. package/bundled/rules/prizm/prizm-documentation.md +2 -4
  109. package/bundled/rules/prizm/prizm-progressive-loading.md +2 -1
  110. package/bundled/skills/_metadata.json +1 -1
  111. package/bundled/skills/app-planner/SKILL.md +24 -16
  112. package/bundled/skills/app-planner/references/architecture-decisions.md +9 -5
  113. package/bundled/skills/app-planner/references/frontend-design-guide.md +1 -1
  114. package/bundled/skills/feature-planner/SKILL.md +9 -2
  115. package/bundled/skills/prizmkit-implement/SKILL.md +1 -0
  116. package/bundled/skills/prizmkit-init/SKILL.md +8 -7
  117. package/bundled/skills/prizmkit-init/references/config-schema.md +3 -1
  118. package/bundled/skills/prizmkit-plan/SKILL.md +2 -0
  119. package/bundled/skills/prizmkit-prizm-docs/assets/prizm-docs-format.md +6 -1
  120. package/bundled/skills/prizmkit-prizm-docs/references/op-init.md +2 -2
  121. package/bundled/skills/prizmkit-prizm-docs/references/op-update.md +2 -1
  122. package/bundled/skills/prizmkit-prizm-docs/references/op-validate.md +2 -2
  123. package/bundled/skills/prizmkit-retrospective/SKILL.md +2 -0
  124. package/bundled/skills/prizmkit-retrospective/references/knowledge-injection-steps.md +2 -0
  125. package/bundled/skills/prizmkit-retrospective/references/structural-sync-steps.md +2 -0
  126. package/bundled/skills/recovery-workflow/scripts/detect-recovery-state.py +2 -0
  127. package/bundled/skills-windows/app-planner/SKILL.md +639 -0
  128. package/bundled/skills-windows/app-planner/assets/app-design-guide.md +101 -0
  129. package/bundled/skills-windows/app-planner/references/architecture-decisions.md +52 -0
  130. package/bundled/skills-windows/app-planner/references/brainstorm-guide.md +101 -0
  131. package/bundled/skills-windows/app-planner/references/frontend-design-guide.md +71 -0
  132. package/bundled/skills-windows/app-planner/references/project-brief-guide.md +82 -0
  133. package/bundled/skills-windows/app-planner/references/red-team-checklist.md +40 -0
  134. package/bundled/skills-windows/app-planner/references/rules/backend/derivation-rules.md +609 -0
  135. package/bundled/skills-windows/app-planner/references/rules/backend/fixed-rules.md +285 -0
  136. package/bundled/skills-windows/app-planner/references/rules/backend/question-bank.md +249 -0
  137. package/bundled/skills-windows/app-planner/references/rules/backend/template.md +173 -0
  138. package/bundled/skills-windows/app-planner/references/rules/database/derivation-rules.md +373 -0
  139. package/bundled/skills-windows/app-planner/references/rules/database/fixed-rules.md +211 -0
  140. package/bundled/skills-windows/app-planner/references/rules/database/question-bank.md +184 -0
  141. package/bundled/skills-windows/app-planner/references/rules/database/template.md +158 -0
  142. package/bundled/skills-windows/app-planner/references/rules/frontend/derivation-rules.md +810 -0
  143. package/bundled/skills-windows/app-planner/references/rules/frontend/fixed-rules.md +188 -0
  144. package/bundled/skills-windows/app-planner/references/rules/frontend/question-bank.md +302 -0
  145. package/bundled/skills-windows/app-planner/references/rules/frontend/template.md +320 -0
  146. package/bundled/skills-windows/app-planner/references/rules/mobile/derivation-rules.md +639 -0
  147. package/bundled/skills-windows/app-planner/references/rules/mobile/fixed-rules.md +290 -0
  148. package/bundled/skills-windows/app-planner/references/rules/mobile/question-bank.md +232 -0
  149. package/bundled/skills-windows/app-planner/references/rules/mobile/template.md +175 -0
  150. package/bundled/skills-windows/bug-fix-workflow/SKILL.md +415 -0
  151. package/bundled/skills-windows/bug-planner/SKILL.md +395 -0
  152. package/bundled/skills-windows/bug-planner/assets/bug-confirmation-template.md +43 -0
  153. package/bundled/skills-windows/bug-planner/references/critic-and-verification.md +44 -0
  154. package/bundled/skills-windows/bug-planner/references/error-recovery.md +73 -0
  155. package/bundled/skills-windows/bug-planner/references/input-formats.md +53 -0
  156. package/bundled/skills-windows/bug-planner/references/schema-validation.md +25 -0
  157. package/bundled/skills-windows/bug-planner/references/severity-rules.md +16 -0
  158. package/bundled/skills-windows/bug-planner/scripts/validate-bug-list.py +322 -0
  159. package/bundled/skills-windows/bugfix-pipeline-launcher/SKILL.md +380 -0
  160. package/bundled/skills-windows/feature-pipeline-launcher/SKILL.md +441 -0
  161. package/bundled/skills-windows/feature-pipeline-launcher/scripts/preflight-check.py +462 -0
  162. package/bundled/skills-windows/feature-planner/SKILL.md +401 -0
  163. package/bundled/skills-windows/feature-planner/assets/evaluation-guide.md +64 -0
  164. package/bundled/skills-windows/feature-planner/assets/planning-guide.md +214 -0
  165. package/bundled/skills-windows/feature-planner/references/browser-interaction.md +59 -0
  166. package/bundled/skills-windows/feature-planner/references/completeness-review.md +57 -0
  167. package/bundled/skills-windows/feature-planner/references/decomposition-patterns.md +75 -0
  168. package/bundled/skills-windows/feature-planner/references/error-recovery.md +90 -0
  169. package/bundled/skills-windows/feature-planner/references/incremental-feature-planning.md +112 -0
  170. package/bundled/skills-windows/feature-planner/references/new-project-planning.md +85 -0
  171. package/bundled/skills-windows/feature-planner/scripts/validate-and-generate.py +1029 -0
  172. package/bundled/skills-windows/feature-workflow/SKILL.md +531 -0
  173. package/bundled/skills-windows/prizmkit-init/SKILL.md +356 -0
  174. package/bundled/skills-windows/prizmkit-init/assets/project-brief-template.md +82 -0
  175. package/bundled/skills-windows/prizmkit-init/references/config-schema.md +70 -0
  176. package/bundled/skills-windows/prizmkit-init/references/rules/layer-detection.md +41 -0
  177. package/bundled/skills-windows/prizmkit-init/references/tech-stack-catalog.md +13 -0
  178. package/bundled/skills-windows/prizmkit-init/references/update-supplement.md +9 -0
  179. package/bundled/skills-windows/recovery-workflow/SKILL.md +456 -0
  180. package/bundled/skills-windows/recovery-workflow/evals/evals.json +46 -0
  181. package/bundled/skills-windows/recovery-workflow/scripts/detect-recovery-state.py +544 -0
  182. package/bundled/skills-windows/refactor-pipeline-launcher/SKILL.md +406 -0
  183. package/bundled/skills-windows/refactor-planner/SKILL.md +540 -0
  184. package/bundled/skills-windows/refactor-planner/assets/planning-guide.md +292 -0
  185. package/bundled/skills-windows/refactor-planner/references/behavior-preservation.md +301 -0
  186. package/bundled/skills-windows/refactor-planner/references/refactor-scoping-guide.md +221 -0
  187. package/bundled/skills-windows/refactor-planner/scripts/validate-and-generate-refactor.py +858 -0
  188. package/bundled/skills-windows/refactor-workflow/SKILL.md +503 -0
  189. package/bundled/templates/hooks/diff-prizm-docs.sh +1 -1
  190. package/bundled/templates/hooks/prizm-pre-commit.sh +53 -4
  191. package/bundled/templates/hooks/validate-prizm-docs.sh +62 -7
  192. package/bundled/templates/project-memory-template.md +2 -1
  193. package/package.json +3 -2
  194. package/src/clean.js +73 -2
  195. package/src/config.js +167 -50
  196. package/src/detect-platform.js +15 -9
  197. package/src/external-skills.js +26 -19
  198. package/src/index.js +28 -10
  199. package/src/manifest.js +6 -2
  200. package/src/metadata.js +43 -5
  201. package/src/platforms.js +38 -0
  202. package/src/prompts.js +50 -9
  203. package/src/runtimes.js +20 -0
  204. package/src/scaffold.js +447 -114
  205. package/src/upgrade.js +87 -44
package/src/config.js CHANGED
@@ -31,8 +31,11 @@ import {
31
31
  installProjectMemory,
32
32
  installPipeline,
33
33
  installGitignore,
34
+ installGitHook,
35
+ installPrizmkitScripts,
34
36
  resolveSkillList,
35
37
  resolvePipelineFileList,
38
+ resolveRuleNamesForRuntime,
36
39
  } from './scaffold.js';
37
40
  import {
38
41
  removeSkillFiles,
@@ -44,11 +47,14 @@ import { detectPlatform } from './detect-platform.js';
44
47
  import {
45
48
  selectPlatform,
46
49
  selectAiCli,
50
+ selectRuntime,
47
51
  selectSkillSuite,
48
52
  selectRulesPreset,
49
53
  confirmTeamMode,
50
54
  confirmPipeline,
51
55
  } from './prompts.js';
56
+ import { expandPlatforms, isKnownPlatform, platformLabel, projectMemoryFile } from './platforms.js';
57
+ import { normalizeRuntime, runtimeLabel } from './runtimes.js';
52
58
 
53
59
  const __dirname = dirname(fileURLToPath(import.meta.url));
54
60
  const pkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf-8'));
@@ -57,36 +63,53 @@ const pkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf-
57
63
  // Helpers
58
64
  // ============================================================
59
65
 
66
+ async function removeGeneratedCodexConfig(projectRoot, dryRun) {
67
+ const configPath = path.join(projectRoot, '.codex', 'config.toml');
68
+ if (!await fs.pathExists(configPath)) return;
69
+
70
+ const content = await fs.readFile(configPath, 'utf8');
71
+ if (!content.includes('Generated by PrizmKit for Codex CLI project configuration')) {
72
+ return;
73
+ }
74
+
75
+ if (dryRun) {
76
+ console.log(chalk.gray(' [dry-run] remove .codex/config.toml'));
77
+ } else {
78
+ await fs.remove(configPath);
79
+ console.log(chalk.red(' ✗ removed .codex/config.toml'));
80
+ }
81
+ }
82
+
60
83
  /**
61
84
  * Detect which platforms are actually installed on filesystem.
62
85
  */
63
86
  async function detectInstalledPlatforms(projectRoot) {
64
- const hasClaude = await fs.pathExists(path.join(projectRoot, '.claude', 'commands'))
65
- || await fs.pathExists(path.join(projectRoot, '.claude', 'agents'));
66
- const hasCodeBuddy = await fs.pathExists(path.join(projectRoot, '.codebuddy', 'skills'))
67
- || await fs.pathExists(path.join(projectRoot, '.codebuddy', 'agents'));
87
+ const hasPlatformFiles = async (dir) => {
88
+ if (!await fs.pathExists(dir)) return false;
89
+ try {
90
+ return (await fs.readdir(dir)).length > 0;
91
+ } catch {
92
+ return false;
93
+ }
94
+ };
68
95
 
96
+ const hasClaude = await hasPlatformFiles(path.join(projectRoot, '.claude', 'commands'))
97
+ || await hasPlatformFiles(path.join(projectRoot, '.claude', 'agents'));
98
+ const hasCodeBuddy = await hasPlatformFiles(path.join(projectRoot, '.codebuddy', 'skills'))
99
+ || await hasPlatformFiles(path.join(projectRoot, '.codebuddy', 'agents'));
100
+ const hasCodex = await hasPlatformFiles(path.join(projectRoot, '.agents', 'skills'))
101
+ || await hasPlatformFiles(path.join(projectRoot, '.codex', 'agents'));
102
+
103
+ if (hasClaude && hasCodeBuddy && hasCodex) return 'all';
69
104
  if (hasClaude && hasCodeBuddy) return 'both';
105
+ if (hasCodex) return 'codex';
70
106
  if (hasCodeBuddy) return 'codebuddy';
71
107
  if (hasClaude) return 'claude';
72
108
  return null;
73
109
  }
74
110
 
75
- /**
76
- * Expand a platform string to an array of individual platforms.
77
- */
78
- function expandPlatforms(platform) {
79
- return platform === 'both' ? ['codebuddy', 'claude'] : [platform];
80
- }
81
-
82
111
  function getProjectMemoryFile(platform) {
83
- if (platform === 'claude') return 'CLAUDE.md';
84
- if (platform === 'codebuddy') return 'CODEBUDDY.md';
85
- return null;
86
- }
87
-
88
- function getCounterpartPlatform(platform) {
89
- return platform === 'claude' ? 'codebuddy' : platform === 'codebuddy' ? 'claude' : null;
112
+ return projectMemoryFile(platform);
90
113
  }
91
114
 
92
115
  async function migrateProjectMemoryOnPlatformDrop(droppedPlatform, newPlatforms, projectRoot, dryRun) {
@@ -96,10 +119,7 @@ async function migrateProjectMemoryOnPlatformDrop(droppedPlatform, newPlatforms,
96
119
  const sourcePath = path.join(projectRoot, sourceFile);
97
120
  if (!await fs.pathExists(sourcePath)) return;
98
121
 
99
- const counterpart = getCounterpartPlatform(droppedPlatform);
100
- const targetPlatform = (counterpart && newPlatforms.includes(counterpart))
101
- ? counterpart
102
- : newPlatforms.find(p => p !== droppedPlatform);
122
+ const targetPlatform = newPlatforms.find(p => p !== droppedPlatform) || newPlatforms[0];
103
123
 
104
124
  if (!targetPlatform || targetPlatform === droppedPlatform) return;
105
125
 
@@ -138,9 +158,6 @@ async function migrateProjectMemoryOnPlatformDrop(droppedPlatform, newPlatforms,
138
158
  console.log(chalk.green(` ✓ merged ${sourceFile} content into ${targetFile}`));
139
159
  }
140
160
 
141
- const platformLabel = (p) => p === 'both' ? 'CodeBuddy + Claude Code'
142
- : p === 'codebuddy' ? 'CodeBuddy' : 'Claude Code';
143
-
144
161
  /**
145
162
  * Remove all PrizmKit-owned platform-specific files for a single platform.
146
163
  * Selective removal — only removes known PrizmKit files, leaves user-created files intact.
@@ -166,15 +183,28 @@ async function removePlatformFiles(platform, projectRoot, manifest, dryRun) {
166
183
  }
167
184
 
168
185
  // Settings
169
- const settingsFile = platform === 'claude'
170
- ? path.join(projectRoot, '.claude', 'settings.json')
171
- : path.join(projectRoot, '.codebuddy', 'settings.json');
172
- if (await fs.pathExists(settingsFile)) {
173
- if (dryRun) {
174
- console.log(chalk.gray(` [dry-run] remove ${path.relative(projectRoot, settingsFile)}`));
175
- } else {
176
- await fs.remove(settingsFile);
177
- console.log(chalk.red(` ✗ removed ${path.relative(projectRoot, settingsFile)}`));
186
+ if (platform === 'codex') {
187
+ await removeGeneratedCodexConfig(projectRoot, dryRun);
188
+ const legacySettingsFile = path.join(projectRoot, '.codex', 'prizmkit-settings.json');
189
+ if (await fs.pathExists(legacySettingsFile)) {
190
+ if (dryRun) {
191
+ console.log(chalk.gray(' [dry-run] remove .codex/prizmkit-settings.json (legacy)'));
192
+ } else {
193
+ await fs.remove(legacySettingsFile);
194
+ console.log(chalk.red(' ✗ removed .codex/prizmkit-settings.json (legacy)'));
195
+ }
196
+ }
197
+ } else {
198
+ const settingsFile = platform === 'claude'
199
+ ? path.join(projectRoot, '.claude', 'settings.json')
200
+ : path.join(projectRoot, '.codebuddy', 'settings.json');
201
+ if (await fs.pathExists(settingsFile)) {
202
+ if (dryRun) {
203
+ console.log(chalk.gray(` [dry-run] remove ${path.relative(projectRoot, settingsFile)}`));
204
+ } else {
205
+ await fs.remove(settingsFile);
206
+ console.log(chalk.red(` ✗ removed ${path.relative(projectRoot, settingsFile)}`));
207
+ }
178
208
  }
179
209
  }
180
210
 
@@ -200,10 +230,21 @@ async function removePlatformFiles(platform, projectRoot, manifest, dryRun) {
200
230
  console.log(chalk.red(' ✗ removed ~/.codebuddy/teams/prizm-dev-team/'));
201
231
  }
202
232
  }
233
+ } else if (platform === 'codex') {
234
+ const teamInfo = path.join(projectRoot, '.codex', 'team-info.json');
235
+ if (await fs.pathExists(teamInfo)) {
236
+ if (dryRun) {
237
+ console.log(chalk.gray(' [dry-run] remove .codex/team-info.json'));
238
+ } else {
239
+ await fs.remove(teamInfo);
240
+ console.log(chalk.red(' ✗ removed .codex/team-info.json'));
241
+ }
242
+ }
203
243
  }
204
244
 
205
245
  // Project memory file
206
- const memoryFile = platform === 'claude' ? 'CLAUDE.md' : 'CODEBUDDY.md';
246
+ const memoryFile = projectMemoryFile(platform);
247
+ if (!memoryFile) return;
207
248
  const memoryPath = path.join(projectRoot, memoryFile);
208
249
  if (await fs.pathExists(memoryPath)) {
209
250
  if (dryRun) {
@@ -225,6 +266,48 @@ async function removePlatformFiles(platform, projectRoot, manifest, dryRun) {
225
266
  console.log(chalk.red(' ✗ removed .claude/command-assets/'));
226
267
  }
227
268
  }
269
+ } else if (platform === 'codex') {
270
+ const agentsSkillsDir = path.join(projectRoot, '.agents', 'skills');
271
+ if (await fs.pathExists(agentsSkillsDir)) {
272
+ if (dryRun) {
273
+ console.log(chalk.gray(' [dry-run] remove .agents/skills/ if empty'));
274
+ } else if ((await fs.readdir(agentsSkillsDir)).length === 0) {
275
+ await fs.remove(agentsSkillsDir);
276
+ console.log(chalk.red(' ✗ removed .agents/skills/'));
277
+ }
278
+ }
279
+ const agentsDir = path.join(projectRoot, '.agents');
280
+ if (await fs.pathExists(agentsDir)) {
281
+ if (dryRun) {
282
+ console.log(chalk.gray(' [dry-run] remove .agents/ if empty'));
283
+ } else if ((await fs.readdir(agentsDir)).length === 0) {
284
+ await fs.remove(agentsDir);
285
+ console.log(chalk.red(' ✗ removed .agents/'));
286
+ }
287
+ }
288
+
289
+ const codexAgentsDir = path.join(projectRoot, '.codex', 'agents');
290
+ if (await fs.pathExists(codexAgentsDir)) {
291
+ if (dryRun) {
292
+ console.log(chalk.gray(' [dry-run] remove .codex/agents/ if empty'));
293
+ } else if ((await fs.readdir(codexAgentsDir)).length === 0) {
294
+ await fs.remove(codexAgentsDir);
295
+ console.log(chalk.red(' ✗ removed .codex/agents/'));
296
+ }
297
+ }
298
+
299
+ const codexDir = path.join(projectRoot, '.codex');
300
+ if (await fs.pathExists(codexDir)) {
301
+ if (dryRun) {
302
+ console.log(chalk.gray(' [dry-run] remove .codex/ if empty'));
303
+ } else {
304
+ const remaining = await fs.readdir(codexDir);
305
+ if (remaining.length === 0) {
306
+ await fs.remove(codexDir);
307
+ console.log(chalk.red(' ✗ removed .codex/'));
308
+ }
309
+ }
310
+ }
228
311
  }
229
312
  }
230
313
 
@@ -265,11 +348,13 @@ export async function runConfig(directory, options = {}) {
265
348
  try { userConfig = await fs.readJSON(configPath); } catch { /* ignore */ }
266
349
  }
267
350
 
268
- // Filesystem-based platform detection (overrides manifest)
269
- const detectedPlatform = await detectInstalledPlatforms(projectRoot) || oldManifest.platform || 'claude';
351
+ // Existing manifests are the source of truth. Filesystem detection is only a
352
+ // legacy fallback for manifests created before platform was recorded.
353
+ const detectedPlatform = oldManifest.platform || await detectInstalledPlatforms(projectRoot) || 'claude';
270
354
 
271
355
  const currentConfig = {
272
356
  platform: detectedPlatform,
357
+ runtime: normalizeRuntime(oldManifest.runtime || 'unix'),
273
358
  suite: oldManifest.suite || 'core',
274
359
  rules: oldManifest.options?.rules || 'recommended',
275
360
  team: oldManifest.options?.team ?? true,
@@ -277,9 +362,14 @@ export async function runConfig(directory, options = {}) {
277
362
  aiCli: userConfig.ai_cli || oldManifest.options?.aiCli || '',
278
363
  };
279
364
 
365
+ if (options.platform !== undefined && !isKnownPlatform(options.platform)) {
366
+ throw new Error(`Unknown platform "${options.platform}". Expected codebuddy, claude, codex, or all.`);
367
+ }
368
+
280
369
  // Display current config
281
370
  console.log(chalk.bold(' 当前配置:'));
282
371
  console.log(` 平台: ${chalk.cyan(platformLabel(currentConfig.platform))}`);
372
+ console.log(` 运行系统: ${chalk.cyan(runtimeLabel(currentConfig.runtime))}`);
283
373
  console.log(` AI CLI: ${currentConfig.aiCli ? chalk.cyan(currentConfig.aiCli) : chalk.gray('(未设置)')}`);
284
374
  console.log(` 技能套件: ${chalk.cyan(currentConfig.suite)}`);
285
375
  console.log(` 规则: ${chalk.cyan(currentConfig.rules)}`);
@@ -290,18 +380,20 @@ export async function runConfig(directory, options = {}) {
290
380
  // ── Phase 2: PROMPT for new config ───────────────────────────────────────
291
381
 
292
382
  let newConfig;
383
+ const optionRuntime = options.runtime !== undefined ? normalizeRuntime(options.runtime) : undefined;
293
384
 
294
385
  if (nonInteractive) {
295
386
  // Non-interactive: only change what's explicitly specified via CLI options
296
- if (!options.platform && !options.skills && !options.rules
387
+ if (!options.platform && !options.skills && !options.rules && options.runtime === undefined
297
388
  && options.aiCli === undefined && options.team === undefined && options.pipeline === undefined) {
298
389
  console.log(chalk.yellow(' ⚠ 非交互式模式下未指定任何变更。'));
299
- console.log(chalk.gray(' 使用 --platform, --skills, --rules, --ai-cli, --team/--no-team, --pipeline/--no-pipeline'));
390
+ console.log(chalk.gray(' 使用 --platform, --runtime, --skills, --rules, --ai-cli, --team/--no-team, --pipeline/--no-pipeline'));
300
391
  return;
301
392
  }
302
393
 
303
394
  newConfig = {
304
395
  platform: options.platform || currentConfig.platform,
396
+ runtime: optionRuntime || currentConfig.runtime,
305
397
  suite: options.skills || currentConfig.suite,
306
398
  rules: options.rules || currentConfig.rules,
307
399
  team: options.team !== undefined ? options.team : currentConfig.team,
@@ -315,6 +407,7 @@ export async function runConfig(directory, options = {}) {
315
407
  try {
316
408
  console.log(chalk.bold(' 选择新配置(按 Enter 保持当前值):\n'));
317
409
 
410
+ const newRuntime = optionRuntime || await selectRuntime(currentConfig.runtime);
318
411
  const newPlatform = await selectPlatform(currentConfig.platform, detected);
319
412
  const newAiCli = await selectAiCli(currentConfig.aiCli, detected);
320
413
 
@@ -326,6 +419,7 @@ export async function runConfig(directory, options = {}) {
326
419
 
327
420
  newConfig = {
328
421
  platform: newPlatform,
422
+ runtime: newRuntime,
329
423
  suite: newSuite,
330
424
  rules: newRules,
331
425
  team: newTeam,
@@ -341,12 +435,19 @@ export async function runConfig(directory, options = {}) {
341
435
  }
342
436
  }
343
437
 
438
+ if (!isKnownPlatform(newConfig.platform)) {
439
+ throw new Error(`Unknown platform "${newConfig.platform}". Expected codebuddy, claude, codex, or all.`);
440
+ }
441
+
344
442
  // ── Phase 3: COMPUTE DIFF ────────────────────────────────────────────────
345
443
 
346
444
  const changes = [];
347
445
  if (newConfig.platform !== currentConfig.platform) {
348
446
  changes.push(`平台: ${platformLabel(currentConfig.platform)} → ${platformLabel(newConfig.platform)}`);
349
447
  }
448
+ if (newConfig.runtime !== currentConfig.runtime) {
449
+ changes.push(`运行系统: ${runtimeLabel(currentConfig.runtime)} → ${runtimeLabel(newConfig.runtime)}`);
450
+ }
350
451
  if (newConfig.aiCli !== currentConfig.aiCli) {
351
452
  changes.push(`AI CLI: ${currentConfig.aiCli || '(未设置)'} → ${newConfig.aiCli || '(未设置)'}`);
352
453
  }
@@ -419,9 +520,8 @@ export async function runConfig(directory, options = {}) {
419
520
  const agentsDir = getAgentsDir();
420
521
  const newAgentFiles = (await fs.readdir(agentsDir)).filter(f => f.endsWith('.md'));
421
522
  const rulesMeta = await loadRulesMetadata();
422
- const rulesPresetDef = rulesMeta.presets[newConfig.rules] || rulesMeta.presets.recommended;
423
- const newRuleFiles = (rulesPresetDef?.rules || []).map(name => `${name}.md`);
424
- const newPipelineFiles = newConfig.pipeline ? resolvePipelineFileList() : [];
523
+ const newRuleFiles = resolveRuleNamesForRuntime(rulesMeta, newConfig.rules, newConfig.runtime).map(name => `${name}.md`);
524
+ const newPipelineFiles = newConfig.pipeline ? resolvePipelineFileList(newConfig.runtime) : [];
425
525
 
426
526
  // 5a. Migrate project memory before removing dropped platforms
427
527
  for (const p of platformsToRemove) {
@@ -434,13 +534,15 @@ export async function runConfig(directory, options = {}) {
434
534
  await removePlatformFiles(p, projectRoot, oldManifest, false);
435
535
  }
436
536
 
437
- // 5c. Handle skill/rule diff for existing platforms (suite or rules changed)
438
- if (newConfig.suite !== currentConfig.suite || newConfig.rules !== currentConfig.rules) {
537
+ // 5c. Handle skill/rule diff for existing platforms (suite, rules, or runtime changed)
538
+ if (newConfig.suite !== currentConfig.suite
539
+ || newConfig.rules !== currentConfig.rules
540
+ || newConfig.runtime !== currentConfig.runtime) {
439
541
  const newTempManifest = buildManifest({
440
542
  version: pkg.version, platform: newConfig.platform, suite: newConfig.suite,
441
543
  skills: newSkillList, agents: newAgentFiles, rules: newRuleFiles,
442
544
  pipeline: newPipelineFiles, team: newConfig.team, aiCli: newConfig.aiCli,
443
- rulesPreset: newConfig.rules, extras: oldManifest?.files?.extras || [],
545
+ runtime: newConfig.runtime, rulesPreset: newConfig.rules, extras: oldManifest?.files?.extras || [],
444
546
  });
445
547
  const diff = diffManifest(oldManifest, newTempManifest);
446
548
 
@@ -460,6 +562,7 @@ export async function runConfig(directory, options = {}) {
460
562
  // 5d. Install/update files for new + existing platforms
461
563
  const allTargetPlatforms = [...platformsToAdd, ...platformsToUpdate];
462
564
  const needsReinstall = newConfig.platform !== currentConfig.platform
565
+ || newConfig.runtime !== currentConfig.runtime
463
566
  || newConfig.suite !== currentConfig.suite
464
567
  || newConfig.rules !== currentConfig.rules
465
568
  || newConfig.team !== currentConfig.team;
@@ -471,13 +574,13 @@ export async function runConfig(directory, options = {}) {
471
574
  console.log(chalk.bold(`\n ${isNew ? '安装' : '更新'} ${platformLabel(p)} 环境...\n`));
472
575
 
473
576
  console.log(chalk.blue(' Skills:'));
474
- await installSkills(p, newSkillList, projectRoot, false);
577
+ await installSkills(p, newSkillList, projectRoot, false, newConfig.runtime);
475
578
 
476
579
  console.log(chalk.blue('\n Agents:'));
477
580
  await installAgents(p, projectRoot, false);
478
581
 
479
582
  console.log(chalk.blue('\n Settings & Rules:'));
480
- await installSettings(p, projectRoot, { pipeline: newConfig.pipeline, rules: newConfig.rules }, false);
583
+ await installSettings(p, projectRoot, { pipeline: newConfig.pipeline, rules: newConfig.rules }, false, newConfig.runtime);
481
584
 
482
585
  if (isNew) {
483
586
  console.log(chalk.blue('\n Project Memory:'));
@@ -492,10 +595,13 @@ export async function runConfig(directory, options = {}) {
492
595
  }
493
596
 
494
597
  // 5e. Pipeline changes
495
- if (newConfig.pipeline !== currentConfig.pipeline) {
598
+ if (newConfig.pipeline !== currentConfig.pipeline || (newConfig.pipeline && newConfig.runtime !== currentConfig.runtime)) {
496
599
  if (newConfig.pipeline) {
497
600
  console.log(chalk.blue('\n 安装 Pipeline:'));
498
- await installPipeline(projectRoot, false);
601
+ await installPipeline(projectRoot, false, {
602
+ runtime: newConfig.runtime,
603
+ previousRuntime: currentConfig.runtime,
604
+ });
499
605
  } else {
500
606
  console.log(chalk.blue('\n 移除 Pipeline 框架文件(保留 state/):'));
501
607
  const oldPipelineFiles = oldManifest?.files?.pipeline || [];
@@ -509,7 +615,16 @@ export async function runConfig(directory, options = {}) {
509
615
  console.log(chalk.blue('\n Gitignore:'));
510
616
  await installGitignore(projectRoot, { pipeline: newConfig.pipeline }, false);
511
617
 
512
- // 5g. Update .prizmkit/config.json (ai_cli)
618
+ // 5g. Runtime-aware git hook and PrizmKit helper scripts
619
+ if (newConfig.runtime !== currentConfig.runtime) {
620
+ console.log(chalk.blue('\n Git Hook:'));
621
+ await installGitHook(projectRoot, false, newConfig.runtime);
622
+
623
+ console.log(chalk.blue('\n PrizmKit Scripts:'));
624
+ await installPrizmkitScripts(projectRoot, false, newConfig.runtime);
625
+ }
626
+
627
+ // 5h. Update .prizmkit/config.json (ai_cli)
513
628
  if (newConfig.aiCli !== currentConfig.aiCli) {
514
629
  const prizmkitDir = path.join(projectRoot, '.prizmkit');
515
630
  await fs.ensureDir(prizmkitDir);
@@ -530,6 +645,7 @@ export async function runConfig(directory, options = {}) {
530
645
  const newManifest = buildManifest({
531
646
  version: pkg.version,
532
647
  platform: newConfig.platform,
648
+ runtime: newConfig.runtime,
533
649
  suite: newConfig.suite,
534
650
  skills: newSkillList,
535
651
  agents: newAgentFiles,
@@ -559,6 +675,7 @@ export async function runConfig(directory, options = {}) {
559
675
 
560
676
  console.log(chalk.gray(' 新配置:'));
561
677
  console.log(chalk.gray(` 平台: ${platformLabel(newConfig.platform)}`));
678
+ console.log(chalk.gray(` 运行系统: ${runtimeLabel(newConfig.runtime)}`));
562
679
  console.log(chalk.gray(` AI CLI: ${newConfig.aiCli || '(未设置)'}`));
563
680
  console.log(chalk.gray(` 技能套件: ${newConfig.suite}`));
564
681
  console.log(chalk.gray(` 规则: ${newConfig.rules}`));
@@ -4,11 +4,12 @@
4
4
  */
5
5
 
6
6
  import { execFileSync } from 'child_process';
7
+ import os from 'os';
7
8
 
8
9
  /**
9
10
  * 允许检测的命令白名单(仅用于 which 检测)
10
11
  */
11
- const ALLOWED_COMMANDS = ['cbc', 'claude'];
12
+ const ALLOWED_COMMANDS = ['cbc', 'claude', 'codex'];
12
13
 
13
14
  /**
14
15
  * 检查命令是否存在于 PATH 中
@@ -18,7 +19,8 @@ function commandExists(cmd) {
18
19
  throw new Error(`Unknown command: ${cmd}`);
19
20
  }
20
21
  try {
21
- execFileSync('which', [cmd], { stdio: 'ignore' });
22
+ const isWindows = os.platform() === 'win32';
23
+ execFileSync(isWindows ? 'where.exe' : 'which', [cmd], { stdio: 'ignore' });
22
24
  return true;
23
25
  } catch {
24
26
  return false;
@@ -31,26 +33,30 @@ function commandExists(cmd) {
31
33
  * 注意:platform 控制目录结构(.codebuddy/ 或 .claude/),
32
34
  * suggestedCli 是实际运行的可执行命令,二者相互独立。
33
35
  *
34
- * @returns {{ cbc: boolean, claude: boolean, claudeInternal: boolean, suggested: string, suggestedCli: string }}
36
+ * @returns {{ cbc: boolean, claude: boolean, codex: boolean, suggested: string, suggestedCli: string }}
35
37
  */
36
38
  export function detectPlatform() {
37
39
  const cbc = commandExists('cbc');
38
40
  const claude = commandExists('claude');
41
+ const codex = commandExists('codex');
39
42
 
40
- // platform 建议:基于目录结构,优先 both
43
+ // platform 建议:基于目录结构
41
44
  let suggested = 'codebuddy';
42
- if ((cbc) && (claude)) {
43
- suggested = 'both';
45
+ if (cbc && claude && codex) {
46
+ suggested = 'all';
47
+ } else if (codex && !cbc && !claude) {
48
+ suggested = 'codex';
44
49
  } else if ((claude) && !cbc) {
45
50
  suggested = 'claude';
46
51
  } else if (cbc && !claude) {
47
52
  suggested = 'codebuddy';
48
53
  }
49
54
 
50
- // CLI 命令建议:优先 cbc,其次 claude-internal,再次 claude
55
+ // CLI 命令建议:优先 cbc,其次 claude,再次 codex
51
56
  let suggestedCli = '';
52
57
  if (cbc) suggestedCli = 'cbc';
53
58
  else if (claude) suggestedCli = 'claude';
59
+ else if (codex) suggestedCli = 'codex';
54
60
 
55
- return { cbc, claude, suggested, suggestedCli };
56
- }
61
+ return { cbc, claude, codex, suggested, suggestedCli };
62
+ }
@@ -7,20 +7,21 @@
7
7
  * - Install all: installs all skills from a repo (e.g. pbakaus/impeccable)
8
8
  *
9
9
  * When run non-interactively (stdio: 'pipe'), npx skills writes canonical
10
- * .agents/skills/<name>/SKILL.md files. We copy those to the selected
11
- * platform dir(s), then clean up .agents/ and skills-lock.json.
10
+ * .agents/skills/<name>/SKILL.md files. Codex uses that location directly;
11
+ * other platforms get copied into their platform-specific skill directories.
12
12
  */
13
13
 
14
14
  import { execSync } from 'node:child_process';
15
15
  import path from 'path';
16
16
  import fs from 'fs-extra';
17
+ import { expandPlatforms } from './platforms.js';
17
18
 
18
19
  /**
19
20
  * @param {Object} skill - Skill definition from external_skills.known
20
21
  * skill.repo {string} - GitHub short ref (e.g. "pbakaus/impeccable") or full URL
21
22
  * skill.name {string} - Skill name (used for display / single-skill mode)
22
23
  * skill.installAll {boolean} - If true, install all skills from the repo
23
- * @param {string} platform - 'claude' | 'codebuddy' | 'both'
24
+ * @param {string} platform - 'claude' | 'codebuddy' | 'codex' | 'all' ('both' supported only for legacy manifests)
24
25
  * @param {string} projectRoot - Target project root
25
26
  * @param {boolean} dryRun
26
27
  */
@@ -64,6 +65,9 @@ export async function installExternalSkill(skill, platform, projectRoot, dryRun)
64
65
  for (const skillName of skillDirs) {
65
66
  const srcDir = path.join(agentsSkillsDir, skillName);
66
67
  const destDir = path.join(targetDir, skillName);
68
+ if (path.resolve(srcDir) === path.resolve(destDir)) {
69
+ continue;
70
+ }
67
71
  await fs.remove(destDir).catch(() => {});
68
72
  await fs.copy(srcDir, destDir);
69
73
  }
@@ -79,14 +83,20 @@ export async function installExternalSkill(skill, platform, projectRoot, dryRun)
79
83
 
80
84
  for (const targetDir of getTargetDirs(platform, projectRoot)) {
81
85
  const skillDir = path.join(targetDir, skill.name);
86
+ if (path.resolve(skillDir) === path.resolve(path.dirname(canonicalSkillMd))) {
87
+ continue;
88
+ }
82
89
  await fs.remove(skillDir).catch(() => {});
83
90
  await fs.ensureDir(skillDir);
84
91
  await fs.writeFile(path.join(skillDir, 'SKILL.md'), content);
85
92
  }
86
93
  }
87
94
 
88
- // Clean up npx skills artifacts after copying
89
- await fs.remove(path.join(projectRoot, '.agents')).catch(() => {});
95
+ // Clean up npx skills artifacts after copying. Preserve .agents/skills when
96
+ // Codex is selected because it is the official repository skill location.
97
+ if (!expandPlatforms(platform).includes('codex')) {
98
+ await fs.remove(path.join(projectRoot, '.agents')).catch(() => {});
99
+ }
90
100
  await fs.remove(path.join(projectRoot, 'skills-lock.json')).catch(() => {});
91
101
  }
92
102
 
@@ -95,21 +105,23 @@ export async function installExternalSkill(skill, platform, projectRoot, dryRun)
95
105
  * Call this once after all external skills have been installed.
96
106
  *
97
107
  * @param {string} projectRoot
98
- * @param {string} platform - 'claude' | 'codebuddy' | 'both'
108
+ * @param {string} platform - 'claude' | 'codebuddy' | 'codex' | 'all' ('both' supported only for legacy manifests)
99
109
  */
100
110
  export async function cleanExternalSkillArtifacts(projectRoot, platform) {
101
111
  const keepDirs = new Set(
102
112
  getTargetDirs(platform, projectRoot).map(d => path.resolve(d))
103
113
  );
104
114
 
115
+ const selectedPlatforms = expandPlatforms(platform);
105
116
  const dirsToCheck = [
106
- '.agents',
117
+ selectedPlatforms.includes('codex') ? null : '.agents',
107
118
  '.agent',
108
119
  '.trae',
109
120
  'skills',
110
121
  '.claude/skills',
111
122
  '.codebuddy/skills',
112
- ];
123
+ '.codex/skills',
124
+ ].filter(Boolean);
113
125
 
114
126
  for (const dir of dirsToCheck) {
115
127
  const abs = path.join(projectRoot, dir);
@@ -135,7 +147,7 @@ export async function cleanExternalSkillArtifacts(projectRoot, platform) {
135
147
  }
136
148
 
137
149
  // Remove empty parent dirs left behind (e.g. .codebuddy/ after removing .codebuddy/skills)
138
- const parentDirs = ['.claude', '.codebuddy', '.trae', '.agent'];
150
+ const parentDirs = ['.claude', '.codebuddy', '.codex', '.trae', '.agent'];
139
151
  for (const dir of parentDirs) {
140
152
  const abs = path.join(projectRoot, dir);
141
153
  if (!await fs.pathExists(abs)) continue;
@@ -155,14 +167,9 @@ export async function cleanExternalSkillArtifacts(projectRoot, platform) {
155
167
  }
156
168
 
157
169
  function getTargetDirs(platform, projectRoot) {
158
- if (platform === 'both') {
159
- return [
160
- path.join(projectRoot, '.claude', 'skills'),
161
- path.join(projectRoot, '.codebuddy', 'skills'),
162
- ];
163
- }
164
- if (platform === 'codebuddy') {
165
- return [path.join(projectRoot, '.codebuddy', 'skills')];
166
- }
167
- return [path.join(projectRoot, '.claude', 'skills')];
170
+ return expandPlatforms(platform).map(p => {
171
+ if (p === 'codebuddy') return path.join(projectRoot, '.codebuddy', 'skills');
172
+ if (p === 'codex') return path.join(projectRoot, '.agents', 'skills');
173
+ return path.join(projectRoot, '.claude', 'skills');
174
+ });
168
175
  }