prizmkit 1.1.57 → 1.1.60

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 (188) 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/dev-pipeline/.env.example +2 -1
  10. package/bundled/dev-pipeline/README.md +10 -7
  11. package/bundled/dev-pipeline/lib/common.sh +278 -37
  12. package/bundled/dev-pipeline/run-bugfix.sh +10 -61
  13. package/bundled/dev-pipeline/run-feature.sh +10 -78
  14. package/bundled/dev-pipeline/run-recovery.sh +10 -46
  15. package/bundled/dev-pipeline/run-refactor.sh +10 -61
  16. package/bundled/dev-pipeline/scripts/generate-bootstrap-prompt.py +17 -7
  17. package/bundled/dev-pipeline/scripts/generate-bugfix-prompt.py +9 -3
  18. package/bundled/dev-pipeline/scripts/generate-refactor-prompt.py +9 -3
  19. package/bundled/dev-pipeline/scripts/utils.py +6 -4
  20. package/bundled/dev-pipeline-windows/.env.example +28 -0
  21. package/bundled/dev-pipeline-windows/README.md +30 -0
  22. package/bundled/dev-pipeline-windows/SCHEMA_ANALYSIS.md +525 -0
  23. package/bundled/dev-pipeline-windows/assets/feature-list-example.json +146 -0
  24. package/bundled/dev-pipeline-windows/assets/prizm-dev-team-integration.md +138 -0
  25. package/bundled/dev-pipeline-windows/launch-bugfix-daemon.ps1 +9 -0
  26. package/bundled/dev-pipeline-windows/launch-feature-daemon.ps1 +9 -0
  27. package/bundled/dev-pipeline-windows/launch-refactor-daemon.ps1 +9 -0
  28. package/bundled/dev-pipeline-windows/lib/common.ps1 +432 -0
  29. package/bundled/dev-pipeline-windows/lib/daemon.ps1 +140 -0
  30. package/bundled/dev-pipeline-windows/lib/pipeline.ps1 +446 -0
  31. package/bundled/dev-pipeline-windows/lib/reset.ps1 +87 -0
  32. package/bundled/dev-pipeline-windows/reset-bug.ps1 +9 -0
  33. package/bundled/dev-pipeline-windows/reset-feature.ps1 +9 -0
  34. package/bundled/dev-pipeline-windows/reset-refactor.ps1 +9 -0
  35. package/bundled/dev-pipeline-windows/run-bugfix.ps1 +9 -0
  36. package/bundled/dev-pipeline-windows/run-feature.ps1 +9 -0
  37. package/bundled/dev-pipeline-windows/run-recovery.ps1 +76 -0
  38. package/bundled/dev-pipeline-windows/run-refactor.ps1 +9 -0
  39. package/bundled/dev-pipeline-windows/scripts/check-session-status.py +228 -0
  40. package/bundled/dev-pipeline-windows/scripts/cleanup-logs.py +192 -0
  41. package/bundled/dev-pipeline-windows/scripts/detect-stuck.py +530 -0
  42. package/bundled/dev-pipeline-windows/scripts/generate-bootstrap-prompt.py +1737 -0
  43. package/bundled/dev-pipeline-windows/scripts/generate-bugfix-prompt.py +685 -0
  44. package/bundled/dev-pipeline-windows/scripts/generate-recovery-prompt.py +805 -0
  45. package/bundled/dev-pipeline-windows/scripts/generate-refactor-prompt.py +763 -0
  46. package/bundled/dev-pipeline-windows/scripts/init-bugfix-pipeline.py +316 -0
  47. package/bundled/dev-pipeline-windows/scripts/init-dev-team.py +134 -0
  48. package/bundled/dev-pipeline-windows/scripts/init-pipeline.py +380 -0
  49. package/bundled/dev-pipeline-windows/scripts/init-refactor-pipeline.py +399 -0
  50. package/bundled/dev-pipeline-windows/scripts/parse-stream-progress.py +388 -0
  51. package/bundled/dev-pipeline-windows/scripts/patch-completion-notes.py +191 -0
  52. package/bundled/dev-pipeline-windows/scripts/update-bug-status.py +864 -0
  53. package/bundled/dev-pipeline-windows/scripts/update-checkpoint.py +173 -0
  54. package/bundled/dev-pipeline-windows/scripts/update-feature-status.py +1501 -0
  55. package/bundled/dev-pipeline-windows/scripts/update-refactor-status.py +1073 -0
  56. package/bundled/dev-pipeline-windows/scripts/utils.py +542 -0
  57. package/bundled/dev-pipeline-windows/templates/agent-prompts/critic-plan-challenge.md +7 -0
  58. package/bundled/dev-pipeline-windows/templates/agent-prompts/dev-fix.md +7 -0
  59. package/bundled/dev-pipeline-windows/templates/agent-prompts/dev-implement.md +30 -0
  60. package/bundled/dev-pipeline-windows/templates/agent-prompts/dev-resume.md +5 -0
  61. package/bundled/dev-pipeline-windows/templates/agent-prompts/reviewer-review.md +7 -0
  62. package/bundled/dev-pipeline-windows/templates/bootstrap-prompt.md +46 -0
  63. package/bundled/dev-pipeline-windows/templates/bootstrap-tier1.md +43 -0
  64. package/bundled/dev-pipeline-windows/templates/bootstrap-tier2.md +43 -0
  65. package/bundled/dev-pipeline-windows/templates/bootstrap-tier3.md +43 -0
  66. package/bundled/dev-pipeline-windows/templates/bug-fix-list-schema.json +263 -0
  67. package/bundled/dev-pipeline-windows/templates/bugfix-bootstrap-prompt.md +320 -0
  68. package/bundled/dev-pipeline-windows/templates/feature-list-schema.json +237 -0
  69. package/bundled/dev-pipeline-windows/templates/refactor-bootstrap-prompt.md +331 -0
  70. package/bundled/dev-pipeline-windows/templates/refactor-list-schema.json +270 -0
  71. package/bundled/dev-pipeline-windows/templates/sections/ac-verification-checklist.md +13 -0
  72. package/bundled/dev-pipeline-windows/templates/sections/checkpoint-system.md +91 -0
  73. package/bundled/dev-pipeline-windows/templates/sections/context-budget-rules.md +33 -0
  74. package/bundled/dev-pipeline-windows/templates/sections/critical-paths-agent.md +10 -0
  75. package/bundled/dev-pipeline-windows/templates/sections/critical-paths-full.md +12 -0
  76. package/bundled/dev-pipeline-windows/templates/sections/critical-paths-lite.md +7 -0
  77. package/bundled/dev-pipeline-windows/templates/sections/directory-convention-agent.md +8 -0
  78. package/bundled/dev-pipeline-windows/templates/sections/directory-convention-full.md +9 -0
  79. package/bundled/dev-pipeline-windows/templates/sections/directory-convention-lite.md +6 -0
  80. package/bundled/dev-pipeline-windows/templates/sections/failure-capture.md +21 -0
  81. package/bundled/dev-pipeline-windows/templates/sections/feature-context.md +31 -0
  82. package/bundled/dev-pipeline-windows/templates/sections/phase-browser-verification-auto.md +72 -0
  83. package/bundled/dev-pipeline-windows/templates/sections/phase-browser-verification-opencli.md +63 -0
  84. package/bundled/dev-pipeline-windows/templates/sections/phase-browser-verification.md +62 -0
  85. package/bundled/dev-pipeline-windows/templates/sections/phase-commit-full.md +71 -0
  86. package/bundled/dev-pipeline-windows/templates/sections/phase-commit.md +64 -0
  87. package/bundled/dev-pipeline-windows/templates/sections/phase-context-snapshot-agent-suffix.md +23 -0
  88. package/bundled/dev-pipeline-windows/templates/sections/phase-context-snapshot-base.md +24 -0
  89. package/bundled/dev-pipeline-windows/templates/sections/phase-context-snapshot-lite-suffix.md +12 -0
  90. package/bundled/dev-pipeline-windows/templates/sections/phase-critic-plan-full.md +53 -0
  91. package/bundled/dev-pipeline-windows/templates/sections/phase-critic-plan.md +32 -0
  92. package/bundled/dev-pipeline-windows/templates/sections/phase-implement-agent.md +37 -0
  93. package/bundled/dev-pipeline-windows/templates/sections/phase-implement-full.md +50 -0
  94. package/bundled/dev-pipeline-windows/templates/sections/phase-implement-lite.md +52 -0
  95. package/bundled/dev-pipeline-windows/templates/sections/phase-plan-agent.md +27 -0
  96. package/bundled/dev-pipeline-windows/templates/sections/phase-plan-lite.md +27 -0
  97. package/bundled/dev-pipeline-windows/templates/sections/phase-review-agent.md +27 -0
  98. package/bundled/dev-pipeline-windows/templates/sections/phase-review-full.md +29 -0
  99. package/bundled/dev-pipeline-windows/templates/sections/phase-specify-plan-full.md +77 -0
  100. package/bundled/dev-pipeline-windows/templates/sections/phase0-init.md +13 -0
  101. package/bundled/dev-pipeline-windows/templates/sections/phase0-test-baseline.md +23 -0
  102. package/bundled/dev-pipeline-windows/templates/sections/session-context.md +5 -0
  103. package/bundled/dev-pipeline-windows/templates/sections/subagent-timeout-recovery.md +6 -0
  104. package/bundled/dev-pipeline-windows/templates/sections/test-failure-recovery-agent.md +67 -0
  105. package/bundled/dev-pipeline-windows/templates/sections/test-failure-recovery-lite.md +58 -0
  106. package/bundled/dev-pipeline-windows/templates/session-status-schema.json +83 -0
  107. package/bundled/skills/_metadata.json +1 -1
  108. package/bundled/skills/app-planner/SKILL.md +26 -18
  109. package/bundled/skills/app-planner/references/architecture-decisions.md +9 -5
  110. package/bundled/skills/app-planner/references/frontend-design-guide.md +1 -1
  111. package/bundled/skills/feature-planner/SKILL.md +9 -2
  112. package/bundled/skills/prizmkit-init/SKILL.md +7 -6
  113. package/bundled/skills/recovery-workflow/scripts/detect-recovery-state.py +2 -0
  114. package/bundled/skills-windows/app-planner/SKILL.md +639 -0
  115. package/bundled/skills-windows/app-planner/assets/app-design-guide.md +101 -0
  116. package/bundled/skills-windows/app-planner/references/architecture-decisions.md +52 -0
  117. package/bundled/skills-windows/app-planner/references/brainstorm-guide.md +101 -0
  118. package/bundled/skills-windows/app-planner/references/frontend-design-guide.md +71 -0
  119. package/bundled/skills-windows/app-planner/references/project-brief-guide.md +82 -0
  120. package/bundled/skills-windows/app-planner/references/red-team-checklist.md +40 -0
  121. package/bundled/skills-windows/app-planner/references/rules/backend/derivation-rules.md +609 -0
  122. package/bundled/skills-windows/app-planner/references/rules/backend/fixed-rules.md +285 -0
  123. package/bundled/skills-windows/app-planner/references/rules/backend/question-bank.md +249 -0
  124. package/bundled/skills-windows/app-planner/references/rules/backend/template.md +173 -0
  125. package/bundled/skills-windows/app-planner/references/rules/database/derivation-rules.md +373 -0
  126. package/bundled/skills-windows/app-planner/references/rules/database/fixed-rules.md +211 -0
  127. package/bundled/skills-windows/app-planner/references/rules/database/question-bank.md +184 -0
  128. package/bundled/skills-windows/app-planner/references/rules/database/template.md +158 -0
  129. package/bundled/skills-windows/app-planner/references/rules/frontend/derivation-rules.md +810 -0
  130. package/bundled/skills-windows/app-planner/references/rules/frontend/fixed-rules.md +188 -0
  131. package/bundled/skills-windows/app-planner/references/rules/frontend/question-bank.md +302 -0
  132. package/bundled/skills-windows/app-planner/references/rules/frontend/template.md +320 -0
  133. package/bundled/skills-windows/app-planner/references/rules/mobile/derivation-rules.md +639 -0
  134. package/bundled/skills-windows/app-planner/references/rules/mobile/fixed-rules.md +290 -0
  135. package/bundled/skills-windows/app-planner/references/rules/mobile/question-bank.md +232 -0
  136. package/bundled/skills-windows/app-planner/references/rules/mobile/template.md +175 -0
  137. package/bundled/skills-windows/bug-fix-workflow/SKILL.md +415 -0
  138. package/bundled/skills-windows/bug-planner/SKILL.md +395 -0
  139. package/bundled/skills-windows/bug-planner/assets/bug-confirmation-template.md +43 -0
  140. package/bundled/skills-windows/bug-planner/references/critic-and-verification.md +44 -0
  141. package/bundled/skills-windows/bug-planner/references/error-recovery.md +73 -0
  142. package/bundled/skills-windows/bug-planner/references/input-formats.md +53 -0
  143. package/bundled/skills-windows/bug-planner/references/schema-validation.md +25 -0
  144. package/bundled/skills-windows/bug-planner/references/severity-rules.md +16 -0
  145. package/bundled/skills-windows/bug-planner/scripts/validate-bug-list.py +322 -0
  146. package/bundled/skills-windows/bugfix-pipeline-launcher/SKILL.md +380 -0
  147. package/bundled/skills-windows/feature-pipeline-launcher/SKILL.md +441 -0
  148. package/bundled/skills-windows/feature-pipeline-launcher/scripts/preflight-check.py +462 -0
  149. package/bundled/skills-windows/feature-planner/SKILL.md +401 -0
  150. package/bundled/skills-windows/feature-planner/assets/evaluation-guide.md +64 -0
  151. package/bundled/skills-windows/feature-planner/assets/planning-guide.md +214 -0
  152. package/bundled/skills-windows/feature-planner/references/browser-interaction.md +59 -0
  153. package/bundled/skills-windows/feature-planner/references/completeness-review.md +57 -0
  154. package/bundled/skills-windows/feature-planner/references/decomposition-patterns.md +75 -0
  155. package/bundled/skills-windows/feature-planner/references/error-recovery.md +90 -0
  156. package/bundled/skills-windows/feature-planner/references/incremental-feature-planning.md +112 -0
  157. package/bundled/skills-windows/feature-planner/references/new-project-planning.md +85 -0
  158. package/bundled/skills-windows/feature-planner/scripts/validate-and-generate.py +1029 -0
  159. package/bundled/skills-windows/feature-workflow/SKILL.md +531 -0
  160. package/bundled/skills-windows/prizmkit-init/SKILL.md +356 -0
  161. package/bundled/skills-windows/prizmkit-init/assets/project-brief-template.md +82 -0
  162. package/bundled/skills-windows/prizmkit-init/references/config-schema.md +68 -0
  163. package/bundled/skills-windows/prizmkit-init/references/rules/layer-detection.md +41 -0
  164. package/bundled/skills-windows/prizmkit-init/references/tech-stack-catalog.md +13 -0
  165. package/bundled/skills-windows/prizmkit-init/references/update-supplement.md +9 -0
  166. package/bundled/skills-windows/recovery-workflow/SKILL.md +456 -0
  167. package/bundled/skills-windows/recovery-workflow/evals/evals.json +46 -0
  168. package/bundled/skills-windows/recovery-workflow/scripts/detect-recovery-state.py +544 -0
  169. package/bundled/skills-windows/refactor-pipeline-launcher/SKILL.md +406 -0
  170. package/bundled/skills-windows/refactor-planner/SKILL.md +540 -0
  171. package/bundled/skills-windows/refactor-planner/assets/planning-guide.md +292 -0
  172. package/bundled/skills-windows/refactor-planner/references/behavior-preservation.md +301 -0
  173. package/bundled/skills-windows/refactor-planner/references/refactor-scoping-guide.md +221 -0
  174. package/bundled/skills-windows/refactor-planner/scripts/validate-and-generate-refactor.py +858 -0
  175. package/bundled/skills-windows/refactor-workflow/SKILL.md +503 -0
  176. package/package.json +3 -2
  177. package/src/clean.js +73 -2
  178. package/src/config.js +159 -50
  179. package/src/detect-platform.js +16 -8
  180. package/src/external-skills.js +26 -19
  181. package/src/index.js +31 -9
  182. package/src/manifest.js +6 -2
  183. package/src/metadata.js +43 -5
  184. package/src/platforms.js +36 -0
  185. package/src/prompts.js +31 -6
  186. package/src/runtimes.js +20 -0
  187. package/src/scaffold.js +314 -110
  188. package/src/upgrade.js +81 -41
