@playcraft/cli 0.1.29 → 0.1.30

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 (320) hide show
  1. package/README.md +38 -3
  2. package/dist/agent/local-backend.js +1 -1
  3. package/dist/auth/account-activation.js +157 -0
  4. package/dist/auth/messages.js +30 -0
  5. package/dist/cli-root-help.js +2 -0
  6. package/dist/commands/build.js +9 -4
  7. package/dist/commands/create.js +26 -33
  8. package/dist/commands/image.js +2 -6
  9. package/dist/commands/login.js +84 -42
  10. package/dist/commands/platform-skills.generated.js +19 -0
  11. package/dist/commands/playable.js +53 -0
  12. package/dist/commands/skills.js +2 -0
  13. package/dist/commands/tools-3d.js +11 -0
  14. package/dist/commands/tools-batch.js +87 -0
  15. package/dist/commands/tools-generation.js +68 -109
  16. package/dist/commands/tools.js +2 -2
  17. package/dist/commands/video.js +111 -0
  18. package/dist/commands/workspace-runtime.js +54 -0
  19. package/dist/config.js +59 -4
  20. package/dist/index.js +34 -3
  21. package/dist/project-skills/builtin-import.js +225 -0
  22. package/dist/project-skills/commands.js +77 -0
  23. package/dist/project-skills/lifecycle.js +28 -0
  24. package/dist/project-skills/local-run.js +112 -0
  25. package/dist/project-skills/local-store.js +221 -0
  26. package/dist/project-skills/messages.js +80 -0
  27. package/dist/project-skills/reconcile.js +62 -0
  28. package/dist/project-skills/remote-cache.js +51 -0
  29. package/dist/project-skills/validation.js +19 -0
  30. package/dist/recording/messages.js +40 -0
  31. package/dist/recording/project-digest.js +85 -0
  32. package/dist/recording/recorder.js +293 -0
  33. package/dist/recording/scenario.js +50 -0
  34. package/dist/remix/clone.js +2 -0
  35. package/dist/remix/init-template.js +2 -0
  36. package/dist/remix/pull.js +2 -0
  37. package/dist/remix/push.js +21 -1
  38. package/dist/utils/agent-api-client.js +185 -30
  39. package/dist/utils/environment-endpoints.js +17 -0
  40. package/dist/utils/playcraft-http-client.js +6 -4
  41. package/dist/utils/tool-operation-journal.js +124 -0
  42. package/dist/utils/video-evidence-git.js +81 -0
  43. package/dist/video-replication/client.js +222 -0
  44. package/dist/video-replication/comparison-evidence.js +149 -0
  45. package/dist/video-replication/comparison-files.js +57 -0
  46. package/dist/video-replication/comparison-log.js +72 -0
  47. package/dist/video-replication/comparison-messages.js +104 -0
  48. package/dist/video-replication/comparison-output.js +63 -0
  49. package/dist/video-replication/comparison-preflight.js +66 -0
  50. package/dist/video-replication/comparison.js +216 -0
  51. package/dist/video-replication/evidence.js +165 -0
  52. package/dist/video-replication/media.js +111 -0
  53. package/dist/video-replication/messages.js +89 -0
  54. package/dist/workspace-runtime/codex/app-server.js +965 -0
  55. package/dist/workspace-runtime/codex/compact.js +46 -0
  56. package/dist/workspace-runtime/codex/config-toml.js +107 -0
  57. package/dist/workspace-runtime/codex/jsonrpc-stdio.js +145 -0
  58. package/dist/workspace-runtime/codex/launcher-client.js +177 -0
  59. package/dist/workspace-runtime/codex/loopback.js +146 -0
  60. package/dist/workspace-runtime/codex/mcp-config.js +39 -0
  61. package/dist/workspace-runtime/codex/model-broker.js +263 -0
  62. package/dist/workspace-runtime/codex/native-adapter.js +9 -0
  63. package/dist/workspace-runtime/codex/permissions.js +70 -0
  64. package/dist/workspace-runtime/codex/work-mode.js +15 -0
  65. package/dist/workspace-runtime/main.js +10 -0
  66. package/dist/workspace-runtime/persistence/journal.js +355 -0
  67. package/dist/workspace-runtime/persistence/native-subagents.js +162 -0
  68. package/dist/workspace-runtime/persistence/sqlite-client.js +77 -0
  69. package/dist/workspace-runtime/persistence/sqlite-worker.js +1096 -0
  70. package/dist/workspace-runtime/previews/publish.js +259 -0
  71. package/dist/workspace-runtime/previews/single-file.js +43 -0
  72. package/dist/workspace-runtime/processes/managed-writes.js +69 -0
  73. package/dist/workspace-runtime/production/contracts.js +17 -0
  74. package/dist/workspace-runtime/production/controller.js +207 -0
  75. package/dist/workspace-runtime/production/dependencies.js +60 -0
  76. package/dist/workspace-runtime/production/native.js +196 -0
  77. package/dist/workspace-runtime/production/processes.js +86 -0
  78. package/dist/workspace-runtime/production/save-command.js +35 -0
  79. package/dist/workspace-runtime/production/source-copy.js +91 -0
  80. package/dist/workspace-runtime/production/stages.js +202 -0
  81. package/dist/workspace-runtime/production/storage.js +32 -0
  82. package/dist/workspace-runtime/serve.js +207 -0
  83. package/dist/workspace-runtime/server/auth.js +21 -0
  84. package/dist/workspace-runtime/server/connectivity.js +201 -0
  85. package/dist/workspace-runtime/server/desktop-host.js +429 -0
  86. package/dist/workspace-runtime/server/desktop-preview-config.js +81 -0
  87. package/dist/workspace-runtime/server/desktop-preview-service.js +157 -0
  88. package/dist/workspace-runtime/server/desktop-preview-static.js +53 -0
  89. package/dist/workspace-runtime/server/desktop-preview.js +189 -0
  90. package/dist/workspace-runtime/server/dispatch.js +1116 -0
  91. package/dist/workspace-runtime/server/endpoints.js +19 -0
  92. package/dist/workspace-runtime/server/execution-group.js +85 -0
  93. package/dist/workspace-runtime/server/http.js +1038 -0
  94. package/dist/workspace-runtime/server/lock.js +53 -0
  95. package/dist/workspace-runtime/server/rfb-watch-gate.js +168 -0
  96. package/dist/workspace-runtime/server/terminal-host.js +366 -0
  97. package/dist/workspace-runtime/server/types.js +1 -0
  98. package/dist/workspace-runtime/workspaces/artifact-events.js +55 -0
  99. package/dist/workspace-runtime/workspaces/artifact-files.js +195 -0
  100. package/dist/workspace-runtime/workspaces/artifact-receipts.js +89 -0
  101. package/dist/workspace-runtime/workspaces/artifacts.js +260 -0
  102. package/dist/workspace-runtime/workspaces/attachment-content.js +111 -0
  103. package/dist/workspace-runtime/workspaces/attachments.js +175 -0
  104. package/dist/workspace-runtime/workspaces/batch-commit.js +142 -0
  105. package/dist/workspace-runtime/workspaces/cli-context.js +23 -0
  106. package/dist/workspace-runtime/workspaces/cli-credentials.js +111 -0
  107. package/dist/workspace-runtime/workspaces/composer-context.js +125 -0
  108. package/dist/workspace-runtime/workspaces/context-error.js +2 -0
  109. package/dist/workspace-runtime/workspaces/dependencies.js +264 -0
  110. package/dist/workspace-runtime/workspaces/developer-command.js +64 -0
  111. package/dist/workspace-runtime/workspaces/directories.js +113 -0
  112. package/dist/workspace-runtime/workspaces/directory-access.js +29 -0
  113. package/dist/workspace-runtime/workspaces/directory-maintenance.js +111 -0
  114. package/dist/workspace-runtime/workspaces/directory-stats.js +49 -0
  115. package/dist/workspace-runtime/workspaces/disk-snapshot.js +31 -0
  116. package/dist/workspace-runtime/workspaces/empty-template.js +40 -0
  117. package/dist/workspace-runtime/workspaces/files-search.js +116 -0
  118. package/dist/workspace-runtime/workspaces/files.js +120 -0
  119. package/dist/workspace-runtime/workspaces/git-credential-broker.js +312 -0
  120. package/dist/workspace-runtime/workspaces/git-credential-helper.js +68 -0
  121. package/dist/workspace-runtime/workspaces/git-direct-store.js +155 -0
  122. package/dist/workspace-runtime/workspaces/git.js +517 -0
  123. package/dist/workspace-runtime/workspaces/json5-edit.js +170 -0
  124. package/dist/workspace-runtime/workspaces/parameters.js +272 -0
  125. package/dist/workspace-runtime/workspaces/prepare.js +264 -0
  126. package/dist/workspace-runtime/workspaces/registry.js +226 -0
  127. package/dist/workspace-runtime/workspaces/revisions.js +31 -0
  128. package/dist/workspace-runtime/workspaces/skill-artifact-loader.js +91 -0
  129. package/dist/workspace-runtime/workspaces/skills.js +189 -0
  130. package/dist/workspace-runtime/workspaces/video-attachments.js +49 -0
  131. package/package.json +14 -5
  132. package/project-template/.claude/skills/playcraft-image-generation/SKILL.md +23 -81
  133. package/project-template/.claude/skills/playcraft-replica-validation/SKILL.md +25 -0
  134. package/project-template/.claude/skills/playcraft-replica-validation/references/contracts.md +79 -0
  135. package/project-template/.claude/skills/playcraft-video-understanding/SKILL.md +29 -0
  136. package/project-template/.claude/skills/playcraft-video-understanding/references/contracts.md +37 -0
  137. package/project-template-v2/.claude/agents/artist.md +82 -0
  138. package/project-template-v2/.claude/agents/developer.md +153 -0
  139. package/project-template-v2/.claude/agents/game-designer.md +264 -0
  140. package/project-template-v2/.claude/agents/refs/artist-art-style-catalog.md +533 -0
  141. package/project-template-v2/.claude/agents/refs/artist-color-audio-recipes.md +153 -0
  142. package/project-template-v2/.claude/agents/refs/artist-dimension-axis.md +27 -0
  143. package/project-template-v2/.claude/agents/refs/artist-master-composite-recipes.md +208 -0
  144. package/project-template-v2/.claude/agents/refs/atom-skill-library.md +81 -0
  145. package/project-template-v2/.claude/agents/refs/developer-impl-cookbook.md +432 -0
  146. package/project-template-v2/.claude/agents/refs/framework-5-component-filter.md +252 -0
  147. package/project-template-v2/.claude/agents/refs/framework-game-feel-juice.md +266 -0
  148. package/project-template-v2/.claude/agents/refs/framework-mda.md +147 -0
  149. package/project-template-v2/.claude/agents/refs/game-designer-gameplay-sufficiency.md +123 -0
  150. package/project-template-v2/.claude/agents/refs/ta-3d-flip-recipe.md +88 -0
  151. package/project-template-v2/.claude/agents/refs/ta-atlas-deliverable-standard.md +67 -0
  152. package/project-template-v2/.claude/agents/refs/ta-batch-pipeline-recipes.md +120 -0
  153. package/project-template-v2/.claude/agents/refs/ta-image-generation-detail.md +300 -0
  154. package/project-template-v2/.claude/agents/refs/ta-image-ops-reference.md +495 -0
  155. package/project-template-v2/.claude/agents/refs/ta-pipeline-cookbook.md +1141 -0
  156. package/project-template-v2/.claude/agents/refs/ta-tools-reference.md +111 -0
  157. package/project-template-v2/.claude/agents/refs/ta-vfx-preset-catalog.md +365 -0
  158. package/project-template-v2/.claude/agents/refs/threejs-cannon-pitfalls.md +412 -0
  159. package/project-template-v2/.claude/agents/reviewer.md +75 -0
  160. package/project-template-v2/.claude/agents/technical-artist.md +86 -0
  161. package/project-template-v2/.claude/hooks/snapshot-milestone.mjs +243 -0
  162. package/project-template-v2/.claude/hooks/user-prompt.mjs +133 -0
  163. package/project-template-v2/.claude/settings.json +33 -0
  164. package/project-template-v2/.claude/skills/brainstorming/SKILL.md +161 -0
  165. package/project-template-v2/.claude/skills/brainstorming/scripts/frame-template.html +270 -0
  166. package/project-template-v2/.claude/skills/brainstorming/scripts/helper.js +177 -0
  167. package/project-template-v2/.claude/skills/brainstorming/scripts/server.cjs +354 -0
  168. package/project-template-v2/.claude/skills/brainstorming/scripts/start-server.sh +148 -0
  169. package/project-template-v2/.claude/skills/brainstorming/scripts/stop-server.sh +56 -0
  170. package/project-template-v2/.claude/skills/brainstorming/scripts/wait-for-selection.sh +62 -0
  171. package/project-template-v2/.claude/skills/brainstorming/spec-document-reviewer-prompt.md +49 -0
  172. package/project-template-v2/.claude/skills/brainstorming/visual-companion.md +309 -0
  173. package/project-template-v2/.claude/skills/playcraft-ad-psychology/SKILL.md +182 -0
  174. package/project-template-v2/.claude/skills/playcraft-art-style-guide/SKILL.md +123 -0
  175. package/project-template-v2/.claude/skills/playcraft-asset-state-sheet/SKILL.md +205 -0
  176. package/project-template-v2/.claude/skills/playcraft-audio-generation/SKILL.md +280 -0
  177. package/project-template-v2/.claude/skills/playcraft-batch-pipeline/SKILL.md +184 -0
  178. package/project-template-v2/.claude/skills/playcraft-build-optimizer/SKILL.md +306 -0
  179. package/project-template-v2/.claude/skills/playcraft-image-generation/SKILL.md +298 -0
  180. package/project-template-v2/.claude/skills/playcraft-image-generation/reference/build-sprite-sheet.template.mjs +123 -0
  181. package/project-template-v2/.claude/skills/playcraft-image-generation/reference/compare-style.template.mjs +254 -0
  182. package/project-template-v2/.claude/skills/playcraft-image-generation/reference/gen-batch-sprite.template.mjs +324 -0
  183. package/project-template-v2/.claude/skills/playcraft-image-generation/reference/gen-batch.template.mjs +97 -0
  184. package/project-template-v2/.claude/skills/playcraft-image-generation/reference/gen-edit-variants.template.mjs +118 -0
  185. package/project-template-v2/.claude/skills/playcraft-image-generation/reference/process-batch.template.mjs +137 -0
  186. package/project-template-v2/.claude/skills/playcraft-image-generation/reference/prompt-cookbook.md +397 -0
  187. package/project-template-v2/.claude/skills/playcraft-image-generation/reference/validate-sprite-sheet.template.mjs +296 -0
  188. package/project-template-v2/.claude/skills/playcraft-image-ops/SKILL.md +122 -0
  189. package/project-template-v2/.claude/skills/playcraft-image-processing/SKILL.md +219 -0
  190. package/project-template-v2/.claude/skills/playcraft-masking/SKILL.md +373 -0
  191. package/project-template-v2/.claude/skills/playcraft-playable-optimization/SKILL.md +161 -0
  192. package/project-template-v2/.claude/skills/playcraft-research/SKILL.md +215 -0
  193. package/project-template-v2/.claude/skills/playcraft-skill-recommender/SKILL.md +382 -0
  194. package/project-template-v2/.claude/skills/playcraft-sprite-generation/SKILL.md +423 -0
  195. package/project-template-v2/.claude/skills/playcraft-sprite-remix/SKILL.md +158 -0
  196. package/project-template-v2/.claude/skills/playcraft-sprite-sheet/SKILL.md +100 -0
  197. package/project-template-v2/.claude/skills/playcraft-storyboard/SKILL.md +167 -0
  198. package/project-template-v2/.claude/skills/playcraft-style-qa/SKILL.md +270 -0
  199. package/project-template-v2/.claude/skills/playcraft-text-rendering/SKILL.md +236 -0
  200. package/project-template-v2/.claude/skills/playcraft-vfx-animation/SKILL.md +130 -0
  201. package/project-template-v2/.claude/skills/playwright-cli/SKILL.md +390 -0
  202. package/project-template-v2/.claude/skills/playwright-cli/references/element-attributes.md +23 -0
  203. package/project-template-v2/.claude/skills/playwright-cli/references/playwright-tests.md +39 -0
  204. package/project-template-v2/.claude/skills/playwright-cli/references/request-mocking.md +87 -0
  205. package/project-template-v2/.claude/skills/playwright-cli/references/running-code.md +240 -0
  206. package/project-template-v2/.claude/skills/playwright-cli/references/session-management.md +226 -0
  207. package/project-template-v2/.claude/skills/playwright-cli/references/spec-driven-testing.md +308 -0
  208. package/project-template-v2/.claude/skills/playwright-cli/references/storage-state.md +275 -0
  209. package/project-template-v2/.claude/skills/playwright-cli/references/test-generation.md +134 -0
  210. package/project-template-v2/.claude/skills/playwright-cli/references/tracing.md +142 -0
  211. package/project-template-v2/.claude/skills/playwright-cli/references/video-recording.md +153 -0
  212. package/project-template-v2/.claude/skills/session-analyzer/SKILL.md +386 -0
  213. package/project-template-v2/.claude/skills/session-analyzer/scripts/execution-breakdown.mjs +182 -0
  214. package/project-template-v2/.claude/skills/session-analyzer/scripts/find-turns.mjs +72 -0
  215. package/project-template-v2/.claude/skills/session-analyzer/scripts/heavy-output.mjs +121 -0
  216. package/project-template-v2/.claude/skills/session-analyzer/scripts/resolve-session.mjs +102 -0
  217. package/project-template-v2/.claude/skills/session-analyzer/scripts/subagent-stats.mjs +127 -0
  218. package/project-template-v2/.claude/skills/session-analyzer/scripts/subagent-tool-timeline.mjs +106 -0
  219. package/project-template-v2/.claude/skills/session-analyzer/scripts/time-gaps.mjs +128 -0
  220. package/project-template-v2/.claude/skills/session-analyzer/scripts/turn-timeline.mjs +67 -0
  221. package/project-template-v2/.claude/snapshot.mjs +263 -0
  222. package/project-template-v2/.playcraft/skills.lock.json +152 -0
  223. package/project-template-v2/CLAUDE.md +146 -0
  224. package/project-template-v2/assets/audio/bgm/.gitkeep +0 -0
  225. package/project-template-v2/assets/audio/sfx/.gitkeep +0 -0
  226. package/project-template-v2/assets/bundles/.gitkeep +0 -0
  227. package/project-template-v2/assets/images/bg/.gitkeep +0 -0
  228. package/project-template-v2/assets/images/reference/.gitkeep +0 -0
  229. package/project-template-v2/assets/images/storyboard/.gitkeep +0 -0
  230. package/project-template-v2/assets/images/tiles/.gitkeep +0 -0
  231. package/project-template-v2/assets/images/ui/.gitkeep +0 -0
  232. package/project-template-v2/assets/images/vfx/.gitkeep +0 -0
  233. package/project-template-v2/assets/models/.gitkeep +0 -0
  234. package/project-template-v2/docs/harness/iteration-1/context-flow.md +254 -0
  235. package/project-template-v2/docs/harness/iteration-1/generate-flow.md +91 -0
  236. package/project-template-v2/docs/harness/iteration-1/ideate-flow.md +214 -0
  237. package/project-template-v2/docs/harness/iteration-1/optimize-flow.md +75 -0
  238. package/project-template-v2/docs/harness/iteration-1/wrapup-flow.md +63 -0
  239. package/project-template-v2/docs/harness/iteration-2/context-flow.md +223 -0
  240. package/project-template-v2/docs/harness/iteration-2/generate-flow.md +129 -0
  241. package/project-template-v2/docs/harness/iteration-2/ideate-flow.md +267 -0
  242. package/project-template-v2/docs/harness/iteration-2/optimize-flow.md +164 -0
  243. package/project-template-v2/docs/harness/iteration-2/wrapup-flow.md +115 -0
  244. package/project-template-v2/docs/harness/orchestrator-flow.md +364 -0
  245. package/project-template-v2/docs/project-state.json +60 -0
  246. package/project-template-v2/docs/project-state.md +72 -0
  247. package/project-template-v2/docs/standards/README.md +225 -0
  248. package/project-template-v2/docs/standards/agent-behavior-standards.md +174 -0
  249. package/project-template-v2/docs/standards/artifacts/design-brief.md +19 -0
  250. package/project-template-v2/docs/standards/artifacts/design.md +22 -0
  251. package/project-template-v2/docs/standards/artifacts/game-code.md +41 -0
  252. package/project-template-v2/docs/standards/artifacts/todo-list.md +41 -0
  253. package/project-template-v2/docs/standards/iter1-agent-behavior-standards.md +343 -0
  254. package/project-template-v2/game/index.ts +18 -0
  255. package/project-template-v2/globals.d.ts +51 -0
  256. package/project-template-v2/index.css +34 -0
  257. package/project-template-v2/index.html +18 -0
  258. package/project-template-v2/main.ts +9 -0
  259. package/project-template-v2/package.json +46 -0
  260. package/project-template-v2/skills/_shared/scripts/dispatch-clear.mjs +31 -0
  261. package/project-template-v2/skills/_shared/scripts/dispatch-set.mjs +86 -0
  262. package/project-template-v2/skills/_shared/scripts/dod-check.mjs +153 -0
  263. package/project-template-v2/skills/_shared/scripts/handoff-append.mjs +70 -0
  264. package/project-template-v2/skills/_shared/scripts/lib/validator-artifacts.mjs +91 -0
  265. package/project-template-v2/skills/_shared/scripts/pipeline/dod-config.mjs +131 -0
  266. package/project-template-v2/skills/_shared/scripts/pipeline/index.mjs +80 -0
  267. package/project-template-v2/skills/_shared/scripts/pipeline/iteration-1-core.mjs +90 -0
  268. package/project-template-v2/skills/_shared/scripts/pipeline/iteration-2-wrap.mjs +93 -0
  269. package/project-template-v2/skills/_shared/scripts/pipeline/iteration-3-visual.mjs +23 -0
  270. package/project-template-v2/skills/_shared/scripts/render-project-state.mjs +230 -0
  271. package/project-template-v2/skills/_shared/scripts/state-advance-stage.mjs +58 -0
  272. package/project-template-v2/skills/_shared/scripts/state-get.mjs +339 -0
  273. package/project-template-v2/skills/_shared/scripts/state-handoff.mjs +39 -0
  274. package/project-template-v2/skills/_shared/scripts/state-set.mjs +94 -0
  275. package/project-template-v2/skills/_shared/scripts/state-store.mjs +783 -0
  276. package/project-template-v2/skills/_shared/scripts/todo-add.mjs +88 -0
  277. package/project-template-v2/skills/_shared/scripts/todo-get.mjs +130 -0
  278. package/project-template-v2/skills/_shared/scripts/todo-remove.mjs +33 -0
  279. package/project-template-v2/skills/_shared/scripts/todo-set.mjs +47 -0
  280. package/project-template-v2/skills/_shared/scripts/verify-asset-code-sync.mjs +268 -0
  281. package/project-template-v2/skills/_shared/scripts/verify-env.mjs +285 -0
  282. package/project-template-v2/skills/_shared/scripts/verify-placeholders.mjs +161 -0
  283. package/project-template-v2/skills/playable-autoplay/SKILL.md +176 -0
  284. package/project-template-v2/skills/playable-autoplay/agents/openai.yaml +4 -0
  285. package/project-template-v2/skills/playable-debug/SKILL.md +116 -0
  286. package/project-template-v2/skills/playable-debug/agents/openai.yaml +4 -0
  287. package/project-template-v2/skills/playable-debug/references/debug-config.md +40 -0
  288. package/project-template-v2/skills/playable-record/SKILL.md +140 -0
  289. package/project-template-v2/skills/playable-record/scripts/lib/dev-server.mjs +104 -0
  290. package/project-template-v2/skills/playable-record/scripts/lib/record-audio-bridge.js +141 -0
  291. package/project-template-v2/skills/playable-record/scripts/record-playable.mjs +425 -0
  292. package/project-template-v2/skills/playable-record/scripts/verify-contract.mjs +261 -0
  293. package/project-template-v2/skills/playable-record/scripts/verify-firstwin.mjs +398 -0
  294. package/project-template-v2/skills/playable-record/scripts/verify-fusion.mjs +322 -0
  295. package/project-template-v2/skills/playable-record/scripts/verify-lifecycle.mjs +105 -0
  296. package/project-template-v2/skills/playable-record/scripts/verify-vlm-video.mjs +230 -0
  297. package/project-template-v2/skills/playable-validate/SKILL.md +61 -0
  298. package/project-template-v2/skills/playable-validate/validation-rules.md +14 -0
  299. package/project-template-v2/skills/playable-verify-ui/SKILL.md +63 -0
  300. package/project-template-v2/skills/playable-verify-ui/scripts/lib/__init__.py +1 -0
  301. package/project-template-v2/skills/playable-verify-ui/scripts/lib/config_expr.js +218 -0
  302. package/project-template-v2/skills/playable-verify-ui/scripts/lib/node_utils.js +51 -0
  303. package/project-template-v2/skills/playable-verify-ui/scripts/lib/profile_loader.py +59 -0
  304. package/project-template-v2/skills/playable-verify-ui/scripts/lib/report.py +106 -0
  305. package/project-template-v2/skills/playable-verify-ui/scripts/lib/runtime_contract.js +116 -0
  306. package/project-template-v2/skills/playable-verify-ui/scripts/verify_fetch_antipatterns.py +59 -0
  307. package/project-template-v2/skills/playable-verify-ui/scripts/verify_hardcoded_layout.js +64 -0
  308. package/project-template-v2/skills/playable-verify-ui/scripts/verify_runtime_contract.js +58 -0
  309. package/project-template-v2/skills/playable-verify-ui/scripts/verify_text_overlap.py +333 -0
  310. package/project-template-v2/ta-workspace/scripts/.gitkeep +0 -0
  311. package/project-template-v2/tsconfig.json +20 -0
  312. package/project-template-v2/vite.config.ts +27 -0
  313. package/project-templates/batch-locks/phaser.yaml +922 -0
  314. package/project-templates/batch-locks/threejs.yaml +978 -0
  315. package/project-templates/empty/AGENTS.md +25 -0
  316. package/project-templates/empty/gitignore +33 -0
  317. package/project-templates/phaser/.claude/skills/playcraft-image-generation/SKILL.md +23 -81
  318. package/project-templates/threejs/.claude/skills/playcraft-image-generation/SKILL.md +23 -81
  319. package/dist/commands/tools-image-context.js +0 -23
  320. package/dist/utils/image-asset-context.js +0 -105
