@playcraft/cli 0.0.40 → 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/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 +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 +19 -1
- 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
|
@@ -52,7 +52,7 @@ function parseIntOpt(label, raw) {
|
|
|
52
52
|
export function registerRecommendCommands(program) {
|
|
53
53
|
const rec = program
|
|
54
54
|
.command('recommend')
|
|
55
|
-
.description('Tag → Atom
|
|
55
|
+
.description('Tag → Atom 推荐(后端 /api/agent/tools/recommend-*);运行 playcraft login 后即可直接使用,也支持 .playcraft.json 或 PLAYCRAFT_SANDBOX_TOKEN 环境变量');
|
|
56
56
|
// ─── meta / data ───────────────────────────────────────────
|
|
57
57
|
rec
|
|
58
58
|
.command('meta')
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import inquirer from 'inquirer';
|
|
2
|
+
import { exec } from 'child_process';
|
|
3
|
+
import { loadGlobalConfig } from '../config.js';
|
|
4
|
+
import { DEFAULT_PLAYCRAFT_CLOUD_BASE } from './login.js';
|
|
5
|
+
function resolveBackendBaseUrl(options) {
|
|
6
|
+
const globalConfig = loadGlobalConfig();
|
|
7
|
+
const raw = options.url ||
|
|
8
|
+
process.env.PLAYCRAFT_URL ||
|
|
9
|
+
globalConfig.url ||
|
|
10
|
+
globalConfig.backendUrl ||
|
|
11
|
+
DEFAULT_PLAYCRAFT_CLOUD_BASE;
|
|
12
|
+
return raw.replace(/\/+$/, '');
|
|
13
|
+
}
|
|
14
|
+
function resolveJwtToken(options) {
|
|
15
|
+
const globalConfig = loadGlobalConfig();
|
|
16
|
+
const token = options.token || process.env.PLAYCRAFT_TOKEN || globalConfig.token;
|
|
17
|
+
if (!token) {
|
|
18
|
+
throw new Error('缺少 JWT token。请先执行 `playcraft login`,或传入 --token / PLAYCRAFT_TOKEN。');
|
|
19
|
+
}
|
|
20
|
+
return token;
|
|
21
|
+
}
|
|
22
|
+
function openBrowser(url) {
|
|
23
|
+
const cmd = process.platform === 'darwin' ? 'open' : process.platform === 'win32' ? 'start' : 'xdg-open';
|
|
24
|
+
exec(`${cmd} "${url}"`, () => undefined);
|
|
25
|
+
}
|
|
26
|
+
function formatIdleSec(idleSec) {
|
|
27
|
+
if (!idleSec || idleSec <= 0)
|
|
28
|
+
return '0s';
|
|
29
|
+
if (idleSec < 60)
|
|
30
|
+
return `${idleSec}s`;
|
|
31
|
+
const m = Math.floor(idleSec / 60);
|
|
32
|
+
const s = idleSec % 60;
|
|
33
|
+
return `${m}m${s}s`;
|
|
34
|
+
}
|
|
35
|
+
function getTtydUrl(agentBaseUrl) {
|
|
36
|
+
if (!agentBaseUrl || !agentBaseUrl.includes('/agent/'))
|
|
37
|
+
return undefined;
|
|
38
|
+
return agentBaseUrl.replace('/agent/', '/ttyd/');
|
|
39
|
+
}
|
|
40
|
+
async function remixCreate(options) {
|
|
41
|
+
const baseUrl = resolveBackendBaseUrl(options);
|
|
42
|
+
const token = resolveJwtToken(options);
|
|
43
|
+
const endpoint = `${baseUrl}/api/projects/${encodeURIComponent(options.projectId)}/branches`;
|
|
44
|
+
const res = await fetch(endpoint, {
|
|
45
|
+
method: 'POST',
|
|
46
|
+
headers: {
|
|
47
|
+
Authorization: `Bearer ${token}`,
|
|
48
|
+
'Content-Type': 'application/json',
|
|
49
|
+
},
|
|
50
|
+
body: JSON.stringify({
|
|
51
|
+
name: options.name,
|
|
52
|
+
ref: options.sourceBranch,
|
|
53
|
+
}),
|
|
54
|
+
});
|
|
55
|
+
if (!res.ok) {
|
|
56
|
+
const text = await res.text();
|
|
57
|
+
throw new Error(`创建 remix 失败: ${res.status} ${text}`);
|
|
58
|
+
}
|
|
59
|
+
const data = (await res.json());
|
|
60
|
+
console.log(`Remix 创建成功: ${data.displayName || data.name}`);
|
|
61
|
+
console.log(`分支: ${data.name}`);
|
|
62
|
+
}
|
|
63
|
+
async function remixEnsureStream(options) {
|
|
64
|
+
const baseUrl = resolveBackendBaseUrl(options);
|
|
65
|
+
const token = resolveJwtToken(options);
|
|
66
|
+
const endpoint = new URL(`${baseUrl}/api/projects/${encodeURIComponent(options.projectId)}/sandbox/ensure-stream`);
|
|
67
|
+
endpoint.searchParams.set('branch', options.branch);
|
|
68
|
+
if (options.evictConfirmed)
|
|
69
|
+
endpoint.searchParams.set('evictConfirmed', 'true');
|
|
70
|
+
const res = await fetch(endpoint.toString(), {
|
|
71
|
+
method: 'GET',
|
|
72
|
+
headers: {
|
|
73
|
+
Authorization: `Bearer ${token}`,
|
|
74
|
+
Accept: 'text/event-stream',
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
if (!res.ok || !res.body) {
|
|
78
|
+
const text = await res.text();
|
|
79
|
+
throw new Error(`ensure-stream 失败: ${res.status} ${text}`);
|
|
80
|
+
}
|
|
81
|
+
const reader = res.body.getReader();
|
|
82
|
+
const decoder = new TextDecoder();
|
|
83
|
+
let buffer = '';
|
|
84
|
+
const parseBlock = (block) => {
|
|
85
|
+
const payload = block
|
|
86
|
+
.split('\n')
|
|
87
|
+
.filter((line) => line.startsWith('data:'))
|
|
88
|
+
.map((line) => line.slice(5).trim())
|
|
89
|
+
.join('\n');
|
|
90
|
+
if (!payload)
|
|
91
|
+
return null;
|
|
92
|
+
try {
|
|
93
|
+
return JSON.parse(payload);
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
while (true) {
|
|
100
|
+
const { done, value } = await reader.read();
|
|
101
|
+
if (done)
|
|
102
|
+
break;
|
|
103
|
+
buffer += decoder.decode(value, { stream: true });
|
|
104
|
+
let sepIdx = buffer.indexOf('\n\n');
|
|
105
|
+
while (sepIdx >= 0) {
|
|
106
|
+
const block = buffer.slice(0, sepIdx);
|
|
107
|
+
buffer = buffer.slice(sepIdx + 2);
|
|
108
|
+
const event = parseBlock(block);
|
|
109
|
+
if (!event) {
|
|
110
|
+
sepIdx = buffer.indexOf('\n\n');
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
if (event.type === 'phase') {
|
|
114
|
+
console.log(`[${event.phase}]${event.detail ? ` ${event.detail}` : ''}`);
|
|
115
|
+
}
|
|
116
|
+
else if (event.type === 'log') {
|
|
117
|
+
console.log(event.message);
|
|
118
|
+
}
|
|
119
|
+
else if (event.type === 'error') {
|
|
120
|
+
throw new Error(event.message || 'ensure-stream 返回 error 事件');
|
|
121
|
+
}
|
|
122
|
+
else if (event.type === 'capacity_exceeded') {
|
|
123
|
+
if (!event.candidates.length) {
|
|
124
|
+
throw new Error(`容量已满 (${event.current}/${event.max}),但服务端未返回可驱逐候选`);
|
|
125
|
+
}
|
|
126
|
+
const selected = await inquirer.prompt([
|
|
127
|
+
{
|
|
128
|
+
type: 'list',
|
|
129
|
+
name: 'slotId',
|
|
130
|
+
message: `沙箱容量已满 (${event.current}/${event.max}),请选择一个候选项继续驱逐:`,
|
|
131
|
+
choices: event.candidates.map((c) => ({
|
|
132
|
+
name: `${c.slotId} (${c.projectId}/${c.branchName}) idle=${formatIdleSec(c.idleSec)}${c.hasUnpushedChanges ? ' [unpushed]' : ''}`,
|
|
133
|
+
value: c.slotId,
|
|
134
|
+
})),
|
|
135
|
+
},
|
|
136
|
+
]);
|
|
137
|
+
const confirmed = await inquirer.prompt([
|
|
138
|
+
{
|
|
139
|
+
type: 'confirm',
|
|
140
|
+
name: 'ok',
|
|
141
|
+
message: `确认继续驱逐并重试? selected=${selected.slotId}`,
|
|
142
|
+
default: false,
|
|
143
|
+
},
|
|
144
|
+
]);
|
|
145
|
+
if (!confirmed.ok) {
|
|
146
|
+
throw new Error('用户取消操作');
|
|
147
|
+
}
|
|
148
|
+
return remixEnsureStream({ ...options, evictConfirmed: true });
|
|
149
|
+
}
|
|
150
|
+
else if (event.type === 'done') {
|
|
151
|
+
return {
|
|
152
|
+
sandboxId: event.sandboxId,
|
|
153
|
+
previewUrl: event.previewUrl,
|
|
154
|
+
agentBaseUrl: event.agentBaseUrl,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
sepIdx = buffer.indexOf('\n\n');
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
throw new Error('ensure-stream 结束但未收到 done 事件');
|
|
161
|
+
}
|
|
162
|
+
export function registerRemixCommands(program) {
|
|
163
|
+
const remix = program
|
|
164
|
+
.command('remix')
|
|
165
|
+
.description('Remix 工作流命令(JWT 鉴权,调用用户态 API)');
|
|
166
|
+
remix
|
|
167
|
+
.command('create')
|
|
168
|
+
.description('创建 remix 分支(POST /api/projects/:id/branches)')
|
|
169
|
+
.requiredOption('--project-id <id>', '项目 ID')
|
|
170
|
+
.requiredOption('--name <name>', 'remix 分支名(或展示名)')
|
|
171
|
+
.option('--source-branch <branch>', '源分支', 'main')
|
|
172
|
+
.option('--url <url>', '后端基址(默认取登录配置 / PLAYCRAFT_URL)')
|
|
173
|
+
.option('--token <jwt>', 'JWT token(默认取登录配置 / PLAYCRAFT_TOKEN)')
|
|
174
|
+
.action(async (opts) => {
|
|
175
|
+
await remixCreate({
|
|
176
|
+
projectId: String(opts.projectId),
|
|
177
|
+
name: String(opts.name),
|
|
178
|
+
sourceBranch: String(opts.sourceBranch || 'main'),
|
|
179
|
+
url: opts.url,
|
|
180
|
+
token: opts.token,
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
remix
|
|
184
|
+
.command('start')
|
|
185
|
+
.description('启动/复用 remix 沙箱(GET ensure-stream SSE)')
|
|
186
|
+
.requiredOption('--project-id <id>', '项目 ID')
|
|
187
|
+
.requiredOption('--branch <name>', '分支名(如 remix/my-branch)')
|
|
188
|
+
.option('--evict-confirmed', '携带 evictConfirmed=true 重试')
|
|
189
|
+
.option('--open', 'ready 后自动打开预览地址')
|
|
190
|
+
.option('--url <url>', '后端基址(默认取登录配置 / PLAYCRAFT_URL)')
|
|
191
|
+
.option('--token <jwt>', 'JWT token(默认取登录配置 / PLAYCRAFT_TOKEN)')
|
|
192
|
+
.action(async (opts) => {
|
|
193
|
+
const ready = await remixEnsureStream({
|
|
194
|
+
projectId: String(opts.projectId),
|
|
195
|
+
branch: String(opts.branch),
|
|
196
|
+
evictConfirmed: Boolean(opts.evictConfirmed),
|
|
197
|
+
url: opts.url,
|
|
198
|
+
token: opts.token,
|
|
199
|
+
});
|
|
200
|
+
const ttydUrl = getTtydUrl(ready.agentBaseUrl);
|
|
201
|
+
console.log('Sandbox ready:');
|
|
202
|
+
console.log(`- sandboxId: ${ready.sandboxId}`);
|
|
203
|
+
if (ready.previewUrl)
|
|
204
|
+
console.log(`- previewUrl: ${ready.previewUrl}`);
|
|
205
|
+
if (ready.agentBaseUrl)
|
|
206
|
+
console.log(`- agentBaseUrl: ${ready.agentBaseUrl}`);
|
|
207
|
+
if (ttydUrl)
|
|
208
|
+
console.log(`- ttydUrl: ${ttydUrl}`);
|
|
209
|
+
if (opts.open && ready.previewUrl) {
|
|
210
|
+
openBrowser(ready.previewUrl);
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
}
|