package/src/scaffold.js CHANGED
@@ -10,13 +10,12 @@
10
10
  import chalk from 'chalk';
11
11
  import fs from 'fs-extra';
12
12
  import path from 'path';
13
- import { readFileSync, existsSync } from 'fs';
14
- import { fileURLToPath } from 'url';
13
+ import { readFileSync } from 'fs';
14
+ import { fileURLToPath, pathToFileURL } from 'url';
15
15
  import { dirname, join } from 'path';
16
16
  import { execSync } from 'child_process';
17
17
  import {
18
18
  loadMetadata,
19
- getSkillsDir,
20
19
  getAgentsDir,
21
20
  getTeamDir,
22
21
  getTemplatesDir,
@@ -24,12 +23,16 @@ import {
24
23
  getAdaptersDir,
25
24
  getRulesDir,
26
25
  loadRulesMetadata,
26
+ getSkillCandidatePaths,
27
27
  } from './metadata.js';
28
28
  import { generateGitignore } from './gitignore-template.js';
29
29
  import { buildManifest, writeManifest } from './manifest.js';
30
+ import { defaultCliForPlatform, expandPlatforms, isKnownPlatform, platformLabel, projectMemoryFile } from './platforms.js';
31
+ import { normalizeRuntime, runtimeLabel, RUNTIME_UNIX, RUNTIME_WINDOWS } from './runtimes.js';
30
32
 
31
33
  const __scaffoldDirname = dirname(fileURLToPath(import.meta.url));
32
34
  const scaffoldPkg = JSON.parse(readFileSync(join(__scaffoldDirname, '..', 'package.json'), 'utf-8'));
35
+ const PIPELINE_INSTALL_EXCLUDE = new Set(['tests', 'docs', '__pycache__', 'node_modules', '.DS_Store']);
33
36
 
34
37
  // ============================================================
35
38
  // Adapter 动态加载
@@ -42,12 +45,12 @@ const scaffoldPkg = JSON.parse(readFileSync(join(__scaffoldDirname, '..', 'packa
42
45
  async function loadAdapter(platform, adapterName) {
43
46
  const adaptersDir = getAdaptersDir();
44
47
  const adapterPath = path.join(adaptersDir, platform, adapterName);
45
- return import(adapterPath);
48
+ return import(pathToFileURL(adapterPath).href);
46
49
  }
47
50
 
48
51
  async function loadSharedFrontmatter() {
49
52
  const adaptersDir = getAdaptersDir();
50
- return import(path.join(adaptersDir, 'shared', 'frontmatter.js'));
53
+ return import(pathToFileURL(path.join(adaptersDir, 'shared', 'frontmatter.js')).href);
51
54
  }
52
55
 
53
56
  // ============================================================
@@ -81,6 +84,13 @@ export async function resolveSkillList(suite) {
81
84
  return suiteDef.skills;
82
85
  }
83
86
 
87
+ export function resolveRuleNamesForRuntime(rulesMeta, rulesPreset = 'recommended', runtime = 'unix') {
88
+ const preset = rulesMeta.presets[rulesPreset] || rulesMeta.presets.recommended;
89
+ const ruleNames = preset?.rules || [];
90
+ if (normalizeRuntime(runtime) !== RUNTIME_WINDOWS) return ruleNames;
91
+ return ruleNames.filter(name => name !== 'general/prefer-linux-commands');
92
+ }
93
+
84
94
  // ============================================================
85
95
  // 安装函数
86
96
  // ============================================================
@@ -88,8 +98,7 @@ export async function resolveSkillList(suite) {
88
98
  /**
89
99
  * 安装 Skills(纯 Copy 模式)
90
100
  */
91
- export async function installSkills(platform, skills, projectRoot, dryRun) {
92
- const skillsDir = getSkillsDir();
101
+ export async function installSkills(platform, skills, projectRoot, dryRun, runtime = 'unix') {
93
102
  const { parseFrontmatter, buildMarkdown } = await loadSharedFrontmatter();
94
103
 
95
104
  // Load metadata to get skill -> category + subcategory mapping
@@ -103,9 +112,13 @@ export async function installSkills(platform, skills, projectRoot, dryRun) {
103
112
 
104
113
  // Load Claude command adapter for skill conversion
105
114
  let convertSkillToCommand;
115
+ let convertSkillToCodex;
106
116
  if (platform === 'claude') {
107
117
  const commandAdapter = await loadAdapter('claude', 'command-adapter.js');
108
118
  convertSkillToCommand = commandAdapter.convertSkillToCommand;
119
+ } else if (platform === 'codex') {
120
+ const codexSkillAdapter = await loadAdapter('codex', 'skill-adapter.js');
121
+ convertSkillToCodex = codexSkillAdapter.convertSkill;
109
122
  }
110
123
 
111
124
  for (const skillName of skills) {
@@ -128,21 +141,9 @@ export async function installSkills(platform, skills, projectRoot, dryRun) {
128
141
  continue;
129
142
  }
130
143
 
131
- // Build path: prefer hierarchical layout (<skillsDir>/<category>/[<subcategory>/]/<skillName>),
132
- // fallback to flat bundled layout (<skillsDir>/<skillName>) for npm package compatibility.
133
- let corePath = subcategory
134
- ? path.resolve(skillsDir, category, subcategory, skillName)
135
- : path.resolve(skillsDir, category, skillName);
136
- // Ensure resolved path stays within skillsDir
137
- if (!corePath.startsWith(path.resolve(skillsDir))) {
138
- console.warn(` ⚠ Skill ${skillName} path escapes skills directory, skipping`);
139
- continue;
140
- }
141
- if (!await fs.pathExists(corePath)) {
142
- corePath = path.resolve(skillsDir, skillName);
143
- if (!corePath.startsWith(path.resolve(skillsDir))) continue;
144
- }
145
- if (!await fs.pathExists(corePath)) continue;
144
+ const corePath = getSkillCandidatePaths(skillName, metadata.skills[skillName], runtime)
145
+ .find(candidatePath => fs.pathExistsSync(candidatePath));
146
+ if (!corePath) continue;
146
147
 
147
148
  const stat = await fs.stat(corePath);
148
149
  if (!stat.isDirectory()) continue;
@@ -205,6 +206,26 @@ export async function installSkills(platform, skills, projectRoot, dryRun) {
205
206
  }
206
207
  }
207
208
  console.log(chalk.green(` ✓ .claude/commands/${skillName}.md`));
209
+ } else if (platform === 'codex') {
210
+ const content = await fs.readFile(skillMdPath, 'utf8');
211
+ const converted = convertSkillToCodex(content, skillName);
212
+ const targetDir = path.join(projectRoot, '.agents', 'skills', skillName);
213
+
214
+ if (dryRun) {
215
+ console.log(chalk.gray(` [dry-run] .agents/skills/${skillName}/`));
216
+ continue;
217
+ }
218
+
219
+ await fs.ensureDir(targetDir);
220
+ await fs.writeFile(path.join(targetDir, 'SKILL.md'), converted);
221
+
222
+ const codexEntries = await fs.readdir(corePath, { withFileTypes: true });
223
+ for (const entry of codexEntries) {
224
+ if (entry.isDirectory()) {
225
+ await fs.copy(path.join(corePath, entry.name), path.join(targetDir, entry.name));
226
+ }
227
+ }
228
+ console.log(chalk.green(` ✓ .agents/skills/${skillName}/`));
208
229
  }
209
230
  }
210
231
  }
@@ -221,6 +242,9 @@ export async function installAgents(platform, projectRoot, dryRun) {
221
242
  if (platform === 'claude') {
222
243
  const agentAdapter = await loadAdapter('claude', 'agent-adapter.js');
223
244
  convertAgentFn = agentAdapter.convertAgent;
245
+ } else if (platform === 'codex') {
246
+ const agentAdapter = await loadAdapter('codex', 'agent-adapter.js');
247
+ convertAgentFn = agentAdapter.convertAgent;
224
248
  }
225
249
 
226
250
  for (const file of agentFiles) {
@@ -247,6 +271,19 @@ export async function installAgents(platform, projectRoot, dryRun) {
247
271
  await fs.ensureDir(targetDir);
248
272
  await fs.writeFile(path.join(targetDir, file), converted);
249
273
  console.log(chalk.green(` ✓ .claude/agents/${file}`));
274
+ } else if (platform === 'codex') {
275
+ const baseName = path.basename(file, '.md');
276
+ const targetFile = `${baseName}.toml`;
277
+ const converted = convertAgentFn(content, { name: baseName });
278
+ const targetDir = path.join(projectRoot, '.codex', 'agents');
279
+ if (dryRun) {
280
+ console.log(chalk.gray(` [dry-run] .codex/agents/${targetFile}`));
281
+ continue;
282
+ }
283
+ await fs.ensureDir(targetDir);
284
+ await fs.writeFile(path.join(targetDir, targetFile), converted);
285
+ await fs.remove(path.join(targetDir, file));
286
+ console.log(chalk.green(` ✓ .codex/agents/${targetFile}`));
250
287
  }
251
288
  }
252
289
  }
@@ -340,18 +377,50 @@ export async function installTeamConfig(platform, projectRoot, dryRun) {
340
377
  );
341
378
 
342
379
  console.log(chalk.green(` ✓ .claude/team-info.json`));
380
+ } else if (platform === 'codex') {
381
+ const codexDir = path.join(projectRoot, '.codex');
382
+
383
+ if (dryRun) {
384
+ console.log(chalk.gray(` [dry-run] .codex/team-info.json`));
385
+ return;
386
+ }
387
+
388
+ await fs.ensureDir(codexDir);
389
+
390
+ const teamConfig = {
391
+ name: teamDef.name,
392
+ description: teamDef.description,
393
+ platform: 'codex',
394
+ orchestrationMode: 'codex-subagents',
395
+ agents: teamDef.members
396
+ .filter(m => m.role !== 'lead')
397
+ .map(m => ({
398
+ name: m.name,
399
+ role: m.role,
400
+ agentFile: `.codex/agents/${m.agentDefinition}.toml`,
401
+ prompt: m.prompt,
402
+ subscriptions: m.subscriptions || [],
403
+ })),
404
+ };
405
+
406
+ await fs.writeFile(
407
+ path.join(codexDir, 'team-info.json'),
408
+ JSON.stringify(teamConfig, null, 2)
409
+ );
410
+
411
+ console.log(chalk.green(` ✓ .codex/team-info.json`));
343
412
  }
344
413
  }
345
414
 
346
415
  /**
347
416
  * 安装平台配置文件(settings/hooks/rules)
348
417
  */
349
- export async function installSettings(platform, projectRoot, options, dryRun) {
418
+ export async function installSettings(platform, projectRoot, options, dryRun, runtime = 'unix') {
419
+ const normalizedRuntime = normalizeRuntime(runtime);
350
420
  // 从文件加载 rules
351
421
  const rulesMeta = await loadRulesMetadata();
352
422
  const rulesPreset = options.rules || 'recommended';
353
- const preset = rulesMeta.presets[rulesPreset] || rulesMeta.presets.recommended;
354
- const ruleNames = preset?.rules || [];
423
+ const ruleNames = resolveRuleNamesForRuntime(rulesMeta, rulesPreset, runtime);
355
424
  const rulesDir = getRulesDir();
356
425
  const ruleFiles = [];
357
426
  for (const name of ruleNames) {
@@ -374,9 +443,12 @@ export async function installSettings(platform, projectRoot, options, dryRun) {
374
443
 
375
444
  await fs.ensureDir(path.dirname(settingsPath));
376
445
 
377
- // Read hook definition from unified template
446
+ // Read hook definition from unified template.
447
+ // Windows runtime intentionally omits POSIX shell hook commands.
378
448
  const templatesDir = getTemplatesDir();
379
- const hookTemplate = await fs.readJSON(path.join(templatesDir, 'hooks', 'commit-intent.json'));
449
+ const hookTemplate = normalizedRuntime === RUNTIME_WINDOWS
450
+ ? { hooks: {} }
451
+ : await fs.readJSON(path.join(templatesDir, 'hooks', 'commit-intent.json'));
380
452
 
381
453
  await fs.writeFile(settingsPath, JSON.stringify(hookTemplate, null, 2));
382
454
  console.log(chalk.green(` ✓ .codebuddy/settings.json`));
@@ -399,13 +471,18 @@ export async function installSettings(platform, projectRoot, options, dryRun) {
399
471
 
400
472
  await fs.ensureDir(claudeDir);
401
473
 
402
- // Read hook definition from unified template
474
+ // Read hook definition from unified template.
475
+ // Windows runtime intentionally omits POSIX shell hook commands.
403
476
  const templatesDir = getTemplatesDir();
404
- const hookTemplate = await fs.readJSON(path.join(templatesDir, 'hooks', 'commit-intent.json'));
477
+ const hookTemplate = normalizedRuntime === RUNTIME_WINDOWS
478
+ ? { hooks: {} }
479
+ : await fs.readJSON(path.join(templatesDir, 'hooks', 'commit-intent.json'));
405
480
 
406
481
  // Settings
407
- const permissions = [
482
+ const permissions = normalizedRuntime === RUNTIME_WINDOWS ? [] : [
408
483
  'Bash(python3 .prizmkit/dev-pipeline/scripts/*)',
484
+ 'Bash(python .prizmkit/dev-pipeline/scripts/*)',
485
+ 'Bash(py -3 .prizmkit/dev-pipeline/scripts/*)',
409
486
  'Bash(git add *)',
410
487
  'Bash(git commit *)',
411
488
  'Bash(git diff *)',
@@ -413,12 +490,15 @@ export async function installSettings(platform, projectRoot, options, dryRun) {
413
490
  'Bash(git log *)',
414
491
  'Bash(jq *)',
415
492
  ];
416
- if (options.pipeline) {
417
- // 动态扫描 pipeline 目录中的 .sh 入口脚本(run-*、launch-*),自动添加执行权限
418
- const pipelineDir = getPipelineDir();
493
+ if (options.pipeline && normalizedRuntime !== RUNTIME_WINDOWS) {
494
+ // 动态扫描 pipeline 目录中的入口脚本,自动添加执行权限
495
+ const pipelineDir = getPipelineDir(runtime);
419
496
  try {
420
497
  const pipelineEntries = await fs.readdir(pipelineDir);
421
- const entryScripts = pipelineEntries.filter(f => /^(run-|launch-|retry-|reset-).*\.sh$/.test(f));
498
+ const entryRe = normalizedRuntime === RUNTIME_WINDOWS
499
+ ? /^(run-|launch-|retry-|reset-).*\.ps1$/i
500
+ : /^(run-|launch-|retry-|reset-).*\.sh$/;
501
+ const entryScripts = pipelineEntries.filter(f => entryRe.test(f));
422
502
  for (const script of entryScripts) {
423
503
  permissions.push(`Bash(./.prizmkit/dev-pipeline/${script} *)`);
424
504
  }
@@ -429,7 +509,7 @@ export async function installSettings(platform, projectRoot, options, dryRun) {
429
509
 
430
510
  const settings = {
431
511
  permissions: { allow: permissions },
432
- hooks: {
512
+ hooks: normalizedRuntime === RUNTIME_WINDOWS ? {} : {
433
513
  ...hookTemplate.hooks,
434
514
  SessionStart: [
435
515
  {
@@ -455,17 +535,54 @@ export async function installSettings(platform, projectRoot, options, dryRun) {
455
535
  const { installRules: installClaudeRules } = await loadAdapter('claude', 'rules-adapter.js');
456
536
  await installClaudeRules(projectRoot, ruleFiles);
457
537
  console.log(chalk.green(` ✓ .claude/rules/ (${ruleFiles.length} 条规则)`));
538
+ } else if (platform === 'codex') {
539
+ const configPath = path.join(projectRoot, '.codex', 'config.toml');
540
+ const legacySettingsPath = path.join(projectRoot, '.codex', 'prizmkit-settings.json');
541
+ const allRuleFileNames = Object.keys(rulesMeta.rules || {})
542
+ .map(name => `${name.includes('/') ? name.split('/').pop() : name}.md`);
543
+
544
+ if (dryRun) {
545
+ console.log(chalk.gray(` [dry-run] .codex/config.toml`));
546
+ console.log(chalk.gray(` [dry-run] .agents/rules/ (${ruleFiles.length} 条规则)`));
547
+ return;
548
+ }
549
+
550
+ await fs.ensureDir(path.dirname(configPath));
551
+ const configToml = `# Generated by PrizmKit for Codex CLI project configuration.
552
+ # Keep project guidance in AGENTS.md; repository skills in .agents/skills/;
553
+ # PrizmKit behavioral rules live in .agents/rules/; native Codex subagents live in .codex/agents/.
554
+
555
+ project_doc_fallback_filenames = ["CLAUDE.md", "CODEBUDDY.md"]
556
+
557
+ [agents]
558
+ max_depth = 1
559
+ `;
560
+ await fs.writeFile(configPath, configToml);
561
+ await fs.remove(legacySettingsPath);
562
+ console.log(chalk.green(` ✓ .codex/config.toml`));
563
+
564
+ const {
565
+ installRules: installCodexRules,
566
+ removeLegacyCodexRules,
567
+ } = await loadAdapter('codex', 'rules-adapter.js');
568
+ await installCodexRules(projectRoot, ruleFiles);
569
+ removeLegacyCodexRules(projectRoot, allRuleFileNames);
570
+ console.log(chalk.green(` ✓ .agents/rules/ (${ruleFiles.length} 条规则)`));
458
571
  }
459
572
  }
460
573
 
461
574
  /**
462
575
  * 安装 git pre-commit hook(prizm 格式校验)
463
576
  */
464
- export async function installGitHook(projectRoot, dryRun) {
577
+ export async function installGitHook(projectRoot, dryRun, runtime = 'unix') {
465
578
  const gitDir = path.join(projectRoot, '.git');
579
+ const normalizedRuntime = normalizeRuntime(runtime);
466
580
 
467
581
  if (dryRun) {
468
- console.log(chalk.gray(' [dry-run] .git/hooks/pre-commit (prizm format check)'));
582
+ const hookLabel = normalizedRuntime === RUNTIME_WINDOWS
583
+ ? '.git/hooks/pre-commit skipped (Windows runtime: no shell hook)'
584
+ : '.git/hooks/pre-commit (prizm format check)';
585
+ console.log(chalk.gray(` [dry-run] ${hookLabel}`));
469
586
  return;
470
587
  }
471
588
 
@@ -475,6 +592,27 @@ export async function installGitHook(projectRoot, dryRun) {
475
592
  const hooksDir = path.join(gitDir, 'hooks');
476
593
  const preCommitPath = path.join(hooksDir, 'pre-commit');
477
594
 
595
+ if (normalizedRuntime === RUNTIME_WINDOWS) {
596
+ if (fs.existsSync(preCommitPath) && fs.existsSync(templatePath)) {
597
+ const existing = fs.readFileSync(preCommitPath, 'utf8');
598
+ const shellTemplate = fs.readFileSync(templatePath, 'utf8');
599
+ if (existing === shellTemplate) {
600
+ await fs.remove(preCommitPath);
601
+ console.log(chalk.gray(' • .git/hooks/pre-commit shell hook removed for Windows runtime'));
602
+ } else if (existing.includes(shellTemplate)) {
603
+ const cleaned = existing.replace(shellTemplate, '').replace(/\n{3,}/g, '\n\n').trimEnd();
604
+ if (cleaned) {
605
+ fs.writeFileSync(preCommitPath, `${cleaned}\n`);
606
+ } else {
607
+ await fs.remove(preCommitPath);
608
+ }
609
+ console.log(chalk.gray(' • PrizmKit shell hook block removed for Windows runtime'));
610
+ }
611
+ }
612
+ console.log(chalk.gray(' • Git hook skipped (Windows runtime uses no shell hook)'));
613
+ return;
614
+ }
615
+
478
616
  fs.mkdirSync(hooksDir, { recursive: true });
479
617
 
480
618
  if (fs.existsSync(preCommitPath)) {
@@ -491,11 +629,12 @@ export async function installGitHook(projectRoot, dryRun) {
491
629
  }
492
630
 
493
631
  /**
494
- * 安装 PrizmKit 脚本到 .prizmkit/scripts/(始终安装)
632
+ * 安装 PrizmKit 脚本到 .prizmkit/scripts/(Windows runtime 跳过 shell-only 脚本)
495
633
  */
496
- export async function installPrizmkitScripts(projectRoot, dryRun) {
634
+ export async function installPrizmkitScripts(projectRoot, dryRun, runtime = 'unix') {
497
635
  const scriptsDir = path.join(projectRoot, '.prizmkit', 'scripts');
498
636
  const templateHooksDir = path.join(getTemplatesDir(), 'hooks');
637
+ const normalizedRuntime = normalizeRuntime(runtime);
499
638
 
500
639
  const scripts = [
501
640
  'validate-prizm-docs.sh',
@@ -504,8 +643,17 @@ export async function installPrizmkitScripts(projectRoot, dryRun) {
504
643
 
505
644
  if (dryRun) {
506
645
  for (const s of scripts) {
507
- console.log(chalk.gray(` [dry-run] .prizmkit/scripts/${s}`));
646
+ const action = normalizedRuntime === RUNTIME_WINDOWS ? 'skip shell script' : `.prizmkit/scripts/${s}`;
647
+ console.log(chalk.gray(` [dry-run] ${action}`));
648
+ }
649
+ return;
650
+ }
651
+
652
+ if (normalizedRuntime === RUNTIME_WINDOWS) {
653
+ for (const s of scripts) {
654
+ await fs.remove(path.join(scriptsDir, s));
508
655
  }
656
+ console.log(chalk.gray(' • shell-only PrizmKit scripts skipped for Windows runtime'));
509
657
  return;
510
658
  }
511
659
 
@@ -525,16 +673,16 @@ const PRIZMKIT_MARKER_START = '<!-- PRIZMKIT:START';
525
673
  const PRIZMKIT_MARKER_END = '<!-- PRIZMKIT:END -->';
526
674
 
527
675
  /**
528
- * Merge PrizmKit template content into a project memory file (CLAUDE.md / CODEBUDDY.md).
676
+ * Merge PrizmKit template content into a project memory file.
529
677
  * - File missing → create with template content
530
678
  * - File exists with markers → replace content between markers
531
679
  * - File exists without markers → append template content
532
680
  */
533
681
  export async function installProjectMemory(platform, projectRoot, dryRun) {
534
682
  const templatesDir = getTemplatesDir();
535
- const skillsDir = getSkillsDir();
536
683
  const templateName = 'project-memory-template.md';
537
- const targetName = platform === 'claude' ? 'CLAUDE.md' : 'CODEBUDDY.md';
684
+ const targetName = projectMemoryFile(platform);
685
+ if (!targetName) return;
538
686
  const targetPath = path.join(projectRoot, targetName);
539
687
 
540
688
  // Template lives in core/templates/ (bundled as templates/).
@@ -548,7 +696,19 @@ export async function installProjectMemory(platform, projectRoot, dryRun) {
548
696
  return;
549
697
  }
550
698
 
551
- const templateContent = (await fs.readFile(templatePath, 'utf-8')).trimEnd();
699
+ let templateContent = (await fs.readFile(templatePath, 'utf-8')).trimEnd();
700
+ if (platform === 'codex') {
701
+ templateContent = templateContent.replace(
702
+ '### Available Commands\nRun `/prizm-kit` to see all available PrizmKit commands.',
703
+ `### Available PrizmKit Skills
704
+ Codex discovers repository skills from \`.agents/skills/\`. When a user or prompt mentions \`/prizmkit-xxx\`, use the matching skill at \`.agents/skills/prizmkit-xxx/SKILL.md\`.
705
+
706
+ - Start with \`.agents/skills/prizm-kit/SKILL.md\` to see all available PrizmKit skills.
707
+ - Skill assets and references live inside each \`.agents/skills/<skill>/\` directory.
708
+ - PrizmKit behavioral rules live in \`.agents/rules/\`; read the relevant Markdown guidance before commit, documentation, or context-loading work.
709
+ - Native Codex subagents live in \`.codex/agents/*.toml\`; use them when spawning or coordinating subagents.`
710
+ );
711
+ }
552
712
 
553
713
  if (dryRun) {
554
714
  console.log(chalk.gray(` [dry-run] ${targetName}`));
@@ -599,25 +759,15 @@ function collectFilesRecursive(dir, base = '') {
599
759
  return files;
600
760
  }
601
761
 
602
- /**
603
- * Resolve the list of pipeline files from the source (bundled) pipeline directory.
604
- * Used by upgrade to compute diff before actual installation.
605
- * @returns {string[]} relative file paths
606
- */
607
- export function resolvePipelineFileList() {
608
- const pipelineSource = getPipelineDir();
609
- if (!pipelineSource || !fs.pathExistsSync(pipelineSource)) return [];
610
-
611
- // 动态扫描,与 installPipeline() 保持一致的排除规则
612
- const EXCLUDE = new Set(['tests', 'docs', '__pycache__', 'node_modules', '.DS_Store']);
762
+ function collectInstallablePipelineFiles(pipelineSource) {
613
763
  let allEntries;
614
764
  try {
615
765
  allEntries = fs.readdirSync(pipelineSource);
616
766
  } catch {
617
767
  return [];
618
768
  }
619
- const items = allEntries.filter(name => !EXCLUDE.has(name) && !name.startsWith('.'));
620
769
 
770
+ const items = allEntries.filter(name => !PIPELINE_INSTALL_EXCLUDE.has(name) && !name.startsWith('.'));
621
771
  const files = [];
622
772
  for (const item of items) {
623
773
  const src = path.join(pipelineSource, item);
@@ -632,13 +782,67 @@ export function resolvePipelineFileList() {
632
782
  return files;
633
783
  }
634
784
 
785
+ /**
786
+ * Resolve the list of pipeline files from the source (bundled) pipeline directory.
787
+ * Used by upgrade to compute diff before actual installation.
788
+ * @returns {string[]} relative file paths
789
+ */
790
+ export function resolvePipelineFileList(runtime = 'unix') {
791
+ const pipelineSource = getPipelineDir(runtime);
792
+ if (!pipelineSource || !fs.pathExistsSync(pipelineSource)) return [];
793
+ return collectInstallablePipelineFiles(pipelineSource);
794
+ }
795
+
796
+ async function pruneStalePipelineRuntimeFiles(pipelineTarget, runtime) {
797
+ if (!await fs.pathExists(pipelineTarget)) return false;
798
+
799
+ const staleRuntime = runtime === RUNTIME_WINDOWS ? RUNTIME_UNIX : RUNTIME_WINDOWS;
800
+ const staleFiles = resolvePipelineFileList(staleRuntime);
801
+ const currentFiles = new Set(resolvePipelineFileList(runtime));
802
+ const staleDirs = new Set();
803
+ let prunedRuntimeSpecificFiles = false;
804
+
805
+ for (const relativeFile of staleFiles) {
806
+ if (currentFiles.has(relativeFile)) continue;
807
+
808
+ const targetFile = path.join(pipelineTarget, relativeFile);
809
+ if (await fs.pathExists(targetFile)) {
810
+ const stat = await fs.stat(targetFile);
811
+ if (stat.isFile()) {
812
+ await fs.remove(targetFile);
813
+ prunedRuntimeSpecificFiles = true;
814
+ }
815
+ }
816
+
817
+ const relativeDir = path.dirname(relativeFile);
818
+ if (relativeDir && relativeDir !== '.') staleDirs.add(relativeDir);
819
+ }
820
+
821
+ const dirsByDepth = [...staleDirs].sort((a, b) => b.split(path.sep).length - a.split(path.sep).length);
822
+ for (const relativeDir of dirsByDepth) {
823
+ const targetDir = path.join(pipelineTarget, relativeDir);
824
+ if (!await fs.pathExists(targetDir)) continue;
825
+ const entries = await fs.readdir(targetDir);
826
+ if (entries.length === 0) {
827
+ await fs.remove(targetDir);
828
+ }
829
+ }
830
+
831
+ return prunedRuntimeSpecificFiles;
832
+ }
833
+
635
834
  /**
636
835
  * 安装 dev-pipeline(纯 Copy 模式)
637
836
  * 安装目标:.prizmkit/dev-pipeline/
638
837
  * Returns an array of installed file relative paths (relative to .prizmkit/dev-pipeline/).
639
838
  */
640
- export async function installPipeline(projectRoot, dryRun, { forceOverwrite = false } = {}) {
641
- const pipelineSource = getPipelineDir();
839
+ export async function installPipeline(projectRoot, dryRun, options = {}) {
840
+ const runtime = normalizeRuntime(options.runtime);
841
+ const previousRuntime = options.previousRuntime === undefined
842
+ ? runtime
843
+ : normalizeRuntime(options.previousRuntime);
844
+ const forceOverwrite = Boolean(options.forceOverwrite);
845
+ const pipelineSource = getPipelineDir(runtime);
642
846
  const pipelineTarget = path.join(projectRoot, '.prizmkit', 'dev-pipeline');
643
847
 
644
848
  if (dryRun) {
@@ -651,51 +855,41 @@ export async function installPipeline(projectRoot, dryRun, { forceOverwrite = fa
651
855
  await fs.ensureDir(path.join(projectRoot, '.prizmkit', 'state', 'bugfix'));
652
856
  await fs.ensureDir(path.join(projectRoot, '.prizmkit', 'state', 'refactor'));
653
857
 
654
- // 动态扫描 bundled dev-pipeline 目录,排除不应安装到用户项目的内容
655
- const EXCLUDE = new Set(['tests', 'docs', '__pycache__', 'node_modules', '.DS_Store']);
656
- const allEntries = await fs.readdir(pipelineSource);
657
- const items = allEntries.filter(name => !EXCLUDE.has(name) && !name.startsWith('.'));
858
+ const runtimeSwitched = previousRuntime !== runtime;
859
+ const prunedStaleRuntimeFiles = await pruneStalePipelineRuntimeFiles(pipelineTarget, runtime);
860
+ const overwriteExisting = forceOverwrite || runtimeSwitched || prunedStaleRuntimeFiles;
658
861
 
659
- let installedCount = 0;
660
- const installedFiles = [];
661
- for (const item of items) {
662
- const src = path.join(pipelineSource, item);
663
- const tgt = path.join(pipelineTarget, item);
862
+ const installedFiles = collectInstallablePipelineFiles(pipelineSource);
863
+ let copiedCount = 0;
864
+ let skippedCount = 0;
664
865
 
665
- if (!await fs.pathExists(src)) continue;
866
+ for (const relativeFile of installedFiles) {
867
+ const src = path.join(pipelineSource, relativeFile);
868
+ const tgt = path.join(pipelineTarget, relativeFile);
666
869
 
667
- const srcStat = await fs.stat(src);
668
-
669
- if (await fs.pathExists(tgt) && !forceOverwrite) {
670
- console.log(chalk.yellow(` ⚠ .prizmkit/dev-pipeline/${item} 已存在,跳过`));
671
- // Still collect existing files for manifest tracking
672
- if (srcStat.isDirectory()) {
673
- installedFiles.push(...collectFilesRecursive(src, item));
674
- } else {
675
- installedFiles.push(item);
676
- }
870
+ if (await fs.pathExists(tgt) && !overwriteExisting) {
871
+ skippedCount++;
677
872
  continue;
678
873
  }
679
874
 
680
- await fs.copy(src, tgt);
681
- installedCount++;
682
-
683
- // Collect installed file paths
684
- if (srcStat.isDirectory()) {
685
- installedFiles.push(...collectFilesRecursive(src, item));
686
- } else {
687
- installedFiles.push(item);
688
- }
875
+ await fs.ensureDir(path.dirname(tgt));
876
+ await fs.copy(src, tgt, { overwrite: true, errorOnExist: false });
877
+ copiedCount++;
689
878
  }
690
879
 
691
- console.log(chalk.green(` ✓ .prizmkit/dev-pipeline/ (${installedCount} 项)`));
880
+ const skipSuffix = skippedCount ? `, preserved ${skippedCount} existing` : '';
881
+ console.log(chalk.green(` ✓ .prizmkit/dev-pipeline/ (${copiedCount} files copied${skipSuffix})`));
692
882
 
693
883
  // Copy .env.example to .prizmkit/.env.example if it exists in pipeline source
694
884
  const envExampleSrc = path.join(pipelineSource, '.env.example');
695
885
  const envExampleTgt = path.join(projectRoot, '.prizmkit', '.env.example');
696
- if (await fs.pathExists(envExampleSrc) && !await fs.pathExists(envExampleTgt)) {
697
- await fs.copy(envExampleSrc, envExampleTgt);
698
- console.log(chalk.green(` .prizmkit/.env.example (pipeline environment configuration)`));
886
+ if (await fs.pathExists(envExampleSrc)) {
887
+ if (overwriteExisting || !(await fs.pathExists(envExampleTgt))) {
888
+ await fs.copy(envExampleSrc, envExampleTgt, { overwrite: true, errorOnExist: false });
889
+ console.log(chalk.green(` ✓ .prizmkit/.env.example (pipeline environment configuration)`));
890
+ } else {
891
+ console.log(chalk.gray(` • .prizmkit/.env.example exists, preserved`));
892
+ }
699
893
  }
700
894
 
701
895
  return installedFiles;
@@ -839,7 +1033,7 @@ export async function installOpenCli(projectRoot, dryRun, autoDownload = true) {
839
1033
  console.log(chalk.yellow(' ⚠ Chrome Browser Bridge extension is NOT connected'));
840
1034
 
841
1035
  if (autoDownload) {
842
- await downloadOpenCliExtension(projectRoot);
1036
+ await downloadOpenCliExtension();
843
1037
  } else {
844
1038
  printOpenCliManualSteps();
845
1039
  }
@@ -859,7 +1053,7 @@ export async function installOpenCli(projectRoot, dryRun, autoDownload = true) {
859
1053
  * 从 GitHub releases 下载并解压 opencli Chrome 扩展。
860
1054
  * 省去用户手动下载解压步骤,但仍需在 Chrome 中手动加载。
861
1055
  */
862
- async function downloadOpenCliExtension(projectRoot) {
1056
+ async function downloadOpenCliExtension() {
863
1057
  const extDir = path.join(process.env.HOME || process.env.USERPROFILE || '.', '.opencli', 'extension');
864
1058
 
865
1059
  // Check if already downloaded
@@ -959,7 +1153,8 @@ export const EXTRAS_REGISTRY = {
959
1153
  /**
960
1154
  * 执行纯净安装
961
1155
  * @param {Object} config
962
- * @param {string} config.platform - 'codebuddy' | 'claude' | 'both'
1156
+ * @param {string} config.platform - 'codebuddy' | 'claude' | 'codex' | 'both' | 'all'
1157
+ * @param {string} [config.runtime] - 'unix' | 'windows'
963
1158
  * @param {string} config.skills - 'core' | 'minimal' | 'recommended:<type>'
964
1159
  * @param {boolean} config.team - 是否启用团队模式
965
1160
  * @param {boolean} config.pipeline - 是否安装 dev-pipeline
@@ -974,7 +1169,11 @@ export const EXTRAS_REGISTRY = {
974
1169
  */
975
1170
  export async function scaffold(config) {
976
1171
  const { platform, skills, team, pipeline, rules, aiCli, externalSkills, playwrightCli, openCli, openCliAutoDownload, projectRoot, dryRun } = config;
977
- const platforms = platform === 'both' ? ['codebuddy', 'claude'] : [platform];
1172
+ const runtime = normalizeRuntime(config.runtime);
1173
+ if (!isKnownPlatform(platform)) {
1174
+ throw new Error(`Unknown platform "${platform}". Expected codebuddy, claude, codex, both, or all.`);
1175
+ }
1176
+ const platforms = expandPlatforms(platform);
978
1177
 
979
1178
  if (dryRun) {
980
1179
  console.log(chalk.yellow('\n [DRY RUN] 预览将要安装的内容:\n'));
@@ -986,12 +1185,11 @@ export async function scaffold(config) {
986
1185
  console.log('');
987
1186
 
988
1187
  for (const p of platforms) {
989
- const platformLabel = p === 'codebuddy' ? 'CodeBuddy' : 'Claude Code';
990
- console.log(chalk.bold(` 正在安装 ${platformLabel} 环境...\n`));
1188
+ console.log(chalk.bold(` 正在安装 ${platformLabel(p)} 环境...\n`));
991
1189
 
992
1190
  // 1. Skills
993
1191
  console.log(chalk.blue(' 技能文件:'));
994
- await installSkills(p, skillList, projectRoot, dryRun);
1192
+ await installSkills(p, skillList, projectRoot, dryRun, runtime);
995
1193
 
996
1194
  // 2. Agents
997
1195
  console.log(chalk.blue('\n Agent 定义:'));
@@ -1005,7 +1203,7 @@ export async function scaffold(config) {
1005
1203
 
1006
1204
  // 4. Settings/Hooks/Rules
1007
1205
  console.log(chalk.blue('\n 平台配置:'));
1008
- await installSettings(p, projectRoot, { pipeline, rules }, dryRun);
1206
+ await installSettings(p, projectRoot, { pipeline, rules }, dryRun, runtime);
1009
1207
 
1010
1208
  // 5. Project Memory
1011
1209
  console.log(chalk.blue('\n 项目记忆文件:'));
@@ -1018,7 +1216,7 @@ export async function scaffold(config) {
1018
1216
  let installedPipelineFiles = [];
1019
1217
  if (pipeline) {
1020
1218
  console.log(chalk.blue(' 自动化流水线:'));
1021
- installedPipelineFiles = await installPipeline(projectRoot, dryRun) || [];
1219
+ installedPipelineFiles = await installPipeline(projectRoot, dryRun, { runtime }) || [];
1022
1220
  console.log('');
1023
1221
  }
1024
1222
 
@@ -1097,11 +1295,11 @@ export async function scaffold(config) {
1097
1295
 
1098
1296
  // 12. Git pre-commit hook
1099
1297
  console.log(chalk.blue(' Git Hook:'));
1100
- await installGitHook(projectRoot, dryRun);
1298
+ await installGitHook(projectRoot, dryRun, runtime);
1101
1299
 
1102
- // 13. PrizmKit scripts (always installed)
1300
+ // 13. PrizmKit scripts
1103
1301
  console.log(chalk.blue(' PrizmKit 脚本:'));
1104
- await installPrizmkitScripts(projectRoot, dryRun);
1302
+ await installPrizmkitScripts(projectRoot, dryRun, runtime);
1105
1303
 
1106
1304
  // 14. Write installation manifest
1107
1305
  if (!dryRun) {
@@ -1109,12 +1307,12 @@ export async function scaffold(config) {
1109
1307
  const agentFileNames = (await fs.readdir(agentsDir)).filter(f => f.endsWith('.md'));
1110
1308
  const rulesMeta = await loadRulesMetadata();
1111
1309
  const rulesPresetName = rules || 'recommended';
1112
- const rulesPresetDef = rulesMeta.presets[rulesPresetName] || rulesMeta.presets.recommended;
1113
- const ruleFileNames = (rulesPresetDef?.rules || []).map(name => `${name}.md`);
1310
+ const ruleFileNames = resolveRuleNamesForRuntime(rulesMeta, rulesPresetName, runtime).map(name => `${name}.md`);
1114
1311
 
1115
1312
  const manifest = buildManifest({
1116
1313
  version: scaffoldPkg.version,
1117
1314
  platform,
1315
+ runtime,
1118
1316
  suite: skills,
1119
1317
  skills: skillList,
1120
1318
  agents: agentFileNames,
@@ -1142,15 +1340,20 @@ export async function scaffold(config) {
1142
1340
 
1143
1341
  if (!dryRun) {
1144
1342
  // 打印下一步提示
1145
- const mainPlatform = platforms.includes('claude') ? 'claude' : 'codebuddy';
1146
- const defaultCli = mainPlatform === 'claude' ? 'claude' : 'cbc';
1343
+ const mainPlatform = platforms.includes('claude') ? 'claude'
1344
+ : platforms.includes('codebuddy') ? 'codebuddy'
1345
+ : platforms[0];
1346
+ const defaultCli = defaultCliForPlatform(mainPlatform);
1147
1347
  const cli = aiCli || defaultCli;
1148
1348
 
1149
1349
  console.log('');
1150
1350
  console.log(' 下一步:');
1151
1351
  console.log(` 1. ${chalk.cyan('cd ' + projectRoot)}`);
1152
1352
  console.log(` 2. ${chalk.cyan(cli)} ${chalk.gray('# 启动 AI 对话')}`);
1153
- console.log(` 3. ${chalk.cyan('说 "/prizmkit-init"')} ${chalk.gray('# 初始化项目上下文')}`);
1353
+ const initInstruction = mainPlatform === 'codex'
1354
+ ? '说 "运行 prizmkit-init"'
1355
+ : '说 "/prizmkit-init"';
1356
+ console.log(` 3. ${chalk.cyan(initInstruction)} ${chalk.gray('# 初始化项目上下文')}`);
1154
1357
 
1155
1358
  if (pipeline) {
1156
1359
  console.log(` 4. ${chalk.cyan('说 "规划一个应用"')} ${chalk.gray('# 生成 feature-list.json')}`);
@@ -1166,7 +1369,8 @@ export async function scaffold(config) {
1166
1369
 
1167
1370
  console.log(chalk.gray(` 安装统计:`));
1168
1371
  console.log(chalk.gray(` 技能: ${suiteLabel}`));
1169
- console.log(chalk.gray(` 平台: ${platforms.map(p => p === 'codebuddy' ? 'CodeBuddy' : 'Claude Code').join(' + ')}`));
1372
+ console.log(chalk.gray(` 平台: ${platforms.map(platformLabel).join(' + ')}`));
1373
+ console.log(chalk.gray(` 运行系统: ${runtimeLabel(runtime)}`));
1170
1374
  console.log(chalk.gray(` 团队: ${team ? '已启用' : '未启用'}`));
1171
1375
  console.log(chalk.gray(` 流水线: ${pipeline ? '已安装' : '未安装'}`));
1172
1376
  const browserTools = [playwrightCli && 'playwright-cli', openCli && 'opencli'].filter(Boolean);
@@ -1175,4 +1379,4 @@ export async function scaffold(config) {
1175
1379
  if (aiCli) console.log(chalk.gray(` AI CLI: ${aiCli}`));
1176
1380
  console.log('');
1177
1381
  }
1178
- }
1382
+ }