@@ -1,7 +1,8 @@
1
1
  import http from 'http';
2
- import { exec } from 'child_process';
2
+ import { execFile } from 'child_process';
3
3
  import crypto from 'crypto';
4
- import { saveGlobalConfig, loadGlobalConfig } from '../config.js';
4
+ import { loadGlobalConfig, normalizeEnvironmentBaseUrl, readEnvironmentProfile, saveEnvironmentProfile } from '../config.js';
5
+ import { activateAccount } from '../auth/account-activation.js';
5
6
  /** 默认云端 Portal(Next.js `/cli-auth` 与 API 同源时的基址;可通过 --url 或 PLAYCRAFT_URL 覆盖) */
6
7
  export const DEFAULT_PLAYCRAFT_CLOUD_BASE = 'https://playcraft.aix.intlgame.com';
7
8
  const CALLBACK_PORT = 3456;
@@ -18,10 +19,11 @@ function openBrowser(url) {
18
19
  const cmd = process.platform === 'darwin'
19
20
  ? 'open'
20
21
  : process.platform === 'win32'
21
- ? 'start'
22
+ ? 'rundll32'
22
23
  : 'xdg-open';
23
24
  try {
24
- exec(`${cmd} "${url}"`, (err, _stdout, stderr) => {
25
+ const arguments_ = process.platform === 'win32' ? ['url.dll,FileProtocolHandler', url] : [url];
26
+ execFile(cmd, arguments_, (err, _stdout, stderr) => {
25
27
  if (err) {
26
28
  console.error('Failed to open browser automatically:', err.message);
27
29
  if (stderr?.trim()) {
@@ -44,21 +46,18 @@ function resolveLoginBaseUrl(options) {
44
46
  globalConfig.url ||
45
47
  globalConfig.backendUrl ||
46
48
  DEFAULT_PLAYCRAFT_CLOUD_BASE;
47
- return raw.replace(/\/+$/, '');
49
+ const base = normalizeEnvironmentBaseUrl(raw);
50
+ if (!base)
51
+ throw new Error('Invalid environment address');
52
+ return base;
48
53
  }
49
54
  function normalizeLoginEnvironmentUrl(raw) {
50
- try {
51
- const url = new URL(raw);
52
- url.hash = '';
53
- url.search = '';
54
- url.pathname = url.pathname.replace(/\/+$/, '');
55
- return url.toString().replace(/\/+$/, '');
56
- }
57
- catch {
58
- return undefined;
59
- }
55
+ return normalizeEnvironmentBaseUrl(raw);
60
56
  }
61
57
  export function resolveExistingTokenForLoginTarget(globalConfig, backendUrl) {
58
+ const profile = readEnvironmentProfile(globalConfig, backendUrl);
59
+ if (profile)
60
+ return profile.token;
62
61
  const token = typeof globalConfig.token === 'string' ? globalConfig.token : undefined;
63
62
  const storedBackendUrl = typeof globalConfig.backendUrl === 'string'
64
63
  ? globalConfig.backendUrl
@@ -74,9 +73,17 @@ export function resolveExistingTokenForLoginTarget(globalConfig, backendUrl) {
74
73
  : undefined;
75
74
  }
76
75
  export function buildCliAuthUrl(options) {
77
- const url = new URL('/cli-auth', options.backendUrl);
76
+ const base = normalizeEnvironmentBaseUrl(options.backendUrl);
77
+ if (!base)
78
+ throw new Error('Invalid environment address');
79
+ const url = new URL(`${base}/cli-auth`);
80
+ url.searchParams.set('callbackUrl', options.callbackUrl);
78
81
  url.searchParams.set('callback', options.callbackUrl);
79
82
  url.searchParams.set('state', options.state);
83
+ if (options.codeChallenge) {
84
+ url.searchParams.set('codeChallenge', options.codeChallenge);
85
+ url.searchParams.set('codeChallengeMethod', 'S256');
86
+ }
80
87
  return url.toString();
81
88
  }
82
89
  export async function renewExistingToken(options) {
@@ -87,12 +94,20 @@ export async function renewExistingToken(options) {
87
94
  try {
88
95
  const res = await fetchFn(`${options.backendUrl}/api/settings/personal-access-tokens/current/renew`, {
89
96
  method: 'PATCH',
97
+ redirect: 'error',
98
+ signal: AbortSignal.timeout(10_000),
90
99
  headers: {
91
100
  Authorization: `Bearer ${options.token}`,
92
101
  'Content-Type': 'application/json',
93
102
  },
94
103
  body: JSON.stringify({ expiresIn: '365d' }),
95
104
  });
105
+ if (res.ok && res.json && options.onRenewed) {
106
+ const result = await res.json();
107
+ if (typeof result.expiresAt === 'string' && Date.parse(result.expiresAt) > Date.now()) {
108
+ options.onRenewed(result.expiresAt);
109
+ }
110
+ }
96
111
  return res.ok;
97
112
  }
98
113
  catch {
@@ -100,29 +115,43 @@ export async function renewExistingToken(options) {
100
115
  }
101
116
  }
102
117
  export async function loginCommand(options) {
118
+ if (options.activate) {
119
+ await activateAccount(options, DEFAULT_PLAYCRAFT_CLOUD_BASE);
120
+ return;
121
+ }
103
122
  const backendUrl = resolveLoginBaseUrl(options);
104
123
  const callbackPort = options.callbackPort ?? CALLBACK_PORT;
105
124
  const globalConfig = loadGlobalConfig();
106
125
  const existingToken = resolveExistingTokenForLoginTarget(globalConfig, backendUrl);
107
126
  if (existingToken) {
108
- const renewed = await renewExistingToken({ backendUrl, token: existingToken });
127
+ let expiresAt;
128
+ const renewed = await renewExistingToken({ backendUrl, token: existingToken,
129
+ onRenewed: (value) => { expiresAt = value; } });
109
130
  if (renewed) {
110
- saveGlobalConfig({
111
- token: existingToken,
112
- url: backendUrl,
113
- backendUrl,
114
- });
131
+ const previous = readEnvironmentProfile(globalConfig, backendUrl);
132
+ saveEnvironmentProfile(backendUrl, { token: existingToken,
133
+ ...(previous?.identity ? { identity: previous.identity } : {}),
134
+ ...(expiresAt ? { expiresAt } : {}) });
115
135
  console.log('Existing CLI token renewed. Token saved to ~/.playcraft/config.json');
116
136
  return;
117
137
  }
118
138
  }
119
139
  const state = crypto.randomBytes(16).toString('hex');
140
+ const codeVerifier = crypto.randomBytes(32).toString('base64url');
141
+ const codeChallenge = crypto.createHash('sha256').update(codeVerifier).digest('base64url');
142
+ let callbackUrl = '';
143
+ let exchanging = false;
120
144
  return new Promise((resolve, reject) => {
121
145
  let timeout;
122
146
  const server = http.createServer((req, res) => {
123
- res.setHeader('Access-Control-Allow-Origin', '*');
147
+ res.setHeader('Access-Control-Allow-Origin', new URL(backendUrl).origin);
124
148
  res.setHeader('Access-Control-Allow-Methods', 'POST, OPTIONS');
125
149
  res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
150
+ if (req.headers.origin && req.headers.origin !== new URL(backendUrl).origin) {
151
+ res.writeHead(403);
152
+ res.end();
153
+ return;
154
+ }
126
155
  if (req.method === 'OPTIONS') {
127
156
  res.writeHead(204);
128
157
  res.end();
@@ -130,8 +159,9 @@ export async function loginCommand(options) {
130
159
  }
131
160
  if (req.url === '/callback' && req.method === 'POST') {
132
161
  let body = '';
133
- req.on('data', (chunk) => (body += chunk));
134
- req.on('end', () => {
162
+ req.on('data', (chunk) => { body += chunk; if (body.length > 16_384)
163
+ req.destroy(); });
164
+ req.on('end', async () => {
135
165
  try {
136
166
  const data = JSON.parse(body);
137
167
  if (data.state !== state) {
@@ -139,22 +169,36 @@ export async function loginCommand(options) {
139
169
  'Content-Type': 'text/html',
140
170
  Connection: 'close',
141
171
  });
142
- res.end('<html><body><h1>Invalid state</h1></body></html>', () => {
143
- closeServer(() => {
144
- reject(new Error('State mismatch - possible CSRF attack'));
145
- });
146
- });
172
+ res.end('<html><body><h1>Invalid state</h1></body></html>');
147
173
  return;
148
174
  }
149
- const callbackToken = typeof data.token === 'string' ? data.token : undefined;
150
- if (!callbackToken) {
151
- throw new Error('Login callback did not include a token');
175
+ if (exchanging) {
176
+ res.writeHead(409);
177
+ res.end();
178
+ return;
179
+ }
180
+ if (typeof data.code !== 'string' || !/^[A-Za-z0-9_-]{43}$/.test(data.code)) {
181
+ throw new Error('Login callback did not include an authorization code');
152
182
  }
153
- saveGlobalConfig({
154
- token: callbackToken,
155
- url: backendUrl,
156
- backendUrl,
183
+ exchanging = true;
184
+ const base = normalizeEnvironmentBaseUrl(backendUrl);
185
+ if (!base)
186
+ throw new Error('Invalid environment address');
187
+ const exchange = await fetch(`${base}/api/cloud-environments/exchange`, {
188
+ method: 'POST', redirect: 'error', signal: AbortSignal.timeout(10_000),
189
+ headers: { 'Content-Type': 'application/json' },
190
+ body: JSON.stringify({ code: data.code, state, callbackUrl, codeVerifier }),
157
191
  });
192
+ if (!exchange.ok)
193
+ throw new Error('Authorization code exchange failed');
194
+ const credential = await exchange.json();
195
+ if (!credential.token || !/^playcraft_pat_[A-Za-z0-9_-]{32}$/.test(credential.token)
196
+ || !credential.identity?.userId || !Array.isArray(credential.identity.identities)
197
+ || !credential.expiresAt || Date.parse(credential.expiresAt) <= Date.now()) {
198
+ throw new Error('Invalid environment credential response');
199
+ }
200
+ saveEnvironmentProfile(base, { token: credential.token,
201
+ identity: credential.identity, expiresAt: credential.expiresAt });
158
202
  res.writeHead(200, {
159
203
  'Content-Type': 'text/html',
160
204
  Connection: 'close',
@@ -174,9 +218,6 @@ export async function loginCommand(options) {
174
218
  catch (err) {
175
219
  const cause = err instanceof Error ? err : new Error(String(err));
176
220
  console.error('[playcraft login] Callback handling failed:', cause.message);
177
- if (cause.stack) {
178
- console.error(cause.stack);
179
- }
180
221
  const userFacing = cause instanceof SyntaxError
181
222
  ? 'Invalid JSON in login callback.'
182
223
  : `Login callback failed: ${cause.message}`;
@@ -221,14 +262,15 @@ export async function loginCommand(options) {
221
262
  });
222
263
  });
223
264
  try {
224
- server.listen(callbackPort, () => {
265
+ server.listen(callbackPort, '127.0.0.1', () => {
225
266
  const address = server.address();
226
267
  const listeningPort = typeof address === 'object' && address ? address.port : callbackPort;
227
- const callbackUrl = `http://localhost:${listeningPort}/callback`;
268
+ callbackUrl = `http://127.0.0.1:${listeningPort}/callback`;
228
269
  const authUrl = buildCliAuthUrl({
229
270
  backendUrl,
230
271
  callbackUrl,
231
272
  state,
273
+ codeChallenge,
232
274
  });
233
275
  console.log('Opening browser for login...');
234
276
  console.log(`If the browser does not open, visit this URL manually:\n ${authUrl}\n`);
@@ -0,0 +1,19 @@
1
+ // 此文件由 scripts/generate-platform-skills.mjs 生成,请勿手改。
2
+ // 重新生成:pnpm --filter @playcraft/cli generate:platform-skills
3
+ /** `@playcraft/skills/playcraft-skills/` 下的 Platform Skill 目录名。 */
4
+ export const PLATFORM_SKILL_DIRS = [
5
+ 'playcraft-3d-flip-sprite',
6
+ 'playcraft-asset-management',
7
+ 'playcraft-build',
8
+ 'playcraft-code-editor',
9
+ 'playcraft-create-remix',
10
+ 'playcraft-deploy',
11
+ 'playcraft-iteration-methodology',
12
+ 'playcraft-platform-intro',
13
+ 'playcraft-prefab',
14
+ 'playcraft-project-management',
15
+ 'playcraft-remix-workflow',
16
+ 'playcraft-save',
17
+ 'playcraft-template-upgrade-merge',
18
+ 'sound-effects',
19
+ ];
@@ -0,0 +1,53 @@
1
+ import { recordPlayable } from '../recording/recorder.js';
2
+ import { RecordingError, recordingMessage as t } from '../recording/messages.js';
3
+ import { dirname, join } from 'node:path';
4
+ import { captureGeneratedArtifact } from '../workspace-runtime/workspaces/artifact-receipts.js';
5
+ export async function runPlayableRecord(options) {
6
+ const interrupted = new AbortController();
7
+ const deadline = new AbortController();
8
+ const onInterrupt = () => interrupted.abort(new DOMException('Interrupted', 'AbortError'));
9
+ process.once('SIGINT', onInterrupt);
10
+ let timer;
11
+ try {
12
+ const seconds = options.timeout === undefined ? 1200 : Number(options.timeout);
13
+ if (!Number.isFinite(seconds) || seconds <= 0 || seconds > 1800)
14
+ throw new RecordingError('invalidArguments', 64);
15
+ timer = setTimeout(() => deadline.abort(new DOMException('Timed out', 'TimeoutError')), seconds * 1000);
16
+ const result = await recordPlayable(options, AbortSignal.any([interrupted.signal, deadline.signal]));
17
+ const metadata = result.envelope.result;
18
+ const output = result.envelope.artifacts;
19
+ if (result.envelope.ok && metadata && output?.candidate) {
20
+ const artifacts = [metadata.candidate, ...metadata.controls];
21
+ const paths = [output.candidate, ...(output.controls ?? [])];
22
+ for (let index = 0; index < artifacts.length; index++) {
23
+ const artifact = artifacts[index], path = paths[index];
24
+ if (!path)
25
+ continue;
26
+ const thumbnail = artifact.screenshots?.[0]?.path;
27
+ await captureGeneratedArtifact(path, 'playable record', {
28
+ width: artifact.video.source.width, height: artifact.video.source.height,
29
+ duration: artifact.video.source.durationMs / 1000,
30
+ ...(thumbnail ? { thumbnailPath: join(dirname(output.metadata), thumbnail) } : {}),
31
+ });
32
+ }
33
+ }
34
+ process.stdout.write(`${JSON.stringify(result.envelope)}\n`);
35
+ process.exitCode = result.exitCode;
36
+ }
37
+ catch (error) {
38
+ const code = error instanceof RecordingError ? error.code : 'recordingFailed';
39
+ process.stdout.write(`${JSON.stringify({ schemaVersion: 'playcraft.video-cli.v1', command: 'playable record', state: 'failed', ok: false, error: { code, message: t(code) } })}\n`);
40
+ process.exitCode = error instanceof RecordingError ? error.exitCode : 4;
41
+ }
42
+ finally {
43
+ if (timer)
44
+ clearTimeout(timer);
45
+ process.removeListener('SIGINT', onInterrupt);
46
+ }
47
+ }
48
+ export function registerPlayableCommands(program) {
49
+ program.command('playable').description(t('topic')).command('record').description(t('record'))
50
+ .option('--url <url>', t('url')).option('--scenario <file>', t('scenario')).option('--out <directory>', t('out'))
51
+ .option('--browser-path <path>', t('browser')).option('--timeout <seconds>', t('timeout'))
52
+ .action(runPlayableRecord);
53
+ }
@@ -18,6 +18,7 @@
18
18
  * 3. <cwd>/node_modules/@playcraft/skills/playcraft-skills(兼容旧路径 skills)
19
19
  * 4. <CLI 自身>/../../skills/playcraft-skills(monorepo 开发时,兼容旧路径 skills)
20
20
  */
21
+ import { registerProjectSkillsCommands } from '../project-skills/commands.js';
21
22
  import * as fs from 'node:fs';
22
23
  import * as path from 'node:path';
23
24
  import { getFusionHintsForGameplayAtoms, } from '../atom-tree/fusion-hints.js';
@@ -1430,6 +1431,7 @@ export function registerSkillsCommands(program) {
1430
1431
  const skills = program
1431
1432
  .command('skills')
1432
1433
  .description('开发时 Skill 发现与脚手架工具(按 engine/intent 推荐 skill 集合,或将 ref 文件复制到项目)');
1434
+ registerProjectSkillsCommands(skills);
1433
1435
  // 公共选项:skills 目录
1434
1436
  const addSkillsDirOption = (cmd) => cmd.option('--skills-dir <path>', '指定 skills 目录路径(默认自动发现)');
1435
1437
  // ── list ──────────────────────────────────────────────────────────────────
@@ -2,6 +2,7 @@ import { writeFileSync, mkdirSync, readFileSync } from 'fs';
2
2
  import { dirname } from 'path';
3
3
  import { AgentApiClient } from '../utils/agent-api-client.js';
4
4
  import { mimeTypeForImagePath, handleError } from './tools-utils.js';
5
+ import { captureGeneratedArtifact } from '../workspace-runtime/workspaces/artifact-receipts.js';
5
6
  const AVATAR_TEMPLATES = [
6
7
  'basketball', 'badminton', 'pingpong', 'gymnastics', 'pilidance',
7
8
  'tennis', 'athletics', 'footballboykicking1', 'footballboykicking2',
@@ -148,6 +149,9 @@ export function register3DCommands(tools) {
148
149
  writeFileSync(opts.savePreview, previewBuf);
149
150
  console.log(` Preview saved: ${opts.savePreview}`);
150
151
  }
152
+ await captureGeneratedArtifact(opts.output, 'generate-3d', {
153
+ ...(opts.savePreview && result.previewImageBase64 ? { thumbnailPath: opts.savePreview } : {}),
154
+ });
151
155
  console.log(`\nNext steps:`);
152
156
  console.log(` Inspect: playcraft 3d info ${opts.output}`);
153
157
  console.log(` Apply texture: playcraft tools generate-texture --model-url <url> --reference-image <img> --output out.glb`);
@@ -209,6 +213,7 @@ export function register3DCommands(tools) {
209
213
  const outDir = dirname(opts.output);
210
214
  mkdirSync(outDir, { recursive: true });
211
215
  writeFileSync(opts.output, buf);
216
+ await captureGeneratedArtifact(opts.output, 'generate-texture');
212
217
  const sizeMB = (buf.length / 1024 / 1024).toFixed(2);
213
218
  console.log(`\nTextured model saved: ${opts.output} (${sizeMB}MB, ${elapsed}s)`);
214
219
  if (result.resultUrl)
@@ -253,6 +258,7 @@ export function register3DCommands(tools) {
253
258
  const outDir = dirname(opts.output);
254
259
  mkdirSync(outDir, { recursive: true });
255
260
  writeFileSync(opts.output, buf);
261
+ await captureGeneratedArtifact(opts.output, 'retopology');
256
262
  const sizeMB = (buf.length / 1024 / 1024).toFixed(2);
257
263
  console.log(`\nRetopologized model saved: ${opts.output} (${sizeMB}MB, ${elapsed}s)`);
258
264
  }
@@ -289,6 +295,7 @@ export function register3DCommands(tools) {
289
295
  const outDir = dirname(opts.output);
290
296
  mkdirSync(outDir, { recursive: true });
291
297
  writeFileSync(opts.output, buf);
298
+ await captureGeneratedArtifact(opts.output, 'uv-unwrap');
292
299
  const sizeMB = (buf.length / 1024 / 1024).toFixed(2);
293
300
  console.log(`\nUV-unwrapped model saved: ${opts.output} (${sizeMB}MB, ${elapsed}s)`);
294
301
  }
@@ -324,6 +331,7 @@ export function register3DCommands(tools) {
324
331
  const outDir = dirname(opts.output);
325
332
  mkdirSync(outDir, { recursive: true });
326
333
  writeFileSync(opts.output, buf);
334
+ await captureGeneratedArtifact(opts.output, 'convert-3d');
327
335
  const sizeMB = (buf.length / 1024 / 1024).toFixed(2);
328
336
  console.log(`\nConverted model saved: ${opts.output} (${sizeMB}MB, ${elapsed}s)`);
329
337
  console.log(` Format: ${result.format.toUpperCase()}`);
@@ -359,6 +367,7 @@ export function register3DCommands(tools) {
359
367
  const outDir = dirname(opts.output);
360
368
  mkdirSync(outDir, { recursive: true });
361
369
  writeFileSync(opts.output, buf);
370
+ await captureGeneratedArtifact(opts.output, 'generate-3d-parts');
362
371
  const sizeMB = (buf.length / 1024 / 1024).toFixed(2);
363
372
  console.log(`\n3D parts model saved: ${opts.output} (${sizeMB}MB, ${elapsed}s)`);
364
373
  }
@@ -402,6 +411,7 @@ export function register3DCommands(tools) {
402
411
  const outDir = dirname(opts.output);
403
412
  mkdirSync(outDir, { recursive: true });
404
413
  writeFileSync(opts.output, buf);
414
+ await captureGeneratedArtifact(opts.output, 'generate-motion');
405
415
  const sizeMB = (buf.length / 1024 / 1024).toFixed(2);
406
416
  console.log(`\nMotion FBX saved: ${opts.output} (${sizeMB}MB, ${elapsed}s)`);
407
417
  }
@@ -463,6 +473,7 @@ export function register3DCommands(tools) {
463
473
  const outDir = dirname(opts.output);
464
474
  mkdirSync(outDir, { recursive: true });
465
475
  writeFileSync(opts.output, buf);
476
+ await captureGeneratedArtifact(opts.output, 'generate-avatar');
466
477
  const sizeMB = (buf.length / 1024 / 1024).toFixed(2);
467
478
  console.log(`\n3D avatar saved: ${opts.output} (${sizeMB}MB, ${elapsed}s)`);
468
479
  }
@@ -0,0 +1,87 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import { batchCreateSchema, batchPromptCreateSchema } from '@playcraft/common/cloud-workspace/runtime';
3
+ import { AgentApiClient } from '../utils/agent-api-client.js';
4
+ import { handleError } from './tools-utils.js';
5
+ const messages = {
6
+ en: {
7
+ create: 'Create and automatically run independent Remix variants from one prompt',
8
+ input: 'Path to the batch draft JSON (JavaScript Object Notation, 数据交换格式) file',
9
+ status: 'Read a batch and its generation status',
10
+ id: 'Batch identifier',
11
+ created: 'Batch created',
12
+ prompt: 'The complete batch generation request', workspace: 'Source workspace identifier',
13
+ directory: 'Source engineering directory; defaults to basic', request: 'Stable batch creation identity for retries',
14
+ team: 'Team identifier for a standalone workspace', invalidMode: 'Provide either --prompt with --workspace-id or --input, exclusively.',
15
+ requestRequired: 'Prompt creation requires a stable --client-request-id; reuse it when retrying the same request.',
16
+ help: '\nUse --prompt with --workspace-id and a stable --client-request-id. The platform plans and generates independent Remix directories while preserving the source. An omitted count defaults to 3; maximum 10. --input also accepts existing structured drafts.\n',
17
+ confirmation: 'Generation is queued. Read the batch status to track its results.',
18
+ review: 'This existing draft requires baseline approval in the browser.',
19
+ },
20
+ zh: {
21
+ create: '用一个 prompt 创建并自动生成独立 Remix 变体',
22
+ input: '批次草案 JSON (JavaScript Object Notation, 数据交换格式) 文件路径',
23
+ status: '查询批次及生成状态',
24
+ id: '批次标识',
25
+ created: '批次已创建',
26
+ prompt: '完整的批量生成需求', workspace: '来源 workspace 标识',
27
+ directory: '来源工程目录,默认 basic', request: '用于重试的稳定批次创建标识',
28
+ team: '独立工作区所属团队标识', invalidMode: '请选择 --prompt 配合 --workspace-id,或 --input,两种方式不能同时使用。',
29
+ requestRequired: '使用 prompt 创建时必须提供稳定的 --client-request-id,重试同一请求时复用该标识。',
30
+ help: '\n使用 --prompt、--workspace-id 和稳定的 --client-request-id。平台自动规划,在副本中生成独立 Remix,保持来源不变。未指定数量默认 3 个,最多 10 个。--input 也兼容现有结构化草案。\n',
31
+ confirmation: '生成已排队,可查询批次状态追踪各项成果。',
32
+ review: '此历史草案需要在浏览器确认基准。',
33
+ },
34
+ };
35
+ const language = () => /^zh/i.test(process.env.PLAYCRAFT_LANG ?? process.env.LANG ?? '') ? messages.zh : messages.en;
36
+ export async function createBatchDraft(path, client = new AgentApiClient()) {
37
+ const draft = batchCreateSchema.parse(JSON.parse(await readFile(path, 'utf8')));
38
+ return client.post('/create-batch', draft);
39
+ }
40
+ export async function createPromptBatch(input, client = new AgentApiClient()) {
41
+ return client.post('/create-batch', batchPromptCreateSchema.parse(input));
42
+ }
43
+ export function registerBatchCommands(tools) {
44
+ const t = language();
45
+ tools.command('create-batch')
46
+ .description(t.create)
47
+ .option('--input <path>', t.input)
48
+ .option('--prompt <text>', t.prompt)
49
+ .option('--workspace-id <id>', t.workspace)
50
+ .option('--source-directory <name>', t.directory, 'basic')
51
+ .option('--client-request-id <id>', t.request)
52
+ .option('--team-id <id>', t.team)
53
+ .option('--json', 'JSON (JavaScript Object Notation, 数据交换格式)')
54
+ .addHelpText('after', t.help)
55
+ .action(async (opts) => {
56
+ try {
57
+ if (Boolean(opts.input) === Boolean(opts.prompt))
58
+ throw new Error(t.invalidMode);
59
+ if (opts.prompt && !opts.workspaceId)
60
+ throw new Error(t.invalidMode);
61
+ if (opts.prompt && !opts.clientRequestId)
62
+ throw new Error(t.requestRequired);
63
+ const result = opts.input ? await createBatchDraft(opts.input) : await createPromptBatch({
64
+ clientRequestId: opts.clientRequestId, prompt: opts.prompt,
65
+ sourceWorkspaceId: opts.workspaceId, sourceDirectoryName: opts.sourceDirectory,
66
+ ...(opts.teamId ? { teamId: opts.teamId } : {}),
67
+ });
68
+ console.log(opts.json ? JSON.stringify(result, null, 2) : `${t.created}: ${result.batchId}\n${result.manifest.autoStart ? t.confirmation : t.review}`);
69
+ }
70
+ catch (error) {
71
+ handleError(error);
72
+ }
73
+ });
74
+ tools.command('get-batch')
75
+ .description(t.status)
76
+ .requiredOption('--batch-id <id>', t.id)
77
+ .action(async (opts) => {
78
+ try {
79
+ const client = new AgentApiClient();
80
+ const result = await client.get('/get-batch', { batchId: opts.batchId });
81
+ console.log(JSON.stringify(result, null, 2));
82
+ }
83
+ catch (error) {
84
+ handleError(error);
85
+ }
86
+ });
87
+ }