@playcraft/cli 0.0.39 → 0.0.41

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. package/README.md +66 -3
  2. package/dist/atom-plan/validate-atom-plan.js +298 -0
  3. package/dist/cli-root-help.js +1 -1
  4. package/dist/commands/3d.js +363 -0
  5. package/dist/commands/create.js +337 -0
  6. package/dist/commands/fix-ids.js +17 -3
  7. package/dist/commands/fix-ids.test.js +264 -0
  8. package/dist/commands/image.js +1337 -43
  9. package/dist/commands/login.js +60 -2
  10. package/dist/commands/recommend.js +1 -1
  11. package/dist/commands/remix.js +213 -0
  12. package/dist/commands/skills.js +1379 -0
  13. package/dist/commands/tools-3d.js +473 -0
  14. package/dist/commands/tools-generation.js +454 -0
  15. package/dist/commands/tools-project.js +400 -0
  16. package/dist/commands/tools-research.js +37 -0
  17. package/dist/commands/tools-research.test.js +216 -0
  18. package/dist/commands/tools-utils.js +164 -0
  19. package/dist/commands/tools.js +7 -616
  20. package/dist/config.js +2 -0
  21. package/dist/index.js +20 -2
  22. package/dist/utils/agent-api-client.js +52 -16
  23. package/package.json +9 -3
  24. package/project-template/.claude/agents/designer.md +116 -0
  25. package/project-template/.claude/agents/developer.md +133 -0
  26. package/project-template/.claude/agents/pm.md +164 -0
  27. package/project-template/.claude/agents/refs/README.md +67 -0
  28. package/project-template/.claude/agents/refs/designer-art-style-catalog.md +533 -0
  29. package/project-template/.claude/agents/refs/designer-color-audio-recipes.md +153 -0
  30. package/project-template/.claude/agents/refs/designer-deliverable-spec.md +167 -0
  31. package/project-template/.claude/agents/refs/designer-dimension-axis.md +27 -0
  32. package/project-template/.claude/agents/refs/designer-handoff-v2-checklist.md +68 -0
  33. package/project-template/.claude/agents/refs/designer-master-composite-recipes.md +216 -0
  34. package/project-template/.claude/agents/refs/designer-style-exploration-flow.md +37 -0
  35. package/project-template/.claude/agents/refs/developer-dev-handoff.md +109 -0
  36. package/project-template/.claude/agents/refs/developer-impl-cookbook.md +134 -0
  37. package/project-template/.claude/agents/refs/developer-phase1-flow.md +211 -0
  38. package/project-template/.claude/agents/refs/pm-workflow-detail.md +545 -0
  39. package/project-template/.claude/agents/refs/reviewer-six-dimension-eval.md +286 -0
  40. package/project-template/.claude/agents/refs/ta-3d-flip-recipe.md +85 -0
  41. package/project-template/.claude/agents/refs/ta-atlas-deliverable-standard.md +46 -0
  42. package/project-template/.claude/agents/refs/ta-batch-pipeline-recipes.md +120 -0
  43. package/project-template/.claude/agents/refs/ta-image-generation-detail.md +356 -0
  44. package/project-template/.claude/agents/refs/ta-image-ops-reference.md +495 -0
  45. package/project-template/.claude/agents/refs/ta-pipeline-cookbook.md +699 -0
  46. package/project-template/.claude/agents/refs/ta-tools-reference.md +111 -0
  47. package/project-template/.claude/agents/refs/ta-vfx-preset-catalog.md +365 -0
  48. package/project-template/.claude/agents/reviewer.md +103 -0
  49. package/project-template/.claude/agents/technical-artist.md +111 -0
  50. package/project-template/.claude/hooks/README.md +36 -0
  51. package/project-template/.claude/hooks/validate-atom-plan.mjs +224 -0
  52. package/project-template/.claude/hooks/validate-workflow-stop.mjs +258 -0
  53. package/project-template/.claude/settings.json +32 -0
  54. package/project-template/.claude/settings.local.json +4 -0
  55. package/project-template/.claude/skills/playcraft-ad-psychology/SKILL.md +182 -0
  56. package/project-template/.claude/skills/playcraft-art-style-guide/SKILL.md +123 -0
  57. package/project-template/.claude/skills/playcraft-asset-state-sheet/SKILL.md +141 -0
  58. package/project-template/.claude/skills/playcraft-audio-generation/SKILL.md +280 -0
  59. package/project-template/.claude/skills/playcraft-batch-pipeline/SKILL.md +184 -0
  60. package/project-template/.claude/skills/playcraft-build-optimizer/SKILL.md +306 -0
  61. package/project-template/.claude/skills/playcraft-image-generation/SKILL.md +229 -0
  62. package/project-template/.claude/skills/playcraft-image-generation/reference/build-sprite-sheet.template.mjs +123 -0
  63. package/project-template/.claude/skills/playcraft-image-generation/reference/compare-style.template.mjs +254 -0
  64. package/project-template/.claude/skills/playcraft-image-generation/reference/gen-batch-sprite.template.mjs +235 -0
  65. package/project-template/.claude/skills/playcraft-image-generation/reference/gen-batch.template.mjs +97 -0
  66. package/project-template/.claude/skills/playcraft-image-generation/reference/gen-edit-variants.template.mjs +118 -0
  67. package/project-template/.claude/skills/playcraft-image-generation/reference/process-batch.template.mjs +137 -0
  68. package/project-template/.claude/skills/playcraft-image-generation/reference/prompt-cookbook.md +397 -0
  69. package/project-template/.claude/skills/playcraft-image-generation/reference/validate-sprite-sheet.template.mjs +296 -0
  70. package/project-template/.claude/skills/playcraft-image-ops/SKILL.md +122 -0
  71. package/project-template/.claude/skills/playcraft-masking/SKILL.md +373 -0
  72. package/project-template/.claude/skills/playcraft-research/SKILL.md +212 -0
  73. package/project-template/.claude/skills/playcraft-sprite-generation/SKILL.md +423 -0
  74. package/project-template/.claude/skills/playcraft-storyboard/SKILL.md +148 -0
  75. package/project-template/.claude/skills/playcraft-style-qa/SKILL.md +270 -0
  76. package/project-template/.claude/skills/playcraft-text-rendering/SKILL.md +236 -0
  77. package/project-template/.claude/skills/playcraft-vfx-animation/SKILL.md +130 -0
  78. package/project-template/.claude/skills/playcraft-workflow/SKILL.md +396 -0
  79. package/project-template/.cursor/hooks.json +17 -0
  80. package/project-template/.cursor/rules/playcraft-orchestrator.mdc +87 -0
  81. package/project-template/.cursor/rules/playcraft-subagent-boundary.mdc +18 -0
  82. package/project-template/CLAUDE.md +240 -0
  83. package/project-template/assets/audio/bgm/.gitkeep +0 -0
  84. package/project-template/assets/audio/sfx/.gitkeep +0 -0
  85. package/project-template/assets/bundles/.gitkeep +0 -0
  86. package/project-template/assets/images/bg/.gitkeep +0 -0
  87. package/project-template/assets/images/reference/.gitkeep +0 -0
  88. package/project-template/assets/images/storyboard/.gitkeep +0 -0
  89. package/project-template/assets/images/tiles/.gitkeep +0 -0
  90. package/project-template/assets/images/ui/.gitkeep +0 -0
  91. package/project-template/assets/images/vfx/.gitkeep +0 -0
  92. package/project-template/assets/models/.gitkeep +0 -0
  93. package/project-template/docs/team/agent-conduct.md +105 -0
  94. package/project-template/docs/team/agent-runtime-matrix.md +62 -0
  95. package/project-template/docs/team/atom-plan-format.md +74 -0
  96. package/project-template/docs/team/collaboration.md +288 -0
  97. package/project-template/docs/team/core-model.md +50 -0
  98. package/project-template/docs/team/platform-capabilities.md +15 -0
  99. package/project-template/docs/team/workflow-changelog.md +51 -0
  100. package/project-template/docs/team/workflow-consistency-checklist.md +128 -0
  101. package/project-template/game/config/.gitkeep +0 -0
  102. package/project-template/game/gameplay/.gitkeep +0 -0
  103. package/project-template/game/scenes/.gitkeep +0 -0
  104. package/project-template/logs/.gitkeep +0 -0
  105. package/project-template/ta-workspace/logs/.gitkeep +0 -0
  106. package/project-template/ta-workspace/scripts/.gitkeep +0 -0
  107. package/project-template/ta-workspace/tmp/.gitkeep +0 -0
  108. package/project-template/templates/atom-plan.template.json +26 -0
  109. package/project-template/templates/atom-plan.template.md +76 -0
  110. package/project-template/templates/design-brief.template.md +195 -0
  111. package/project-template/templates/design-lens-checklist.reference.md +117 -0
  112. package/project-template/templates/design-methodology.md +99 -0
  113. package/project-template/templates/designer-log.template.md +98 -0
  114. package/project-template/templates/developer-log.template.md +140 -0
  115. package/project-template/templates/five-axis-framework.md +186 -0
  116. package/project-template/templates/intent-clarifications.template.md +58 -0
  117. package/project-template/templates/layout-spec.template.md +132 -0
  118. package/project-template/templates/project-state.template.md +219 -0
  119. package/project-template/templates/review-report.template.md +166 -0
  120. package/project-template/templates/style-exploration.template.md +93 -0
  121. package/project-template/templates/ta-log.template.md +205 -0
