prizmkit 1.1.58 → 1.1.61
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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/agents/prizm-dev-team-dev.md +6 -5
- 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/rules/prizm/prizm-documentation.md +2 -4
- package/bundled/rules/prizm/prizm-progressive-loading.md +2 -1
- package/bundled/skills/_metadata.json +1 -1
- package/bundled/skills/app-planner/SKILL.md +24 -16
- 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-implement/SKILL.md +1 -0
- package/bundled/skills/prizmkit-init/SKILL.md +8 -7
- package/bundled/skills/prizmkit-init/references/config-schema.md +3 -1
- package/bundled/skills/prizmkit-plan/SKILL.md +2 -0
- package/bundled/skills/prizmkit-prizm-docs/assets/prizm-docs-format.md +6 -1
- package/bundled/skills/prizmkit-prizm-docs/references/op-init.md +2 -2
- package/bundled/skills/prizmkit-prizm-docs/references/op-update.md +2 -1
- package/bundled/skills/prizmkit-prizm-docs/references/op-validate.md +2 -2
- package/bundled/skills/prizmkit-retrospective/SKILL.md +2 -0
- package/bundled/skills/prizmkit-retrospective/references/knowledge-injection-steps.md +2 -0
- package/bundled/skills/prizmkit-retrospective/references/structural-sync-steps.md +2 -0
- 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 +70 -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/bundled/templates/hooks/diff-prizm-docs.sh +1 -1
- package/bundled/templates/hooks/prizm-pre-commit.sh +53 -4
- package/bundled/templates/hooks/validate-prizm-docs.sh +62 -7
- package/bundled/templates/project-memory-template.md +2 -1
- package/package.json +3 -2
- package/src/clean.js +73 -2
- package/src/config.js +167 -50
- package/src/detect-platform.js +15 -9
- package/src/external-skills.js +26 -19
- package/src/index.js +28 -10
- package/src/manifest.js +6 -2
- package/src/metadata.js +43 -5
- package/src/platforms.js +38 -0
- package/src/prompts.js +50 -9
- package/src/runtimes.js +20 -0
- package/src/scaffold.js +447 -114
- package/src/upgrade.js +87 -44
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,183 @@ 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
|
-
|
|
577
|
+
const PRIZMKIT_HOOK_MARKER_START = '# PRIZMKIT_PRE_COMMIT_HOOK_START';
|
|
578
|
+
const PRIZMKIT_HOOK_MARKER_END = '# PRIZMKIT_PRE_COMMIT_HOOK_END';
|
|
579
|
+
|
|
580
|
+
function escapeRegExp(value) {
|
|
581
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
function ensureTrailingNewline(value) {
|
|
585
|
+
return value.endsWith('\n') ? value : `${value}\n`;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
function buildStandalonePrizmHook(templateContent) {
|
|
589
|
+
const content = templateContent.trimEnd();
|
|
590
|
+
const firstNewline = content.indexOf('\n');
|
|
591
|
+
|
|
592
|
+
if (content.startsWith('#!') && firstNewline !== -1) {
|
|
593
|
+
return [
|
|
594
|
+
content.slice(0, firstNewline),
|
|
595
|
+
PRIZMKIT_HOOK_MARKER_START,
|
|
596
|
+
content.slice(firstNewline + 1),
|
|
597
|
+
PRIZMKIT_HOOK_MARKER_END,
|
|
598
|
+
'',
|
|
599
|
+
].join('\n');
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
return [
|
|
603
|
+
PRIZMKIT_HOOK_MARKER_START,
|
|
604
|
+
content,
|
|
605
|
+
PRIZMKIT_HOOK_MARKER_END,
|
|
606
|
+
'',
|
|
607
|
+
].join('\n');
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
function buildAppendedPrizmHook(templateContent) {
|
|
611
|
+
return [
|
|
612
|
+
PRIZMKIT_HOOK_MARKER_START,
|
|
613
|
+
templateContent.trimEnd(),
|
|
614
|
+
PRIZMKIT_HOOK_MARKER_END,
|
|
615
|
+
'',
|
|
616
|
+
].join('\n');
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
function insertPrizmHookIntoUserHook(userHookContent, prizmHookBlock) {
|
|
620
|
+
const content = userHookContent.trimEnd();
|
|
621
|
+
const terminalSuccessExitPattern = /(^|\n)([ \t]*exit[ \t]+0[ \t]*(?:#.*)?)[ \t]*$/;
|
|
622
|
+
const match = content.match(terminalSuccessExitPattern);
|
|
623
|
+
|
|
624
|
+
if (!match) {
|
|
625
|
+
return `${content}\n\n${prizmHookBlock}`;
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
const exitLineStart = match.index + match[1].length;
|
|
629
|
+
const beforeExit = content.slice(0, exitLineStart).trimEnd();
|
|
630
|
+
const exitLine = match[2];
|
|
631
|
+
|
|
632
|
+
return [
|
|
633
|
+
beforeExit,
|
|
634
|
+
prizmHookBlock.trimEnd(),
|
|
635
|
+
exitLine,
|
|
636
|
+
'',
|
|
637
|
+
].filter((part, index) => index > 0 || part.length > 0).join('\n\n');
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
function findLegacyPrizmHookEnd(content, markerIndex) {
|
|
641
|
+
const afterMarker = content.slice(markerIndex);
|
|
642
|
+
const exitMatch = afterMarker.match(/(^|\n)[ \t]*exit[ \t]+0[ \t]*(\r?\n|$)/);
|
|
643
|
+
if (exitMatch) {
|
|
644
|
+
return markerIndex + exitMatch.index + exitMatch[0].length;
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
const markerLineEnd = content.indexOf('\n', markerIndex);
|
|
648
|
+
const searchStart = markerLineEnd === -1 ? content.length : markerLineEnd + 1;
|
|
649
|
+
const blankLineIndex = content.slice(searchStart).search(/\r?\n[ \t]*(\r?\n|$)/);
|
|
650
|
+
if (blankLineIndex !== -1) {
|
|
651
|
+
return searchStart + blankLineIndex + 1;
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
return content.length;
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
function stripLegacyPrizmHookBlocks(existing) {
|
|
658
|
+
const legacyMarker = '# prizm-pre-commit.sh';
|
|
659
|
+
let cleaned = '';
|
|
660
|
+
let cursor = 0;
|
|
661
|
+
|
|
662
|
+
while (cursor < existing.length) {
|
|
663
|
+
const markerIndex = existing.indexOf(legacyMarker, cursor);
|
|
664
|
+
if (markerIndex === -1) {
|
|
665
|
+
cleaned += existing.slice(cursor);
|
|
666
|
+
break;
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
const markerLineStart = existing.lastIndexOf('\n', markerIndex) + 1;
|
|
670
|
+
let blockStart = markerLineStart;
|
|
671
|
+
if (markerLineStart > 0) {
|
|
672
|
+
const previousLineEnd = markerLineStart - 1;
|
|
673
|
+
const previousLineStart = existing.lastIndexOf('\n', previousLineEnd - 1) + 1;
|
|
674
|
+
const previousLine = existing.slice(previousLineStart, previousLineEnd).replace(/\r$/, '');
|
|
675
|
+
if (previousLine.startsWith('#!')) {
|
|
676
|
+
blockStart = previousLineStart;
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
cleaned += existing.slice(cursor, blockStart);
|
|
681
|
+
cursor = findLegacyPrizmHookEnd(existing, markerIndex);
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
return cleaned;
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
function stripManagedPrizmHook(existing) {
|
|
688
|
+
const markedBlockPattern = new RegExp(
|
|
689
|
+
`(^|\\n)${escapeRegExp(PRIZMKIT_HOOK_MARKER_START)}[\\s\\S]*?${escapeRegExp(PRIZMKIT_HOOK_MARKER_END)}\\n?`,
|
|
690
|
+
'g',
|
|
691
|
+
);
|
|
692
|
+
|
|
693
|
+
let cleaned = existing.replace(markedBlockPattern, '\n');
|
|
694
|
+
cleaned = stripLegacyPrizmHookBlocks(cleaned);
|
|
695
|
+
|
|
696
|
+
return cleaned.replace(/\n{3,}/g, '\n\n').trimEnd();
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
function hasUserHookContent(content) {
|
|
700
|
+
return content
|
|
701
|
+
.replace(/^#!.*(\r?\n|$)/, '')
|
|
702
|
+
.trim()
|
|
703
|
+
.length > 0;
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
export async function installGitHook(projectRoot, dryRun, runtime = 'unix') {
|
|
465
707
|
const gitDir = path.join(projectRoot, '.git');
|
|
708
|
+
const normalizedRuntime = normalizeRuntime(runtime);
|
|
466
709
|
|
|
467
710
|
if (dryRun) {
|
|
468
|
-
|
|
711
|
+
const hookLabel = normalizedRuntime === RUNTIME_WINDOWS
|
|
712
|
+
? '.git/hooks/pre-commit skipped (Windows runtime: no shell hook)'
|
|
713
|
+
: '.git/hooks/pre-commit (prizm format check)';
|
|
714
|
+
console.log(chalk.gray(` [dry-run] ${hookLabel}`));
|
|
469
715
|
return;
|
|
470
716
|
}
|
|
471
717
|
|
|
@@ -474,16 +720,37 @@ export async function installGitHook(projectRoot, dryRun) {
|
|
|
474
720
|
const templatePath = path.join(getTemplatesDir(), 'hooks', 'prizm-pre-commit.sh');
|
|
475
721
|
const hooksDir = path.join(gitDir, 'hooks');
|
|
476
722
|
const preCommitPath = path.join(hooksDir, 'pre-commit');
|
|
723
|
+
const hookContent = fs.readFileSync(templatePath, 'utf8');
|
|
724
|
+
|
|
725
|
+
if (normalizedRuntime === RUNTIME_WINDOWS) {
|
|
726
|
+
if (fs.existsSync(preCommitPath)) {
|
|
727
|
+
const existing = fs.readFileSync(preCommitPath, 'utf8');
|
|
728
|
+
const cleaned = stripManagedPrizmHook(existing);
|
|
729
|
+
if (!hasUserHookContent(cleaned)) {
|
|
730
|
+
await fs.remove(preCommitPath);
|
|
731
|
+
console.log(chalk.gray(' • .git/hooks/pre-commit shell hook removed for Windows runtime'));
|
|
732
|
+
} else if (cleaned !== existing.trimEnd()) {
|
|
733
|
+
fs.writeFileSync(preCommitPath, ensureTrailingNewline(cleaned));
|
|
734
|
+
console.log(chalk.gray(' • PrizmKit shell hook block removed for Windows runtime'));
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
console.log(chalk.gray(' • Git hook skipped (Windows runtime uses no shell hook)'));
|
|
738
|
+
return;
|
|
739
|
+
}
|
|
477
740
|
|
|
478
741
|
fs.mkdirSync(hooksDir, { recursive: true });
|
|
479
742
|
|
|
480
743
|
if (fs.existsSync(preCommitPath)) {
|
|
481
744
|
const existing = fs.readFileSync(preCommitPath, 'utf8');
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
745
|
+
const cleaned = stripManagedPrizmHook(existing);
|
|
746
|
+
if (hasUserHookContent(cleaned)) {
|
|
747
|
+
fs.writeFileSync(preCommitPath, insertPrizmHookIntoUserHook(cleaned, buildAppendedPrizmHook(hookContent)));
|
|
748
|
+
} else {
|
|
749
|
+
fs.writeFileSync(preCommitPath, buildStandalonePrizmHook(hookContent));
|
|
750
|
+
}
|
|
751
|
+
fs.chmodSync(preCommitPath, 0o755);
|
|
485
752
|
} else {
|
|
486
|
-
|
|
753
|
+
fs.writeFileSync(preCommitPath, buildStandalonePrizmHook(hookContent));
|
|
487
754
|
fs.chmodSync(preCommitPath, 0o755);
|
|
488
755
|
}
|
|
489
756
|
|
|
@@ -491,11 +758,12 @@ export async function installGitHook(projectRoot, dryRun) {
|
|
|
491
758
|
}
|
|
492
759
|
|
|
493
760
|
/**
|
|
494
|
-
* 安装 PrizmKit 脚本到 .prizmkit/scripts
|
|
761
|
+
* 安装 PrizmKit 脚本到 .prizmkit/scripts/(Windows runtime 跳过 shell-only 脚本)
|
|
495
762
|
*/
|
|
496
|
-
export async function installPrizmkitScripts(projectRoot, dryRun) {
|
|
763
|
+
export async function installPrizmkitScripts(projectRoot, dryRun, runtime = 'unix') {
|
|
497
764
|
const scriptsDir = path.join(projectRoot, '.prizmkit', 'scripts');
|
|
498
765
|
const templateHooksDir = path.join(getTemplatesDir(), 'hooks');
|
|
766
|
+
const normalizedRuntime = normalizeRuntime(runtime);
|
|
499
767
|
|
|
500
768
|
const scripts = [
|
|
501
769
|
'validate-prizm-docs.sh',
|
|
@@ -504,8 +772,17 @@ export async function installPrizmkitScripts(projectRoot, dryRun) {
|
|
|
504
772
|
|
|
505
773
|
if (dryRun) {
|
|
506
774
|
for (const s of scripts) {
|
|
507
|
-
|
|
775
|
+
const action = normalizedRuntime === RUNTIME_WINDOWS ? 'skip shell script' : `.prizmkit/scripts/${s}`;
|
|
776
|
+
console.log(chalk.gray(` [dry-run] ${action}`));
|
|
777
|
+
}
|
|
778
|
+
return;
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
if (normalizedRuntime === RUNTIME_WINDOWS) {
|
|
782
|
+
for (const s of scripts) {
|
|
783
|
+
await fs.remove(path.join(scriptsDir, s));
|
|
508
784
|
}
|
|
785
|
+
console.log(chalk.gray(' • shell-only PrizmKit scripts skipped for Windows runtime'));
|
|
509
786
|
return;
|
|
510
787
|
}
|
|
511
788
|
|
|
@@ -525,16 +802,16 @@ const PRIZMKIT_MARKER_START = '<!-- PRIZMKIT:START';
|
|
|
525
802
|
const PRIZMKIT_MARKER_END = '<!-- PRIZMKIT:END -->';
|
|
526
803
|
|
|
527
804
|
/**
|
|
528
|
-
* Merge PrizmKit template content into a project memory file
|
|
805
|
+
* Merge PrizmKit template content into a project memory file.
|
|
529
806
|
* - File missing → create with template content
|
|
530
807
|
* - File exists with markers → replace content between markers
|
|
531
808
|
* - File exists without markers → append template content
|
|
532
809
|
*/
|
|
533
810
|
export async function installProjectMemory(platform, projectRoot, dryRun) {
|
|
534
811
|
const templatesDir = getTemplatesDir();
|
|
535
|
-
const skillsDir = getSkillsDir();
|
|
536
812
|
const templateName = 'project-memory-template.md';
|
|
537
|
-
const targetName = platform
|
|
813
|
+
const targetName = projectMemoryFile(platform);
|
|
814
|
+
if (!targetName) return;
|
|
538
815
|
const targetPath = path.join(projectRoot, targetName);
|
|
539
816
|
|
|
540
817
|
// Template lives in core/templates/ (bundled as templates/).
|
|
@@ -548,7 +825,19 @@ export async function installProjectMemory(platform, projectRoot, dryRun) {
|
|
|
548
825
|
return;
|
|
549
826
|
}
|
|
550
827
|
|
|
551
|
-
|
|
828
|
+
let templateContent = (await fs.readFile(templatePath, 'utf-8')).trimEnd();
|
|
829
|
+
if (platform === 'codex') {
|
|
830
|
+
templateContent = templateContent.replace(
|
|
831
|
+
'### Available Commands\nRun `/prizm-kit` to see all available PrizmKit commands.',
|
|
832
|
+
`### Available PrizmKit Skills
|
|
833
|
+
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\`.
|
|
834
|
+
|
|
835
|
+
- Start with \`.agents/skills/prizm-kit/SKILL.md\` to see all available PrizmKit skills.
|
|
836
|
+
- Skill assets and references live inside each \`.agents/skills/<skill>/\` directory.
|
|
837
|
+
- PrizmKit behavioral rules live in \`.agents/rules/\`; read the relevant Markdown guidance before commit, documentation, or context-loading work.
|
|
838
|
+
- Native Codex subagents live in \`.codex/agents/*.toml\`; use them when spawning or coordinating subagents.`
|
|
839
|
+
);
|
|
840
|
+
}
|
|
552
841
|
|
|
553
842
|
if (dryRun) {
|
|
554
843
|
console.log(chalk.gray(` [dry-run] ${targetName}`));
|
|
@@ -599,25 +888,15 @@ function collectFilesRecursive(dir, base = '') {
|
|
|
599
888
|
return files;
|
|
600
889
|
}
|
|
601
890
|
|
|
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']);
|
|
891
|
+
function collectInstallablePipelineFiles(pipelineSource) {
|
|
613
892
|
let allEntries;
|
|
614
893
|
try {
|
|
615
894
|
allEntries = fs.readdirSync(pipelineSource);
|
|
616
895
|
} catch {
|
|
617
896
|
return [];
|
|
618
897
|
}
|
|
619
|
-
const items = allEntries.filter(name => !EXCLUDE.has(name) && !name.startsWith('.'));
|
|
620
898
|
|
|
899
|
+
const items = allEntries.filter(name => !PIPELINE_INSTALL_EXCLUDE.has(name) && !name.startsWith('.'));
|
|
621
900
|
const files = [];
|
|
622
901
|
for (const item of items) {
|
|
623
902
|
const src = path.join(pipelineSource, item);
|
|
@@ -632,13 +911,67 @@ export function resolvePipelineFileList() {
|
|
|
632
911
|
return files;
|
|
633
912
|
}
|
|
634
913
|
|
|
914
|
+
/**
|
|
915
|
+
* Resolve the list of pipeline files from the source (bundled) pipeline directory.
|
|
916
|
+
* Used by upgrade to compute diff before actual installation.
|
|
917
|
+
* @returns {string[]} relative file paths
|
|
918
|
+
*/
|
|
919
|
+
export function resolvePipelineFileList(runtime = 'unix') {
|
|
920
|
+
const pipelineSource = getPipelineDir(runtime);
|
|
921
|
+
if (!pipelineSource || !fs.pathExistsSync(pipelineSource)) return [];
|
|
922
|
+
return collectInstallablePipelineFiles(pipelineSource);
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
async function pruneStalePipelineRuntimeFiles(pipelineTarget, runtime) {
|
|
926
|
+
if (!await fs.pathExists(pipelineTarget)) return false;
|
|
927
|
+
|
|
928
|
+
const staleRuntime = runtime === RUNTIME_WINDOWS ? RUNTIME_UNIX : RUNTIME_WINDOWS;
|
|
929
|
+
const staleFiles = resolvePipelineFileList(staleRuntime);
|
|
930
|
+
const currentFiles = new Set(resolvePipelineFileList(runtime));
|
|
931
|
+
const staleDirs = new Set();
|
|
932
|
+
let prunedRuntimeSpecificFiles = false;
|
|
933
|
+
|
|
934
|
+
for (const relativeFile of staleFiles) {
|
|
935
|
+
if (currentFiles.has(relativeFile)) continue;
|
|
936
|
+
|
|
937
|
+
const targetFile = path.join(pipelineTarget, relativeFile);
|
|
938
|
+
if (await fs.pathExists(targetFile)) {
|
|
939
|
+
const stat = await fs.stat(targetFile);
|
|
940
|
+
if (stat.isFile()) {
|
|
941
|
+
await fs.remove(targetFile);
|
|
942
|
+
prunedRuntimeSpecificFiles = true;
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
const relativeDir = path.dirname(relativeFile);
|
|
947
|
+
if (relativeDir && relativeDir !== '.') staleDirs.add(relativeDir);
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
const dirsByDepth = [...staleDirs].sort((a, b) => b.split(path.sep).length - a.split(path.sep).length);
|
|
951
|
+
for (const relativeDir of dirsByDepth) {
|
|
952
|
+
const targetDir = path.join(pipelineTarget, relativeDir);
|
|
953
|
+
if (!await fs.pathExists(targetDir)) continue;
|
|
954
|
+
const entries = await fs.readdir(targetDir);
|
|
955
|
+
if (entries.length === 0) {
|
|
956
|
+
await fs.remove(targetDir);
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
return prunedRuntimeSpecificFiles;
|
|
961
|
+
}
|
|
962
|
+
|
|
635
963
|
/**
|
|
636
964
|
* 安装 dev-pipeline(纯 Copy 模式)
|
|
637
965
|
* 安装目标:.prizmkit/dev-pipeline/
|
|
638
966
|
* Returns an array of installed file relative paths (relative to .prizmkit/dev-pipeline/).
|
|
639
967
|
*/
|
|
640
|
-
export async function installPipeline(projectRoot, dryRun,
|
|
641
|
-
const
|
|
968
|
+
export async function installPipeline(projectRoot, dryRun, options = {}) {
|
|
969
|
+
const runtime = normalizeRuntime(options.runtime);
|
|
970
|
+
const previousRuntime = options.previousRuntime === undefined
|
|
971
|
+
? runtime
|
|
972
|
+
: normalizeRuntime(options.previousRuntime);
|
|
973
|
+
const forceOverwrite = Boolean(options.forceOverwrite);
|
|
974
|
+
const pipelineSource = getPipelineDir(runtime);
|
|
642
975
|
const pipelineTarget = path.join(projectRoot, '.prizmkit', 'dev-pipeline');
|
|
643
976
|
|
|
644
977
|
if (dryRun) {
|
|
@@ -651,51 +984,41 @@ export async function installPipeline(projectRoot, dryRun, { forceOverwrite = fa
|
|
|
651
984
|
await fs.ensureDir(path.join(projectRoot, '.prizmkit', 'state', 'bugfix'));
|
|
652
985
|
await fs.ensureDir(path.join(projectRoot, '.prizmkit', 'state', 'refactor'));
|
|
653
986
|
|
|
654
|
-
|
|
655
|
-
const
|
|
656
|
-
const
|
|
657
|
-
const items = allEntries.filter(name => !EXCLUDE.has(name) && !name.startsWith('.'));
|
|
987
|
+
const runtimeSwitched = previousRuntime !== runtime;
|
|
988
|
+
const prunedStaleRuntimeFiles = await pruneStalePipelineRuntimeFiles(pipelineTarget, runtime);
|
|
989
|
+
const overwriteExisting = forceOverwrite || runtimeSwitched || prunedStaleRuntimeFiles;
|
|
658
990
|
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
const src = path.join(pipelineSource, item);
|
|
663
|
-
const tgt = path.join(pipelineTarget, item);
|
|
991
|
+
const installedFiles = collectInstallablePipelineFiles(pipelineSource);
|
|
992
|
+
let copiedCount = 0;
|
|
993
|
+
let skippedCount = 0;
|
|
664
994
|
|
|
665
|
-
|
|
995
|
+
for (const relativeFile of installedFiles) {
|
|
996
|
+
const src = path.join(pipelineSource, relativeFile);
|
|
997
|
+
const tgt = path.join(pipelineTarget, relativeFile);
|
|
666
998
|
|
|
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
|
-
}
|
|
999
|
+
if (await fs.pathExists(tgt) && !overwriteExisting) {
|
|
1000
|
+
skippedCount++;
|
|
677
1001
|
continue;
|
|
678
1002
|
}
|
|
679
1003
|
|
|
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
|
-
}
|
|
1004
|
+
await fs.ensureDir(path.dirname(tgt));
|
|
1005
|
+
await fs.copy(src, tgt, { overwrite: true, errorOnExist: false });
|
|
1006
|
+
copiedCount++;
|
|
689
1007
|
}
|
|
690
1008
|
|
|
691
|
-
|
|
1009
|
+
const skipSuffix = skippedCount ? `, preserved ${skippedCount} existing` : '';
|
|
1010
|
+
console.log(chalk.green(` ✓ .prizmkit/dev-pipeline/ (${copiedCount} files copied${skipSuffix})`));
|
|
692
1011
|
|
|
693
1012
|
// Copy .env.example to .prizmkit/.env.example if it exists in pipeline source
|
|
694
1013
|
const envExampleSrc = path.join(pipelineSource, '.env.example');
|
|
695
1014
|
const envExampleTgt = path.join(projectRoot, '.prizmkit', '.env.example');
|
|
696
|
-
if (await fs.pathExists(envExampleSrc)
|
|
697
|
-
await fs.
|
|
698
|
-
|
|
1015
|
+
if (await fs.pathExists(envExampleSrc)) {
|
|
1016
|
+
if (overwriteExisting || !(await fs.pathExists(envExampleTgt))) {
|
|
1017
|
+
await fs.copy(envExampleSrc, envExampleTgt, { overwrite: true, errorOnExist: false });
|
|
1018
|
+
console.log(chalk.green(` ✓ .prizmkit/.env.example (pipeline environment configuration)`));
|
|
1019
|
+
} else {
|
|
1020
|
+
console.log(chalk.gray(` • .prizmkit/.env.example exists, preserved`));
|
|
1021
|
+
}
|
|
699
1022
|
}
|
|
700
1023
|
|
|
701
1024
|
return installedFiles;
|
|
@@ -839,7 +1162,7 @@ export async function installOpenCli(projectRoot, dryRun, autoDownload = true) {
|
|
|
839
1162
|
console.log(chalk.yellow(' ⚠ Chrome Browser Bridge extension is NOT connected'));
|
|
840
1163
|
|
|
841
1164
|
if (autoDownload) {
|
|
842
|
-
await downloadOpenCliExtension(
|
|
1165
|
+
await downloadOpenCliExtension();
|
|
843
1166
|
} else {
|
|
844
1167
|
printOpenCliManualSteps();
|
|
845
1168
|
}
|
|
@@ -859,7 +1182,7 @@ export async function installOpenCli(projectRoot, dryRun, autoDownload = true) {
|
|
|
859
1182
|
* 从 GitHub releases 下载并解压 opencli Chrome 扩展。
|
|
860
1183
|
* 省去用户手动下载解压步骤,但仍需在 Chrome 中手动加载。
|
|
861
1184
|
*/
|
|
862
|
-
async function downloadOpenCliExtension(
|
|
1185
|
+
async function downloadOpenCliExtension() {
|
|
863
1186
|
const extDir = path.join(process.env.HOME || process.env.USERPROFILE || '.', '.opencli', 'extension');
|
|
864
1187
|
|
|
865
1188
|
// Check if already downloaded
|
|
@@ -959,7 +1282,8 @@ export const EXTRAS_REGISTRY = {
|
|
|
959
1282
|
/**
|
|
960
1283
|
* 执行纯净安装
|
|
961
1284
|
* @param {Object} config
|
|
962
|
-
* @param {string} config.platform - 'codebuddy' | 'claude' | '
|
|
1285
|
+
* @param {string} config.platform - 'codebuddy' | 'claude' | 'codex' | 'all'
|
|
1286
|
+
* @param {string} [config.runtime] - 'unix' | 'windows'
|
|
963
1287
|
* @param {string} config.skills - 'core' | 'minimal' | 'recommended:<type>'
|
|
964
1288
|
* @param {boolean} config.team - 是否启用团队模式
|
|
965
1289
|
* @param {boolean} config.pipeline - 是否安装 dev-pipeline
|
|
@@ -974,7 +1298,11 @@ export const EXTRAS_REGISTRY = {
|
|
|
974
1298
|
*/
|
|
975
1299
|
export async function scaffold(config) {
|
|
976
1300
|
const { platform, skills, team, pipeline, rules, aiCli, externalSkills, playwrightCli, openCli, openCliAutoDownload, projectRoot, dryRun } = config;
|
|
977
|
-
const
|
|
1301
|
+
const runtime = normalizeRuntime(config.runtime);
|
|
1302
|
+
if (!isKnownPlatform(platform)) {
|
|
1303
|
+
throw new Error(`Unknown platform "${platform}". Expected codebuddy, claude, codex, or all.`);
|
|
1304
|
+
}
|
|
1305
|
+
const platforms = expandPlatforms(platform);
|
|
978
1306
|
|
|
979
1307
|
if (dryRun) {
|
|
980
1308
|
console.log(chalk.yellow('\n [DRY RUN] 预览将要安装的内容:\n'));
|
|
@@ -986,12 +1314,11 @@ export async function scaffold(config) {
|
|
|
986
1314
|
console.log('');
|
|
987
1315
|
|
|
988
1316
|
for (const p of platforms) {
|
|
989
|
-
|
|
990
|
-
console.log(chalk.bold(` 正在安装 ${platformLabel} 环境...\n`));
|
|
1317
|
+
console.log(chalk.bold(` 正在安装 ${platformLabel(p)} 环境...\n`));
|
|
991
1318
|
|
|
992
1319
|
// 1. Skills
|
|
993
1320
|
console.log(chalk.blue(' 技能文件:'));
|
|
994
|
-
await installSkills(p, skillList, projectRoot, dryRun);
|
|
1321
|
+
await installSkills(p, skillList, projectRoot, dryRun, runtime);
|
|
995
1322
|
|
|
996
1323
|
// 2. Agents
|
|
997
1324
|
console.log(chalk.blue('\n Agent 定义:'));
|
|
@@ -1005,7 +1332,7 @@ export async function scaffold(config) {
|
|
|
1005
1332
|
|
|
1006
1333
|
// 4. Settings/Hooks/Rules
|
|
1007
1334
|
console.log(chalk.blue('\n 平台配置:'));
|
|
1008
|
-
await installSettings(p, projectRoot, { pipeline, rules }, dryRun);
|
|
1335
|
+
await installSettings(p, projectRoot, { pipeline, rules }, dryRun, runtime);
|
|
1009
1336
|
|
|
1010
1337
|
// 5. Project Memory
|
|
1011
1338
|
console.log(chalk.blue('\n 项目记忆文件:'));
|
|
@@ -1018,7 +1345,7 @@ export async function scaffold(config) {
|
|
|
1018
1345
|
let installedPipelineFiles = [];
|
|
1019
1346
|
if (pipeline) {
|
|
1020
1347
|
console.log(chalk.blue(' 自动化流水线:'));
|
|
1021
|
-
installedPipelineFiles = await installPipeline(projectRoot, dryRun) || [];
|
|
1348
|
+
installedPipelineFiles = await installPipeline(projectRoot, dryRun, { runtime }) || [];
|
|
1022
1349
|
console.log('');
|
|
1023
1350
|
}
|
|
1024
1351
|
|
|
@@ -1097,11 +1424,11 @@ export async function scaffold(config) {
|
|
|
1097
1424
|
|
|
1098
1425
|
// 12. Git pre-commit hook
|
|
1099
1426
|
console.log(chalk.blue(' Git Hook:'));
|
|
1100
|
-
await installGitHook(projectRoot, dryRun);
|
|
1427
|
+
await installGitHook(projectRoot, dryRun, runtime);
|
|
1101
1428
|
|
|
1102
|
-
// 13. PrizmKit scripts
|
|
1429
|
+
// 13. PrizmKit scripts
|
|
1103
1430
|
console.log(chalk.blue(' PrizmKit 脚本:'));
|
|
1104
|
-
await installPrizmkitScripts(projectRoot, dryRun);
|
|
1431
|
+
await installPrizmkitScripts(projectRoot, dryRun, runtime);
|
|
1105
1432
|
|
|
1106
1433
|
// 14. Write installation manifest
|
|
1107
1434
|
if (!dryRun) {
|
|
@@ -1109,12 +1436,12 @@ export async function scaffold(config) {
|
|
|
1109
1436
|
const agentFileNames = (await fs.readdir(agentsDir)).filter(f => f.endsWith('.md'));
|
|
1110
1437
|
const rulesMeta = await loadRulesMetadata();
|
|
1111
1438
|
const rulesPresetName = rules || 'recommended';
|
|
1112
|
-
const
|
|
1113
|
-
const ruleFileNames = (rulesPresetDef?.rules || []).map(name => `${name}.md`);
|
|
1439
|
+
const ruleFileNames = resolveRuleNamesForRuntime(rulesMeta, rulesPresetName, runtime).map(name => `${name}.md`);
|
|
1114
1440
|
|
|
1115
1441
|
const manifest = buildManifest({
|
|
1116
1442
|
version: scaffoldPkg.version,
|
|
1117
1443
|
platform,
|
|
1444
|
+
runtime,
|
|
1118
1445
|
suite: skills,
|
|
1119
1446
|
skills: skillList,
|
|
1120
1447
|
agents: agentFileNames,
|
|
@@ -1142,15 +1469,20 @@ export async function scaffold(config) {
|
|
|
1142
1469
|
|
|
1143
1470
|
if (!dryRun) {
|
|
1144
1471
|
// 打印下一步提示
|
|
1145
|
-
const mainPlatform = platforms.includes('claude') ? 'claude'
|
|
1146
|
-
|
|
1472
|
+
const mainPlatform = platforms.includes('claude') ? 'claude'
|
|
1473
|
+
: platforms.includes('codebuddy') ? 'codebuddy'
|
|
1474
|
+
: platforms[0];
|
|
1475
|
+
const defaultCli = defaultCliForPlatform(mainPlatform);
|
|
1147
1476
|
const cli = aiCli || defaultCli;
|
|
1148
1477
|
|
|
1149
1478
|
console.log('');
|
|
1150
1479
|
console.log(' 下一步:');
|
|
1151
1480
|
console.log(` 1. ${chalk.cyan('cd ' + projectRoot)}`);
|
|
1152
1481
|
console.log(` 2. ${chalk.cyan(cli)} ${chalk.gray('# 启动 AI 对话')}`);
|
|
1153
|
-
|
|
1482
|
+
const initInstruction = mainPlatform === 'codex'
|
|
1483
|
+
? '说 "运行 prizmkit-init"'
|
|
1484
|
+
: '说 "/prizmkit-init"';
|
|
1485
|
+
console.log(` 3. ${chalk.cyan(initInstruction)} ${chalk.gray('# 初始化项目上下文')}`);
|
|
1154
1486
|
|
|
1155
1487
|
if (pipeline) {
|
|
1156
1488
|
console.log(` 4. ${chalk.cyan('说 "规划一个应用"')} ${chalk.gray('# 生成 feature-list.json')}`);
|
|
@@ -1166,7 +1498,8 @@ export async function scaffold(config) {
|
|
|
1166
1498
|
|
|
1167
1499
|
console.log(chalk.gray(` 安装统计:`));
|
|
1168
1500
|
console.log(chalk.gray(` 技能: ${suiteLabel}`));
|
|
1169
|
-
console.log(chalk.gray(` 平台: ${platforms.map(
|
|
1501
|
+
console.log(chalk.gray(` 平台: ${platforms.map(platformLabel).join(' + ')}`));
|
|
1502
|
+
console.log(chalk.gray(` 运行系统: ${runtimeLabel(runtime)}`));
|
|
1170
1503
|
console.log(chalk.gray(` 团队: ${team ? '已启用' : '未启用'}`));
|
|
1171
1504
|
console.log(chalk.gray(` 流水线: ${pipeline ? '已安装' : '未安装'}`));
|
|
1172
1505
|
const browserTools = [playwrightCli && 'playwright-cli', openCli && 'opencli'].filter(Boolean);
|
|
@@ -1175,4 +1508,4 @@ export async function scaffold(config) {
|
|
|
1175
1508
|
if (aiCli) console.log(chalk.gray(` AI CLI: ${aiCli}`));
|
|
1176
1509
|
console.log('');
|
|
1177
1510
|
}
|
|
1178
|
-
}
|
|
1511
|
+
}
|