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.
- package/bin/create-prizmkit.js +8 -6
- package/bundled/VERSION.json +3 -3
- package/bundled/adapters/codex/agent-adapter.js +38 -0
- package/bundled/adapters/codex/paths.js +27 -0
- package/bundled/adapters/codex/rules-adapter.js +30 -0
- package/bundled/adapters/codex/settings-adapter.js +27 -0
- package/bundled/adapters/codex/skill-adapter.js +65 -0
- package/bundled/adapters/codex/team-adapter.js +37 -0
- package/bundled/dev-pipeline/.env.example +2 -1
- package/bundled/dev-pipeline/README.md +10 -7
- package/bundled/dev-pipeline/lib/common.sh +278 -37
- package/bundled/dev-pipeline/run-bugfix.sh +10 -61
- package/bundled/dev-pipeline/run-feature.sh +10 -78
- package/bundled/dev-pipeline/run-recovery.sh +10 -46
- package/bundled/dev-pipeline/run-refactor.sh +10 -61
- package/bundled/dev-pipeline/scripts/generate-bootstrap-prompt.py +17 -7
- package/bundled/dev-pipeline/scripts/generate-bugfix-prompt.py +9 -3
- package/bundled/dev-pipeline/scripts/generate-refactor-prompt.py +9 -3
- package/bundled/dev-pipeline/scripts/utils.py +6 -4
- package/bundled/dev-pipeline-windows/.env.example +28 -0
- package/bundled/dev-pipeline-windows/README.md +30 -0
- package/bundled/dev-pipeline-windows/SCHEMA_ANALYSIS.md +525 -0
- package/bundled/dev-pipeline-windows/assets/feature-list-example.json +146 -0
- package/bundled/dev-pipeline-windows/assets/prizm-dev-team-integration.md +138 -0
- package/bundled/dev-pipeline-windows/launch-bugfix-daemon.ps1 +9 -0
- package/bundled/dev-pipeline-windows/launch-feature-daemon.ps1 +9 -0
- package/bundled/dev-pipeline-windows/launch-refactor-daemon.ps1 +9 -0
- package/bundled/dev-pipeline-windows/lib/common.ps1 +432 -0
- package/bundled/dev-pipeline-windows/lib/daemon.ps1 +140 -0
- package/bundled/dev-pipeline-windows/lib/pipeline.ps1 +446 -0
- package/bundled/dev-pipeline-windows/lib/reset.ps1 +87 -0
- package/bundled/dev-pipeline-windows/reset-bug.ps1 +9 -0
- package/bundled/dev-pipeline-windows/reset-feature.ps1 +9 -0
- package/bundled/dev-pipeline-windows/reset-refactor.ps1 +9 -0
- package/bundled/dev-pipeline-windows/run-bugfix.ps1 +9 -0
- package/bundled/dev-pipeline-windows/run-feature.ps1 +9 -0
- package/bundled/dev-pipeline-windows/run-recovery.ps1 +76 -0
- package/bundled/dev-pipeline-windows/run-refactor.ps1 +9 -0
- package/bundled/dev-pipeline-windows/scripts/check-session-status.py +228 -0
- package/bundled/dev-pipeline-windows/scripts/cleanup-logs.py +192 -0
- package/bundled/dev-pipeline-windows/scripts/detect-stuck.py +530 -0
- package/bundled/dev-pipeline-windows/scripts/generate-bootstrap-prompt.py +1737 -0
- package/bundled/dev-pipeline-windows/scripts/generate-bugfix-prompt.py +685 -0
- package/bundled/dev-pipeline-windows/scripts/generate-recovery-prompt.py +805 -0
- package/bundled/dev-pipeline-windows/scripts/generate-refactor-prompt.py +763 -0
- package/bundled/dev-pipeline-windows/scripts/init-bugfix-pipeline.py +316 -0
- package/bundled/dev-pipeline-windows/scripts/init-dev-team.py +134 -0
- package/bundled/dev-pipeline-windows/scripts/init-pipeline.py +380 -0
- package/bundled/dev-pipeline-windows/scripts/init-refactor-pipeline.py +399 -0
- package/bundled/dev-pipeline-windows/scripts/parse-stream-progress.py +388 -0
- package/bundled/dev-pipeline-windows/scripts/patch-completion-notes.py +191 -0
- package/bundled/dev-pipeline-windows/scripts/update-bug-status.py +864 -0
- package/bundled/dev-pipeline-windows/scripts/update-checkpoint.py +173 -0
- package/bundled/dev-pipeline-windows/scripts/update-feature-status.py +1501 -0
- package/bundled/dev-pipeline-windows/scripts/update-refactor-status.py +1073 -0
- package/bundled/dev-pipeline-windows/scripts/utils.py +542 -0
- package/bundled/dev-pipeline-windows/templates/agent-prompts/critic-plan-challenge.md +7 -0
- package/bundled/dev-pipeline-windows/templates/agent-prompts/dev-fix.md +7 -0
- package/bundled/dev-pipeline-windows/templates/agent-prompts/dev-implement.md +30 -0
- package/bundled/dev-pipeline-windows/templates/agent-prompts/dev-resume.md +5 -0
- package/bundled/dev-pipeline-windows/templates/agent-prompts/reviewer-review.md +7 -0
- package/bundled/dev-pipeline-windows/templates/bootstrap-prompt.md +46 -0
- package/bundled/dev-pipeline-windows/templates/bootstrap-tier1.md +43 -0
- package/bundled/dev-pipeline-windows/templates/bootstrap-tier2.md +43 -0
- package/bundled/dev-pipeline-windows/templates/bootstrap-tier3.md +43 -0
- package/bundled/dev-pipeline-windows/templates/bug-fix-list-schema.json +263 -0
- package/bundled/dev-pipeline-windows/templates/bugfix-bootstrap-prompt.md +320 -0
- package/bundled/dev-pipeline-windows/templates/feature-list-schema.json +237 -0
- package/bundled/dev-pipeline-windows/templates/refactor-bootstrap-prompt.md +331 -0
- package/bundled/dev-pipeline-windows/templates/refactor-list-schema.json +270 -0
- package/bundled/dev-pipeline-windows/templates/sections/ac-verification-checklist.md +13 -0
- package/bundled/dev-pipeline-windows/templates/sections/checkpoint-system.md +91 -0
- package/bundled/dev-pipeline-windows/templates/sections/context-budget-rules.md +33 -0
- package/bundled/dev-pipeline-windows/templates/sections/critical-paths-agent.md +10 -0
- package/bundled/dev-pipeline-windows/templates/sections/critical-paths-full.md +12 -0
- package/bundled/dev-pipeline-windows/templates/sections/critical-paths-lite.md +7 -0
- package/bundled/dev-pipeline-windows/templates/sections/directory-convention-agent.md +8 -0
- package/bundled/dev-pipeline-windows/templates/sections/directory-convention-full.md +9 -0
- package/bundled/dev-pipeline-windows/templates/sections/directory-convention-lite.md +6 -0
- package/bundled/dev-pipeline-windows/templates/sections/failure-capture.md +21 -0
- package/bundled/dev-pipeline-windows/templates/sections/feature-context.md +31 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-browser-verification-auto.md +72 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-browser-verification-opencli.md +63 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-browser-verification.md +62 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-commit-full.md +71 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-commit.md +64 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-context-snapshot-agent-suffix.md +23 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-context-snapshot-base.md +24 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-context-snapshot-lite-suffix.md +12 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-critic-plan-full.md +53 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-critic-plan.md +32 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-implement-agent.md +37 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-implement-full.md +50 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-implement-lite.md +52 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-plan-agent.md +27 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-plan-lite.md +27 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-review-agent.md +27 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-review-full.md +29 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-specify-plan-full.md +77 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase0-init.md +13 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase0-test-baseline.md +23 -0
- package/bundled/dev-pipeline-windows/templates/sections/session-context.md +5 -0
- package/bundled/dev-pipeline-windows/templates/sections/subagent-timeout-recovery.md +6 -0
- package/bundled/dev-pipeline-windows/templates/sections/test-failure-recovery-agent.md +67 -0
- package/bundled/dev-pipeline-windows/templates/sections/test-failure-recovery-lite.md +58 -0
- package/bundled/dev-pipeline-windows/templates/session-status-schema.json +83 -0
- package/bundled/skills/_metadata.json +1 -1
- package/bundled/skills/app-planner/SKILL.md +26 -18
- package/bundled/skills/app-planner/references/architecture-decisions.md +9 -5
- package/bundled/skills/app-planner/references/frontend-design-guide.md +1 -1
- package/bundled/skills/feature-planner/SKILL.md +9 -2
- package/bundled/skills/prizmkit-init/SKILL.md +7 -6
- package/bundled/skills/recovery-workflow/scripts/detect-recovery-state.py +2 -0
- package/bundled/skills-windows/app-planner/SKILL.md +639 -0
- package/bundled/skills-windows/app-planner/assets/app-design-guide.md +101 -0
- package/bundled/skills-windows/app-planner/references/architecture-decisions.md +52 -0
- package/bundled/skills-windows/app-planner/references/brainstorm-guide.md +101 -0
- package/bundled/skills-windows/app-planner/references/frontend-design-guide.md +71 -0
- package/bundled/skills-windows/app-planner/references/project-brief-guide.md +82 -0
- package/bundled/skills-windows/app-planner/references/red-team-checklist.md +40 -0
- package/bundled/skills-windows/app-planner/references/rules/backend/derivation-rules.md +609 -0
- package/bundled/skills-windows/app-planner/references/rules/backend/fixed-rules.md +285 -0
- package/bundled/skills-windows/app-planner/references/rules/backend/question-bank.md +249 -0
- package/bundled/skills-windows/app-planner/references/rules/backend/template.md +173 -0
- package/bundled/skills-windows/app-planner/references/rules/database/derivation-rules.md +373 -0
- package/bundled/skills-windows/app-planner/references/rules/database/fixed-rules.md +211 -0
- package/bundled/skills-windows/app-planner/references/rules/database/question-bank.md +184 -0
- package/bundled/skills-windows/app-planner/references/rules/database/template.md +158 -0
- package/bundled/skills-windows/app-planner/references/rules/frontend/derivation-rules.md +810 -0
- package/bundled/skills-windows/app-planner/references/rules/frontend/fixed-rules.md +188 -0
- package/bundled/skills-windows/app-planner/references/rules/frontend/question-bank.md +302 -0
- package/bundled/skills-windows/app-planner/references/rules/frontend/template.md +320 -0
- package/bundled/skills-windows/app-planner/references/rules/mobile/derivation-rules.md +639 -0
- package/bundled/skills-windows/app-planner/references/rules/mobile/fixed-rules.md +290 -0
- package/bundled/skills-windows/app-planner/references/rules/mobile/question-bank.md +232 -0
- package/bundled/skills-windows/app-planner/references/rules/mobile/template.md +175 -0
- package/bundled/skills-windows/bug-fix-workflow/SKILL.md +415 -0
- package/bundled/skills-windows/bug-planner/SKILL.md +395 -0
- package/bundled/skills-windows/bug-planner/assets/bug-confirmation-template.md +43 -0
- package/bundled/skills-windows/bug-planner/references/critic-and-verification.md +44 -0
- package/bundled/skills-windows/bug-planner/references/error-recovery.md +73 -0
- package/bundled/skills-windows/bug-planner/references/input-formats.md +53 -0
- package/bundled/skills-windows/bug-planner/references/schema-validation.md +25 -0
- package/bundled/skills-windows/bug-planner/references/severity-rules.md +16 -0
- package/bundled/skills-windows/bug-planner/scripts/validate-bug-list.py +322 -0
- package/bundled/skills-windows/bugfix-pipeline-launcher/SKILL.md +380 -0
- package/bundled/skills-windows/feature-pipeline-launcher/SKILL.md +441 -0
- package/bundled/skills-windows/feature-pipeline-launcher/scripts/preflight-check.py +462 -0
- package/bundled/skills-windows/feature-planner/SKILL.md +401 -0
- package/bundled/skills-windows/feature-planner/assets/evaluation-guide.md +64 -0
- package/bundled/skills-windows/feature-planner/assets/planning-guide.md +214 -0
- package/bundled/skills-windows/feature-planner/references/browser-interaction.md +59 -0
- package/bundled/skills-windows/feature-planner/references/completeness-review.md +57 -0
- package/bundled/skills-windows/feature-planner/references/decomposition-patterns.md +75 -0
- package/bundled/skills-windows/feature-planner/references/error-recovery.md +90 -0
- package/bundled/skills-windows/feature-planner/references/incremental-feature-planning.md +112 -0
- package/bundled/skills-windows/feature-planner/references/new-project-planning.md +85 -0
- package/bundled/skills-windows/feature-planner/scripts/validate-and-generate.py +1029 -0
- package/bundled/skills-windows/feature-workflow/SKILL.md +531 -0
- package/bundled/skills-windows/prizmkit-init/SKILL.md +356 -0
- package/bundled/skills-windows/prizmkit-init/assets/project-brief-template.md +82 -0
- package/bundled/skills-windows/prizmkit-init/references/config-schema.md +68 -0
- package/bundled/skills-windows/prizmkit-init/references/rules/layer-detection.md +41 -0
- package/bundled/skills-windows/prizmkit-init/references/tech-stack-catalog.md +13 -0
- package/bundled/skills-windows/prizmkit-init/references/update-supplement.md +9 -0
- package/bundled/skills-windows/recovery-workflow/SKILL.md +456 -0
- package/bundled/skills-windows/recovery-workflow/evals/evals.json +46 -0
- package/bundled/skills-windows/recovery-workflow/scripts/detect-recovery-state.py +544 -0
- package/bundled/skills-windows/refactor-pipeline-launcher/SKILL.md +406 -0
- package/bundled/skills-windows/refactor-planner/SKILL.md +540 -0
- package/bundled/skills-windows/refactor-planner/assets/planning-guide.md +292 -0
- package/bundled/skills-windows/refactor-planner/references/behavior-preservation.md +301 -0
- package/bundled/skills-windows/refactor-planner/references/refactor-scoping-guide.md +221 -0
- package/bundled/skills-windows/refactor-planner/scripts/validate-and-generate-refactor.py +858 -0
- package/bundled/skills-windows/refactor-workflow/SKILL.md +503 -0
- package/package.json +3 -2
- package/src/clean.js +73 -2
- package/src/config.js +159 -50
- package/src/detect-platform.js +16 -8
- package/src/external-skills.js +26 -19
- package/src/index.js +31 -9
- package/src/manifest.js +6 -2
- package/src/metadata.js +43 -5
- package/src/platforms.js +36 -0
- package/src/prompts.js +31 -6
- package/src/runtimes.js +20 -0
- package/src/scaffold.js +314 -110
- 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
|
|
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
|
-
|
|
132
|
-
|
|
133
|
-
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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,
|
|
641
|
-
const
|
|
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
|
-
|
|
655
|
-
const
|
|
656
|
-
const
|
|
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
|
-
|
|
660
|
-
|
|
661
|
-
|
|
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
|
-
|
|
866
|
+
for (const relativeFile of installedFiles) {
|
|
867
|
+
const src = path.join(pipelineSource, relativeFile);
|
|
868
|
+
const tgt = path.join(pipelineTarget, relativeFile);
|
|
666
869
|
|
|
667
|
-
|
|
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.
|
|
681
|
-
|
|
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
|
-
|
|
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)
|
|
697
|
-
await fs.
|
|
698
|
-
|
|
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(
|
|
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(
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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'
|
|
1146
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
+
}
|