package/README.md CHANGED
@@ -40,7 +40,15 @@ playcraft <command>
40
40
 
41
41
  ## 🚀 快速开始
42
42
 
43
- ### 1. 初始化配置
43
+ ### 1. 登录账号
44
+
45
+ ```bash
46
+ npx @playcraft/cli login
47
+ ```
48
+
49
+ 交互式登录,PAT(个人访问令牌)自动保存到 `~/.playcraft/config.json`。登录后所有需要后端 API 的命令(`tools`、`recommend`、`fix-ids` 等)均可直接使用,无需额外配置。
50
+
51
+ ### 2. 初始化配置
44
52
 
45
53
  ```bash
46
54
  npx @playcraft/cli init
@@ -48,7 +56,7 @@ npx @playcraft/cli init
48
56
 
49
57
  交互式创建 `playcraft.config.json` 配置文件。
50
58
 
51
- ### 2. 启动本地开发服务
59
+ ### 3. 启动本地开发服务
52
60
 
53
61
  ```bash
54
62
  # 前台运行
@@ -60,7 +68,7 @@ npx @playcraft/cli start --project <project-id> --daemon
60
68
 
61
69
  启动后,云端编辑器会自动检测本地服务,直接读取本地文件进行运行与调试。
62
70
 
63
- ### 3. 打包 Playable Ads
71
+ ### 4. 打包 Playable Ads
64
72
 
