@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.
- 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/fix-ids.js +17 -3
- package/dist/commands/fix-ids.test.js +264 -0
- package/dist/commands/image.js +1337 -43
- package/dist/commands/login.js +60 -2
- 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 +454 -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 +164 -0
- package/dist/commands/tools.js +7 -616
- package/dist/config.js +2 -0
- package/dist/index.js +20 -2
- package/dist/utils/agent-api-client.js +52 -16
- package/package.json +9 -3
- package/project-template/.claude/agents/designer.md +116 -0
- package/project-template/.claude/agents/developer.md +133 -0
- package/project-template/.claude/agents/pm.md +164 -0
- package/project-template/.claude/agents/refs/README.md +67 -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 +167 -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 +216 -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 +211 -0
- package/project-template/.claude/agents/refs/pm-workflow-detail.md +545 -0
- package/project-template/.claude/agents/refs/reviewer-six-dimension-eval.md +286 -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 +46 -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 +699 -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 +103 -0
- package/project-template/.claude/agents/technical-artist.md +111 -0
- package/project-template/.claude/hooks/README.md +36 -0
- package/project-template/.claude/hooks/validate-atom-plan.mjs +224 -0
- package/project-template/.claude/hooks/validate-workflow-stop.mjs +258 -0
- package/project-template/.claude/settings.json +32 -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 +229 -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 +148 -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 +396 -0
- package/project-template/.cursor/hooks.json +17 -0
- package/project-template/.cursor/rules/playcraft-orchestrator.mdc +87 -0
- package/project-template/.cursor/rules/playcraft-subagent-boundary.mdc +18 -0
- package/project-template/CLAUDE.md +240 -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 +105 -0
- package/project-template/docs/team/agent-runtime-matrix.md +62 -0
- package/project-template/docs/team/atom-plan-format.md +74 -0
- package/project-template/docs/team/collaboration.md +288 -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 +51 -0
- package/project-template/docs/team/workflow-consistency-checklist.md +128 -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 +76 -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 +98 -0
- package/project-template/templates/developer-log.template.md +140 -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 +132 -0
- package/project-template/templates/project-state.template.md +219 -0
- package/project-template/templates/review-report.template.md +166 -0
- package/project-template/templates/style-exploration.template.md +93 -0
- 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
|
-
###
|
|
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
|
-
###
|
|
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
|
+
}
|
package/dist/cli-root-help.js
CHANGED
|
@@ -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)',
|