@playcraft/cli 0.0.53 → 0.0.56

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 (242) hide show
  1. package/README.md +21 -0
  2. package/REMIX.md +103 -19
  3. package/dist/atom-tree/fusion-hints.js +8 -3
  4. package/dist/atom-tree/fusion-intent.js +62 -0
  5. package/dist/atom-tree/selection.js +3 -1
  6. package/dist/commands/create.js +439 -159
  7. package/dist/commands/image.js +79 -69
  8. package/dist/commands/levels.js +29 -0
  9. package/dist/commands/patterns.js +75 -0
  10. package/dist/commands/remix.js +47 -0
  11. package/dist/commands/skills.js +48 -17
  12. package/dist/index.js +13 -6
  13. package/dist/remix/clone-source.js +208 -0
  14. package/dist/remix/clone.js +255 -0
  15. package/dist/scaffold/templates.js +35 -0
  16. package/dist/utils/agent-api-client.js +10 -3
  17. package/dist/utils/remix-image-attachments.js +4 -32
  18. package/dist/utils/sandbox-path.js +20 -0
  19. package/package.json +3 -3
  20. package/project-template/.claude/agents/creative-director.md +114 -0
  21. package/project-template/.claude/agents/developer.md +5 -2
  22. package/project-template/.claude/agents/game-designer.md +41 -20
  23. package/project-template/.claude/agents/refs/README.md +8 -0
  24. package/project-template/.claude/agents/refs/creative-director-guide.md +35 -0
  25. package/project-template/.claude/agents/refs/creative-director-kickoff-guide.md +52 -0
  26. package/project-template/.claude/agents/refs/creative-discovery-guide.md +76 -0
  27. package/project-template/.claude/agents/refs/creative-north-star-examples.md +82 -0
  28. package/project-template/.claude/agents/refs/developer-dev-handoff.md +1 -1
  29. package/project-template/.claude/agents/refs/game-designer-fusion-depth-playbook.md +28 -0
  30. package/project-template/.claude/agents/refs/game-designer-gameplay-sufficiency.md +4 -4
  31. package/project-template/.claude/agents/reviewer.md +11 -0
  32. package/project-template/.claude/hooks/README.md +12 -12
  33. package/project-template/.claude/hooks/block-developer-preflight.mjs +49 -13
  34. package/project-template/.claude/hooks/block-ring1-tasklist.mjs +1 -1
  35. package/project-template/.claude/hooks/user-prompt.mjs +3 -13
  36. package/project-template/.claude/hooks/validate-workflow-stop.mjs +209 -12
  37. package/project-template/.claude/settings.json +2 -2
  38. package/project-template/.claude/skills/playcraft-playable-optimization/SKILL.md +20 -20
  39. package/project-template/.claude/skills/session-analyzer/scripts/resolve-session.mjs +0 -1
  40. package/project-template/.claude/snapshot.mjs +0 -1
  41. package/project-template/.codebuddy/settings.json +1 -1
  42. package/project-template/.cursor/rules/playcraft-orchestrator.mdc +1 -1
  43. package/project-template/CLAUDE.md +9 -9
  44. package/project-template/docs/project-state.md +1 -1
  45. package/project-template/docs/team/agent-conduct.md +18 -18
  46. package/project-template/docs/team/collaboration.md +19 -7
  47. package/project-template/docs/team/workflow-changelog.md +15 -0
  48. package/project-template/fusion-patterns/catalog.json +77 -0
  49. package/project-template/package.json +2 -0
  50. package/project-template/skills/_shared/scripts/create-playable.mjs +31 -11
  51. package/project-template/skills/_shared/scripts/dev-start.mjs +3 -3
  52. package/project-template/skills/_shared/scripts/disclosure-config.mjs +65 -39
  53. package/project-template/skills/_shared/scripts/harness-smoke.mjs +3 -1
  54. package/project-template/skills/_shared/scripts/lib/fusion-intent.mjs +88 -0
  55. package/project-template/skills/_shared/scripts/lib/skill-fidelity.mjs +185 -0
  56. package/project-template/skills/_shared/scripts/slice-set.mjs +29 -2
  57. package/project-template/skills/_shared/scripts/state-get.mjs +124 -7
  58. package/project-template/skills/_shared/scripts/state-store.mjs +25 -2
  59. package/project-template/skills/_shared/scripts/validate-level-sketch.mjs +444 -0
  60. package/project-template/skills/_shared/scripts/verify-design-consistency.mjs +728 -0
  61. package/project-template/skills/playable-record/scripts/lib/dev-server.mjs +46 -11
  62. package/project-template/skills/playable-record/scripts/record-playable.mjs +2 -1
  63. package/project-template/skills/playable-record/scripts/verify-contract.mjs +4 -4
  64. package/project-template/skills/playable-record/scripts/verify-firstwin.mjs +3 -3
  65. package/project-template/skills/playable-record/scripts/verify-fusion.mjs +37 -2
  66. package/project-template/skills/playable-record/scripts/verify-slice.mjs +2 -2
  67. package/project-template/templates/design-brief.template.md +34 -0
  68. package/project-template/templates/design-methodology.md +1 -2
  69. package/project-template/templates/design.template.md +65 -0
  70. package/project-template/templates/developer-log.template.md +12 -1
  71. package/project-template/templates/five-axis-framework.md +12 -0
  72. package/project-template/templates/game-designer-creative-process.template.md +99 -0
  73. package/project-template/templates/level-sketch.template.json +56 -0
  74. package/project-template/templates/north-star.template.md +43 -0
  75. package/project-template/templates/project-state.template.md +1 -1
  76. package/project-template/vite.config.ts +4 -5
  77. package/project-templates/phaser/.claude/agents/creative-director.md +114 -0
  78. package/project-templates/phaser/.claude/agents/game-designer.md +41 -20
  79. package/project-templates/phaser/.claude/agents/refs/creative-director-guide.md +35 -0
  80. package/project-templates/phaser/.claude/agents/refs/creative-director-kickoff-guide.md +52 -0
  81. package/project-templates/phaser/.claude/agents/refs/creative-discovery-guide.md +76 -0
  82. package/project-templates/phaser/.claude/agents/refs/creative-north-star-examples.md +82 -0
  83. package/project-templates/phaser/.claude/agents/refs/developer-dev-handoff.md +1 -1
  84. package/project-templates/phaser/.claude/agents/refs/game-designer-fusion-depth-playbook.md +28 -0
  85. package/project-templates/phaser/.claude/agents/reviewer.md +11 -0
  86. package/project-templates/phaser/.claude/hooks/block-ring1-tasklist.mjs +2 -2
  87. package/project-templates/phaser/.claude/hooks/user-prompt.mjs +17 -18
  88. package/project-templates/phaser/.claude/hooks/validate-workflow-stop.mjs +209 -12
  89. package/project-templates/phaser/.claude/skills/playcraft-playable-optimization/SKILL.md +20 -20
  90. package/project-templates/phaser/.cursor/rules/playcraft-orchestrator.mdc +1 -1
  91. package/project-templates/phaser/CLAUDE.md +88 -54
  92. package/project-templates/phaser/docs/DESIGN.md +25 -0
  93. package/project-templates/phaser/docs/project-state.md +1 -1
  94. package/project-templates/phaser/docs/team/collaboration.md +19 -7
  95. package/project-templates/phaser/docs/team/workflow-changelog.md +14 -0
  96. package/project-templates/phaser/fusion-patterns/catalog.json +77 -0
  97. package/project-templates/phaser/package.json +3 -1
  98. package/project-templates/phaser/skills/_shared/scripts/dev-start.mjs +3 -3
  99. package/project-templates/phaser/skills/_shared/scripts/disclosure-config.mjs +65 -39
  100. package/project-templates/phaser/skills/_shared/scripts/harness-smoke.mjs +3 -1
  101. package/project-templates/phaser/skills/_shared/scripts/lib/fusion-intent.mjs +88 -0
  102. package/project-templates/phaser/skills/_shared/scripts/lib/skill-fidelity.mjs +185 -0
  103. package/project-templates/phaser/skills/_shared/scripts/state-get.mjs +124 -7
  104. package/project-templates/phaser/skills/_shared/scripts/state-store.mjs +25 -2
  105. package/project-templates/phaser/skills/_shared/scripts/validate-level-sketch.mjs +444 -0
  106. package/project-templates/phaser/skills/_shared/scripts/verify-design-consistency.mjs +728 -0
  107. package/project-templates/phaser/skills/playable-record/scripts/lib/dev-server.mjs +46 -11
  108. package/project-templates/phaser/skills/playable-record/scripts/record-playable.mjs +2 -1
  109. package/project-templates/phaser/skills/playable-record/scripts/verify-contract.mjs +4 -4
  110. package/project-templates/phaser/skills/playable-record/scripts/verify-firstwin.mjs +3 -3
  111. package/project-templates/phaser/skills/playable-record/scripts/verify-fusion.mjs +37 -2
  112. package/project-templates/phaser/skills/playable-record/scripts/verify-slice.mjs +2 -2
  113. package/project-templates/phaser/templates/design-brief.template.md +42 -11
  114. package/project-templates/phaser/templates/design-methodology.md +1 -2
  115. package/project-templates/phaser/templates/design.template.md +77 -0
  116. package/project-templates/phaser/templates/five-axis-framework.md +50 -1
  117. package/project-templates/phaser/templates/game-designer-creative-process.template.md +99 -0
  118. package/project-templates/phaser/templates/level-sketch.template.json +56 -0
  119. package/project-templates/phaser/templates/north-star.template.md +43 -0
  120. package/project-templates/phaser/templates/project-state.template.md +1 -1
  121. package/project-templates/phaser/vite.config.ts +4 -5
  122. package/project-templates/playable-template-phaser/.playcraftignore +10 -0
  123. package/project-templates/playable-template-phaser/CLAUDE.md +178 -0
  124. package/project-templates/playable-template-phaser/PLAYCRAFT-MIGRATION.md +328 -0
  125. package/project-templates/playable-template-phaser/README.md +381 -0
  126. package/project-templates/playable-template-phaser/assets/brain.png +0 -0
  127. package/project-templates/playable-template-phaser/assets/button.png +0 -0
  128. package/project-templates/playable-template-phaser/assets/interact.png +0 -0
  129. package/project-templates/playable-template-phaser/assets/logo.webp +0 -0
  130. package/project-templates/playable-template-phaser/assets/warn_brain.png +0 -0
  131. package/project-templates/playable-template-phaser/builds.config.js +12 -0
  132. package/project-templates/playable-template-phaser/globals.d.ts +2 -0
  133. package/project-templates/playable-template-phaser/index.html +12 -0
  134. package/project-templates/playable-template-phaser/package.json +45 -0
  135. package/project-templates/playable-template-phaser/scripts/base64-to-mp3.js +340 -0
  136. package/project-templates/playable-template-phaser/scripts/convert-to-webp.js +370 -0
  137. package/project-templates/playable-template-phaser/src/Game.ts +154 -0
  138. package/project-templates/playable-template-phaser/src/config/LoadingConfig.ts +40 -0
  139. package/project-templates/playable-template-phaser/src/config/ThemeConfig.ts +81 -0
  140. package/project-templates/playable-template-phaser/src/index.css +18 -0
  141. package/project-templates/playable-template-phaser/src/index.html +9 -0
  142. package/project-templates/playable-template-phaser/src/index.ts +30 -0
  143. package/project-templates/playable-template-phaser/src/json5.d.ts +4 -0
  144. package/project-templates/playable-template-phaser/src/managers/AudioManager.ts +559 -0
  145. package/project-templates/playable-template-phaser/src/scenes/MainScene.ts +301 -0
  146. package/project-templates/playable-template-phaser/src/scenes/PreloadScene.ts +256 -0
  147. package/project-templates/playable-template-phaser/src/theme/README.md +194 -0
  148. package/project-templates/playable-template-phaser/src/theme/blue-theme/assets/.gitkeep +24 -0
  149. package/project-templates/playable-template-phaser/src/theme/blue-theme/index.ts +27 -0
  150. package/project-templates/playable-template-phaser/src/theme/blue-theme/theme.data.json5 +19 -0
  151. package/project-templates/playable-template-phaser/src/theme/green-theme/assets/.gitkeep +3 -0
  152. package/project-templates/playable-template-phaser/src/theme/green-theme/index.ts +20 -0
  153. package/project-templates/playable-template-phaser/src/theme/green-theme/theme.data.json5 +19 -0
  154. package/project-templates/playable-template-phaser/src/theme/index.ts +1 -0
  155. package/project-templates/playable-template-phaser/src/theme/red-theme/assets/.gitkeep +3 -0
  156. package/project-templates/playable-template-phaser/src/theme/red-theme/index.ts +20 -0
  157. package/project-templates/playable-template-phaser/src/theme/red-theme/theme.data.json5 +19 -0
  158. package/project-templates/playable-template-phaser/src/theme/theme.metadata.md +60 -0
  159. package/project-templates/playable-template-phaser/src/theme/theme.schema.json5 +149 -0
  160. package/project-templates/playable-template-phaser/src/ui/BrainAgeUI.ts +681 -0
  161. package/project-templates/playable-template-phaser/src/ui/CountdownUI.ts +565 -0
  162. package/project-templates/playable-template-phaser/src/ui/PlayButton.ts +51 -0
  163. package/project-templates/playable-template-phaser/src/ui/ScoreUI.ts +207 -0
  164. package/project-templates/playable-template-phaser/src/ui/SuccessUI.ts +43 -0
  165. package/project-templates/playable-template-phaser/src/ui/index.ts +15 -0
  166. package/project-templates/playable-template-phaser/tsconfig.json +26 -0
  167. package/project-templates/playable-template-phaser/vite.config.ts +64 -0
  168. package/project-templates/playable-template-phaser//346/226/260/346/211/213/345/274/200/345/217/221/346/214/207/345/215/227.md +209 -0
  169. package/project-templates/playable-template-three/README.md +795 -0
  170. package/project-templates/playable-template-three/assets/Stork.glb +0 -0
  171. package/project-templates/playable-template-three/assets/button.png +0 -0
  172. package/project-templates/playable-template-three/assets/logo.webp +0 -0
  173. package/project-templates/playable-template-three/build.json +7 -0
  174. package/project-templates/playable-template-three/builds.config.example.js +151 -0
  175. package/project-templates/playable-template-three/builds.config.js +158 -0
  176. package/project-templates/playable-template-three/package.json +36 -0
  177. package/project-templates/playable-template-three/scripts/convert-to-webp.js +370 -0
  178. package/project-templates/playable-template-three/scripts/theme-builds.js +514 -0
  179. package/project-templates/playable-template-three/src/Game.ts +215 -0
  180. package/project-templates/playable-template-three/src/config/LoadingConfig.ts +27 -0
  181. package/project-templates/playable-template-three/src/config/ThemeConfig.ts +36 -0
  182. package/project-templates/playable-template-three/src/global.d.ts +108 -0
  183. package/project-templates/playable-template-three/src/index.css +18 -0
  184. package/project-templates/playable-template-three/src/index.html +9 -0
  185. package/project-templates/playable-template-three/src/index.ts +20 -0
  186. package/project-templates/playable-template-three/src/managers/AudioManager.ts +538 -0
  187. package/project-templates/playable-template-three/src/scenes/PreloadScene.ts +305 -0
  188. package/project-templates/playable-template-three/src/theme/README.md +225 -0
  189. package/project-templates/playable-template-three/src/theme/blue-theme/assets/.gitkeep +28 -0
  190. package/project-templates/playable-template-three/src/theme/blue-theme/index.ts +45 -0
  191. package/project-templates/playable-template-three/src/theme/current.ts +12 -0
  192. package/project-templates/playable-template-three/src/theme/green-theme/assets/.gitkeep +3 -0
  193. package/project-templates/playable-template-three/src/theme/green-theme/index.ts +34 -0
  194. package/project-templates/playable-template-three/src/theme/red-theme/assets/.gitkeep +3 -0
  195. package/project-templates/playable-template-three/src/theme/red-theme/index.ts +34 -0
  196. package/project-templates/playable-template-three/tsconfig.json +24 -0
  197. package/project-templates/threejs/.claude/agents/creative-director.md +114 -0
  198. package/project-templates/threejs/.claude/agents/game-designer.md +41 -20
  199. package/project-templates/threejs/.claude/agents/refs/creative-director-guide.md +35 -0
  200. package/project-templates/threejs/.claude/agents/refs/creative-director-kickoff-guide.md +52 -0
  201. package/project-templates/threejs/.claude/agents/refs/creative-discovery-guide.md +76 -0
  202. package/project-templates/threejs/.claude/agents/refs/creative-north-star-examples.md +82 -0
  203. package/project-templates/threejs/.claude/agents/refs/developer-dev-handoff.md +1 -1
  204. package/project-templates/threejs/.claude/agents/refs/game-designer-fusion-depth-playbook.md +28 -0
  205. package/project-templates/threejs/.claude/agents/reviewer.md +11 -0
  206. package/project-templates/threejs/.claude/hooks/block-ring1-tasklist.mjs +2 -2
  207. package/project-templates/threejs/.claude/hooks/user-prompt.mjs +17 -18
  208. package/project-templates/threejs/.claude/hooks/validate-workflow-stop.mjs +209 -12
  209. package/project-templates/threejs/.claude/skills/playcraft-playable-optimization/SKILL.md +20 -20
  210. package/project-templates/threejs/.cursor/rules/playcraft-orchestrator.mdc +1 -1
  211. package/project-templates/threejs/CLAUDE.md +88 -54
  212. package/project-templates/threejs/docs/DESIGN.md +25 -0
  213. package/project-templates/threejs/docs/project-state.md +1 -1
  214. package/project-templates/threejs/docs/team/collaboration.md +19 -7
  215. package/project-templates/threejs/docs/team/workflow-changelog.md +14 -0
  216. package/project-templates/threejs/fusion-patterns/catalog.json +77 -0
  217. package/project-templates/threejs/package.json +3 -1
  218. package/project-templates/threejs/skills/_shared/scripts/dev-start.mjs +3 -3
  219. package/project-templates/threejs/skills/_shared/scripts/disclosure-config.mjs +65 -39
  220. package/project-templates/threejs/skills/_shared/scripts/harness-smoke.mjs +3 -1
  221. package/project-templates/threejs/skills/_shared/scripts/lib/fusion-intent.mjs +88 -0
  222. package/project-templates/threejs/skills/_shared/scripts/lib/skill-fidelity.mjs +185 -0
  223. package/project-templates/threejs/skills/_shared/scripts/state-get.mjs +124 -7
  224. package/project-templates/threejs/skills/_shared/scripts/state-store.mjs +25 -2
  225. package/project-templates/threejs/skills/_shared/scripts/validate-level-sketch.mjs +444 -0
  226. package/project-templates/threejs/skills/_shared/scripts/verify-design-consistency.mjs +728 -0
  227. package/project-templates/threejs/skills/playable-record/scripts/lib/dev-server.mjs +46 -11
  228. package/project-templates/threejs/skills/playable-record/scripts/record-playable.mjs +2 -1
  229. package/project-templates/threejs/skills/playable-record/scripts/verify-contract.mjs +4 -4
  230. package/project-templates/threejs/skills/playable-record/scripts/verify-firstwin.mjs +3 -3
  231. package/project-templates/threejs/skills/playable-record/scripts/verify-fusion.mjs +37 -2
  232. package/project-templates/threejs/skills/playable-record/scripts/verify-slice.mjs +2 -2
  233. package/project-templates/threejs/templates/design-brief.template.md +42 -11
  234. package/project-templates/threejs/templates/design-methodology.md +1 -2
  235. package/project-templates/threejs/templates/design.template.md +77 -0
  236. package/project-templates/threejs/templates/five-axis-framework.md +50 -1
  237. package/project-templates/threejs/templates/game-designer-creative-process.template.md +99 -0
  238. package/project-templates/threejs/templates/level-sketch.template.json +56 -0
  239. package/project-templates/threejs/templates/north-star.template.md +43 -0
  240. package/project-templates/threejs/templates/project-state.template.md +1 -1
  241. package/project-templates/threejs/vite.config.ts +4 -5
  242. package/project-template/docs/DESIGN.md +0 -65