65
73
  ```bash
66
74
  # 交互式打包(推荐)
@@ -70,12 +78,50 @@ npx @playcraft/cli build
70
78
  npx @playcraft/cli build --platform facebook --format html --output ./dist
71
79
  ```
72
80
 
81
+ ## 🔐 鉴权说明
82
+
83
+ PlayCraft CLI 支持三种鉴权方式,按优先级依次生效:
84
+
85
+ | 优先级 | 方式 | 适用场景 |
86
+ |--------|------|---------|
87
+ | 1 | **`playcraft login`**(推荐) | 交互式登录后自动保存 PAT 到 `~/.playcraft/config.json`,所有命令开箱即用 |
88
+ | 2 | **`.playcraft.json`** | 项目目录下的配置文件,适合 CI/沙箱环境(支持沙箱 JWT) |
89
+ | 3 | **环境变量** | `PLAYCRAFT_TOKEN`(PAT)或 `PLAYCRAFT_SANDBOX_TOKEN`(沙箱 JWT) |
90
+
91
+ ### 快速登录
92
+
93
+ ```bash
94
+ # 交互式登录(推荐)
95
+ playcraft login
96
+
97
+ # 指定私有部署地址登录
98
+ playcraft login --url https://your-playcraft.example.com
99
+ ```
100
+
101
+ 登录成功后,`tools`、`recommend`、`fix-ids` 等所有需要后端 API 的命令均无需额外配置,直接使用。
102
+
103
+ ### 沙箱环境(CI / Agent)
104
+
105
+ 在沙箱或 CI 中可通过 `.playcraft.json` 或环境变量配置:
106
+
107
+ ```bash
108
+ # 方式 1:环境变量
109
+ export PLAYCRAFT_SANDBOX_TOKEN=<sandbox-jwt> # 沙箱 JWT
110
+ export PLAYCRAFT_API_URL=https://playcraft.aix.intlgame.com
111
+
112
+ # 方式 2:.playcraft.json
113
+ echo '{"token":"<sandbox-jwt>","apiUrl":"https://..."}' > .playcraft.json
114
+ ```
115
+
116
+ ---
117
+
73
118
  ## 📖 命令说明
