prizmkit 1.0.0

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.
@@ -0,0 +1,55 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * prizmkit CLI
5
+ *
6
+ * Usage:
7
+ * npx prizmkit install .
8
+ * npx prizmkit install my-project --platform claude --non-interactive
9
+ * npx prizmkit uninstall . --dry-run
10
+ */
11
+
12
+ import { program } from 'commander';
13
+ import { runScaffold } from '../src/index.js';
14
+ import { runClean } from '../src/clean.js';
15
+
16
+ program
17
+ .name('prizmkit')
18
+ .description('PrizmKit project installer/uninstaller')
19
+ .version('1.0.0');
20
+
21
+ program
22
+ .command('install [directory]')
23
+ .description('Install PrizmKit into a project directory')
24
+ .option('--platform <platform>', 'Target platform: codebuddy, claude, or both')
25
+ .option('--skills <suite>', 'Skill suite: full, core, or minimal', 'full')
26
+ .option('--team', 'Enable multi-agent team mode (default: true)')
27
+ .option('--no-team', 'Disable multi-agent team mode')
28
+ .option('--pipeline', 'Install dev-pipeline (default: true)')
29
+ .option('--no-pipeline', 'Disable dev-pipeline')
30
+ .option('--non-interactive', 'Skip interactive prompts (requires --platform)')
31
+ .option('--dry-run', 'Show what would be created without making changes')
32
+ .action(async (directory = '.', options) => {
33
+ try {
34
+ await runScaffold(directory, options);
35
+ } catch (err) {
36
+ console.error(`\n Error: ${err.message}\n`);
37
+ process.exit(1);
38
+ }
39
+ });
40
+
41
+ program
42
+ .command('uninstall [directory]')
43
+ .description('Remove PrizmKit files from an existing project')
44
+ .option('--dry-run', 'Show what would be removed without deleting')
45
+ .option('--global-team', 'Also remove ~/.codebuddy/teams/prizm-dev-team')
46
+ .action(async (directory = '.', options) => {
47
+ try {
48
+ await runClean(directory, options);
49
+ } catch (err) {
50
+ console.error(`\n Error: ${err.message}\n`);
51
+ process.exit(1);
52
+ }
53
+ });
54
+
55
+ program.parse();
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "prizmkit",
3
+ "version": "1.0.0",
4
+ "description": "Create a new PrizmKit-powered project with clean initialization — no framework dev files, just what you need.",
5
+ "type": "module",
6
+ "bin": {
7
+ "prizmkit": "bin/create-prizmkit.js"
8
+ },
9
+ "files": [
10
+ "bin/",
11
+ "src/",
12
+ "bundled/"
13
+ ],
14
+ "scripts": {
15
+ "bundle": "node ../scripts/bundle.js",
16
+ "dev": "node bin/create-prizmkit.js install . --dry-run",
17
+ "test": "node bin/create-prizmkit.js uninstall . --dry-run"
18
+ },
19
+ "dependencies": {
20
+ "@inquirer/prompts": "^7.0.0",
21
+ "chalk": "^5.3.0",
22
+ "commander": "^12.1.0",
23
+ "fs-extra": "^11.2.0",
24
+ "yaml": "^2.6.0"
25
+ },
26
+ "engines": {
27
+ "node": ">=18.0.0"
28
+ },
29
+ "keywords": [
30
+ "create",
31
+ "prizmkit",
32
+ "scaffold",
33
+ "ai",
34
+ "autonomous",
35
+ "development",
36
+ "framework",
37
+ "codebuddy",
38
+ "claude-code"
39
+ ],
40
+ "author": "PrizmKit",
41
+ "license": "MIT"
42
+ }
package/src/clean.js ADDED
@@ -0,0 +1,110 @@
1
+ import chalk from 'chalk';
2
+ import path from 'path';
3
+ import fs from 'fs-extra';
4
+
5
+ async function removePath(targetPath, dryRun) {
6
+ if (!await fs.pathExists(targetPath)) {
7
+ return { path: targetPath, removed: false, reason: 'not_found' };
8
+ }
9
+ if (dryRun) {
10
+ return { path: targetPath, removed: false, reason: 'dry_run' };
11
+ }
12
+ await fs.remove(targetPath);
13
+ return { path: targetPath, removed: true };
14
+ }
15
+
16
+ async function cleanGitignore(projectRoot, dryRun) {
17
+ const gitignorePath = path.join(projectRoot, '.gitignore');
18
+ if (!await fs.pathExists(gitignorePath)) {
19
+ return { updated: false, reason: 'not_found' };
20
+ }
21
+
22
+ const content = await fs.readFile(gitignorePath, 'utf8');
23
+ const next = content
24
+ .replace(/\n\n# PrizmKit\n\.prizmkit\/\n?/g, '\n')
25
+ .replace(/\n# PrizmKit\n\.prizmkit\/\n?/g, '\n');
26
+
27
+ if (next === content) {
28
+ return { updated: false, reason: 'no_prizmkit_section' };
29
+ }
30
+
31
+ if (dryRun) {
32
+ return { updated: true, reason: 'dry_run' };
33
+ }
34
+
35
+ await fs.writeFile(gitignorePath, next.trimEnd() + '\n');
36
+ return { updated: true };
37
+ }
38
+
39
+ export async function runClean(directory, options = {}) {
40
+ const projectRoot = path.resolve(directory || '.');
41
+ const dryRun = Boolean(options.dryRun);
42
+ const cleanGlobalTeam = Boolean(options.globalTeam);
43
+
44
+ console.log('');
45
+ console.log(chalk.bold(' PrizmKit 清理模式'));
46
+ console.log(` 目标目录: ${projectRoot}`);
47
+ console.log(` 全局 Team: ${cleanGlobalTeam ? chalk.yellow('将清理') : chalk.gray('跳过')}`);
48
+ console.log(` 预览模式: ${dryRun ? chalk.yellow('是') : chalk.green('否')}`);
49
+ console.log('');
50
+
51
+ const targets = [
52
+ '.prizmkit',
53
+ '.dev-team',
54
+ 'dev-pipeline',
55
+ 'CODEBUDDY.md',
56
+ 'CLAUDE.md',
57
+ path.join('.codebuddy', 'skills'),
58
+ path.join('.codebuddy', 'agents'),
59
+ path.join('.codebuddy', 'settings.json'),
60
+ path.join('.claude', 'commands'),
61
+ path.join('.claude', 'agents'),
62
+ path.join('.claude', 'rules'),
63
+ path.join('.claude', 'settings.json'),
64
+ path.join('.claude', 'team-info.json'),
65
+ ];
66
+
67
+ const results = [];
68
+ for (const rel of targets) {
69
+ const abs = path.join(projectRoot, rel);
70
+ results.push(await removePath(abs, dryRun));
71
+ }
72
+
73
+ const gitignoreResult = await cleanGitignore(projectRoot, dryRun);
74
+
75
+ if (cleanGlobalTeam) {
76
+ const homeDir = process.env.HOME || process.env.USERPROFILE;
77
+ if (homeDir) {
78
+ const globalTeamPath = path.join(homeDir, '.codebuddy', 'teams', 'prizm-dev-team');
79
+ results.push(await removePath(globalTeamPath, dryRun));
80
+ }
81
+ }
82
+
83
+ const removed = results.filter(r => r.removed).length;
84
+ const found = results.filter(r => r.reason !== 'not_found').length;
85
+
86
+ console.log(chalk.bold(' 清理结果:'));
87
+ for (const item of results) {
88
+ if (item.removed) {
89
+ console.log(chalk.green(` ✓ 已删除: ${item.path}`));
90
+ } else if (item.reason === 'dry_run') {
91
+ console.log(chalk.gray(` [dry-run] ${item.path}`));
92
+ }
93
+ }
94
+
95
+ if (gitignoreResult.updated) {
96
+ if (gitignoreResult.reason === 'dry_run') {
97
+ console.log(chalk.gray(' [dry-run] 将更新 .gitignore(移除 PrizmKit 段)'));
98
+ } else {
99
+ console.log(chalk.green(' ✓ 已更新 .gitignore(移除 PrizmKit 段)'));
100
+ }
101
+ }
102
+
103
+ console.log('');
104
+ if (dryRun) {
105
+ console.log(chalk.yellow(` 预览完成:将影响 ${found} 项。`));
106
+ } else {
107
+ console.log(chalk.green(` 清理完成:已删除 ${removed} 项。`));
108
+ }
109
+ console.log('');
110
+ }
@@ -0,0 +1,38 @@
1
+ /**
2
+ * 平台检测工具
3
+ * 检测系统中已安装的 AI CLI 工具。
4
+ */
5
+
6
+ import { execSync } from 'child_process';
7
+
8
+ /**
9
+ * 检查命令是否存在于 PATH 中
10
+ */
11
+ function commandExists(cmd) {
12
+ try {
13
+ execSync(`command -v ${cmd}`, { stdio: 'ignore' });
14
+ return true;
15
+ } catch {
16
+ return false;
17
+ }
18
+ }
19
+
20
+ /**
21
+ * 检测已安装的 AI CLI 工具,并返回推荐的默认平台。
22
+ * @returns {{ cbc: boolean, claude: boolean, suggested: string }}
23
+ */
24
+ export function detectPlatform() {
25
+ const cbc = commandExists('cbc');
26
+ const claude = commandExists('claude');
27
+
28
+ let suggested = 'codebuddy';
29
+ if (cbc && claude) {
30
+ suggested = 'both';
31
+ } else if (claude && !cbc) {
32
+ suggested = 'claude';
33
+ } else if (cbc && !claude) {
34
+ suggested = 'codebuddy';
35
+ }
36
+
37
+ return { cbc, claude, suggested };
38
+ }
@@ -0,0 +1,61 @@
1
+ /**
2
+ * .gitignore 模板
3
+ * 为新创建的 PrizmKit 项目生成合适的 .gitignore 内容。
4
+ */
5
+
6
+ /**
7
+ * 生成 .gitignore 内容
8
+ * @param {Object} options - { pipeline: boolean }
9
+ * @returns {string}
10
+ */
11
+ export function generateGitignore(options = {}) {
12
+ const lines = [
13
+ '# ===========================',
14
+ '# PrizmKit Runtime Artifacts',
15
+ '# ===========================',
16
+ '',
17
+ '# Prizm 文档(按需决定是否提交)',
18
+ '# .prizm-docs/',
19
+ '',
20
+ '# PrizmKit 工作流制品',
21
+ '.prizmkit/',
22
+ '',
23
+ ];
24
+
25
+ if (options.pipeline) {
26
+ lines.push(
27
+ '# Dev-Pipeline 运行时状态',
28
+ 'dev-pipeline/state/',
29
+ 'dev-pipeline/bugfix-state/',
30
+ 'dev-pipeline/logs/',
31
+ 'dev-pipeline/*.pid',
32
+ '',
33
+ );
34
+ }
35
+
36
+ lines.push(
37
+ '# ===========================',
38
+ '# Common',
39
+ '# ===========================',
40
+ '',
41
+ '# Dependencies',
42
+ 'node_modules/',
43
+ '',
44
+ '# OS files',
45
+ '.DS_Store',
46
+ 'Thumbs.db',
47
+ '',
48
+ '# IDE',
49
+ '.idea/',
50
+ '.vscode/',
51
+ '*.swp',
52
+ '*.swo',
53
+ '',
54
+ '# Environment',
55
+ '.env',
56
+ '.env.local',
57
+ '',
58
+ );
59
+
60
+ return lines.join('\n');
61
+ }
package/src/index.js ADDED
@@ -0,0 +1,162 @@
1
+ /**
2
+ * create-prizmkit 交互式主流程
3
+ *
4
+ * 引导用户选择平台、技能套件、团队模式、Pipeline,
5
+ * 然后调用 scaffold.js 执行纯净安装。
6
+ */
7
+
8
+ import { select, confirm } from '@inquirer/prompts';
9
+ import chalk from 'chalk';
10
+ import path from 'path';
11
+ import fs from 'fs-extra';
12
+ import { detectPlatform } from './detect-platform.js';
13
+ import { scaffold } from './scaffold.js';
14
+ import { loadMetadata } from './metadata.js';
15
+
16
+ /**
17
+ * 主入口:交互式脚手架流程
18
+ * @param {string} directory - 目标目录
19
+ * @param {Object} options - CLI 选项
20
+ */
21
+ export async function runScaffold(directory, options) {
22
+ const projectRoot = path.resolve(directory);
23
+
24
+ // 打印欢迎 banner
25
+ console.log('');
26
+ console.log(chalk.bold('╔════════════════════════════════════════════════════════╗'));
27
+ console.log(chalk.bold('║') + chalk.bold.cyan(' PrizmKit') + chalk.bold(' — 自演化自主开发框架 ') + chalk.bold('║'));
28
+ console.log(chalk.bold('║') + ' Create a clean, focused project environment ' + chalk.bold('║'));
29
+ console.log(chalk.bold('╚════════════════════════════════════════════════════════╝'));
30
+ console.log('');
31
+
32
+ // 检测已安装的 CLI 工具
33
+ const detected = detectPlatform();
34
+ const cliStatus = [
35
+ detected.cbc ? chalk.green('cbc ✓') : chalk.gray('cbc ✗'),
36
+ detected.claude ? chalk.green('claude ✓') : chalk.gray('claude ✗'),
37
+ ].join(' ');
38
+ console.log(` 检测到的 CLI 工具: ${cliStatus}`);
39
+ console.log(` 目标目录: ${projectRoot}`);
40
+ console.log('');
41
+
42
+ // 加载 Skill 元数据
43
+ const metadata = await loadMetadata();
44
+ const totalSkills = Object.keys(metadata.skills).length;
45
+
46
+ // === 非交互式模式 ===
47
+ if (options.nonInteractive) {
48
+ if (!options.platform) {
49
+ console.error(chalk.red(' 错误: 非交互式模式需要指定 --platform'));
50
+ process.exit(1);
51
+ }
52
+ const config = {
53
+ platform: options.platform,
54
+ skills: options.skills || 'full',
55
+ team: options.team !== false,
56
+ pipeline: options.pipeline !== false,
57
+ projectRoot,
58
+ dryRun: options.dryRun || false,
59
+ };
60
+ return scaffold(config);
61
+ }
62
+
63
+ // === 交互式模式 ===
64
+ try {
65
+ // 1. 选择平台
66
+ const platform = await select({
67
+ message: '选择目标平台:',
68
+ choices: [
69
+ {
70
+ name: `CodeBuddy CLI (cbc)${detected.cbc ? chalk.green(' ← 已检测到') : ''}`,
71
+ value: 'codebuddy',
72
+ },
73
+ {
74
+ name: `Claude Code CLI (claude)${detected.claude ? chalk.green(' ← 已检测到') : ''}`,
75
+ value: 'claude',
76
+ },
77
+ {
78
+ name: '同时安装两个平台',
79
+ value: 'both',
80
+ },
81
+ ],
82
+ default: detected.suggested,
83
+ });
84
+
85
+ // 2. 选择技能套件
86
+ const skillSuite = await select({
87
+ message: '选择技能套件:',
88
+ choices: [
89
+ {
90
+ name: `Full — 全部 ${totalSkills} 个技能`,
91
+ value: 'full',
92
+ },
93
+ {
94
+ name: `Core — 核心工作流 (${metadata.suites.core.skills.length} 个技能)`,
95
+ value: 'core',
96
+ },
97
+ {
98
+ name: `Minimal — 最小可用 (${metadata.suites.minimal.skills.length} 个技能)`,
99
+ value: 'minimal',
100
+ },
101
+ ],
102
+ default: 'full',
103
+ });
104
+
105
+ // 3. 团队模式
106
+ const team = await confirm({
107
+ message: '启用多 Agent 团队模式 (prizm-dev-team)?',
108
+ default: true,
109
+ });
110
+
111
+ // 4. Pipeline
112
+ const pipeline = await confirm({
113
+ message: '安装自动化流水线 (dev-pipeline)?',
114
+ default: true,
115
+ });
116
+
117
+ // 显示安装摘要
118
+ const platformLabel = platform === 'both' ? 'CodeBuddy + Claude Code'
119
+ : platform === 'codebuddy' ? 'CodeBuddy' : 'Claude Code';
120
+ const suiteLabel = skillSuite === 'full' ? `Full (${totalSkills} 个)`
121
+ : skillSuite === 'core' ? `Core (${metadata.suites.core.skills.length} 个)`
122
+ : `Minimal (${metadata.suites.minimal.skills.length} 个)`;
123
+
124
+ console.log('');
125
+ console.log(chalk.bold(' 安装摘要:'));
126
+ console.log(` 平台: ${chalk.cyan(platformLabel)}`);
127
+ console.log(` 技能: ${chalk.cyan(suiteLabel)}`);
128
+ console.log(` 团队模式: ${team ? chalk.green('启用') : chalk.gray('禁用')}`);
129
+ console.log(` 流水线: ${pipeline ? chalk.green('启用') : chalk.gray('禁用')}`);
130
+ console.log(` 目标目录: ${projectRoot}`);
131
+ console.log('');
132
+
133
+ // 确认
134
+ const proceed = await confirm({
135
+ message: '确认安装?',
136
+ default: true,
137
+ });
138
+
139
+ if (!proceed) {
140
+ console.log(chalk.yellow(' 安装已取消。'));
141
+ return;
142
+ }
143
+
144
+ const config = {
145
+ platform,
146
+ skills: skillSuite,
147
+ team,
148
+ pipeline,
149
+ projectRoot,
150
+ dryRun: options.dryRun || false,
151
+ };
152
+
153
+ await scaffold(config);
154
+
155
+ } catch (err) {
156
+ if (err.message?.includes('User force closed')) {
157
+ console.log(chalk.yellow('\n 安装已取消。'));
158
+ return;
159
+ }
160
+ throw err;
161
+ }
162
+ }
@@ -0,0 +1,134 @@
1
+ /**
2
+ * Skill 元数据加载器
3
+ *
4
+ * 加载策略:
5
+ * 1. 优先从 bundled/ 目录加载(npm 发布后的独立场景)
6
+ * 2. 回退到 core/skills/_metadata.json(本地开发场景)
7
+ */
8
+
9
+ import fs from 'fs-extra';
10
+ import path from 'path';
11
+ import { fileURLToPath } from 'url';
12
+
13
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
14
+
15
+ /**
16
+ * 获取框架根目录路径(用于本地开发模式)
17
+ */
18
+ export function getFrameworkRoot() {
19
+ return path.resolve(__dirname, '../..');
20
+ }
21
+
22
+ /**
23
+ * 获取 bundled 目录路径(用于 npm 发布模式)
24
+ */
25
+ export function getBundledRoot() {
26
+ return path.resolve(__dirname, '../bundled');
27
+ }
28
+
29
+ /**
30
+ * 判断是否处于 bundled 模式(已通过 npm 发布)
31
+ */
32
+ export function isBundledMode() {
33
+ return fs.pathExistsSync(path.join(getBundledRoot(), 'skills', '_metadata.json'));
34
+ }
35
+
36
+ /**
37
+ * 加载 Skill 元数据
38
+ * @returns {Promise<Object>} metadata 对象(包含 skills 和 suites)
39
+ */
40
+ export async function loadMetadata() {
41
+ const bundledPath = path.join(getBundledRoot(), 'skills', '_metadata.json');
42
+ const corePath = path.join(getFrameworkRoot(), 'core', 'skills', '_metadata.json');
43
+
44
+ if (await fs.pathExists(bundledPath)) {
45
+ return fs.readJSON(bundledPath);
46
+ }
47
+
48
+ if (await fs.pathExists(corePath)) {
49
+ return fs.readJSON(corePath);
50
+ }
51
+
52
+ throw new Error(
53
+ '无法找到 Skill 元数据文件。\n' +
54
+ '请确保在 PrizmKit 框架目录中运行,或已执行 `npm run bundle` 打包资源。'
55
+ );
56
+ }
57
+
58
+ /**
59
+ * 获取 core skills 目录路径
60
+ */
61
+ export function getSkillsDir() {
62
+ const bundledDir = path.join(getBundledRoot(), 'skills');
63
+ const coreDir = path.join(getFrameworkRoot(), 'core', 'skills');
64
+
65
+ if (fs.pathExistsSync(bundledDir)) return bundledDir;
66
+ if (fs.pathExistsSync(coreDir)) return coreDir;
67
+
68
+ throw new Error('无法找到 Skills 目录。');
69
+ }
70
+
71
+ /**
72
+ * 获取 core agents 目录路径
73
+ */
74
+ export function getAgentsDir() {
75
+ const bundledDir = path.join(getBundledRoot(), 'agents');
76
+ const coreDir = path.join(getFrameworkRoot(), 'core', 'agents');
77
+
78
+ if (fs.pathExistsSync(bundledDir)) return bundledDir;
79
+ if (fs.pathExistsSync(coreDir)) return coreDir;
80
+
81
+ throw new Error('无法找到 Agents 目录。');
82
+ }
83
+
84
+ /**
85
+ * 获取 core team 目录路径
86
+ */
87
+ export function getTeamDir() {
88
+ const bundledDir = path.join(getBundledRoot(), 'team');
89
+ const coreDir = path.join(getFrameworkRoot(), 'core', 'team');
90
+
91
+ if (fs.pathExistsSync(bundledDir)) return bundledDir;
92
+ if (fs.pathExistsSync(coreDir)) return coreDir;
93
+
94
+ throw new Error('无法找到 Team 目录。');
95
+ }
96
+
97
+ /**
98
+ * 获取 templates 目录路径
99
+ */
100
+ export function getTemplatesDir() {
101
+ const bundledDir = path.join(getBundledRoot(), 'templates');
102
+ const coreDir = path.join(getFrameworkRoot(), 'core', 'templates');
103
+
104
+ if (fs.pathExistsSync(bundledDir)) return bundledDir;
105
+ if (fs.pathExistsSync(coreDir)) return coreDir;
106
+
107
+ throw new Error('无法找到 Templates 目录。');
108
+ }
109
+
110
+ /**
111
+ * 获取 dev-pipeline 目录路径
112
+ */
113
+ export function getPipelineDir() {
114
+ const bundledDir = path.join(getBundledRoot(), 'dev-pipeline');
115
+ const coreDir = path.join(getFrameworkRoot(), 'dev-pipeline');
116
+
117
+ if (fs.pathExistsSync(bundledDir)) return bundledDir;
118
+ if (fs.pathExistsSync(coreDir)) return coreDir;
119
+
120
+ throw new Error('无法找到 dev-pipeline 目录。');
121
+ }
122
+
123
+ /**
124
+ * 获取 adapters 目录路径
125
+ */
126
+ export function getAdaptersDir() {
127
+ const bundledDir = path.join(getBundledRoot(), 'adapters');
128
+ const coreDir = path.join(getFrameworkRoot(), 'adapters');
129
+
130
+ if (fs.pathExistsSync(bundledDir)) return bundledDir;
131
+ if (fs.pathExistsSync(coreDir)) return coreDir;
132
+
133
+ throw new Error('无法找到 Adapters 目录。');
134
+ }
@@ -0,0 +1,628 @@
1
+ /**
2
+ * 纯净安装核心逻辑
3
+ *
4
+ * 与 cli/src/installer.js 的关键区别:
5
+ * - 全部使用 Copy 模式(不使用 symlink),项目完全独立于框架仓库
6
+ * - 通过 adapters 转换 core/ 资源为平台特定格式
7
+ * - 不安装框架开发文件(docs、tests、scripts 等)
8
+ * - 自动生成 .gitignore
9
+ */
10
+
11
+ import chalk from 'chalk';
12
+ import fs from 'fs-extra';
13
+ import path from 'path';
14
+ import { fileURLToPath } from 'url';
15
+ import {
16
+ loadMetadata,
17
+ getSkillsDir,
18
+ getAgentsDir,
19
+ getTeamDir,
20
+ getTemplatesDir,
21
+ getPipelineDir,
22
+ getAdaptersDir,
23
+ getFrameworkRoot,
24
+ } from './metadata.js';
25
+ import { generateGitignore } from './gitignore-template.js';
26
+
27
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
28
+
29
+ // ============================================================
30
+ // Adapter 动态加载
31
+ // ============================================================
32
+
33
+ /**
34
+ * 动态加载平台适配器
35
+ * 支持从 framework/adapters/ 或 bundled/adapters/ 加载
36
+ */
37
+ async function loadAdapter(platform, adapterName) {
38
+ const adaptersDir = getAdaptersDir();
39
+ const adapterPath = path.join(adaptersDir, platform, adapterName);
40
+ return import(adapterPath);
41
+ }
42
+
43
+ async function loadSharedFrontmatter() {
44
+ const adaptersDir = getAdaptersDir();
45
+ return import(path.join(adaptersDir, 'shared', 'frontmatter.js'));
46
+ }
47
+
48
+ // ============================================================
49
+ // Skill 解析
50
+ // ============================================================
51
+
52
+ /**
53
+ * 根据套件名称解析 Skill 列表
54
+ */
55
+ async function resolveSkillList(suite) {
56
+ const metadata = await loadMetadata();
57
+
58
+ const suiteDef = metadata.suites[suite] || metadata.suites.full;
59
+ if (suiteDef.skills === '*') {
60
+ return Object.keys(metadata.skills);
61
+ }
62
+ return suiteDef.skills;
63
+ }
64
+
65
+ // ============================================================
66
+ // 安装函数
67
+ // ============================================================
68
+
69
+ /**
70
+ * 安装 Skills(纯 Copy 模式)
71
+ */
72
+ async function installSkills(platform, skills, projectRoot, dryRun) {
73
+ const skillsDir = getSkillsDir();
74
+ const { parseFrontmatter, buildMarkdown } = await loadSharedFrontmatter();
75
+
76
+ for (const skillName of skills) {
77
+ const corePath = path.join(skillsDir, skillName);
78
+ if (!await fs.pathExists(corePath)) continue;
79
+
80
+ const stat = await fs.stat(corePath);
81
+ if (!stat.isDirectory()) continue;
82
+
83
+ const skillMdPath = path.join(corePath, 'SKILL.md');
84
+ if (!await fs.pathExists(skillMdPath)) continue;
85
+
86
+ if (platform === 'codebuddy') {
87
+ const targetDir = path.join(projectRoot, '.codebuddy', 'skills', skillName);
88
+
89
+ if (dryRun) {
90
+ console.log(chalk.gray(` [dry-run] .codebuddy/skills/${skillName}/`));
91
+ continue;
92
+ }
93
+
94
+ await fs.ensureDir(targetDir);
95
+
96
+ // 读取并写入 SKILL.md(CodeBuddy 格式基本透传)
97
+ const content = await fs.readFile(skillMdPath, 'utf8');
98
+ const { frontmatter, body } = parseFrontmatter(content);
99
+ if (!frontmatter.name) frontmatter.name = skillName;
100
+ await fs.writeFile(path.join(targetDir, 'SKILL.md'), buildMarkdown(frontmatter, body));
101
+
102
+ // 复制 assets/ 和 scripts/
103
+ for (const subdir of ['assets', 'scripts']) {
104
+ const srcSubdir = path.join(corePath, subdir);
105
+ if (await fs.pathExists(srcSubdir)) {
106
+ await fs.copy(srcSubdir, path.join(targetDir, subdir));
107
+ }
108
+ }
109
+
110
+ console.log(chalk.green(` ✓ .codebuddy/skills/${skillName}/`));
111
+
112
+ } else if (platform === 'claude') {
113
+ const content = await fs.readFile(skillMdPath, 'utf8');
114
+ const { frontmatter, body } = parseFrontmatter(content);
115
+
116
+ // Claude Code 格式转换
117
+ const claudeFm = {
118
+ description: String(frontmatter.description || `PrizmKit ${skillName}`)
119
+ .replace(/prizmkit\.(\w+)/g, (_, sub) => `/prizmkit-${sub.replace(/_/g, '-')}`),
120
+ };
121
+
122
+ let convertedBody = body
123
+ .replace(/\$\{SKILL_DIR\}/g, `.claude/commands/${skillName}`)
124
+ .replace(/prizmkit\.(\w+)/g, (_, sub) => `/prizmkit-${sub.replace(/_/g, '-')}`);
125
+
126
+ const hasAssets = await fs.pathExists(path.join(corePath, 'assets'));
127
+ const hasScripts = await fs.pathExists(path.join(corePath, 'scripts'));
128
+
129
+ if (hasAssets || hasScripts) {
130
+ const targetDir = path.join(projectRoot, '.claude', 'commands', skillName);
131
+ if (dryRun) {
132
+ console.log(chalk.gray(` [dry-run] .claude/commands/${skillName}/`));
133
+ continue;
134
+ }
135
+ await fs.ensureDir(targetDir);
136
+ await fs.writeFile(
137
+ path.join(targetDir, `${skillName}.md`),
138
+ buildMarkdown(claudeFm, convertedBody)
139
+ );
140
+ for (const subdir of ['assets', 'scripts']) {
141
+ const srcSubdir = path.join(corePath, subdir);
142
+ if (await fs.pathExists(srcSubdir)) {
143
+ await fs.copy(srcSubdir, path.join(targetDir, subdir));
144
+ }
145
+ }
146
+ console.log(chalk.green(` ✓ .claude/commands/${skillName}/`));
147
+ } else {
148
+ const targetDir = path.join(projectRoot, '.claude', 'commands');
149
+ if (dryRun) {
150
+ console.log(chalk.gray(` [dry-run] .claude/commands/${skillName}.md`));
151
+ continue;
152
+ }
153
+ await fs.ensureDir(targetDir);
154
+ await fs.writeFile(
155
+ path.join(targetDir, `${skillName}.md`),
156
+ buildMarkdown(claudeFm, convertedBody)
157
+ );
158
+ console.log(chalk.green(` ✓ .claude/commands/${skillName}.md`));
159
+ }
160
+ }
161
+ }
162
+ }
163
+
164
+ /**
165
+ * 安装 Agent 定义(纯 Copy 模式)
166
+ */
167
+ async function installAgents(platform, projectRoot, dryRun) {
168
+ const agentsDir = getAgentsDir();
169
+ const { parseFrontmatter, buildMarkdown } = await loadSharedFrontmatter();
170
+ const agentFiles = (await fs.readdir(agentsDir)).filter(f => f.endsWith('.md'));
171
+
172
+ // Claude Code 工具映射
173
+ const TOOL_MAPPING = {
174
+ 'TaskCreate': 'Task',
175
+ 'TaskGet': 'Task',
176
+ 'TaskUpdate': 'Task',
177
+ 'TaskList': 'Task',
178
+ 'SendMessage': 'SendMessage',
179
+ };
180
+
181
+ for (const file of agentFiles) {
182
+ const content = await fs.readFile(path.join(agentsDir, file), 'utf8');
183
+
184
+ if (platform === 'codebuddy') {
185
+ const targetDir = path.join(projectRoot, '.codebuddy', 'agents');
186
+ if (dryRun) {
187
+ console.log(chalk.gray(` [dry-run] .codebuddy/agents/${file}`));
188
+ continue;
189
+ }
190
+ await fs.ensureDir(targetDir);
191
+ await fs.writeFile(path.join(targetDir, file), content);
192
+ console.log(chalk.green(` ✓ .codebuddy/agents/${file}`));
193
+
194
+ } else if (platform === 'claude') {
195
+ const { frontmatter, body } = parseFrontmatter(content);
196
+
197
+ // 工具映射
198
+ let tools = [];
199
+ if (frontmatter.tools) {
200
+ const rawTools = frontmatter.tools.split(',').map(t => t.trim());
201
+ const mapped = rawTools.map(t => TOOL_MAPPING[t] || t).filter(Boolean);
202
+ tools = [...new Set(mapped)];
203
+ }
204
+
205
+ // 转换 body 中的命令引用
206
+ let convertedBody = body
207
+ .replace(/prizmkit\.(\w+)/g, (_, sub) => `/prizmkit-${sub.replace(/_/g, '-')}`);
208
+
209
+ const claudeFm = {
210
+ name: frontmatter.name,
211
+ description: frontmatter.description,
212
+ tools: tools,
213
+ model: 'inherit',
214
+ };
215
+
216
+ const targetDir = path.join(projectRoot, '.claude', 'agents');
217
+ if (dryRun) {
218
+ console.log(chalk.gray(` [dry-run] .claude/agents/${file}`));
219
+ continue;
220
+ }
221
+ await fs.ensureDir(targetDir);
222
+ await fs.writeFile(path.join(targetDir, file), buildMarkdown(claudeFm, convertedBody));
223
+ console.log(chalk.green(` ✓ .claude/agents/${file}`));
224
+ }
225
+ }
226
+ }
227
+
228
+ /**
229
+ * 安装 Team 配置
230
+ */
231
+ async function installTeamConfig(platform, projectRoot, dryRun) {
232
+ const teamDir = getTeamDir();
233
+ const teamDefPath = path.join(teamDir, 'prizm-dev-team.json');
234
+
235
+ if (!await fs.pathExists(teamDefPath)) {
236
+ console.log(chalk.yellow(' ⚠ 未找到团队配置文件,跳过'));
237
+ return;
238
+ }
239
+
240
+ const teamDef = await fs.readJSON(teamDefPath);
241
+
242
+ if (platform === 'codebuddy') {
243
+ const homeDir = process.env.HOME || process.env.USERPROFILE;
244
+ const targetDir = path.join(homeDir, '.codebuddy', 'teams', 'prizm-dev-team');
245
+
246
+ if (dryRun) {
247
+ console.log(chalk.gray(` [dry-run] ~/.codebuddy/teams/prizm-dev-team/`));
248
+ return;
249
+ }
250
+
251
+ await fs.ensureDir(path.join(targetDir, 'inboxes'));
252
+
253
+ const config = {
254
+ name: teamDef.name,
255
+ description: teamDef.description,
256
+ createdAt: Date.now(),
257
+ leadAgentId: `${teamDef.lead}@${teamDef.name}`,
258
+ leadSessionId: '',
259
+ members: teamDef.members.map((m, i) => ({
260
+ agentId: `${m.name}@${teamDef.name}`,
261
+ name: m.name,
262
+ agentType: m.agentDefinition || m.name,
263
+ ...(m.prompt ? { prompt: m.prompt } : {}),
264
+ joinedAt: Date.now() + i,
265
+ tmuxPaneId: '',
266
+ cwd: '',
267
+ subscriptions: m.subscriptions || [],
268
+ })),
269
+ };
270
+
271
+ await fs.writeFile(path.join(targetDir, 'config.json'), JSON.stringify(config, null, 2));
272
+
273
+ for (const member of teamDef.members) {
274
+ const inboxPath = path.join(targetDir, 'inboxes', `${member.name}.json`);
275
+ if (!await fs.pathExists(inboxPath)) {
276
+ await fs.writeFile(inboxPath, JSON.stringify([], null, 2));
277
+ }
278
+ }
279
+
280
+ console.log(chalk.green(` ✓ ~/.codebuddy/teams/prizm-dev-team/`));
281
+
282
+ } else if (platform === 'claude') {
283
+ const claudeDir = path.join(projectRoot, '.claude');
284
+
285
+ if (dryRun) {
286
+ console.log(chalk.gray(` [dry-run] .claude/team-info.json`));
287
+ return;
288
+ }
289
+
290
+ await fs.ensureDir(claudeDir);
291
+
292
+ const teamConfig = {
293
+ name: teamDef.name,
294
+ description: teamDef.description,
295
+ platform: 'claude',
296
+ orchestrationMode: 'subagent',
297
+ agents: teamDef.members
298
+ .filter(m => m.role !== 'lead')
299
+ .map(m => ({
300
+ name: m.name,
301
+ role: m.role,
302
+ agentFile: `.claude/agents/${m.agentDefinition}.md`,
303
+ prompt: m.prompt,
304
+ })),
305
+ upgrade: {
306
+ envVar: 'CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS',
307
+ command: '设置 CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1 可启用原生 Agent Teams 模式',
308
+ },
309
+ };
310
+
311
+ await fs.writeFile(
312
+ path.join(claudeDir, 'team-info.json'),
313
+ JSON.stringify(teamConfig, null, 2)
314
+ );
315
+
316
+ console.log(chalk.green(` ✓ .claude/team-info.json`));
317
+ }
318
+ }
319
+
320
+ /**
321
+ * 安装平台配置文件(settings/hooks/rules)
322
+ */
323
+ async function installSettings(platform, projectRoot, options, dryRun) {
324
+ if (platform === 'codebuddy') {
325
+ const settingsPath = path.join(projectRoot, '.codebuddy', 'settings.json');
326
+
327
+ if (dryRun) {
328
+ console.log(chalk.gray(` [dry-run] .codebuddy/settings.json`));
329
+ return;
330
+ }
331
+
332
+ await fs.ensureDir(path.dirname(settingsPath));
333
+
334
+ const settings = {
335
+ hooks: {
336
+ UserPromptSubmit: [
337
+ {
338
+ matcher: '',
339
+ hooks: [
340
+ {
341
+ type: 'prompt',
342
+ prompt: 'If this message mentions committing, pushing, or finishing a task, remind about updating .prizm-docs/ first. Suggest using the prizmkit-committer skill.',
343
+ },
344
+ ],
345
+ },
346
+ ],
347
+ },
348
+ };
349
+
350
+ await fs.writeFile(settingsPath, JSON.stringify(settings, null, 2));
351
+ console.log(chalk.green(` ✓ .codebuddy/settings.json`));
352
+
353
+ } else if (platform === 'claude') {
354
+ const claudeDir = path.join(projectRoot, '.claude');
355
+
356
+ if (dryRun) {
357
+ console.log(chalk.gray(` [dry-run] .claude/settings.json`));
358
+ console.log(chalk.gray(` [dry-run] .claude/rules/`));
359
+ return;
360
+ }
361
+
362
+ await fs.ensureDir(claudeDir);
363
+
364
+ // Settings
365
+ const permissions = [
366
+ 'Bash(python3 *)',
367
+ 'Bash(git *)',
368
+ 'Bash(jq *)',
369
+ ];
370
+ if (options.pipeline) {
371
+ permissions.push('Bash(./dev-pipeline/*)', 'Bash(python3 dev-pipeline/*)');
372
+ }
373
+
374
+ const settings = {
375
+ permissions: { allow: permissions },
376
+ hooks: {
377
+ SessionStart: [
378
+ {
379
+ matcher: 'startup',
380
+ hooks: [
381
+ {
382
+ type: 'command',
383
+ command: 'test -f .prizm-docs/root.prizm && echo "PrizmKit: .prizm-docs found. Read root.prizm for project context." || echo "PrizmKit: No .prizm-docs found. Run /prizmkit-init to bootstrap."',
384
+ },
385
+ ],
386
+ },
387
+ ],
388
+ },
389
+ };
390
+
391
+ await fs.writeFile(
392
+ path.join(claudeDir, 'settings.json'),
393
+ JSON.stringify(settings, null, 2)
394
+ );
395
+ console.log(chalk.green(` ✓ .claude/settings.json`));
396
+
397
+ // Rules
398
+ const rulesDir = path.join(claudeDir, 'rules');
399
+ await fs.ensureDir(rulesDir);
400
+
401
+ const rules = [
402
+ {
403
+ filename: 'prizm-documentation.md',
404
+ content: `---\ndescription: "PrizmKit documentation rules"\nglobs:\n - "**/*.ts"\n - "**/*.tsx"\n - "**/*.js"\n - "**/*.jsx"\n - "**/*.py"\n - "**/*.go"\n - "**/*.rs"\n - "**/*.java"\n---\n\nWhen modifying source files in this project:\n1. Check if \`.prizm-docs/root.prizm\` exists\n2. If it does, read it before making changes to understand project structure\n3. After making changes, update affected \`.prizm-docs/\` files\n4. Follow the Prizm doc format (KEY: value, not prose)\n5. Size limits: L0 = 4KB, L1 = 3KB, L2 = 5KB\n`,
405
+ },
406
+ {
407
+ filename: 'prizm-commit-workflow.md',
408
+ content: `---\ndescription: "PrizmKit commit workflow rules"\n---\n\nBefore any git commit in this project:\n1. Update \`.prizm-docs/\` for affected modules\n2. Use Conventional Commits format: type(scope): description\n3. Bug fixes use \`fix()\` prefix, not \`feat()\`\n4. Do NOT create REGISTRY.md entries for bug fixes\n5. Use \`/prizmkit-committer\` command for the complete commit workflow\n`,
409
+ },
410
+ {
411
+ filename: 'prizm-progressive-loading.md',
412
+ content: `---\ndescription: "PrizmKit progressive context loading protocol"\n---\n\nThis project uses PrizmKit's progressive loading protocol:\n- ON SESSION START: Read \`.prizm-docs/root.prizm\` (L0 — project map)\n- ON TASK: Read L1 (\`.prizm-docs/<module>.prizm\`) for relevant modules\n- ON FILE EDIT: Read L2 (\`.prizm-docs/<module>/<submodule>.prizm\`) before modifying\n- NEVER load all .prizm docs at once\n- Arrow notation (->) in .prizm files indicates load pointers\n- DECISIONS and CHANGELOG in .prizm files are append-only\n`,
413
+ },
414
+ ];
415
+
416
+ for (const rule of rules) {
417
+ await fs.writeFile(path.join(rulesDir, rule.filename), rule.content);
418
+ }
419
+ console.log(chalk.green(` ✓ .claude/rules/ (${rules.length} 条规则)`));
420
+ }
421
+ }
422
+
423
+ /**
424
+ * 安装项目记忆文件(CODEBUDDY.md / CLAUDE.md)
425
+ */
426
+ async function installProjectMemory(platform, projectRoot, dryRun) {
427
+ const templatesDir = getTemplatesDir();
428
+ const templateName = platform === 'claude' ? 'claude-md-template.md' : 'codebuddy-md-template.md';
429
+ const targetName = platform === 'claude' ? 'CLAUDE.md' : 'CODEBUDDY.md';
430
+ const templatePath = path.join(templatesDir, templateName);
431
+ const targetPath = path.join(projectRoot, targetName);
432
+
433
+ if (await fs.pathExists(targetPath)) {
434
+ console.log(chalk.yellow(` ⚠ ${targetName} 已存在,跳过`));
435
+ return;
436
+ }
437
+
438
+ if (dryRun) {
439
+ console.log(chalk.gray(` [dry-run] ${targetName}`));
440
+ return;
441
+ }
442
+
443
+ if (await fs.pathExists(templatePath)) {
444
+ await fs.copy(templatePath, targetPath);
445
+ console.log(chalk.green(` ✓ ${targetName}`));
446
+ } else {
447
+ console.log(chalk.yellow(` ⚠ 模板文件不存在: ${templateName},跳过`));
448
+ }
449
+ }
450
+
451
+ /**
452
+ * 安装 dev-pipeline(纯 Copy 模式)
453
+ */
454
+ async function installPipeline(projectRoot, dryRun) {
455
+ const pipelineSource = getPipelineDir();
456
+ const pipelineTarget = path.join(projectRoot, 'dev-pipeline');
457
+
458
+ if (dryRun) {
459
+ console.log(chalk.gray(` [dry-run] dev-pipeline/`));
460
+ return;
461
+ }
462
+
463
+ await fs.ensureDir(path.join(pipelineTarget, 'state'));
464
+ await fs.ensureDir(path.join(pipelineTarget, 'bugfix-state'));
465
+
466
+ // 需要安装的 Pipeline 文件和目录
467
+ const items = [
468
+ 'run.sh', 'retry-feature.sh', 'reset-feature.sh', 'launch-daemon.sh',
469
+ 'run-bugfix.sh', 'retry-bug.sh', 'launch-bugfix-daemon.sh',
470
+ 'scripts', 'templates', 'assets', 'README.md', '.gitignore',
471
+ ];
472
+
473
+ let installedCount = 0;
474
+ for (const item of items) {
475
+ const src = path.join(pipelineSource, item);
476
+ const tgt = path.join(pipelineTarget, item);
477
+
478
+ if (!await fs.pathExists(src)) continue;
479
+ if (await fs.pathExists(tgt)) {
480
+ console.log(chalk.yellow(` ⚠ dev-pipeline/${item} 已存在,跳过`));
481
+ continue;
482
+ }
483
+
484
+ await fs.copy(src, tgt);
485
+ installedCount++;
486
+ }
487
+
488
+ console.log(chalk.green(` ✓ dev-pipeline/ (${installedCount} 项)`));
489
+ }
490
+
491
+ /**
492
+ * 生成 .gitignore
493
+ */
494
+ async function installGitignore(projectRoot, options, dryRun) {
495
+ const targetPath = path.join(projectRoot, '.gitignore');
496
+
497
+ if (dryRun) {
498
+ console.log(chalk.gray(` [dry-run] .gitignore`));
499
+ return;
500
+ }
501
+
502
+ if (await fs.pathExists(targetPath)) {
503
+ // 追加 PrizmKit 相关条目
504
+ const existing = await fs.readFile(targetPath, 'utf8');
505
+ if (existing.includes('.prizmkit/')) {
506
+ console.log(chalk.yellow(` ⚠ .gitignore 已包含 PrizmKit 条目,跳过`));
507
+ return;
508
+ }
509
+ const prizmkitSection = '\n\n# PrizmKit\n.prizmkit/\n';
510
+ await fs.appendFile(targetPath, prizmkitSection);
511
+ console.log(chalk.green(` ✓ .gitignore (追加 PrizmKit 条目)`));
512
+ } else {
513
+ const content = generateGitignore({ pipeline: options.pipeline });
514
+ await fs.writeFile(targetPath, content);
515
+ console.log(chalk.green(` ✓ .gitignore`));
516
+ }
517
+ }
518
+
519
+ // ============================================================
520
+ // 主安装函数
521
+ // ============================================================
522
+
523
+ /**
524
+ * 执行纯净安装
525
+ * @param {Object} config
526
+ * @param {string} config.platform - 'codebuddy' | 'claude' | 'both'
527
+ * @param {string} config.skills - 'full' | 'core' | 'minimal'
528
+ * @param {boolean} config.team - 是否启用团队模式
529
+ * @param {boolean} config.pipeline - 是否安装 dev-pipeline
530
+ * @param {string} config.projectRoot - 目标项目根目录
531
+ * @param {boolean} config.dryRun - 是否为预览模式
532
+ */
533
+ export async function scaffold(config) {
534
+ const { platform, skills, team, pipeline, projectRoot, dryRun } = config;
535
+ const platforms = platform === 'both' ? ['codebuddy', 'claude'] : [platform];
536
+
537
+ if (dryRun) {
538
+ console.log(chalk.yellow('\n [DRY RUN] 预览将要安装的内容:\n'));
539
+ }
540
+
541
+ // 解析 Skill 列表
542
+ const skillList = await resolveSkillList(skills);
543
+
544
+ console.log('');
545
+
546
+ for (const p of platforms) {
547
+ const platformLabel = p === 'codebuddy' ? 'CodeBuddy' : 'Claude Code';
548
+ console.log(chalk.bold(` 正在安装 ${platformLabel} 环境...\n`));
549
+
550
+ // 1. Skills
551
+ console.log(chalk.blue(' 技能文件:'));
552
+ await installSkills(p, skillList, projectRoot, dryRun);
553
+
554
+ // 2. Agents
555
+ console.log(chalk.blue('\n Agent 定义:'));
556
+ await installAgents(p, projectRoot, dryRun);
557
+
558
+ // 3. Team
559
+ if (team) {
560
+ console.log(chalk.blue('\n 团队配置:'));
561
+ await installTeamConfig(p, projectRoot, dryRun);
562
+ }
563
+
564
+ // 4. Settings/Hooks/Rules
565
+ console.log(chalk.blue('\n 平台配置:'));
566
+ await installSettings(p, projectRoot, { pipeline }, dryRun);
567
+
568
+ // 5. Project Memory
569
+ console.log(chalk.blue('\n 项目记忆文件:'));
570
+ await installProjectMemory(p, projectRoot, dryRun);
571
+
572
+ console.log('');
573
+ }
574
+
575
+ // 6. Pipeline
576
+ if (pipeline) {
577
+ console.log(chalk.blue(' 自动化流水线:'));
578
+ await installPipeline(projectRoot, dryRun);
579
+ console.log('');
580
+ }
581
+
582
+ // 7. .gitignore
583
+ console.log(chalk.blue(' 项目配置:'));
584
+ await installGitignore(projectRoot, { pipeline }, dryRun);
585
+
586
+ // === 完成 ===
587
+ console.log('');
588
+ console.log(chalk.bold(' ════════════════════════════════════════════════'));
589
+
590
+ if (dryRun) {
591
+ console.log(chalk.yellow(' 预览完成。未修改任何文件。'));
592
+ } else {
593
+ console.log(chalk.green.bold(' ✅ 初始化完成!'));
594
+ }
595
+ console.log(chalk.bold(' ════════════════════════════════════════════════'));
596
+
597
+ if (!dryRun) {
598
+ // 打印下一步提示
599
+ const mainPlatform = platforms.includes('claude') ? 'claude' : 'codebuddy';
600
+ const cli = mainPlatform === 'claude' ? 'claude' : 'cbc';
601
+
602
+ console.log('');
603
+ console.log(' 下一步:');
604
+ console.log(` 1. ${chalk.cyan('cd ' + projectRoot)}`);
605
+ console.log(` 2. ${chalk.cyan(cli)} ${chalk.gray('# 启动 AI 对话')}`);
606
+ console.log(` 3. ${chalk.cyan('说 "prizmkit.init"')} ${chalk.gray('# 初始化项目上下文')}`);
607
+
608
+ if (pipeline) {
609
+ console.log(` 4. ${chalk.cyan('说 "规划一个应用"')} ${chalk.gray('# 生成 feature-list.json')}`);
610
+ console.log(` 5. ${chalk.cyan('说 "启动流水线"')} ${chalk.gray('# 开始自动开发')}`);
611
+ }
612
+
613
+ console.log('');
614
+
615
+ // 安装统计
616
+ const metadata = await loadMetadata();
617
+ const suiteLabel = skills === 'full' ? `全部 ${skillList.length} 个`
618
+ : skills === 'core' ? `核心 ${skillList.length} 个`
619
+ : `最小 ${skillList.length} 个`;
620
+
621
+ console.log(chalk.gray(` 安装统计:`));
622
+ console.log(chalk.gray(` 技能: ${suiteLabel}`));
623
+ console.log(chalk.gray(` 平台: ${platforms.map(p => p === 'codebuddy' ? 'CodeBuddy' : 'Claude Code').join(' + ')}`));
624
+ console.log(chalk.gray(` 团队: ${team ? '已启用' : '未启用'}`));
625
+ console.log(chalk.gray(` 流水线: ${pipeline ? '已安装' : '未安装'}`));
626
+ console.log('');
627
+ }
628
+ }