@@ -0,0 +1,255 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ import { mkdirSync, writeFileSync, readFileSync, existsSync } from 'node:fs';
3
+ import path from 'node:path';
4
+ import { loadGlobalConfig } from '../config.js';
5
+ import { PlayCraftHttpClient } from '../utils/playcraft-http-client.js';
6
+ import { isRemoteCloneSource, resolveRemixCloneTarget, } from './clone-source.js';
7
+ /** Build the branch-scoped Git Smart HTTP URL (no credentials in URL). */
8
+ export function buildRemixGitProxyUrl(apiBaseUrl, projectId, branch) {
9
+ const base = apiBaseUrl.replace(/\/+$/, '');
10
+ const encodedBranch = encodeURIComponent(branch);
11
+ return `${base}/api/git/projects/${encodeURIComponent(projectId)}/branches/${encodedBranch}.git`;
12
+ }
13
+ export function resolveCloneTargetDir(projectId, branch, dir) {
14
+ if (dir?.trim())
15
+ return path.resolve(dir.trim());
16
+ const safeBranch = branch.replace(/[/\\]+/g, '-').replace(/[^a-zA-Z0-9._-]+/g, '-');
17
+ return path.resolve(process.cwd(), `${projectId}-${safeBranch}`);
18
+ }
19
+ function localServiceUrl(baseUrl, fromPort, toPort) {
20
+ try {
21
+ const url = new URL(baseUrl);
22
+ const isLocal = ['localhost', '127.0.0.1', '[::1]'].includes(url.hostname);
23
+ if (isLocal && url.port === fromPort)
24
+ url.port = toPort;
25
+ return url.toString().replace(/\/+$/, '');
26
+ }
27
+ catch {
28
+ return baseUrl.replace(/\/+$/, '');
29
+ }
30
+ }
31
+ function createApiClient(options) {
32
+ const configured = new PlayCraftHttpClient(options);
33
+ const apiBaseUrl = localServiceUrl(configured.baseUrl, '3000', '3001');
34
+ if (apiBaseUrl === configured.baseUrl)
35
+ return configured;
36
+ return new PlayCraftHttpClient({
37
+ url: apiBaseUrl,
38
+ token: configured.token,
39
+ requestId: configured.requestId,
40
+ });
41
+ }
42
+ /**
43
+ * Prefer explicit --url; else env/login config (via PlayCraftHttpClient).
44
+ * Only use pasted portal origin when no API base is configured yet.
45
+ */
46
+ function resolveApiBaseHint(options, portalOrigin) {
47
+ if (options.url?.trim())
48
+ return options.url.trim();
49
+ const globalConfig = loadGlobalConfig();
50
+ const configured = process.env.PLAYCRAFT_API_URL ||
51
+ process.env.PLAYCRAFT_URL ||
52
+ (typeof globalConfig.backendUrl === 'string' && globalConfig.backendUrl) ||
53
+ (typeof globalConfig.url === 'string' && globalConfig.url);
54
+ if (configured)
55
+ return undefined;
56
+ if (portalOrigin?.trim()) {
57
+ return localServiceUrl(portalOrigin.trim(), '3000', '3001');
58
+ }
59
+ return undefined;
60
+ }
61
+ function writePlaycraftMeta(targetDir, meta) {
62
+ writeFileSync(path.join(targetDir, '.playcraft.json'), `${JSON.stringify(meta, null, 2)}\n`, 'utf8');
63
+ }
64
+ function configureRepoCredentialHelper(targetDir) {
65
+ // Prefer invoking the same CLI binary the user used.
66
+ const helper = `!playcraft git-credential`;
67
+ spawnSync('git', ['config', 'credential.helper', helper], {
68
+ cwd: targetDir,
69
+ encoding: 'utf8',
70
+ });
71
+ spawnSync('git', ['config', 'credential.useHttpPath', 'true'], {
72
+ cwd: targetDir,
73
+ encoding: 'utf8',
74
+ });
75
+ }
76
+ async function resolveProjectIdFromGitHost(client, repositoryUrl) {
77
+ try {
78
+ const data = await client.get(`/git/resolve-repo?repositoryUrl=${encodeURIComponent(repositoryUrl)}`);
79
+ const projectId = String(data?.projectId || data?.projectUuid || '').trim();
80
+ if (!projectId) {
81
+ throw new Error('empty_project_id');
82
+ }
83
+ return projectId;
84
+ }
85
+ catch {
86
+ throw new Error('无法根据工蜂/Git 地址定位 PlayCraft 项目(无权限或不存在)。请改用 Remix 详情页 URL,或 --project-id / --branch。');
87
+ }
88
+ }
89
+ export async function remixClone(options) {
90
+ const target = resolveRemixCloneTarget({
91
+ source: options.source,
92
+ projectId: options.projectId,
93
+ branch: options.branch,
94
+ });
95
+ let projectId = target.projectId?.trim() || '';
96
+ const branch = String(target.branch || '').trim();
97
+ if (!branch) {
98
+ throw new Error('缺少 Remix 分支名。');
99
+ }
100
+ if (branch === 'main' || branch === 'master') {
101
+ throw new Error('不允许 clone 主干分支;请指定 Remix 分支(例如 remix/...)。');
102
+ }
103
+ const client = createApiClient({
104
+ url: resolveApiBaseHint(options, target.origin),
105
+ token: options.token,
106
+ });
107
+ if (target.kind === 'git-host') {
108
+ if (!target.repositoryUrl) {
109
+ throw new Error('工蜂/Git 地址无效。');
110
+ }
111
+ projectId = await resolveProjectIdFromGitHost(client, target.repositoryUrl);
112
+ }
113
+ if (!projectId) {
114
+ throw new Error('缺少项目 ID。');
115
+ }
116
+ const cloneUrl = buildRemixGitProxyUrl(client.baseUrl, projectId, branch);
117
+ const targetDir = resolveCloneTargetDir(projectId, branch, options.dir);
118
+ if (existsSync(targetDir) && existsSync(path.join(targetDir, '.git'))) {
119
+ throw new Error(`目标目录已存在 Git 仓库:${targetDir}`);
120
+ }
121
+ mkdirSync(path.dirname(targetDir), { recursive: true });
122
+ if (!options.json) {
123
+ process.stderr.write(`[clone] ${cloneUrl}\n`);
124
+ process.stderr.write(`[clone] → ${targetDir}\n`);
125
+ }
126
+ const result = spawnSync('git', [
127
+ '-c',
128
+ `http.extraHeader=Authorization: Bearer ${client.token}`,
129
+ 'clone',
130
+ '--branch',
131
+ branch,
132
+ '--single-branch',
133
+ '--depth',
134
+ '1',
135
+ cloneUrl,
136
+ targetDir,
137
+ ], {
138
+ encoding: 'utf8',
139
+ stdio: options.json ? 'pipe' : 'inherit',
140
+ });
141
+ if (result.status !== 0) {
142
+ const detail = [result.stderr, result.stdout].filter(Boolean).join('\n').trim();
143
+ throw new Error(detail || `git clone 失败(exit ${result.status})`);
144
+ }
145
+ const meta = {
146
+ schemaVersion: '1.0',
147
+ projectId,
148
+ branch,
149
+ cloneUrl,
150
+ clonedAt: new Date().toISOString(),
151
+ };
152
+ writePlaycraftMeta(targetDir, meta);
153
+ configureRepoCredentialHelper(targetDir);
154
+ let projectType;
155
+ try {
156
+ const project = await client.get(`/projects/${encodeURIComponent(projectId)}`);
157
+ projectType = project?.projectType;
158
+ }
159
+ catch {
160
+ // optional — next-step hints only
161
+ }
162
+ if (options.install || options.dev) {
163
+ const install = spawnSync('npm', ['install'], {
164
+ cwd: targetDir,
165
+ encoding: 'utf8',
166
+ stdio: options.json ? 'pipe' : 'inherit',
167
+ shell: process.platform === 'win32',
168
+ });
169
+ if (install.status !== 0) {
170
+ throw new Error('npm install 失败');
171
+ }
172
+ }
173
+ if (options.dev) {
174
+ spawnSync('npm', ['run', 'dev'], {
175
+ cwd: targetDir,
176
+ encoding: 'utf8',
177
+ stdio: 'inherit',
178
+ shell: process.platform === 'win32',
179
+ });
180
+ }
181
+ if (options.json) {
182
+ console.log(JSON.stringify({
183
+ schemaVersion: '1.0',
184
+ ok: true,
185
+ command: 'remix.clone',
186
+ requestId: client.requestId,
187
+ data: { ...meta, targetDir, projectType, sourceKind: target.kind },
188
+ }));
189
+ return;
190
+ }
191
+ console.log(`✓ Remix 已克隆到 ${targetDir}`);
192
+ console.log(` 分支: ${branch}`);
193
+ console.log(` remote: ${cloneUrl}`);
194
+ console.log('');
195
+ console.log('下一步:');
196
+ console.log(` cd ${targetDir}`);
197
+ if (projectType === 'external') {
198
+ console.log(' npm install && npm run dev');
199
+ }
200
+ else {
201
+ console.log(' playcraft start --project <project-id> # PlayCanvas / 本地 Agent 预览');
202
+ }
203
+ console.log(' git pull / git push # 经 PlayCraft Git 代理(需已 login)');
204
+ }
205
+ export { isRemoteCloneSource, resolveRemixCloneTarget };
206
+ /**
207
+ * Git credential helper (get/store/erase). Only `get` is implemented:
208
+ * returns PAT from ~/.playcraft/config.json for PlayCraft git proxy hosts.
209
+ */
210
+ export function gitCredentialCommand(action) {
211
+ const op = (action || process.argv[3] || 'get').trim();
212
+ if (op !== 'get') {
213
+ // store/erase: no-op (PAT lives in global config)
214
+ return;
215
+ }
216
+ const input = readFileSync(0, 'utf8');
217
+ const attrs = {};
218
+ for (const line of input.split(/\r?\n/)) {
219
+ const eq = line.indexOf('=');
220
+ if (eq <= 0)
221
+ continue;
222
+ attrs[line.slice(0, eq)] = line.slice(eq + 1);
223
+ }
224
+ const host = attrs.host || '';
225
+ const protocol = attrs.protocol || 'https';
226
+ const reqPath = attrs.path || '';
227
+ if (!reqPath.includes('/api/git/projects/')) {
228
+ return;
229
+ }
230
+ const globalConfig = loadGlobalConfig();
231
+ const token = process.env.PLAYCRAFT_TOKEN ||
232
+ (typeof globalConfig.token === 'string' ? globalConfig.token : undefined);
233
+ if (!token || !token.startsWith('playcraft_pat_')) {
234
+ return;
235
+ }
236
+ // Optional host check against saved backend URL
237
+ const configured = (typeof globalConfig.backendUrl === 'string' && globalConfig.backendUrl) ||
238
+ (typeof globalConfig.url === 'string' && globalConfig.url) ||
239
+ process.env.PLAYCRAFT_API_URL ||
240
+ process.env.PLAYCRAFT_URL ||
241
+ '';
242
+ if (configured && host) {
243
+ try {
244
+ const configuredHost = new URL(configured).host;
245
+ if (configuredHost && configuredHost !== host) {
246
+ // Still allow if path is clearly our git proxy (e.g. custom CDN host)
247
+ }
248
+ }
249
+ catch {
250
+ // ignore
251
+ }
252
+ }
253
+ process.stdout.write(`username=playcraft\npassword=${token}\n\n`);
254
+ void protocol;
255
+ }
@@ -0,0 +1,35 @@
1
+ import path from 'node:path';
2
+ import { resolveCliPackageRoot } from './compose.js';
3
+ export const CREATE_TEMPLATES = {
4
+ agent: {
5
+ id: 'agent',
6
+ dir: 'project-template-v2',
7
+ label: 'Agent Team 工作流(默认)',
8
+ kind: 'agent',
9
+ },
10
+ 'phaser-playable': {
11
+ id: 'phaser-playable',
12
+ dir: 'project-templates/playable-template-phaser',
13
+ label: 'Phaser 可玩广告模板',
14
+ kind: 'playable',
15
+ },
16
+ 'three-playable': {
17
+ id: 'three-playable',
18
+ dir: 'project-templates/playable-template-three',
19
+ label: 'Three.js 可玩广告模板',
20
+ kind: 'playable',
21
+ },
22
+ };
23
+ export const CREATE_TEMPLATE_IDS = Object.keys(CREATE_TEMPLATES);
24
+ export function resolveCreateTemplate(templateId) {
25
+ const normalized = templateId.trim();
26
+ const meta = CREATE_TEMPLATES[normalized];
27
+ if (!meta) {
28
+ throw new Error(`Unknown template "${templateId}". Supported: ${CREATE_TEMPLATE_IDS.join(', ')}`);
29
+ }
30
+ return meta;
31
+ }
32
+ export function resolveCreateTemplateDir(templateId, cliRoot = resolveCliPackageRoot()) {
33
+ const meta = resolveCreateTemplate(templateId);
34
+ return path.join(cliRoot, meta.dir);
35
+ }
@@ -26,6 +26,7 @@ export class AgentApiClient {
26
26
  apiUrl;
27
27
  token;
28
28
  isPat;
29
+ playcraftEnv;
29
30
  constructor() {
30
31
  const localConfig = AgentApiClient.loadLocalConfig();
31
32
  const globalConfig = loadGlobalConfig();
@@ -47,13 +48,19 @@ export class AgentApiClient {
47
48
  this.apiUrl = apiUrl;
48
49
  this.token = token;
49
50
  this.isPat = isPatToken(token);
51
+ this.playcraftEnv = (process.env.PLAYCRAFT_ENV ||
52
+ localConfig.env ||
53
+ '').trim();
50
54
  }
51
55
  /** 根据 token 类型构造鉴权 headers */
52
56
  authHeaders() {
53
- if (this.isPat) {
54
- return { 'Authorization': `Bearer ${this.token}` };
57
+ const headers = this.isPat
58
+ ? { Authorization: `Bearer ${this.token}` }
59
+ : { 'X-Sandbox-Token': this.token };
60
+ if (this.playcraftEnv) {
61
+ headers['playcraft-env'] = this.playcraftEnv;
55
62
  }
56
- return { 'X-Sandbox-Token': this.token };
63
+ return headers;
57
64
  }
58
65
  /** Absolute pathname under /api/agent/tools (path may omit leading slash). */
59
66
  toolsRequestUrl(path) {
@@ -1,15 +1,9 @@
1
1
  import { readFileSync, statSync } from 'node:fs';
2
2
  import { basename, extname } from 'node:path';
3
3
  import { PlayCraftApiError } from './playcraft-http-client.js';
4
+ import { appendRemixReferenceImagesPrompt, referenceImageExtensions, referenceImageMimeFromExtension, } from '@playcraft/common/agent/reference-images';
4
5
  const MAX_IMAGES = 4;
5
6
  const MAX_IMAGE_BYTES = 5 * 1024 * 1024;
6
- const IMAGE_MIME_BY_EXTENSION = {
7
- '.gif': 'image/gif',
8
- '.jpeg': 'image/jpeg',
9
- '.jpg': 'image/jpeg',
10
- '.png': 'image/png',
11
- '.webp': 'image/webp',
12
- };
13
7
  function attachmentError(code, message, details, retryable = false) {
14
8
  return new PlayCraftApiError({ code, message, details, retryable });
15
9
  }
@@ -22,9 +16,9 @@ export function readImageAttachments(paths) {
22
16
  }
23
17
  return candidates.map((sourcePath) => {
24
18
  const extension = extname(sourcePath).toLowerCase();
25
- const mimeType = IMAGE_MIME_BY_EXTENSION[extension];
19
+ const mimeType = referenceImageMimeFromExtension(extension);
26
20
  if (!mimeType) {
27
- throw attachmentError('ATTACHMENT_TYPE_UNSUPPORTED', `不支持的图片格式:${sourcePath}。仅支持 JPEG (Joint Photographic Experts Group, 联合图像专家组)、PNG (Portable Network Graphics, 便携式网络图形)、WebP 和 GIF (Graphics Interchange Format, 图形交换格式)。`, { path: sourcePath, supportedExtensions: Object.keys(IMAGE_MIME_BY_EXTENSION) });
21
+ throw attachmentError('ATTACHMENT_TYPE_UNSUPPORTED', `不支持的图片格式:${sourcePath}。仅支持 JPEG (Joint Photographic Experts Group, 联合图像专家组)、PNG (Portable Network Graphics, 便携式网络图形)、WebP 和 GIF (Graphics Interchange Format, 图形交换格式)。`, { path: sourcePath, supportedExtensions: [...referenceImageExtensions()] });
28
22
  }
29
23
  let size;
30
24
  try {
@@ -98,28 +92,6 @@ export async function uploadImageAttachments(client, projectId, branch, attachme
98
92
  return uploaded;
99
93
  }
100
94
  export function buildReferenceImagesPrompt(prompt, attachments) {
101
- if (attachments.length === 0)
102
- return prompt;
103
- const pathList = attachments.map((attachment) => `- ${attachment.sandboxPath}`).join('\n');
104
- return [
105
- prompt,
106
- '',
107
- '[Reference Images]',
108
- `The user attached ${attachments.length} image(s) at these sandbox filesystem paths:`,
109
- pathList,
110
- '',
111
- 'These files have already been written to the sandbox filesystem.',
112
- '',
113
- '**IMPORTANT:** Do NOT read, open, or inspect any of these image files. Do NOT use tools like read_file, cat, or any other file reader on them. The paths themselves are all you need.',
114
- '',
115
- "Infer each image's purpose from the user's message (e.g. reference for style/composition, UI mockup, screenshot illustrating an issue, asset to import, etc.). For any image you judge to be a visual reference, pass its path directly to `playcraft-generate-image` — do NOT ask the user to confirm, and do NOT attempt to examine the image content first.",
116
- '',
117
- 'When passing references to `playcraft-generate-image`:',
118
- '- Put the first reference in `referenceImagePath`, remaining ones in `additionalReferenceImagePaths` as a comma-separated string.',
119
- '- Preserve the original order.',
120
- '- Up to 8 references total; paths must not contain commas.',
121
- '- Save generated images under `src/theme/draft/assets/` unless the user explicitly asks for another project asset location.',
122
- '- When reporting saved image paths back to the user, prefer `/project/<repo-relative-path>` such as `/project/src/theme/draft/assets/level-ref.png`, not the full `/projects/<projectId>/<branch>/...` path.',
123
- ].join('\n');
95
+ return appendRemixReferenceImagesPrompt(prompt, attachments.map((attachment) => attachment.sandboxPath));
124
96
  }
125
97
  export const IMAGE_OPTION_HELP = '一个或多个图片路径(最多 4 张;JPEG (Joint Photographic Experts Group, 联合图像专家组)、PNG (Portable Network Graphics, 便携式网络图形)、WebP、GIF (Graphics Interchange Format, 图形交换格式),每张不超过 5 MB (Megabyte, 兆字节))';
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Map legacy sandbox project root aliases onto the real cwd.
3
+ *
4
+ * Multi-slot sandboxes use workdir `/projects/{projectId}/{branch}` while many
5
+ * agent prompts / tools still advertise `/project/...`. CLI commands must treat
6
+ * `/project` as cwd so post-processing (e.g. remove-background) finds files.
7
+ */
8
+ import { resolve } from 'path';
9
+ export function resolveSandboxPath(userPath, cwd = process.cwd()) {
10
+ if (!userPath)
11
+ return userPath;
12
+ const forward = userPath.replace(/\\/g, '/');
13
+ if (forward === '/project') {
14
+ return cwd;
15
+ }
16
+ if (forward.startsWith('/project/')) {
17
+ return resolve(cwd, forward.slice('/project/'.length));
18
+ }
19
+ return userPath;
20
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@playcraft/cli",
3
- "version": "0.0.53",
3
+ "version": "0.0.56",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "engines": {
@@ -45,8 +45,8 @@
45
45
  "@gltf-transform/core": "^4.3.0",
46
46
  "@gltf-transform/extensions": "^4.3.0",
47
47
  "@gltf-transform/functions": "^4.3.0",
48
- "@playcraft/build": "^0.0.53",
49
- "@playcraft/common": "^0.0.53",
48
+ "@playcraft/build": "^0.0.56",
49
+ "@playcraft/common": "^0.0.56",
50
50
  "chokidar": "^4.0.3",
51
51
  "commander": "^13.1.0",
52
52
  "cors": "^2.8.6",
@@ -0,0 +1,114 @@
1
+ ---
2
+ name: creative-director
3
+ description: 'Creative Director: Ring 1 creative discovery — Micro-Story Spine + north star + creative-process before spec work.'
4
+ tools:
5
+ - 'Read'
6
+ - 'Write'
7
+ - 'Grep'
8
+ - 'Glob'
9
+ - 'WebSearch'
10
+ - 'WebFetch'
11
+ - 'Bash(npm run state:set:*)'
12
+ - 'Bash(npm run state:get:*)'
13
+ - 'Bash(playcraft skills read:*)'
14
+ ---
15
+
16
+ # Creative Director — Role Contract
17
+
18
+ > **Workflow spec**: growth-rings-v1 · Ring 1 creative discovery subagent (fresh context).
19
+
20
+ ## Role Essence
21
+
22
+ You are the **15-second playable-ad Creative Director**. You own **creative discovery only**: diverge gameplay core-moment candidates, adversarial review, commitment, and a readable **Creative North Star** with a **Micro-Story Spine**.
23
+
24
+ You do **not** match skills, write `DESIGN.md`, build level-sketch, seed slices, or run design verifiers. That is `@game-designer` spec mode after you STOP.
25
+
26
+ **Mechanism-first wrap:** `@game-designer` already locked `handoffContext.primaryGameplaySkill`. **Step 0:** `playcraft skills read <primaryGameplaySkill>` — PGS (PlayCraft Gameplay Schema, 玩法模式) is the authority. Your story must **wrap** slide-out / tray / blocking moments; **do not invent** a new core verb (e.g. rotate-to-aim replacing path slide).
27
+
28
+ **Persona:** Default **L1–L2** narrative (light story that fits the mechanic). L0 (pure mechanic) **only** when the user explicitly opts out of story — record their exact words in Phase 3. You decide Narrative Level; orchestrator hints are not binding.
29
+
30
+ **MDA reverse (within PGS):** Start from Fantasy / Challenge / Urgency, then land on **PGS step names** (pathClick / slideOut / flyToTray / matchDetection) — not a different core loop.
31
+
32
+ **Micro-Story Spine is non-negotiable:** L1/L2 require Who + Situation + Stakes + Payoff image. L0 requires Situation + Payoff + Phase 3 opt-out quote.
33
+
34
+ **One-liner bar:** Answer _who is in what situation, betting on what_ — not _which buttons they press_.
35
+
36
+ ## Dispatch Contract
37
+
38
+ Start every run with:
39
+
40
+ ```bash
41
+ npm run state:get -- --json dispatchPacket contextPacket referenceReads
42
+ ```
43
+
44
+ Read **`referenceReads`** from `state:get` for methodology files (five-axis, design-methodology, examples). Do not hard-code paths — the disclosure layer is the single source of truth.
45
+
46
+ If `dispatchPacket.role` is `creative-director`, execute only that packet:
47
+
48
+ - Obey `doWhat`, `flow`, `allowedFiles`, `forbiddenTasks`, and `acceptance`.
49
+ - Do not infer spec-phase work from file names or validator docs.
50
+ - Write state through `npm run state:set`; do not edit generated state markdown.
51
+
52
+ Execution guide: `.claude/agents/refs/creative-discovery-guide.md` (read on start).
53
+
54
+ ## Four-Phase Creative Process
55
+
56
+ Before STOP, produce:
57
+
58
+ 1. **`docs/creative-process.md`** — Phases 1–3 complete + Phase 4 **Design Intent** line only (from `templates/game-designer-creative-process.template.md`).
59
+ 2. **`docs/north-star.md`** — from `templates/north-star.template.md` (includes **Micro-Story Spine** before One-liner).
60
+
61
+ | Phase | Name | Forbidden |
62
+ | ----- | ------------------------------------- | --------------------------- |
63
+ | 0 | Skills read primary PGS | No DESIGN.md |
64
+ | 1 | Radical diverge (≥3 candidates) | No DESIGN.md |
65
+ | 2 | Adversarial review (five questions) | No aesthetic veto |
66
+ | 3 | Commitment (pick 1 + Narrative level) | No skills match |
67
+ | 4 | Design Intent line | No level-sketch / atom-tree |
68
+
69
+ **Five adversarial questions (phase 2):**
70
+
71
+ 1. After the first moment passes, why keep tapping?
72
+ 2. Can the player ignore core rules and still win?
73
+ 3. White-box: with art/audio removed, is the rule still fun?
74
+ 4. Depth ceiling: how fast does a skilled player finish — and does it feel like "that's it?"
75
+ 5. Strip story — remove Who and Situation; does the player still care?
76
+
77
+ ## One-liner quality (non-negotiable)
78
+
79
+ **Good (L1/L2):** names Who or Situation + tension — e.g. "Help the night-shift loader race the closing gate before the cargo bay locks."
80
+
81
+ **Bad:** mechanism chain or abstract metaphor alone — "Tap the path, watch debt grow, match three…" / "Play the nerve of debt before it bursts."
82
+
83
+ If your one-liner only lists actions, UI nouns, or tension words without a story anchor, rewrite before STOP.
84
+
85
+ ## Forbidden
86
+
87
+ - Read or write `docs/DESIGN.md`, `docs/level-sketch.json` (except scratch forbidden)
88
+ - Run `playcraft skills match`, `playcraft patterns match`, `init-atom-tree` — **read only** the locked primary skill
89
+ - Run `npm run verify:design`, `npm run verify:fusion`, `npm run levels:validate`
90
+ - Seed or update `sliceState`
91
+ - Open validator scripts or `templates/design.template.md` for "what to pass"
92
+
93
+ ## Outputs
94
+
95
+ - `docs/north-star.md` — standalone Creative North Star with Micro-Story Spine (source of truth for §0)
96
+ - `docs/creative-process.md` — audit trail Phases 1–4 Design Intent
97
+ - `handoffContext.northStarSummary` — ≤80 words for orchestrator
98
+ - Optional scratch in `docs/design-brief.md` **Creative Angle** only if packet allows
99
+
100
+ ## STOP Contract
101
+
102
+ Before STOP:
103
+
104
+ - Confirm `docs/north-star.md` has complete **Micro-Story Spine**, non-empty One-liner, System Dynamic, Why this fusion, ≥2 Rejected vibe, Design Intent **matching One-liner verbatim**.
105
+ - Confirm `docs/creative-process.md` has ≥3 Phase 1 candidates (each with Story hook), Phase 2 vulnerability list, Phase 3 **Narrative level** (+ L0 opt-out quote if L0).
106
+ - Set `subagent_stop: true`, `waiting_for: orchestrator`.
107
+ - Set `next_orchestrator_action` to: `Invoke @game-designer — ring 1 design spec (copy §0 from docs/north-star.md)`.
108
+ - Write `handoffContext.northStarSummary` via `npm run state:set`.
109
+ - Do **not** run verifiers.
110
+
111
+ ## Harness experiments
112
+
113
+ - **Treatment** (`CREATIVE_DIRECTOR_SPLIT=1`, default): you run first; Game Designer spec follows.
114
+ - **Control** (`CREATIVE_DIRECTOR_SPLIT=0`): orchestrator skips you — monolithic Game Designer only.
@@ -48,6 +48,8 @@ If `dispatchPacket.role` is `developer`, execute only that packet:
48
48
  - Preserve existing runtime contracts unless the packet explicitly changes them.
49
49
  - Do not infer extra win, ENDCARD, HUD, CTA (Call To Action, 行动号召), tutorial, polish, or asset work.
50
50
  - Do not run standalone verifier commands unless the packet explicitly asks. The intended acceptance path is `npm run build`, which will converge on `validate/build.json`.
51
+ - Read every file in `dispatchPacket.requiredContext.mustRead` before any `game/**` edit.
52
+ - When `dispatchPacket.requiredContext.atomTreeFocus.selectedAtomsPolicy` is `read-all-selected-skills-before-game-edit`, read `docs/atom-tree.json` plus every `.claude/skills/<atom>/SKILL.md` named in `meta.selectedAtoms`, even if the current slice only implements one `skillRefs` item.
51
53
 
52
54
  Default completion loop:
53
55
 
@@ -74,12 +76,13 @@ Never mark a slice verified from intuition or from a hand-written summary.
74
76
  Use the packet first:
75
77
 
76
78
  - `dispatchPacket`
79
+ - `dispatchPacket.requiredContext.mustRead`
77
80
  - files in `allowedFiles`
78
81
  - `docs/DESIGN.md` only when included or necessary to interpret the slice
79
- - `docs/atom-tree.json` only for skill refs attached to the slice
82
+ - `docs/atom-tree.json` according to `dispatchPacket.requiredContext.atomTreeFocus`
80
83
  - validation artifact such as `validate/build.json` after build
81
84
 
82
- Do not read layout, style, production, review, or handoff logs unless the packet includes them.
85
+ Do not read layout, style, production, review, or handoff logs unless the packet includes them. `skillRefs` narrows the implementation focus; it does not remove the requirement to understand all selected atom Skills disclosed in `requiredContext.mustRead`.
83
86
 
84
87
  ## Outputs
85
88
 
@@ -14,8 +14,7 @@ tools:
14
14
  - 'Bash(find:*)'
15
15
  - 'Bash(npm run state:set:*)'
16
16
  - 'Bash(npm run state:get:*)'
17
- - 'Bash(playcraft skills match:*)'
18
- - 'Bash(playcraft skills read:*)'
17
+ - 'Bash(playcraft skills:*)'
19
18
  - 'Bash(playcraft tools research:*)'
20
19
  - 'Bash(playcraft tools fetch-url:*)'
21
20
  - 'Bash(playcraft tools search-image:*)'
@@ -37,22 +36,32 @@ player intent -> playcraft skills init-atom-tree / match -> selected skill refs
37
36
 
38
37
  Do not seed `sliceState` before `docs/atom-tree.json` has been generated/refreshed by the PlayCraft skills CLI. The slice queue is derived from programmatic match refs and selected atoms; it is not invented directly from vibes.
39
38
 
40
- You are not an implementer, planner, artist, reviewer, or orchestrator. You do not choose process phase from project state. The orchestrator gives scope through `dispatchPacket`; you produce only the requested design artifact or slice queue.
39
+ You are not an implementer, planner, artist, reviewer, or orchestrator.
41
40
 
42
- ## Dispatch Contract
41
+ ## Research Contract (mandatory)
43
42
 
44
- Start every run with:
43
+ Before any brainstorm or slice seeding, complete two mandatory discovery passes:
45
44
 
46
- ```bash
47
- npm run state:get -- --json dispatchPacket sliceState
48
- ```
45
+ 1. **Web research** — understand real-world gameplay patterns and playable-ad references for this game type:
49
46
 
50
- If `dispatchPacket.role` is `game-designer`, execute only that packet:
47
+ ```bash
48
+ playcraft tools research --query "<game type> gameplay mechanics rules" --focus gameplay --json
49
+ ```
51
50
 
52
- - Obey `doWhat`, `flow`, `allowedFiles`, `forbiddenTasks`, and `acceptance`.
53
- - Do not infer a broader process phase from `iteration`, `loop`, file names, or old workflow docs.
54
- - Keep outputs compact enough for the next role to act without reading a long design essay.
55
- - Write state through `npm run state:set`; do not edit generated state markdown.
51
+ Supplement with `WebSearch` for fast factual checks (install numbers, ad format trends, competitor names, gameplay variants).
52
+
53
+ 2. **Skill library exploration** — load **`playcraft-skill-recommender`** to learn how to discover and evaluate skills from the library, then explore what gameplay patterns are available for this game type.
54
+
55
+ Record findings in `docs/design-brief.md` § Discovery Feed with `[WEB]`, `[SKILL]`, and `[USER]` source tags. These are mandatory regardless of genre familiarity — even familiar game types benefit from competitive reference and skill-library awareness.
56
+
57
+ ## Brainstorming Contract
58
+
59
+ After research, run structured brainstorm rounds with the user to lock the core gameplay direction. Load the **`brainstorming` skill** for the collaborative dialogue protocol.
60
+
61
+ - **Brainstorming is mandatory** — do not skip to writing DESIGN.md or seeding slices without confirmed gameplay direction.
62
+ - Use `AskUserQuestion` with concrete options (never open-ended text). Batch 2–4 related dimensions per turn.
63
+ - Self-answer when research gives a clear best answer. Lead with your recommendation.
64
+ - When you need to STOP with open questions, set `waiting_for=user_confirm` so the orchestrator can answer them.
56
65
 
57
66
  ## Discovery Reflection Contract
58
67
 
@@ -130,20 +139,34 @@ Use evidence depth intentionally:
130
139
  | Hook / CTA (Call To Action, 行动号召) work | `video-vlm` |
131
140
  | Production visual work | `video-vlm` plus visual gate evidence |
132
141
 
133
- Do not introduce Hook / CTA, style direction, layout tables, production asset mappings, or full feature plans unless the packet explicitly asks for them.
142
+ Do not introduce Hook / CTA, style direction, layout tables, production asset mappings, or full feature plans unless explicitly requested.
143
+
144
+ Optional discovery before `init-atom-tree`:
145
+
146
+ ```bash
147
+ playcraft patterns match --intent "<genre, core action>"
148
+ ```
149
+
150
+ ## Dispatch Contract
151
+
152
+ Start every run with:
153
+
154
+ ```bash
155
+ npm run state:get -- --json dispatchPacket sliceState
156
+ ```
157
+
158
+ Orchestrator invokes you with the user's original intent only. When `dispatchPacket` is present, execute that packet scope. Do not infer Ring / Gate / Loop, and do not expand into implementation or production work.
134
159
 
135
160
  ## Inputs
136
161
 
137
162
  Use the smallest useful set:
138
163
 
139
- - `dispatchPacket`
140
- - `sliceState`
141
164
  - user prompt / orchestrator notes
142
165
  - `docs/design-brief.md` when discovery already exists
143
166
  - `docs/DESIGN.md` when updating the playable seed
144
167
  - `docs/atom-tree.json` for programmatic skill match refs
145
168
 
146
- Do not read whole logs or future production docs unless `dispatchPacket.allowedFiles` or `requiredFiles` includes them.
169
+ Do not read whole logs or future production docs unless explicitly needed.
147
170
 
148
171
  ## Outputs
149
172
 
@@ -187,6 +210,4 @@ You must not:
187
210
 
188
211
  ## Legacy Fallback
189
212
 
190
- Only when `dispatchPacket` is null and `sliceState` is empty, follow [`refs/game-designer-legacy-fallback.md`](refs/game-designer-legacy-fallback.md).
191
-
192
- Legacy fallback does not apply to slice-axis dispatch.
213
+ Only when `dispatchPacket` is null and `sliceState` is empty, follow [game-designer-legacy-fallback.md](refs/game-designer-legacy-fallback.md).