74
119
 
75
120
  ### 开发相关
76
121
 
77
122
  | 命令 | 说明 | 示例 |
78
123
  |------|------|------|
124
+ | `login` | 登录账号,保存 PAT 到全局配置 | `playcraft login` |
79
125
  | `init` | 交互式创建配置文件 | `playcraft init` |
80
126
  | `start` | 启动本地开发服务 | `playcraft start --project <id>` |
81
127
  | `stop` | 停止运行中的服务 | `playcraft stop --project <id>` |
@@ -84,6 +130,23 @@ npx @playcraft/cli build --platform facebook --format html --output ./dist
84
130
  | `config` | 读取/修改配置项 | `playcraft config get --key projectId` |
85
131
  | `inspect` | 检测项目类型与结构 | `playcraft inspect .` |
86
132
 
133
+ ### 平台 API(需登录或配置鉴权)
134
+
135
+ > 运行 `playcraft login` 后所有子命令均可直接使用。
136
+
137
+ | 命令 | 说明 | 示例 |
138
+ |------|------|------|
139
+ | `tools generate-image` | AI 生成图片 | `playcraft tools generate-image --prompt "pixel art sky"` |
140
+ | `tools generate-sfx` | AI 生成音效 | `playcraft tools generate-sfx --prompt "UI click"` |
141
+ | `tools generate-bgm` | AI 生成 BGM | `playcraft tools generate-bgm --prompt "casual game music"` |
142
+ | `tools list-image-models` | 列出可用生图模型 | `playcraft tools list-image-models` |
143
+ | `tools list-remixes` | 查看 Remix 项目列表 | `playcraft tools list-remixes` |
144
+ | `tools save-to-git` | 同步沙箱内容到 Git | `playcraft tools save-to-git --project-id <id> --message "feat: ..."` |
145
+ | `tools build-project` | 在沙箱中执行构建 | `playcraft tools build-project --project-id <id>` |
146
+ | `recommend similar` | 相似 Creative 推荐 | `playcraft recommend similar --channel Applovin` |
147
+ | `recommend improvements` | Tag 改进建议 | `playcraft recommend improvements --channel Facebook` |
148
+ | `recommend random` | 随机 Tag 组合推荐 | `playcraft recommend random --channel Google` |
149
+
87
150
  ### 构建相关
88
151
 
89
152
  | 命令 | 说明 | 示例 |
