@playcraft/cli 0.0.40 → 0.0.42
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/README.md +66 -3
- package/dist/atom-plan/validate-atom-plan.js +298 -0
- package/dist/cli-root-help.js +1 -1
- package/dist/commands/3d.js +363 -0
- package/dist/commands/create.js +337 -0
- package/dist/commands/image.js +1337 -43
- package/dist/commands/recommend.js +1 -1
- package/dist/commands/remix.js +213 -0
- package/dist/commands/skills.js +1379 -0
- package/dist/commands/tools-3d.js +473 -0
- package/dist/commands/tools-generation.js +452 -0
- package/dist/commands/tools-project.js +400 -0
- package/dist/commands/tools-research.js +37 -0
- package/dist/commands/tools-research.test.js +216 -0
- package/dist/commands/tools-utils.js +183 -0
- package/dist/commands/tools.js +7 -616
- package/dist/config.js +2 -0
- package/dist/index.js +19 -1
- package/dist/utils/version-checker.js +8 -11
- package/package.json +9 -3
- package/project-template/.claude/agents/designer.md +120 -0
- package/project-template/.claude/agents/developer.md +124 -0
- package/project-template/.claude/agents/pm.md +164 -0
- package/project-template/.claude/agents/refs/README.md +73 -0
- package/project-template/.claude/agents/refs/designer-art-style-catalog.md +533 -0
- package/project-template/.claude/agents/refs/designer-color-audio-recipes.md +153 -0
- package/project-template/.claude/agents/refs/designer-deliverable-spec.md +191 -0
- package/project-template/.claude/agents/refs/designer-dimension-axis.md +27 -0
- package/project-template/.claude/agents/refs/designer-handoff-v2-checklist.md +68 -0
- package/project-template/.claude/agents/refs/designer-master-composite-recipes.md +208 -0
- package/project-template/.claude/agents/refs/designer-style-exploration-flow.md +37 -0
- package/project-template/.claude/agents/refs/developer-dev-handoff.md +109 -0
- package/project-template/.claude/agents/refs/developer-impl-cookbook.md +134 -0
- package/project-template/.claude/agents/refs/developer-phase1-flow.md +136 -0
- package/project-template/.claude/agents/refs/pm-workflow-detail.md +551 -0
- package/project-template/.claude/agents/refs/reviewer-convergence-eval.md +130 -0
- package/project-template/.claude/agents/refs/reviewer-six-dimension-eval.md +6 -0
- package/project-template/.claude/agents/refs/ta-3d-flip-recipe.md +85 -0
- package/project-template/.claude/agents/refs/ta-atlas-deliverable-standard.md +67 -0
- package/project-template/.claude/agents/refs/ta-batch-pipeline-recipes.md +120 -0
- package/project-template/.claude/agents/refs/ta-image-generation-detail.md +356 -0
- package/project-template/.claude/agents/refs/ta-image-ops-reference.md +495 -0
- package/project-template/.claude/agents/refs/ta-pipeline-cookbook.md +1108 -0
- package/project-template/.claude/agents/refs/ta-tools-reference.md +111 -0
- package/project-template/.claude/agents/refs/ta-vfx-preset-catalog.md +365 -0
- package/project-template/.claude/agents/reviewer.md +127 -0
- package/project-template/.claude/agents/technical-artist.md +122 -0
- package/project-template/.claude/hooks/README.md +44 -0
- package/project-template/.claude/hooks/validate-atom-plan.mjs +224 -0
- package/project-template/.claude/hooks/validate-workflow-stop.mjs +343 -0
- package/project-template/.claude/settings.json +36 -0
- package/project-template/.claude/settings.local.json +4 -0
- package/project-template/.claude/skills/playcraft-ad-psychology/SKILL.md +182 -0
- package/project-template/.claude/skills/playcraft-art-style-guide/SKILL.md +123 -0
- package/project-template/.claude/skills/playcraft-asset-state-sheet/SKILL.md +141 -0
- package/project-template/.claude/skills/playcraft-audio-generation/SKILL.md +280 -0
- package/project-template/.claude/skills/playcraft-batch-pipeline/SKILL.md +184 -0
- package/project-template/.claude/skills/playcraft-build-optimizer/SKILL.md +306 -0
- package/project-template/.claude/skills/playcraft-image-generation/SKILL.md +279 -0
- package/project-template/.claude/skills/playcraft-image-generation/reference/build-sprite-sheet.template.mjs +123 -0
- package/project-template/.claude/skills/playcraft-image-generation/reference/compare-style.template.mjs +254 -0
- package/project-template/.claude/skills/playcraft-image-generation/reference/gen-batch-sprite.template.mjs +235 -0
- package/project-template/.claude/skills/playcraft-image-generation/reference/gen-batch.template.mjs +97 -0
- package/project-template/.claude/skills/playcraft-image-generation/reference/gen-edit-variants.template.mjs +118 -0
- package/project-template/.claude/skills/playcraft-image-generation/reference/process-batch.template.mjs +137 -0
- package/project-template/.claude/skills/playcraft-image-generation/reference/prompt-cookbook.md +397 -0
- package/project-template/.claude/skills/playcraft-image-generation/reference/validate-sprite-sheet.template.mjs +296 -0
- package/project-template/.claude/skills/playcraft-image-ops/SKILL.md +122 -0
- package/project-template/.claude/skills/playcraft-masking/SKILL.md +373 -0
- package/project-template/.claude/skills/playcraft-research/SKILL.md +212 -0
- package/project-template/.claude/skills/playcraft-sprite-generation/SKILL.md +423 -0
- package/project-template/.claude/skills/playcraft-storyboard/SKILL.md +167 -0
- package/project-template/.claude/skills/playcraft-style-qa/SKILL.md +270 -0
- package/project-template/.claude/skills/playcraft-text-rendering/SKILL.md +236 -0
- package/project-template/.claude/skills/playcraft-vfx-animation/SKILL.md +130 -0
- package/project-template/.claude/skills/playcraft-workflow/SKILL.md +485 -0
- package/project-template/.claude/skills/playwright-cli/SKILL.md +390 -0
- package/project-template/.claude/skills/playwright-cli/references/element-attributes.md +23 -0
- package/project-template/.claude/skills/playwright-cli/references/playwright-tests.md +39 -0
- package/project-template/.claude/skills/playwright-cli/references/request-mocking.md +87 -0
- package/project-template/.claude/skills/playwright-cli/references/running-code.md +240 -0
- package/project-template/.claude/skills/playwright-cli/references/session-management.md +226 -0
- package/project-template/.claude/skills/playwright-cli/references/spec-driven-testing.md +312 -0
- package/project-template/.claude/skills/playwright-cli/references/storage-state.md +275 -0
- package/project-template/.claude/skills/playwright-cli/references/test-generation.md +138 -0
- package/project-template/.claude/skills/playwright-cli/references/tracing.md +142 -0
- package/project-template/.claude/skills/playwright-cli/references/video-recording.md +157 -0
- package/project-template/.cursor/hooks.json +17 -0
- package/project-template/.cursor/rules/playcraft-orchestrator.mdc +137 -0
- package/project-template/.cursor/rules/playcraft-subagent-boundary.mdc +18 -0
- package/project-template/CLAUDE.md +280 -0
- package/project-template/assets/audio/bgm/.gitkeep +0 -0
- package/project-template/assets/audio/sfx/.gitkeep +0 -0
- package/project-template/assets/bundles/.gitkeep +0 -0
- package/project-template/assets/images/bg/.gitkeep +0 -0
- package/project-template/assets/images/reference/.gitkeep +0 -0
- package/project-template/assets/images/storyboard/.gitkeep +0 -0
- package/project-template/assets/images/tiles/.gitkeep +0 -0
- package/project-template/assets/images/ui/.gitkeep +0 -0
- package/project-template/assets/images/vfx/.gitkeep +0 -0
- package/project-template/assets/models/.gitkeep +0 -0
- package/project-template/docs/team/agent-conduct.md +121 -0
- package/project-template/docs/team/agent-runtime-matrix.md +62 -0
- package/project-template/docs/team/atom-plan-format.md +105 -0
- package/project-template/docs/team/collaboration.md +297 -0
- package/project-template/docs/team/core-model.md +50 -0
- package/project-template/docs/team/platform-capabilities.md +15 -0
- package/project-template/docs/team/workflow-changelog.md +65 -0
- package/project-template/docs/team/workflow-consistency-checklist.md +140 -0
- package/project-template/game/config/.gitkeep +0 -0
- package/project-template/game/gameplay/.gitkeep +0 -0
- package/project-template/game/scenes/.gitkeep +0 -0
- package/project-template/logs/.gitkeep +0 -0
- package/project-template/ta-workspace/logs/.gitkeep +0 -0
- package/project-template/ta-workspace/scripts/.gitkeep +0 -0
- package/project-template/ta-workspace/tmp/.gitkeep +0 -0
- package/project-template/templates/atom-plan.template.json +26 -0
- package/project-template/templates/atom-plan.template.md +108 -0
- package/project-template/templates/design-brief.template.md +195 -0
- package/project-template/templates/design-lens-checklist.reference.md +117 -0
- package/project-template/templates/design-methodology.md +99 -0
- package/project-template/templates/designer-log.template.md +114 -0
- package/project-template/templates/developer-log.template.md +134 -0
- package/project-template/templates/five-axis-framework.md +186 -0
- package/project-template/templates/intent-clarifications.template.md +58 -0
- package/project-template/templates/layout-spec.template.md +146 -0
- package/project-template/templates/project-state.template.md +237 -0
- package/project-template/templates/review-report.template.md +91 -0
- package/project-template/templates/style-exploration.template.md +93 -0
- package/project-template/templates/ta-log.template.md +343 -0
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
import { execSync } from 'child_process';
|
|
2
|
+
import inquirer from 'inquirer';
|
|
3
|
+
import fs from 'fs/promises';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
import { fileURLToPath } from 'url';
|
|
6
|
+
import pc from 'picocolors';
|
|
7
|
+
import ora from 'ora';
|
|
8
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
9
|
+
const __dirname = path.dirname(__filename);
|
|
10
|
+
/** 生成一个本地占位 Project ID(99xxxxxx),后续可通过 playcraft config set 更新为真实 ID */
|
|
11
|
+
function generateMockProjectId() {
|
|
12
|
+
const suffix = Math.floor(Math.random() * 90_000_000) + 10_000_000;
|
|
13
|
+
return `99${suffix}`;
|
|
14
|
+
}
|
|
15
|
+
/** 将项目名转为合法的 npm package name */
|
|
16
|
+
function toPackageName(name) {
|
|
17
|
+
return name
|
|
18
|
+
.toLowerCase()
|
|
19
|
+
.replace(/\s+/g, '-')
|
|
20
|
+
.replace(/[^a-z0-9\-]/g, '')
|
|
21
|
+
.replace(/^-+|-+$/g, '');
|
|
22
|
+
}
|
|
23
|
+
/** 初始化 git 仓库并创建初始提交(供 Claude Code 子 agent worktree 隔离使用) */
|
|
24
|
+
async function initGitRepository(targetDir) {
|
|
25
|
+
const gitDir = path.join(targetDir, '.git');
|
|
26
|
+
try {
|
|
27
|
+
await fs.access(gitDir);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
// 尚未初始化,继续
|
|
32
|
+
}
|
|
33
|
+
const gitEnv = {
|
|
34
|
+
...process.env,
|
|
35
|
+
GIT_AUTHOR_NAME: process.env.GIT_AUTHOR_NAME ?? 'PlayCraft',
|
|
36
|
+
GIT_AUTHOR_EMAIL: process.env.GIT_AUTHOR_EMAIL ?? 'playcraft@local',
|
|
37
|
+
GIT_COMMITTER_NAME: process.env.GIT_COMMITTER_NAME ?? 'PlayCraft',
|
|
38
|
+
GIT_COMMITTER_EMAIL: process.env.GIT_COMMITTER_EMAIL ?? 'playcraft@local',
|
|
39
|
+
};
|
|
40
|
+
execSync('git init -b main', { cwd: targetDir, stdio: 'pipe', env: gitEnv });
|
|
41
|
+
execSync('git add -A', { cwd: targetDir, stdio: 'pipe', env: gitEnv });
|
|
42
|
+
execSync('git commit -m "chore: initial commit from playcraft create"', {
|
|
43
|
+
cwd: targetDir,
|
|
44
|
+
stdio: 'pipe',
|
|
45
|
+
env: gitEnv,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
/** 递归复制目录,跳过 .DS_Store */
|
|
49
|
+
async function copyDir(src, dest) {
|
|
50
|
+
await fs.mkdir(dest, { recursive: true });
|
|
51
|
+
const entries = await fs.readdir(src, { withFileTypes: true });
|
|
52
|
+
for (const entry of entries) {
|
|
53
|
+
if (entry.name === '.DS_Store')
|
|
54
|
+
continue;
|
|
55
|
+
const srcPath = path.join(src, entry.name);
|
|
56
|
+
const destPath = path.join(dest, entry.name);
|
|
57
|
+
if (entry.isDirectory()) {
|
|
58
|
+
await copyDir(srcPath, destPath);
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
await fs.copyFile(srcPath, destPath);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* 在新项目的 .claude/skills/ 下为每个 playcraft-* Platform Tool Skill 创建软连接。
|
|
67
|
+
*
|
|
68
|
+
* 软连接目标优先级:
|
|
69
|
+
* 1. 从 CLI 安装包的相对路径(monorepo dev 模式 / npm pack 后结构)
|
|
70
|
+
* 2. 相对于项目的 node_modules(运行时解析,npm install 后生效)
|
|
71
|
+
*
|
|
72
|
+
* 无论在 monorepo 开发还是全局安装场景,两种路径至少有一个能工作。
|
|
73
|
+
* 相对路径软连接(node_modules 指向)在 npm install 后自动激活。
|
|
74
|
+
*/
|
|
75
|
+
async function setupPlatformSkillLinks(targetDir, cliCommandsDir) {
|
|
76
|
+
const skillsLinkDir = path.join(targetDir, '.claude', 'skills');
|
|
77
|
+
await fs.mkdir(skillsLinkDir, { recursive: true });
|
|
78
|
+
// 以下 Skill 在项目模板中以静态目录形式存在,不创建软连接覆盖它们。
|
|
79
|
+
// 这些是仅供可玩广告工作流使用的 Agent 文档类 Skill,不在 @playcraft/skills 包中发布。
|
|
80
|
+
const staticSkills = new Set([
|
|
81
|
+
'playcraft-workflow', // 工作流入口(静态,无包版本)
|
|
82
|
+
'playcraft-research', // 研究协议(静态,无包版本)
|
|
83
|
+
'playcraft-ad-psychology', // 广告心理设计(静态,无包版本)
|
|
84
|
+
'playcraft-batch-pipeline', // 批处理管线(静态,无包版本)
|
|
85
|
+
'playcraft-build-optimizer', // 构建优化(静态,无包版本)
|
|
86
|
+
'playcraft-image-ops', // 图形操作(静态,无包版本)
|
|
87
|
+
'playcraft-masking', // 蒙版/抠图(静态,无包版本)
|
|
88
|
+
'playcraft-storyboard', // 故事板生成(静态,无包版本)
|
|
89
|
+
'playcraft-style-qa', // 风格质量验证(静态,无包版本)
|
|
90
|
+
'playcraft-vfx-animation', // VFX/动画预设(静态,无包版本)
|
|
91
|
+
// 以下两个在 @playcraft/skills 包中也有同名版本,模板静态版本优先
|
|
92
|
+
'playcraft-image-generation',
|
|
93
|
+
'playcraft-audio-generation',
|
|
94
|
+
]);
|
|
95
|
+
// 尝试从 CLI 安装包的位置定位 @playcraft/skills/skills 目录
|
|
96
|
+
// 打包结构:packages/cli/dist/commands/ → 向上 4 层 → packages/skills/skills(monorepo)
|
|
97
|
+
// dist/commands/ → 向上 3 层 → 与 CLI 包同级的 skills/skills(已不适用)
|
|
98
|
+
const candidateDirs = [
|
|
99
|
+
path.resolve(cliCommandsDir, '..', '..', '..', '..', 'skills', 'skills'),
|
|
100
|
+
path.resolve(cliCommandsDir, '..', '..', '..', 'skills', 'skills'),
|
|
101
|
+
path.resolve(cliCommandsDir, '..', '..', 'skills', 'skills'),
|
|
102
|
+
];
|
|
103
|
+
let resolvedSkillsDir = null;
|
|
104
|
+
for (const dir of candidateDirs) {
|
|
105
|
+
try {
|
|
106
|
+
await fs.access(dir);
|
|
107
|
+
resolvedSkillsDir = dir;
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
// 继续下一个候选
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
// 相对路径(指向 npm install 后的 node_modules)始终创建,作为安装后激活的备用
|
|
115
|
+
const relativeSkillsBase = path.join('..', '..', 'node_modules', '@playcraft', 'skills', 'skills');
|
|
116
|
+
if (resolvedSkillsDir) {
|
|
117
|
+
// 能立即解析 → 创建指向绝对路径的软连接,当下即可使用
|
|
118
|
+
const entries = await fs.readdir(resolvedSkillsDir, { withFileTypes: true });
|
|
119
|
+
for (const entry of entries) {
|
|
120
|
+
if (!entry.name.startsWith('playcraft-') || staticSkills.has(entry.name))
|
|
121
|
+
continue;
|
|
122
|
+
const linkPath = path.join(skillsLinkDir, entry.name);
|
|
123
|
+
const targetPath = path.join(resolvedSkillsDir, entry.name);
|
|
124
|
+
try {
|
|
125
|
+
await fs.symlink(targetPath, linkPath, 'dir');
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
// 已存在则跳过
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
// 无法立即解析 → 创建指向 node_modules 的相对路径软连接,npm install 后自动激活
|
|
134
|
+
// 预置已知的 Platform Tool 名称列表,以便在未安装时也能提前创建软连接骨架
|
|
135
|
+
const knownPlatformTools = [
|
|
136
|
+
'playcraft-asset-management',
|
|
137
|
+
'playcraft-audio-generation',
|
|
138
|
+
'playcraft-build',
|
|
139
|
+
'playcraft-code-editor',
|
|
140
|
+
'playcraft-create-remix',
|
|
141
|
+
'playcraft-deploy',
|
|
142
|
+
'playcraft-image-generation',
|
|
143
|
+
'playcraft-image-processing',
|
|
144
|
+
'playcraft-platform-intro',
|
|
145
|
+
'playcraft-prefab',
|
|
146
|
+
'playcraft-project-management',
|
|
147
|
+
'playcraft-remix-workflow',
|
|
148
|
+
'playcraft-save',
|
|
149
|
+
'playcraft-skill-recommender',
|
|
150
|
+
'playcraft-sprite-generation',
|
|
151
|
+
'playcraft-sprite-remix',
|
|
152
|
+
'playcraft-sprite-sheet',
|
|
153
|
+
'playcraft-3d-flip-sprite',
|
|
154
|
+
'playcraft-prefab',
|
|
155
|
+
];
|
|
156
|
+
for (const name of knownPlatformTools) {
|
|
157
|
+
if (staticSkills.has(name))
|
|
158
|
+
continue;
|
|
159
|
+
const linkPath = path.join(skillsLinkDir, name);
|
|
160
|
+
const linkTarget = path.join(relativeSkillsBase, name);
|
|
161
|
+
try {
|
|
162
|
+
await fs.symlink(linkTarget, linkPath, 'dir');
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
// 已存在则跳过
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
export async function createCommand(projectName, options) {
|
|
171
|
+
console.log(pc.cyan('\n🎮 PlayCraft 可玩广告项目创建\n'));
|
|
172
|
+
const baseDir = options.dir ? path.resolve(options.dir) : process.cwd();
|
|
173
|
+
const answers = await inquirer.prompt([
|
|
174
|
+
{
|
|
175
|
+
type: 'input',
|
|
176
|
+
name: 'projectName',
|
|
177
|
+
message: '项目目录名:',
|
|
178
|
+
default: projectName,
|
|
179
|
+
when: !projectName,
|
|
180
|
+
validate: (input) => {
|
|
181
|
+
if (!input.trim())
|
|
182
|
+
return '项目名称不能为空';
|
|
183
|
+
if (/[/\\:*?"<>|]/.test(input))
|
|
184
|
+
return '项目名称不能包含特殊字符';
|
|
185
|
+
return true;
|
|
186
|
+
},
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
type: 'input',
|
|
190
|
+
name: 'port',
|
|
191
|
+
message: '本地 Agent 端口:',
|
|
192
|
+
default: '2468',
|
|
193
|
+
validate: (input) => {
|
|
194
|
+
const port = parseInt(input, 10);
|
|
195
|
+
if (isNaN(port) || port < 1 || port > 65535) {
|
|
196
|
+
return '端口号必须是 1-65535 之间的数字';
|
|
197
|
+
}
|
|
198
|
+
return true;
|
|
199
|
+
},
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
type: 'input',
|
|
203
|
+
name: 'skillsDir',
|
|
204
|
+
message: 'Skills 目录路径 (可选,供 playcraft skills 命令使用,直接回车跳过):',
|
|
205
|
+
default: '',
|
|
206
|
+
validate: async (input) => {
|
|
207
|
+
if (!input.trim())
|
|
208
|
+
return true;
|
|
209
|
+
try {
|
|
210
|
+
const stat = await fs.stat(path.resolve(input));
|
|
211
|
+
if (!stat.isDirectory())
|
|
212
|
+
return '路径不是一个目录';
|
|
213
|
+
return true;
|
|
214
|
+
}
|
|
215
|
+
catch {
|
|
216
|
+
return '目录不存在,请检查路径';
|
|
217
|
+
}
|
|
218
|
+
},
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
type: 'input',
|
|
222
|
+
name: 'token',
|
|
223
|
+
message: '认证令牌 (可选,可稍后通过 playcraft config set 更新):',
|
|
224
|
+
default: '',
|
|
225
|
+
},
|
|
226
|
+
]);
|
|
227
|
+
const name = projectName || answers.projectName;
|
|
228
|
+
const port = parseInt(answers.port, 10);
|
|
229
|
+
const skillsDir = answers.skillsDir?.trim() || '';
|
|
230
|
+
const token = answers.token;
|
|
231
|
+
const projectId = generateMockProjectId();
|
|
232
|
+
const targetDir = path.join(baseDir, name);
|
|
233
|
+
// 检查目标目录是否已存在
|
|
234
|
+
try {
|
|
235
|
+
const stat = await fs.stat(targetDir);
|
|
236
|
+
if (stat.isDirectory()) {
|
|
237
|
+
const entries = await fs.readdir(targetDir);
|
|
238
|
+
if (entries.length > 0) {
|
|
239
|
+
const { overwrite } = await inquirer.prompt([
|
|
240
|
+
{
|
|
241
|
+
type: 'confirm',
|
|
242
|
+
name: 'overwrite',
|
|
243
|
+
message: pc.yellow(`目录 ${name} 已存在且非空,继续会覆盖同名文件,是否继续?`),
|
|
244
|
+
default: false,
|
|
245
|
+
},
|
|
246
|
+
]);
|
|
247
|
+
if (!overwrite) {
|
|
248
|
+
console.log(pc.yellow('已取消'));
|
|
249
|
+
process.exit(0);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
catch {
|
|
255
|
+
// 目录不存在,正常继续
|
|
256
|
+
}
|
|
257
|
+
const spinner = ora('复制项目模板...').start();
|
|
258
|
+
try {
|
|
259
|
+
// 定位 project-template 目录(打包后路径:dist/commands/ → ../../project-template)
|
|
260
|
+
const templateDir = path.resolve(__dirname, '..', '..', 'project-template');
|
|
261
|
+
try {
|
|
262
|
+
await fs.access(templateDir);
|
|
263
|
+
}
|
|
264
|
+
catch {
|
|
265
|
+
spinner.fail(`找不到项目模板目录: ${templateDir}`);
|
|
266
|
+
process.exit(1);
|
|
267
|
+
}
|
|
268
|
+
// 复制模板
|
|
269
|
+
await copyDir(templateDir, targetDir);
|
|
270
|
+
spinner.succeed('项目模板复制完成');
|
|
271
|
+
// 动态生成 package.json
|
|
272
|
+
const pkgSpinner = ora('生成 package.json...').start();
|
|
273
|
+
const packageJson = {
|
|
274
|
+
name: toPackageName(name),
|
|
275
|
+
version: '0.1.0',
|
|
276
|
+
description: 'PlayCraft Playable Ads project',
|
|
277
|
+
private: true,
|
|
278
|
+
scripts: {
|
|
279
|
+
start: 'playcraft start',
|
|
280
|
+
build: `playcraft build . -p facebook`,
|
|
281
|
+
'build:all': `playcraft build-all .`,
|
|
282
|
+
},
|
|
283
|
+
devDependencies: {
|
|
284
|
+
'@playcraft/cli': 'latest',
|
|
285
|
+
'@playcraft/skills': 'latest',
|
|
286
|
+
},
|
|
287
|
+
};
|
|
288
|
+
await fs.writeFile(path.join(targetDir, 'package.json'), JSON.stringify(packageJson, null, 2) + '\n', 'utf-8');
|
|
289
|
+
pkgSpinner.succeed('package.json 生成完成');
|
|
290
|
+
// 生成 playcraft.config.json
|
|
291
|
+
const configSpinner = ora('生成 playcraft.config.json...').start();
|
|
292
|
+
const config = {
|
|
293
|
+
agent: {
|
|
294
|
+
projectId,
|
|
295
|
+
dir: '.',
|
|
296
|
+
port,
|
|
297
|
+
...(skillsDir && { skillsDir }),
|
|
298
|
+
...(token && { token }),
|
|
299
|
+
},
|
|
300
|
+
};
|
|
301
|
+
await fs.writeFile(path.join(targetDir, 'playcraft.config.json'), JSON.stringify(config, null, 2) + '\n', 'utf-8');
|
|
302
|
+
configSpinner.succeed('playcraft.config.json 生成完成');
|
|
303
|
+
// 为 Platform Tool skills 创建 .claude/skills/ 软连接
|
|
304
|
+
await setupPlatformSkillLinks(targetDir, __dirname);
|
|
305
|
+
const gitSpinner = ora('初始化 Git 仓库...').start();
|
|
306
|
+
try {
|
|
307
|
+
await initGitRepository(targetDir);
|
|
308
|
+
gitSpinner.succeed('Git 仓库初始化完成');
|
|
309
|
+
}
|
|
310
|
+
catch (error) {
|
|
311
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
312
|
+
gitSpinner.warn(`Git 初始化跳过: ${message}`);
|
|
313
|
+
console.log(pc.dim(' 提示: 安装 git 后可在项目目录执行 git init && git add -A && git commit -m "initial"'));
|
|
314
|
+
}
|
|
315
|
+
// 完成提示
|
|
316
|
+
console.log(pc.green(`\n✅ 项目创建完成!\n`));
|
|
317
|
+
console.log(pc.bold(' 目录: ') + pc.cyan(targetDir));
|
|
318
|
+
console.log(pc.bold(' 项目ID: ') + pc.cyan(projectId) + pc.dim(' (临时占位 ID,正式接入 PlayCraft 后请更新)'));
|
|
319
|
+
console.log(pc.bold(' 端口: ') + pc.cyan(String(port)));
|
|
320
|
+
if (skillsDir) {
|
|
321
|
+
console.log(pc.bold(' Skills: ') + pc.cyan(skillsDir));
|
|
322
|
+
}
|
|
323
|
+
console.log();
|
|
324
|
+
console.log(pc.dim('下一步:'));
|
|
325
|
+
console.log(` ${pc.cyan(`cd ${name}`)}`);
|
|
326
|
+
console.log(` ${pc.cyan('playcraft start')}`);
|
|
327
|
+
console.log();
|
|
328
|
+
console.log(pc.dim('正式接入 PlayCraft 后,替换项目 ID:'));
|
|
329
|
+
console.log(` ${pc.cyan(`playcraft config set --key projectId --value <真实项目ID>`)}`);
|
|
330
|
+
console.log();
|
|
331
|
+
}
|
|
332
|
+
catch (error) {
|
|
333
|
+
spinner.fail('项目创建失败');
|
|
334
|
+
console.error(pc.red(`错误: ${error.message}`));
|
|
335
|
+
process.exit(1);
|
|
336
|
+
}
|
|
337
|
+
}
|