@@ -0,0 +1,298 @@
1
+ import * as fs from 'node:fs';
2
+ import * as path from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+ import { loadConfig } from '../config.js';
5
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
6
+ const SKILL_REF_EMPTY = new Set(['', '—', '-', '–', 'null', 'none', 'n/a']);
7
+ const PROTECTED_SKILL_PREFIX = 'playcraft-';
8
+ const SKILL_REF_SUFFIX_RE = /\.(aigameplay|aiconfig)$/;
9
+ function normalizeSkillRef(ref) {
10
+ if (ref === null || ref === undefined)
11
+ return null;
12
+ const s = String(ref).trim();
13
+ if (SKILL_REF_EMPTY.has(s) || s.startsWith('{{'))
14
+ return null;
15
+ return s;
16
+ }
17
+ /** 与 skills match 命令相同的 skills 目录发现逻辑(精简版) */
18
+ export function resolveSkillsDirs(customDir, configDir) {
19
+ if (customDir)
20
+ return [path.resolve(customDir)];
21
+ const dirs = [];
22
+ if (configDir)
23
+ dirs.push(path.resolve(configDir));
24
+ const envPaths = process.env.AGENT_SKILLS_PATHS?.split(',').map((p) => p.trim()).filter(Boolean) ?? [];
25
+ dirs.push(...envPaths);
26
+ const cwdModulesPath = path.join(process.cwd(), 'node_modules', '@playcraft', 'skills', 'skills');
27
+ if (fs.existsSync(cwdModulesPath))
28
+ dirs.push(cwdModulesPath);
29
+ const monoSkillsPath = path.resolve(__dirname, '..', '..', '..', '..', 'skills', 'skills');
30
+ if (fs.existsSync(monoSkillsPath))
31
+ dirs.push(monoSkillsPath);
32
+ return [...new Set(dirs)];
33
+ }
34
+ /** 从项目根解析 skills 目录(与 playcraft skills 命令同源) */
35
+ export async function resolveProjectSkillsDirs(projectDir, skillsDirOverride) {
36
+ const root = path.resolve(projectDir);
37
+ const config = await loadConfig({ dir: root }).catch(() => ({ skillsDir: undefined }));
38
+ return resolveSkillsDirs(skillsDirOverride, config.skillsDir);
39
+ }
40
+ export function loadSkillAtomIdSet(skillsDirs) {
41
+ const ids = new Set();
42
+ for (const dir of skillsDirs) {
43
+ if (!fs.existsSync(dir) || !fs.statSync(dir).isDirectory())
44
+ continue;
45
+ for (const name of fs.readdirSync(dir, { withFileTypes: true })) {
46
+ if (!name.isDirectory())
47
+ continue;
48
+ const manifestPath = path.join(dir, name.name, 'manifest.json');
49
+ if (!fs.existsSync(manifestPath))
50
+ continue;
51
+ try {
52
+ const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf-8'));
53
+ if (manifest.atomId)
54
+ ids.add(manifest.atomId);
55
+ }
56
+ catch {
57
+ // skip invalid manifest
58
+ }
59
+ }
60
+ }
61
+ return ids;
62
+ }
63
+ function snapshotItemAtomIds(plan) {
64
+ const ids = new Set();
65
+ for (const item of plan.skillsMatch?.items ?? []) {
66
+ if (item.atomId)
67
+ ids.add(item.atomId);
68
+ }
69
+ return ids;
70
+ }
71
+ export function parseAtomPlanJson(content) {
72
+ const parsed = JSON.parse(content);
73
+ if (!parsed || typeof parsed !== 'object') {
74
+ throw new Error('atom-plan JSON 必须是对象');
75
+ }
76
+ if (!Array.isArray(parsed.atoms)) {
77
+ throw new Error('atom-plan JSON 缺少 atoms 数组');
78
+ }
79
+ return parsed;
80
+ }
81
+ /** 从 atom-plan.md Atom List 表解析 skillRef(兼容旧项目) */
82
+ export function parseSkillRefsFromAtomPlanMarkdown(content) {
83
+ const lines = content.split('\n');
84
+ let inAtomList = false;
85
+ let skillRefIdx = -1;
86
+ const refs = new Set();
87
+ for (const line of lines) {
88
+ if (/^##\s+Atom List\b/i.test(line)) {
89
+ inAtomList = true;
90
+ skillRefIdx = -1;
91
+ continue;
92
+ }
93
+ if (inAtomList && /^##\s+/.test(line))
94
+ break;
95
+ if (!inAtomList || !line.trim().startsWith('|'))
96
+ continue;
97
+ const cols = line
98
+ .split('|')
99
+ .slice(1, -1)
100
+ .map((c) => c.trim());
101
+ if (cols.length === 0)
102
+ continue;
103
+ if (skillRefIdx < 0) {
104
+ if (cols.includes('skillRef'))
105
+ skillRefIdx = cols.indexOf('skillRef');
106
+ continue;
107
+ }
108
+ if (cols.every((c) => /^:?-+:?$/.test(c) || c === ''))
109
+ continue;
110
+ if (cols.includes('atomId') && cols.includes('assignTo'))
111
+ continue;
112
+ const ref = normalizeSkillRef(cols[skillRefIdx]);
113
+ if (ref)
114
+ refs.add(ref);
115
+ }
116
+ return [...refs];
117
+ }
118
+ function extractSnapshotAtomIdsFromMarkdown(content) {
119
+ const ids = new Set();
120
+ const jsonBlock = content.match(/```json\s*([\s\S]*?)```/);
121
+ if (!jsonBlock)
122
+ return ids;
123
+ try {
124
+ const snap = JSON.parse(jsonBlock[1]);
125
+ for (const item of snap?.items ?? []) {
126
+ if (item.atomId)
127
+ ids.add(item.atomId);
128
+ }
129
+ }
130
+ catch {
131
+ // ignore parse errors — validated separately
132
+ }
133
+ return ids;
134
+ }
135
+ export function validateSkillRefs(options) {
136
+ const errors = [];
137
+ const warnings = [];
138
+ const { skillRefs, skillIndex, snapshotItemIds, atomBySkillRef } = options;
139
+ for (const ref of skillRefs) {
140
+ if (!SKILL_REF_SUFFIX_RE.test(ref)) {
141
+ errors.push(`skillRef 格式非法(须为 *.aigameplay 或 *.aiconfig):${ref}`);
142
+ continue;
143
+ }
144
+ if (ref.startsWith(PROTECTED_SKILL_PREFIX)) {
145
+ errors.push(`skillRef 不得使用平台 skill(playcraft-*):${ref}`);
146
+ continue;
147
+ }
148
+ if (!skillIndex.has(ref)) {
149
+ errors.push(`skillRef 不在 skills 库中:${ref}(运行 playcraft skills list --json 核对)`);
150
+ continue;
151
+ }
152
+ if (snapshotItemIds && snapshotItemIds.size > 0 && !snapshotItemIds.has(ref)) {
153
+ const owner = atomBySkillRef?.get(ref);
154
+ const who = owner ? `(production atomId: ${owner.atomId})` : '';
155
+ errors.push(`skillRef 未出现在 skillsMatch.items 中:${ref}${who}`);
156
+ }
157
+ }
158
+ if (skillRefs.length === 0) {
159
+ warnings.push('未找到任何非空 skillRef');
160
+ }
161
+ return { errors, warnings };
162
+ }
163
+ /** 从项目 docs/ 读取 atom-plan(JSON 优先,否则 Markdown)中的 skillRef */
164
+ export function parseSkillRefsFromAtomPlanProject(projectDir) {
165
+ const root = path.resolve(projectDir);
166
+ const jsonPath = path.join(root, 'docs', 'atom-plan.json');
167
+ if (fs.existsSync(jsonPath)) {
168
+ const plan = parseAtomPlanJson(fs.readFileSync(jsonPath, 'utf-8'));
169
+ const refs = [];
170
+ for (const atom of plan.atoms) {
171
+ const ref = normalizeSkillRef(atom.skillRef);
172
+ if (ref)
173
+ refs.push(ref);
174
+ }
175
+ return refs;
176
+ }
177
+ const mdPath = path.join(root, 'docs', 'atom-plan.md');
178
+ if (fs.existsSync(mdPath)) {
179
+ return parseSkillRefsFromAtomPlanMarkdown(fs.readFileSync(mdPath, 'utf-8'));
180
+ }
181
+ return [];
182
+ }
183
+ export async function validateAtomPlanProject(options) {
184
+ const projectDir = path.resolve(options.projectDir);
185
+ const jsonPath = path.join(projectDir, 'docs', 'atom-plan.json');
186
+ const mdPath = path.join(projectDir, 'docs', 'atom-plan.md');
187
+ const dirs = await resolveProjectSkillsDirs(projectDir, options.skillsDir);
188
+ const skillIndex = loadSkillAtomIdSet(dirs);
189
+ if (dirs.length === 0) {
190
+ return {
191
+ ok: false,
192
+ errors: ['未找到 skills 目录。请安装 @playcraft/skills 或设置 AGENT_SKILLS_PATHS / playcraft.config.json skillsDir'],
193
+ warnings: [],
194
+ skillRefsChecked: [],
195
+ source: 'json',
196
+ };
197
+ }
198
+ if (fs.existsSync(jsonPath)) {
199
+ let plan;
200
+ try {
201
+ plan = parseAtomPlanJson(fs.readFileSync(jsonPath, 'utf-8'));
202
+ }
203
+ catch (e) {
204
+ return {
205
+ ok: false,
206
+ errors: [`docs/atom-plan.json 解析失败:${e.message}`],
207
+ warnings: [],
208
+ skillRefsChecked: [],
209
+ source: 'json',
210
+ };
211
+ }
212
+ if (plan.schemaVersion !== 1) {
213
+ return {
214
+ ok: false,
215
+ errors: [`docs/atom-plan.json schemaVersion 须为 1,当前:${plan.schemaVersion}`],
216
+ warnings: [],
217
+ skillRefsChecked: [],
218
+ source: 'json',
219
+ };
220
+ }
221
+ const skillRefs = [];
222
+ const atomBySkillRef = new Map();
223
+ for (const atom of plan.atoms) {
224
+ const ref = normalizeSkillRef(atom.skillRef);
225
+ if (!ref)
226
+ continue;
227
+ skillRefs.push(ref);
228
+ atomBySkillRef.set(ref, atom);
229
+ if (atom.type !== 'GameplayAtom' && atom.type !== 'ConfigAtom' && ref) {
230
+ return {
231
+ ok: false,
232
+ errors: [`${atom.atomId}:仅 GameplayAtom/ConfigAtom 允许 skillRef,当前 type=${atom.type}`],
233
+ warnings: [],
234
+ skillRefsChecked: skillRefs,
235
+ source: 'json',
236
+ };
237
+ }
238
+ }
239
+ const snapshotIds = snapshotItemAtomIds(plan);
240
+ const { errors, warnings } = validateSkillRefs({
241
+ skillRefs,
242
+ skillIndex,
243
+ snapshotItemIds: snapshotIds,
244
+ atomBySkillRef,
245
+ });
246
+ if (!plan.skillsMatch?.items?.length) {
247
+ warnings.push('skillsMatch.items 为空 — Gate #1 应先运行 playcraft skills match --json 并写入 skillsMatch');
248
+ }
249
+ const configuredRoot = dirs[0];
250
+ if (plan.packageSkillsRoot?.trim()) {
251
+ const declared = path.resolve(projectDir, plan.packageSkillsRoot.trim());
252
+ if (configuredRoot && path.resolve(configuredRoot) !== declared) {
253
+ warnings.push(`packageSkillsRoot 与 playcraft.config.json 解析结果不一致;请删除 atom-plan 中的 packageSkillsRoot,或改为 skillsDir:${configuredRoot}`);
254
+ }
255
+ }
256
+ return {
257
+ ok: errors.length === 0,
258
+ errors,
259
+ warnings,
260
+ skillRefsChecked: skillRefs,
261
+ source: 'json',
262
+ };
263
+ }
264
+ if (options.preferJson !== false) {
265
+ return {
266
+ ok: false,
267
+ errors: ['缺少 docs/atom-plan.json(Gate #1 请使用 templates/atom-plan.template.json)'],
268
+ warnings: fs.existsSync(mdPath) ? ['检测到 docs/atom-plan.md,请迁移为 JSON 主格式'] : [],
269
+ skillRefsChecked: [],
270
+ source: 'json',
271
+ };
272
+ }
273
+ if (!fs.existsSync(mdPath)) {
274
+ return {
275
+ ok: false,
276
+ errors: ['缺少 docs/atom-plan.json 与 docs/atom-plan.md'],
277
+ warnings: [],
278
+ skillRefsChecked: [],
279
+ source: 'markdown',
280
+ };
281
+ }
282
+ const md = fs.readFileSync(mdPath, 'utf-8');
283
+ const skillRefs = parseSkillRefsFromAtomPlanMarkdown(md);
284
+ const snapshotIds = extractSnapshotAtomIdsFromMarkdown(md);
285
+ const { errors, warnings } = validateSkillRefs({
286
+ skillRefs,
287
+ skillIndex,
288
+ snapshotItemIds: snapshotIds,
289
+ });
290
+ warnings.push('使用 Markdown atom-plan(已弃用为主格式,请改用 docs/atom-plan.json)');
291
+ return {
292
+ ok: errors.length === 0,
293
+ errors,
294
+ warnings,
295
+ skillRefsChecked: skillRefs,
296
+ source: 'markdown',
297
+ };
298
+ }
@@ -3,7 +3,7 @@ export function getCliTopicsHelpText() {
3
3
  return [
4
4
  '命令分区(完整说明见仓库 docs/cli/capabilities.md):',
5
5
  '',
6
- ' 本地开发 init, start, stop, status, logs, config, sync, fix-ids',
6
+ ' 本地开发 create, init, start, stop, status, logs, config, sync, fix-ids',
7
7
  ' 素材 tools generate-* | image <子命令> | audio <子命令> | prefab <子命令>',
8
8
  ' 平台 build, build-base, build-playable, build-all, analyze, inspect, upgrade',
9
9
  ' tools 除 generate-* 外(Git / 沙箱构建 / 发布 / 列表 / Prefab 等,需后端 API)',