@playcraft/cli 0.1.40-beta → 0.1.42

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 (368) hide show
  1. package/README.md +3 -58
  2. package/dist/agent/local-backend.js +1 -1
  3. package/dist/cli-root-help.js +0 -2
  4. package/dist/commands/build-all.js +19 -5
  5. package/dist/commands/build.js +28 -15
  6. package/dist/commands/create.js +33 -26
  7. package/dist/commands/image.js +6 -2
  8. package/dist/commands/login.js +42 -84
  9. package/dist/commands/skills.js +0 -4
  10. package/dist/commands/tools-3d.js +0 -11
  11. package/dist/commands/tools-generation.js +137 -69
  12. package/dist/commands/tools-image-context.js +29 -0
  13. package/dist/commands/tools-utils.js +12 -0
  14. package/dist/commands/tools.js +2 -2
  15. package/dist/config.js +4 -59
  16. package/dist/index.js +3 -40
  17. package/dist/remix/clone.js +0 -2
  18. package/dist/remix/init-template.js +0 -2
  19. package/dist/remix/pull.js +0 -2
  20. package/dist/remix/push.js +1 -21
  21. package/dist/utils/agent-api-client.js +30 -185
  22. package/dist/utils/image-asset-context.js +228 -0
  23. package/dist/utils/playcraft-http-client.js +4 -6
  24. package/package.json +5 -15
  25. package/project-template/.claude/agents/refs/ta-image-generation-detail.md +67 -123
  26. package/project-template/.claude/skills/playcraft-image-generation/SKILL.md +135 -63
  27. package/project-template/.claude/skills/playcraft-image-generation/reference/gen-batch-sprite.template.mjs +42 -33
  28. package/project-template/.claude/skills/playcraft-image-generation/reference/gen-batch.template.mjs +18 -20
  29. package/project-template/.claude/skills/playcraft-image-generation/reference/process-batch.template.mjs +6 -7
  30. package/project-template/.claude/skills/playcraft-image-generation/reference/prompt-cookbook.md +39 -39
  31. package/project-templates/phaser/.claude/agents/refs/ta-image-generation-detail.md +59 -59
  32. package/project-templates/phaser/.claude/skills/playcraft-image-generation/SKILL.md +135 -63
  33. package/project-templates/phaser/.claude/skills/playcraft-image-generation/reference/gen-batch-sprite.template.mjs +42 -33
  34. package/project-templates/phaser/.claude/skills/playcraft-image-generation/reference/gen-batch.template.mjs +18 -20
  35. package/project-templates/phaser/.claude/skills/playcraft-image-generation/reference/process-batch.template.mjs +6 -7
  36. package/project-templates/phaser/.claude/skills/playcraft-image-generation/reference/prompt-cookbook.md +39 -39
  37. package/project-templates/threejs/.claude/agents/refs/ta-image-generation-detail.md +59 -59
  38. package/project-templates/threejs/.claude/skills/playcraft-image-generation/SKILL.md +135 -63
  39. package/project-templates/threejs/.claude/skills/playcraft-image-generation/reference/gen-batch-sprite.template.mjs +42 -33
  40. package/project-templates/threejs/.claude/skills/playcraft-image-generation/reference/gen-batch.template.mjs +18 -20
  41. package/project-templates/threejs/.claude/skills/playcraft-image-generation/reference/process-batch.template.mjs +6 -7
  42. package/project-templates/threejs/.claude/skills/playcraft-image-generation/reference/prompt-cookbook.md +39 -39
  43. package/SKILLS_REGISTRY.md +0 -149
  44. package/dist/auth/account-activation.js +0 -157
  45. package/dist/auth/messages.js +0 -30
  46. package/dist/commands/build-temp.js +0 -26
  47. package/dist/commands/platform-skills.generated.js +0 -19
  48. package/dist/commands/playable.js +0 -53
  49. package/dist/commands/tools-batch.js +0 -87
  50. package/dist/commands/video.js +0 -111
  51. package/dist/commands/workspace-runtime.js +0 -54
  52. package/dist/commands/workspace.js +0 -35
  53. package/dist/project-skills/builtin-import.js +0 -225
  54. package/dist/project-skills/commands.js +0 -77
  55. package/dist/project-skills/lifecycle.js +0 -28
  56. package/dist/project-skills/local-run.js +0 -112
  57. package/dist/project-skills/local-store.js +0 -221
  58. package/dist/project-skills/messages.js +0 -80
  59. package/dist/project-skills/reconcile.js +0 -62
  60. package/dist/project-skills/remote-cache.js +0 -51
  61. package/dist/project-skills/validation.js +0 -19
  62. package/dist/recording/messages.js +0 -40
  63. package/dist/recording/project-digest.js +0 -85
  64. package/dist/recording/recorder.js +0 -293
  65. package/dist/recording/scenario.js +0 -50
  66. package/dist/skill-registry/client.js +0 -345
  67. package/dist/skill-registry/commands.js +0 -180
  68. package/dist/skill-registry/evaluation-client.js +0 -156
  69. package/dist/skill-registry/evaluation-commands.js +0 -174
  70. package/dist/skill-registry/messages.js +0 -177
  71. package/dist/skill-registry/package.js +0 -275
  72. package/dist/skill-registry/platform-package.js +0 -100
  73. package/dist/utils/environment-endpoints.js +0 -17
  74. package/dist/utils/tool-operation-journal.js +0 -124
  75. package/dist/utils/video-evidence-git.js +0 -81
  76. package/dist/video-replication/client.js +0 -222
  77. package/dist/video-replication/comparison-evidence.js +0 -149
  78. package/dist/video-replication/comparison-files.js +0 -57
  79. package/dist/video-replication/comparison-log.js +0 -72
  80. package/dist/video-replication/comparison-messages.js +0 -104
  81. package/dist/video-replication/comparison-output.js +0 -63
  82. package/dist/video-replication/comparison-preflight.js +0 -66
  83. package/dist/video-replication/comparison.js +0 -216
  84. package/dist/video-replication/evidence.js +0 -165
  85. package/dist/video-replication/media.js +0 -111
  86. package/dist/video-replication/messages.js +0 -89
  87. package/dist/workspace/messages.js +0 -35
  88. package/dist/workspace/transfer-client.js +0 -174
  89. package/dist/workspace-runtime/codex/app-server.js +0 -1040
  90. package/dist/workspace-runtime/codex/compact.js +0 -46
  91. package/dist/workspace-runtime/codex/config-toml.js +0 -107
  92. package/dist/workspace-runtime/codex/jsonrpc-stdio.js +0 -145
  93. package/dist/workspace-runtime/codex/launcher-client.js +0 -177
  94. package/dist/workspace-runtime/codex/loopback.js +0 -146
  95. package/dist/workspace-runtime/codex/mcp-config.js +0 -39
  96. package/dist/workspace-runtime/codex/model-broker.js +0 -263
  97. package/dist/workspace-runtime/codex/native-adapter.js +0 -9
  98. package/dist/workspace-runtime/codex/permissions.js +0 -70
  99. package/dist/workspace-runtime/codex/thread-image.js +0 -38
  100. package/dist/workspace-runtime/codex/ui-instructions.js +0 -4
  101. package/dist/workspace-runtime/codex/ui-tools.js +0 -183
  102. package/dist/workspace-runtime/codex/work-mode.js +0 -15
  103. package/dist/workspace-runtime/main.js +0 -10
  104. package/dist/workspace-runtime/persistence/journal.js +0 -365
  105. package/dist/workspace-runtime/persistence/native-subagents.js +0 -162
  106. package/dist/workspace-runtime/persistence/sqlite-client.js +0 -77
  107. package/dist/workspace-runtime/persistence/sqlite-worker.js +0 -1161
  108. package/dist/workspace-runtime/persistence/stopped-execution.js +0 -77
  109. package/dist/workspace-runtime/previews/output.js +0 -36
  110. package/dist/workspace-runtime/previews/publish.js +0 -262
  111. package/dist/workspace-runtime/previews/single-file.js +0 -43
  112. package/dist/workspace-runtime/processes/managed-writes.js +0 -69
  113. package/dist/workspace-runtime/production/content-changes.js +0 -36
  114. package/dist/workspace-runtime/production/contracts.js +0 -19
  115. package/dist/workspace-runtime/production/controller.js +0 -218
  116. package/dist/workspace-runtime/production/dependencies.js +0 -60
  117. package/dist/workspace-runtime/production/native.js +0 -212
  118. package/dist/workspace-runtime/production/processes.js +0 -102
  119. package/dist/workspace-runtime/production/save-command.js +0 -35
  120. package/dist/workspace-runtime/production/source-archive.js +0 -137
  121. package/dist/workspace-runtime/production/source-copy.js +0 -91
  122. package/dist/workspace-runtime/production/stages.js +0 -334
  123. package/dist/workspace-runtime/production/storage.js +0 -75
  124. package/dist/workspace-runtime/serve.js +0 -214
  125. package/dist/workspace-runtime/server/auth.js +0 -21
  126. package/dist/workspace-runtime/server/connectivity.js +0 -201
  127. package/dist/workspace-runtime/server/desktop-host.js +0 -429
  128. package/dist/workspace-runtime/server/desktop-preview-config.js +0 -81
  129. package/dist/workspace-runtime/server/desktop-preview-service.js +0 -157
  130. package/dist/workspace-runtime/server/desktop-preview-static.js +0 -53
  131. package/dist/workspace-runtime/server/desktop-preview.js +0 -189
  132. package/dist/workspace-runtime/server/dispatch.js +0 -1217
  133. package/dist/workspace-runtime/server/endpoints.js +0 -21
  134. package/dist/workspace-runtime/server/execution-group.js +0 -90
  135. package/dist/workspace-runtime/server/http.js +0 -1243
  136. package/dist/workspace-runtime/server/lock.js +0 -53
  137. package/dist/workspace-runtime/server/rfb-watch-gate.js +0 -168
  138. package/dist/workspace-runtime/server/source-uploader.js +0 -138
  139. package/dist/workspace-runtime/server/terminal-host.js +0 -366
  140. package/dist/workspace-runtime/server/types.js +0 -1
  141. package/dist/workspace-runtime/server/workspace-transfer.js +0 -99
  142. package/dist/workspace-runtime/workspaces/archive.js +0 -207
  143. package/dist/workspace-runtime/workspaces/artifact-events.js +0 -55
  144. package/dist/workspace-runtime/workspaces/artifact-files.js +0 -197
  145. package/dist/workspace-runtime/workspaces/artifact-receipts.js +0 -89
  146. package/dist/workspace-runtime/workspaces/artifacts.js +0 -282
  147. package/dist/workspace-runtime/workspaces/attachment-content.js +0 -123
  148. package/dist/workspace-runtime/workspaces/attachments.js +0 -185
  149. package/dist/workspace-runtime/workspaces/batch-commit.js +0 -142
  150. package/dist/workspace-runtime/workspaces/cli-context.js +0 -23
  151. package/dist/workspace-runtime/workspaces/cli-credentials.js +0 -111
  152. package/dist/workspace-runtime/workspaces/composer-context.js +0 -147
  153. package/dist/workspace-runtime/workspaces/context-error.js +0 -2
  154. package/dist/workspace-runtime/workspaces/dependencies.js +0 -264
  155. package/dist/workspace-runtime/workspaces/developer-command.js +0 -64
  156. package/dist/workspace-runtime/workspaces/directories.js +0 -133
  157. package/dist/workspace-runtime/workspaces/directory-access.js +0 -37
  158. package/dist/workspace-runtime/workspaces/directory-attachments.js +0 -245
  159. package/dist/workspace-runtime/workspaces/directory-maintenance.js +0 -111
  160. package/dist/workspace-runtime/workspaces/directory-stats.js +0 -49
  161. package/dist/workspace-runtime/workspaces/disk-snapshot.js +0 -31
  162. package/dist/workspace-runtime/workspaces/empty-template.js +0 -42
  163. package/dist/workspace-runtime/workspaces/files-search.js +0 -116
  164. package/dist/workspace-runtime/workspaces/files.js +0 -138
  165. package/dist/workspace-runtime/workspaces/git-credential-broker.js +0 -312
  166. package/dist/workspace-runtime/workspaces/git-credential-helper.js +0 -68
  167. package/dist/workspace-runtime/workspaces/git-direct-store.js +0 -155
  168. package/dist/workspace-runtime/workspaces/git.js +0 -670
  169. package/dist/workspace-runtime/workspaces/instructions.js +0 -56
  170. package/dist/workspace-runtime/workspaces/json5-edit.js +0 -170
  171. package/dist/workspace-runtime/workspaces/library-attachments.js +0 -268
  172. package/dist/workspace-runtime/workspaces/optional-platform-skills.js +0 -14
  173. package/dist/workspace-runtime/workspaces/parameters.js +0 -272
  174. package/dist/workspace-runtime/workspaces/prepare.js +0 -278
  175. package/dist/workspace-runtime/workspaces/registry.js +0 -251
  176. package/dist/workspace-runtime/workspaces/remix-references.js +0 -301
  177. package/dist/workspace-runtime/workspaces/revisions.js +0 -31
  178. package/dist/workspace-runtime/workspaces/session-image.js +0 -104
  179. package/dist/workspace-runtime/workspaces/shared-git.js +0 -40
  180. package/dist/workspace-runtime/workspaces/skill-artifact-loader.js +0 -95
  181. package/dist/workspace-runtime/workspaces/skills.js +0 -191
  182. package/dist/workspace-runtime/workspaces/transfer.js +0 -288
  183. package/dist/workspace-runtime/workspaces/turn-changes.js +0 -75
  184. package/dist/workspace-runtime/workspaces/video-attachments.js +0 -49
  185. package/project-template/.claude/skills/playcraft-replica-validation/SKILL.md +0 -25
  186. package/project-template/.claude/skills/playcraft-replica-validation/references/contracts.md +0 -79
  187. package/project-template/.claude/skills/playcraft-video-understanding/SKILL.md +0 -29
  188. package/project-template/.claude/skills/playcraft-video-understanding/references/contracts.md +0 -37
  189. package/project-template-v2/.claude/agents/artist.md +0 -82
  190. package/project-template-v2/.claude/agents/developer.md +0 -153
  191. package/project-template-v2/.claude/agents/game-designer.md +0 -264
  192. package/project-template-v2/.claude/agents/refs/artist-art-style-catalog.md +0 -533
  193. package/project-template-v2/.claude/agents/refs/artist-color-audio-recipes.md +0 -153
  194. package/project-template-v2/.claude/agents/refs/artist-dimension-axis.md +0 -27
  195. package/project-template-v2/.claude/agents/refs/artist-master-composite-recipes.md +0 -208
  196. package/project-template-v2/.claude/agents/refs/atom-skill-library.md +0 -81
  197. package/project-template-v2/.claude/agents/refs/developer-impl-cookbook.md +0 -432
  198. package/project-template-v2/.claude/agents/refs/framework-5-component-filter.md +0 -252
  199. package/project-template-v2/.claude/agents/refs/framework-game-feel-juice.md +0 -266
  200. package/project-template-v2/.claude/agents/refs/framework-mda.md +0 -147
  201. package/project-template-v2/.claude/agents/refs/game-designer-gameplay-sufficiency.md +0 -123
  202. package/project-template-v2/.claude/agents/refs/ta-3d-flip-recipe.md +0 -88
  203. package/project-template-v2/.claude/agents/refs/ta-atlas-deliverable-standard.md +0 -67
  204. package/project-template-v2/.claude/agents/refs/ta-batch-pipeline-recipes.md +0 -120
  205. package/project-template-v2/.claude/agents/refs/ta-image-generation-detail.md +0 -300
  206. package/project-template-v2/.claude/agents/refs/ta-image-ops-reference.md +0 -495
  207. package/project-template-v2/.claude/agents/refs/ta-pipeline-cookbook.md +0 -1141
  208. package/project-template-v2/.claude/agents/refs/ta-tools-reference.md +0 -111
  209. package/project-template-v2/.claude/agents/refs/ta-vfx-preset-catalog.md +0 -365
  210. package/project-template-v2/.claude/agents/refs/threejs-cannon-pitfalls.md +0 -412
  211. package/project-template-v2/.claude/agents/reviewer.md +0 -75
  212. package/project-template-v2/.claude/agents/technical-artist.md +0 -86
  213. package/project-template-v2/.claude/hooks/snapshot-milestone.mjs +0 -243
  214. package/project-template-v2/.claude/hooks/user-prompt.mjs +0 -133
  215. package/project-template-v2/.claude/settings.json +0 -33
  216. package/project-template-v2/.claude/skills/brainstorming/SKILL.md +0 -161
  217. package/project-template-v2/.claude/skills/brainstorming/scripts/frame-template.html +0 -270
  218. package/project-template-v2/.claude/skills/brainstorming/scripts/helper.js +0 -177
  219. package/project-template-v2/.claude/skills/brainstorming/scripts/server.cjs +0 -354
  220. package/project-template-v2/.claude/skills/brainstorming/scripts/start-server.sh +0 -148
  221. package/project-template-v2/.claude/skills/brainstorming/scripts/stop-server.sh +0 -56
  222. package/project-template-v2/.claude/skills/brainstorming/scripts/wait-for-selection.sh +0 -62
  223. package/project-template-v2/.claude/skills/brainstorming/spec-document-reviewer-prompt.md +0 -49
  224. package/project-template-v2/.claude/skills/brainstorming/visual-companion.md +0 -309
  225. package/project-template-v2/.claude/skills/playcraft-ad-psychology/SKILL.md +0 -182
  226. package/project-template-v2/.claude/skills/playcraft-art-style-guide/SKILL.md +0 -123
  227. package/project-template-v2/.claude/skills/playcraft-asset-state-sheet/SKILL.md +0 -205
  228. package/project-template-v2/.claude/skills/playcraft-audio-generation/SKILL.md +0 -280
  229. package/project-template-v2/.claude/skills/playcraft-batch-pipeline/SKILL.md +0 -184
  230. package/project-template-v2/.claude/skills/playcraft-build-optimizer/SKILL.md +0 -306
  231. package/project-template-v2/.claude/skills/playcraft-image-generation/SKILL.md +0 -298
  232. package/project-template-v2/.claude/skills/playcraft-image-generation/reference/build-sprite-sheet.template.mjs +0 -123
  233. package/project-template-v2/.claude/skills/playcraft-image-generation/reference/compare-style.template.mjs +0 -254
  234. package/project-template-v2/.claude/skills/playcraft-image-generation/reference/gen-batch-sprite.template.mjs +0 -324
  235. package/project-template-v2/.claude/skills/playcraft-image-generation/reference/gen-batch.template.mjs +0 -97
  236. package/project-template-v2/.claude/skills/playcraft-image-generation/reference/gen-edit-variants.template.mjs +0 -118
  237. package/project-template-v2/.claude/skills/playcraft-image-generation/reference/process-batch.template.mjs +0 -137
  238. package/project-template-v2/.claude/skills/playcraft-image-generation/reference/prompt-cookbook.md +0 -397
  239. package/project-template-v2/.claude/skills/playcraft-image-generation/reference/validate-sprite-sheet.template.mjs +0 -296
  240. package/project-template-v2/.claude/skills/playcraft-image-ops/SKILL.md +0 -122
  241. package/project-template-v2/.claude/skills/playcraft-image-processing/SKILL.md +0 -219
  242. package/project-template-v2/.claude/skills/playcraft-masking/SKILL.md +0 -373
  243. package/project-template-v2/.claude/skills/playcraft-playable-optimization/SKILL.md +0 -161
  244. package/project-template-v2/.claude/skills/playcraft-research/SKILL.md +0 -215
  245. package/project-template-v2/.claude/skills/playcraft-skill-recommender/SKILL.md +0 -382
  246. package/project-template-v2/.claude/skills/playcraft-sprite-generation/SKILL.md +0 -423
  247. package/project-template-v2/.claude/skills/playcraft-sprite-remix/SKILL.md +0 -158
  248. package/project-template-v2/.claude/skills/playcraft-sprite-sheet/SKILL.md +0 -100
  249. package/project-template-v2/.claude/skills/playcraft-storyboard/SKILL.md +0 -167
  250. package/project-template-v2/.claude/skills/playcraft-style-qa/SKILL.md +0 -270
  251. package/project-template-v2/.claude/skills/playcraft-text-rendering/SKILL.md +0 -236
  252. package/project-template-v2/.claude/skills/playcraft-vfx-animation/SKILL.md +0 -130
  253. package/project-template-v2/.claude/skills/playwright-cli/SKILL.md +0 -390
  254. package/project-template-v2/.claude/skills/playwright-cli/references/element-attributes.md +0 -23
  255. package/project-template-v2/.claude/skills/playwright-cli/references/playwright-tests.md +0 -39
  256. package/project-template-v2/.claude/skills/playwright-cli/references/request-mocking.md +0 -87
  257. package/project-template-v2/.claude/skills/playwright-cli/references/running-code.md +0 -240
  258. package/project-template-v2/.claude/skills/playwright-cli/references/session-management.md +0 -226
  259. package/project-template-v2/.claude/skills/playwright-cli/references/spec-driven-testing.md +0 -308
  260. package/project-template-v2/.claude/skills/playwright-cli/references/storage-state.md +0 -275
  261. package/project-template-v2/.claude/skills/playwright-cli/references/test-generation.md +0 -134
  262. package/project-template-v2/.claude/skills/playwright-cli/references/tracing.md +0 -142
  263. package/project-template-v2/.claude/skills/playwright-cli/references/video-recording.md +0 -153
  264. package/project-template-v2/.claude/skills/session-analyzer/SKILL.md +0 -386
  265. package/project-template-v2/.claude/skills/session-analyzer/scripts/execution-breakdown.mjs +0 -182
  266. package/project-template-v2/.claude/skills/session-analyzer/scripts/find-turns.mjs +0 -72
  267. package/project-template-v2/.claude/skills/session-analyzer/scripts/heavy-output.mjs +0 -121
  268. package/project-template-v2/.claude/skills/session-analyzer/scripts/resolve-session.mjs +0 -102
  269. package/project-template-v2/.claude/skills/session-analyzer/scripts/subagent-stats.mjs +0 -127
  270. package/project-template-v2/.claude/skills/session-analyzer/scripts/subagent-tool-timeline.mjs +0 -106
  271. package/project-template-v2/.claude/skills/session-analyzer/scripts/time-gaps.mjs +0 -128
  272. package/project-template-v2/.claude/skills/session-analyzer/scripts/turn-timeline.mjs +0 -67
  273. package/project-template-v2/.claude/snapshot.mjs +0 -263
  274. package/project-template-v2/.playcraft/skills.lock.json +0 -152
  275. package/project-template-v2/CLAUDE.md +0 -146
  276. package/project-template-v2/assets/audio/bgm/.gitkeep +0 -0
  277. package/project-template-v2/assets/audio/sfx/.gitkeep +0 -0
  278. package/project-template-v2/assets/bundles/.gitkeep +0 -0
  279. package/project-template-v2/assets/images/bg/.gitkeep +0 -0
  280. package/project-template-v2/assets/images/reference/.gitkeep +0 -0
  281. package/project-template-v2/assets/images/storyboard/.gitkeep +0 -0
  282. package/project-template-v2/assets/images/tiles/.gitkeep +0 -0
  283. package/project-template-v2/assets/images/ui/.gitkeep +0 -0
  284. package/project-template-v2/assets/images/vfx/.gitkeep +0 -0
  285. package/project-template-v2/assets/models/.gitkeep +0 -0
  286. package/project-template-v2/docs/harness/iteration-1/context-flow.md +0 -254
  287. package/project-template-v2/docs/harness/iteration-1/generate-flow.md +0 -91
  288. package/project-template-v2/docs/harness/iteration-1/ideate-flow.md +0 -214
  289. package/project-template-v2/docs/harness/iteration-1/optimize-flow.md +0 -75
  290. package/project-template-v2/docs/harness/iteration-1/wrapup-flow.md +0 -63
  291. package/project-template-v2/docs/harness/iteration-2/context-flow.md +0 -223
  292. package/project-template-v2/docs/harness/iteration-2/generate-flow.md +0 -129
  293. package/project-template-v2/docs/harness/iteration-2/ideate-flow.md +0 -267
  294. package/project-template-v2/docs/harness/iteration-2/optimize-flow.md +0 -164
  295. package/project-template-v2/docs/harness/iteration-2/wrapup-flow.md +0 -115
  296. package/project-template-v2/docs/harness/orchestrator-flow.md +0 -364
  297. package/project-template-v2/docs/project-state.json +0 -60
  298. package/project-template-v2/docs/project-state.md +0 -72
  299. package/project-template-v2/docs/standards/README.md +0 -225
  300. package/project-template-v2/docs/standards/agent-behavior-standards.md +0 -174
  301. package/project-template-v2/docs/standards/artifacts/design-brief.md +0 -19
  302. package/project-template-v2/docs/standards/artifacts/design.md +0 -22
  303. package/project-template-v2/docs/standards/artifacts/game-code.md +0 -41
  304. package/project-template-v2/docs/standards/artifacts/todo-list.md +0 -41
  305. package/project-template-v2/docs/standards/iter1-agent-behavior-standards.md +0 -343
  306. package/project-template-v2/game/index.ts +0 -18
  307. package/project-template-v2/globals.d.ts +0 -51
  308. package/project-template-v2/index.css +0 -34
  309. package/project-template-v2/index.html +0 -18
  310. package/project-template-v2/main.ts +0 -9
  311. package/project-template-v2/package.json +0 -46
  312. package/project-template-v2/skills/_shared/scripts/dispatch-clear.mjs +0 -31
  313. package/project-template-v2/skills/_shared/scripts/dispatch-set.mjs +0 -86
  314. package/project-template-v2/skills/_shared/scripts/dod-check.mjs +0 -153
  315. package/project-template-v2/skills/_shared/scripts/handoff-append.mjs +0 -70
  316. package/project-template-v2/skills/_shared/scripts/lib/validator-artifacts.mjs +0 -91
  317. package/project-template-v2/skills/_shared/scripts/pipeline/dod-config.mjs +0 -131
  318. package/project-template-v2/skills/_shared/scripts/pipeline/index.mjs +0 -80
  319. package/project-template-v2/skills/_shared/scripts/pipeline/iteration-1-core.mjs +0 -90
  320. package/project-template-v2/skills/_shared/scripts/pipeline/iteration-2-wrap.mjs +0 -93
  321. package/project-template-v2/skills/_shared/scripts/pipeline/iteration-3-visual.mjs +0 -23
  322. package/project-template-v2/skills/_shared/scripts/render-project-state.mjs +0 -230
  323. package/project-template-v2/skills/_shared/scripts/state-advance-stage.mjs +0 -58
  324. package/project-template-v2/skills/_shared/scripts/state-get.mjs +0 -339
  325. package/project-template-v2/skills/_shared/scripts/state-handoff.mjs +0 -39
  326. package/project-template-v2/skills/_shared/scripts/state-set.mjs +0 -94
  327. package/project-template-v2/skills/_shared/scripts/state-store.mjs +0 -783
  328. package/project-template-v2/skills/_shared/scripts/todo-add.mjs +0 -88
  329. package/project-template-v2/skills/_shared/scripts/todo-get.mjs +0 -130
  330. package/project-template-v2/skills/_shared/scripts/todo-remove.mjs +0 -33
  331. package/project-template-v2/skills/_shared/scripts/todo-set.mjs +0 -47
  332. package/project-template-v2/skills/_shared/scripts/verify-asset-code-sync.mjs +0 -268
  333. package/project-template-v2/skills/_shared/scripts/verify-env.mjs +0 -285
  334. package/project-template-v2/skills/_shared/scripts/verify-placeholders.mjs +0 -161
  335. package/project-template-v2/skills/playable-autoplay/SKILL.md +0 -176
  336. package/project-template-v2/skills/playable-autoplay/agents/openai.yaml +0 -4
  337. package/project-template-v2/skills/playable-debug/SKILL.md +0 -116
  338. package/project-template-v2/skills/playable-debug/agents/openai.yaml +0 -4
  339. package/project-template-v2/skills/playable-debug/references/debug-config.md +0 -40
  340. package/project-template-v2/skills/playable-record/SKILL.md +0 -140
  341. package/project-template-v2/skills/playable-record/scripts/lib/dev-server.mjs +0 -104
  342. package/project-template-v2/skills/playable-record/scripts/lib/record-audio-bridge.js +0 -141
  343. package/project-template-v2/skills/playable-record/scripts/record-playable.mjs +0 -425
  344. package/project-template-v2/skills/playable-record/scripts/verify-contract.mjs +0 -261
  345. package/project-template-v2/skills/playable-record/scripts/verify-firstwin.mjs +0 -398
  346. package/project-template-v2/skills/playable-record/scripts/verify-fusion.mjs +0 -322
  347. package/project-template-v2/skills/playable-record/scripts/verify-lifecycle.mjs +0 -105
  348. package/project-template-v2/skills/playable-record/scripts/verify-vlm-video.mjs +0 -230
  349. package/project-template-v2/skills/playable-validate/SKILL.md +0 -61
  350. package/project-template-v2/skills/playable-validate/validation-rules.md +0 -14
  351. package/project-template-v2/skills/playable-verify-ui/SKILL.md +0 -63
  352. package/project-template-v2/skills/playable-verify-ui/scripts/lib/__init__.py +0 -1
  353. package/project-template-v2/skills/playable-verify-ui/scripts/lib/config_expr.js +0 -218
  354. package/project-template-v2/skills/playable-verify-ui/scripts/lib/node_utils.js +0 -51
  355. package/project-template-v2/skills/playable-verify-ui/scripts/lib/profile_loader.py +0 -59
  356. package/project-template-v2/skills/playable-verify-ui/scripts/lib/report.py +0 -106
  357. package/project-template-v2/skills/playable-verify-ui/scripts/lib/runtime_contract.js +0 -116
  358. package/project-template-v2/skills/playable-verify-ui/scripts/verify_fetch_antipatterns.py +0 -59
  359. package/project-template-v2/skills/playable-verify-ui/scripts/verify_hardcoded_layout.js +0 -64
  360. package/project-template-v2/skills/playable-verify-ui/scripts/verify_runtime_contract.js +0 -58
  361. package/project-template-v2/skills/playable-verify-ui/scripts/verify_text_overlap.py +0 -333
  362. package/project-template-v2/ta-workspace/scripts/.gitkeep +0 -0
  363. package/project-template-v2/tsconfig.json +0 -20
  364. package/project-template-v2/vite.config.ts +0 -27
  365. package/project-templates/batch-locks/phaser.yaml +0 -922
  366. package/project-templates/batch-locks/threejs.yaml +0 -978
  367. package/project-templates/empty/AGENTS.md +0 -41
  368. package/project-templates/empty/gitignore +0 -33
@@ -1,288 +0,0 @@
1
- import { spawnSync } from 'node:child_process';
2
- import { createHash, randomUUID } from 'node:crypto';
3
- import { constants } from 'node:fs';
4
- import { chmod, copyFile, lstat, mkdir, mkdtemp, open, readdir, realpath, rename, rm, rmdir, writeFile } from 'node:fs/promises';
5
- import { tmpdir } from 'node:os';
6
- import { dirname, join, resolve, sep } from 'node:path';
7
- import { isCredentialFile } from './git.js';
8
- export const WORKSPACE_TRANSFER_MAX_BYTES = 256 * 1024 * 1024;
9
- const MAX_FILES = 20_000;
10
- const EXCLUDED = new Set(['.git', 'node_modules', '.playcraft-workspace.json', '.playcraft.json']);
11
- export class WorkspaceTransferError extends Error {
12
- code;
13
- constructor(code) {
14
- super(code);
15
- this.code = code;
16
- }
17
- }
18
- function excluded(path) {
19
- const parts = path.split('/');
20
- return parts.some(part => EXCLUDED.has(part.toLowerCase()) || /^\.playcraft-(?:credential|transfer)-.*\.tmp$/i.test(part)) ||
21
- parts.some((part, index) => part === '.playcraft' && /^(?:workspace-context(?:\..*)?|video-replication)$/.test(parts[index + 1] ?? '')) ||
22
- isCredentialFile(path);
23
- }
24
- export function transferGit(root, args) {
25
- const env = { PATH: process.env.PATH, HOME: tmpdir(),
26
- GIT_CONFIG_NOSYSTEM: '1', GIT_CONFIG_GLOBAL: '/dev/null', GIT_TERMINAL_PROMPT: '0', GIT_ALLOW_PROTOCOL: 'file' };
27
- const result = spawnSync('git', ['-c', 'core.hooksPath=/dev/null', '-c', 'commit.gpgsign=false',
28
- '-c', 'core.autocrlf=false', '-c', 'user.name=PlayCraft Workspace', '-c', 'user.email=workspace@playcraft.local', ...args], { cwd: root, encoding: 'utf8', env, timeout: 120_000, maxBuffer: 8 * 1024 * 1024 });
29
- if (result.status !== 0)
30
- throw new WorkspaceTransferError('workspace_transfer_git_failed');
31
- return result.stdout.trim();
32
- }
33
- async function copySnapshot(source, target) {
34
- let bytes = 0, files = 0;
35
- async function copy(directory, prefix) {
36
- const directoryInfo = await lstat(directory);
37
- const directoryReal = await realpath(directory);
38
- if (!directoryInfo.isDirectory() || directoryInfo.isSymbolicLink() ||
39
- (directoryReal !== source && !directoryReal.startsWith(`${source}${sep}`)))
40
- throw new WorkspaceTransferError('workspace_transfer_conflict');
41
- for (const entry of await readdir(directory, { withFileTypes: true })) {
42
- const path = prefix ? `${prefix}/${entry.name}` : entry.name;
43
- if (excluded(path))
44
- continue;
45
- const from = join(directory, entry.name), to = join(target, path);
46
- const info = await lstat(from);
47
- if (info.isSymbolicLink())
48
- continue;
49
- if (info.isDirectory()) {
50
- await mkdir(to, { recursive: true });
51
- await copy(from, path);
52
- }
53
- else if (info.isFile()) {
54
- bytes += info.size;
55
- files++;
56
- if (bytes > WORKSPACE_TRANSFER_MAX_BYTES || files > MAX_FILES)
57
- throw new WorkspaceTransferError('workspace_transfer_too_large');
58
- if (!(await realpath(from)).startsWith(`${source}${sep}`))
59
- throw new WorkspaceTransferError('workspace_transfer_conflict');
60
- const handle = await open(from, constants.O_RDONLY | constants.O_NOFOLLOW);
61
- try {
62
- const before = await handle.stat();
63
- if (!before.isFile() || before.dev !== info.dev || before.ino !== info.ino || before.size !== info.size ||
64
- before.mtimeMs !== info.mtimeMs || before.ctimeMs !== info.ctimeMs)
65
- throw new WorkspaceTransferError('workspace_transfer_conflict');
66
- const chunks = [];
67
- let size = 0;
68
- for await (const chunk of handle.createReadStream({ autoClose: false })) {
69
- size += chunk.length;
70
- if (size > before.size)
71
- throw new WorkspaceTransferError('workspace_transfer_conflict');
72
- chunks.push(chunk);
73
- }
74
- const after = await handle.stat();
75
- if (size !== before.size || after.size !== before.size || after.mtimeMs !== before.mtimeMs || after.ctimeMs !== before.ctimeMs)
76
- throw new WorkspaceTransferError('workspace_transfer_conflict');
77
- await writeFile(to, Buffer.concat(chunks));
78
- }
79
- finally {
80
- await handle.close();
81
- }
82
- await chmod(to, info.mode & 0o111 ? 0o755 : 0o644);
83
- }
84
- }
85
- }
86
- await copy(source, '');
87
- }
88
- async function matchesFile(root, path, expected) {
89
- const target = await safeTarget(root, path);
90
- const info = await lstat(target).catch(error => { if (['ENOENT', 'ENOTDIR'].includes(error.code))
91
- return undefined; throw error; });
92
- if (!expected)
93
- return !info || info.isDirectory();
94
- if (!info?.isFile() || Boolean(info.mode & 0o111) !== expected.executable)
95
- return false;
96
- const handle = await open(target, constants.O_RDONLY | constants.O_NOFOLLOW);
97
- try {
98
- const stat = await handle.stat();
99
- const hash = createHash(expected.sha.length === 64 ? 'sha256' : 'sha1');
100
- hash.update(`blob ${stat.size}\0`);
101
- for await (const chunk of handle.createReadStream({ autoClose: false }))
102
- hash.update(chunk);
103
- return hash.digest('hex') === expected.sha;
104
- }
105
- finally {
106
- await handle.close();
107
- }
108
- }
109
- async function removeEmptyParents(root, target) {
110
- let parent = dirname(target);
111
- while (parent !== root) {
112
- if ((await readdir(parent)).length > 0)
113
- break;
114
- await rmdir(parent);
115
- parent = dirname(parent);
116
- }
117
- }
118
- async function installFile(root, path, source, executable) {
119
- const target = await safeTarget(root, path);
120
- const existing = await lstat(target).catch(error => { if (error.code === 'ENOENT')
121
- return undefined; throw error; });
122
- if (existing?.isDirectory())
123
- await rmdir(target);
124
- await mkdir(dirname(target), { recursive: true });
125
- const stage = join(dirname(target), `.playcraft-transfer-${randomUUID()}.tmp`);
126
- try {
127
- await copyFile(source, stage);
128
- await chmod(stage, executable ? 0o755 : 0o644);
129
- await rename(stage, target);
130
- }
131
- catch (error) {
132
- await rm(stage, { force: true }).catch(() => undefined);
133
- await removeEmptyParents(root, target).catch(() => undefined);
134
- throw error;
135
- }
136
- finally {
137
- await rm(stage, { force: true }).catch(() => undefined);
138
- }
139
- }
140
- /** A fresh history contains only the current portable files, including uncommitted changes. */
141
- export async function createWorkspaceTransfer(root) {
142
- const temporary = await mkdtemp(join(tmpdir(), 'playcraft-transfer-'));
143
- const cleanup = () => rm(temporary, { recursive: true, force: true });
144
- try {
145
- const checkout = join(temporary, 'checkout');
146
- await mkdir(checkout);
147
- await copySnapshot(await realpath(root), checkout);
148
- transferGit(checkout, ['init', '--initial-branch=main']);
149
- transferGit(checkout, ['add', '--force', '--all']);
150
- transferGit(checkout, ['commit', '--allow-empty', '-m', 'Workspace snapshot']);
151
- const tree = transferGit(checkout, ['rev-parse', 'HEAD^{tree}']);
152
- const head = transferGit(checkout, ['rev-parse', 'HEAD']);
153
- const bundlePath = join(temporary, 'workspace.bundle');
154
- transferGit(checkout, ['bundle', 'create', bundlePath, 'HEAD']);
155
- return { bundlePath, tree, head, cleanup };
156
- }
157
- catch (error) {
158
- await cleanup();
159
- throw error;
160
- }
161
- }
162
- function treeEntries(root) {
163
- const entries = transferGit(root, ['ls-tree', '-r', '-z', 'HEAD']).split('\0').filter(Boolean).map(line => {
164
- const match = /^(100644|100755) blob ([a-f0-9]{40,64})\t(.+)$/s.exec(line);
165
- if (!match)
166
- throw new WorkspaceTransferError('invalid_workspace_transfer');
167
- const path = match[3];
168
- if (path.length > 1024 || path.includes('\\') || path.startsWith('/') || /[\x00-\x1f\x7f]/.test(path) ||
169
- path.split('/').some(part => !part || part === '.' || part === '..') || excluded(path))
170
- throw new WorkspaceTransferError('invalid_workspace_transfer');
171
- return { path, sha: match[2], executable: match[1] === '100755' };
172
- });
173
- if (entries.length > MAX_FILES)
174
- throw new WorkspaceTransferError('workspace_transfer_too_large');
175
- const normalized = entries.map(entry => entry.path.toLowerCase());
176
- if (new Set(normalized).size !== entries.length)
177
- throw new WorkspaceTransferError('invalid_workspace_transfer');
178
- return entries;
179
- }
180
- async function safeTarget(root, path) {
181
- const target = resolve(root, path);
182
- if (!target.startsWith(`${root}${sep}`))
183
- throw new WorkspaceTransferError('invalid_workspace_transfer');
184
- let current = root;
185
- for (const segment of path.split('/')) {
186
- current = join(current, segment);
187
- const info = await lstat(current).catch(error => { if (['ENOENT', 'ENOTDIR'].includes(error.code))
188
- return undefined; throw error; });
189
- if (info?.isSymbolicLink())
190
- throw new WorkspaceTransferError('workspace_transfer_symlink');
191
- }
192
- return target;
193
- }
194
- /** Compare the entire checkout before applying local commits, so cloud edits are never overwritten. */
195
- export async function applyWorkspaceTransfer(root, bundle, expectedTree) {
196
- if (!/^[a-f0-9]{40,64}$/.test(expectedTree))
197
- throw new WorkspaceTransferError('invalid_workspace_transfer');
198
- if (!bundle.byteLength || bundle.byteLength > WORKSPACE_TRANSFER_MAX_BYTES)
199
- throw new WorkspaceTransferError('workspace_transfer_too_large');
200
- const rootReal = await realpath(root);
201
- const temporary = await mkdtemp(join(tmpdir(), 'playcraft-import-'));
202
- let current;
203
- try {
204
- const bundlePath = join(temporary, 'incoming.bundle'), incoming = join(temporary, 'checkout');
205
- await writeFile(bundlePath, bundle, { mode: 0o600 });
206
- transferGit(temporary, ['clone', '--no-checkout', '--', bundlePath, incoming]);
207
- const entries = treeEntries(incoming);
208
- const tree = transferGit(incoming, ['rev-parse', 'HEAD^{tree}']);
209
- const head = transferGit(incoming, ['rev-parse', 'HEAD']);
210
- let bytes = 0;
211
- for (const entry of entries) {
212
- bytes += Number(transferGit(incoming, ['cat-file', '-s', entry.sha]));
213
- if (bytes > WORKSPACE_TRANSFER_MAX_BYTES)
214
- throw new WorkspaceTransferError('workspace_transfer_too_large');
215
- }
216
- current = await createWorkspaceTransfer(rootReal);
217
- if (current.tree === tree)
218
- return { tree, head, changedPaths: [] };
219
- if (current.tree !== expectedTree)
220
- throw new WorkspaceTransferError('workspace_transfer_conflict');
221
- const baseline = join(dirname(current.bundlePath), 'checkout');
222
- const previous = new Map(treeEntries(baseline).map(entry => [entry.path, entry]));
223
- const next = new Map(entries.map(entry => [entry.path, entry]));
224
- const changedPaths = [...new Set([...previous.keys(), ...next.keys()])]
225
- .filter(path => previous.get(path)?.sha !== next.get(path)?.sha || previous.get(path)?.executable !== next.get(path)?.executable);
226
- for (const path of changedPaths) {
227
- const target = await safeTarget(rootReal, path);
228
- const info = await lstat(target).catch(error => { if (['ENOENT', 'ENOTDIR'].includes(error.code))
229
- return undefined; throw error; });
230
- if (next.has(path) && info?.isDirectory()) {
231
- const children = await readdir(target, { recursive: true });
232
- for (const child of children) {
233
- const childPath = `${path}/${child.split(sep).join('/')}`;
234
- const childInfo = await lstat(join(target, child));
235
- if (!childInfo.isDirectory() && (!previous.has(childPath) || next.has(childPath)))
236
- throw new WorkspaceTransferError('workspace_transfer_conflict');
237
- }
238
- }
239
- }
240
- transferGit(incoming, ['checkout', '--force']);
241
- const recheck = await createWorkspaceTransfer(rootReal);
242
- try {
243
- if (recheck.tree !== expectedTree)
244
- throw new WorkspaceTransferError('workspace_transfer_conflict');
245
- }
246
- finally {
247
- await recheck.cleanup();
248
- }
249
- const applied = [];
250
- try {
251
- // Deleting files first also permits a file to become a directory, or the reverse.
252
- for (const path of changedPaths.filter(path => !next.has(path)).sort((a, b) => b.length - a.length)) {
253
- if (!await matchesFile(rootReal, path, previous.get(path)))
254
- throw new WorkspaceTransferError('workspace_transfer_conflict');
255
- await rm(await safeTarget(rootReal, path), { force: true });
256
- applied.push(path);
257
- await removeEmptyParents(rootReal, join(rootReal, path));
258
- }
259
- for (const path of changedPaths.filter(path => next.has(path))) {
260
- if (!await matchesFile(rootReal, path, previous.get(path)))
261
- throw new WorkspaceTransferError('workspace_transfer_conflict');
262
- const entry = next.get(path);
263
- await installFile(rootReal, path, join(incoming, path), entry.executable);
264
- applied.push(path);
265
- }
266
- }
267
- catch (error) {
268
- // Roll back only our writes; never replace another writer's intervening content.
269
- for (const path of applied.reverse()) {
270
- if (!await matchesFile(rootReal, path, next.get(path)))
271
- throw new WorkspaceTransferError('workspace_transfer_rollback_conflict');
272
- const old = previous.get(path);
273
- if (old)
274
- await installFile(rootReal, path, join(baseline, path), old.executable);
275
- else {
276
- await rm(await safeTarget(rootReal, path), { force: true });
277
- await removeEmptyParents(rootReal, join(rootReal, path));
278
- }
279
- }
280
- throw error;
281
- }
282
- return { tree, head, changedPaths };
283
- }
284
- finally {
285
- await current?.cleanup();
286
- await rm(temporary, { recursive: true, force: true });
287
- }
288
- }
@@ -1,75 +0,0 @@
1
- import { TURN_FILES_CHANGED_EVENT, } from '@playcraft/common/cloud-workspace/runtime';
2
- import { diffWorkspaceTrees, retainTurnSnapshots, snapshotWorkspaceTree } from './git.js';
3
- import { shareWorkspaceGit } from './shared-git.js';
4
- const TURN_ENDS = new Set(['turn/completed', 'turn.completed', 'turn.interrupted', 'turn.failed']);
5
- /**
6
- * Snapshots taken when a session's turn starts, until that turn ends. Kept in memory only: a
7
- * restart ends the native turn as well, and that turn then simply reports no changes.
8
- */
9
- const started = new WeakMap();
10
- function turnsOf(state) {
11
- let turns = started.get(state);
12
- if (!turns)
13
- started.set(state, turns = new Map());
14
- return turns;
15
- }
16
- /**
17
- * Records the work tree before a turn is sent. A directory that cannot be snapshotted (no
18
- * repository, Git failure) gets no change summary; the turn itself is never held back by it.
19
- */
20
- export async function beginTurnChanges(state, sessionId, input) {
21
- turnsOf(state).delete(sessionId);
22
- if (input.directoryName === 'root')
23
- return;
24
- await shareWorkspaceGit(state, input.root).catch(() => undefined);
25
- const before = await snapshotWorkspaceTree(input.root).catch(() => undefined);
26
- if (before)
27
- turnsOf(state).set(sessionId, { ...input, before });
28
- }
29
- /** A turn that never started has nothing to compare. */
30
- export function abandonTurnChanges(state, sessionId) {
31
- turnsOf(state).delete(sessionId);
32
- }
33
- /**
34
- * When a session's turn ends, compares the work tree with its start and records the files that
35
- * changed for that session's conversation. Returns no event when nothing changed.
36
- */
37
- export async function finishTurnChanges(state, event) {
38
- const { threadId, turnId } = event;
39
- if (!TURN_ENDS.has(event.method) || !threadId || !turnId)
40
- return [];
41
- const session = state.journal.listSessions().find((item) => item.threadId === threadId);
42
- const start = session && turnsOf(state).get(session.sessionId);
43
- if (!session || !start)
44
- return [];
45
- turnsOf(state).delete(session.sessionId);
46
- try {
47
- await shareWorkspaceGit(state, start.root);
48
- const after = await snapshotWorkspaceTree(start.root);
49
- const { files, truncated } = await diffWorkspaceTrees(start.root, start.before, after);
50
- if (!files.length)
51
- return [];
52
- await retainTurnSnapshots(start.root, turnId, start.before, after);
53
- const changes = { threadId, turnId, before: start.before, after, files, truncated };
54
- return [await state.journal.allocateSourceEvent({
55
- runtimeId: state.config.runtimeId,
56
- generation: state.config.generation,
57
- runtimeEpoch: state.runtimeEpoch,
58
- type: TURN_FILES_CHANGED_EVENT,
59
- payload: {
60
- ...changes,
61
- sessionId: session.sessionId,
62
- ...(start.workspaceId ? { workspaceId: start.workspaceId } : {}),
63
- ...(start.directoryName ? { directoryName: start.directoryName } : {}),
64
- },
65
- occurredAt: new Date().toISOString(),
66
- sessionId: session.sessionId,
67
- workspaceId: start.workspaceId,
68
- threadId,
69
- turnId,
70
- })];
71
- }
72
- catch {
73
- return [];
74
- }
75
- }
@@ -1,49 +0,0 @@
1
- import { spawn } from 'node:child_process';
2
- import { createHash } from 'node:crypto';
3
- import { WORKSPACE_VIDEO_ATTACHMENT_MAX_DURATION_MS, WORKSPACE_VIDEO_ATTACHMENT_MAX_BYTES } from '@playcraft/common/cloud-workspace/runtime';
4
- /** Probe the opened descriptor so replacing a pathname cannot redirect the media reader. */
5
- export async function probeAttachmentVideo(handle) {
6
- const output = await new Promise((resolve, reject) => {
7
- const child = spawn(process.env.PLAYCRAFT_FFPROBE_PATH ?? 'ffprobe', [
8
- '-v', 'error', '-protocol_whitelist', 'file,pipe', '-show_entries',
9
- 'format=format_name,duration:stream=codec_type,width,height', '-of', 'json',
10
- process.platform === 'linux' ? '/proc/self/fd/3' : '/dev/fd/3',
11
- ], { stdio: ['ignore', 'pipe', 'ignore', handle.fd] });
12
- let text = '';
13
- const timer = setTimeout(() => { child.kill('SIGKILL'); reject(new Error('invalid_attachment')); }, 15_000);
14
- timer.unref();
15
- child.stdout.on('data', (chunk) => {
16
- text += chunk.toString('utf8');
17
- if (text.length > 64 * 1024) {
18
- child.kill('SIGKILL');
19
- reject(new Error('invalid_attachment'));
20
- }
21
- });
22
- child.once('error', () => { clearTimeout(timer); reject(new Error('video_probe_unavailable')); });
23
- child.once('close', code => { clearTimeout(timer); code === 0 ? resolve(text) : reject(new Error('invalid_attachment')); });
24
- });
25
- const result = JSON.parse(output);
26
- const stream = result.streams?.find(item => item.codec_type === 'video');
27
- const durationMs = Math.round(Number(result.format?.duration) * 1000);
28
- const formats = result.format?.format_name?.split(',') ?? [];
29
- const mimeType = formats.includes('webm') ? 'video/webm' : formats.includes('mp4') ? 'video/mp4' : formats.includes('mov') ? 'video/quicktime' : undefined;
30
- if (!stream || !mimeType || !Number.isFinite(durationMs) || durationMs <= 0 || durationMs > WORKSPACE_VIDEO_ATTACHMENT_MAX_DURATION_MS
31
- || !Number.isSafeInteger(stream.width) || !Number.isSafeInteger(stream.height) || stream.width <= 0 || stream.height <= 0
32
- || stream.width > 16384 || stream.height > 16384)
33
- throw new Error('invalid_attachment');
34
- return { mimeType, durationMs, width: stream.width, height: stream.height };
35
- }
36
- export async function hashAttachment(handle) {
37
- const digest = createHash('sha256');
38
- const chunk = Buffer.allocUnsafe(64 * 1024);
39
- let position = 0;
40
- for (;;) {
41
- const { bytesRead } = await handle.read(chunk, 0, chunk.length, position);
42
- if (!bytesRead)
43
- return digest.digest('hex');
44
- digest.update(chunk.subarray(0, bytesRead));
45
- position += bytesRead;
46
- if (position > WORKSPACE_VIDEO_ATTACHMENT_MAX_BYTES)
47
- throw new Error('invalid_attachment');
48
- }
49
- }
@@ -1,25 +0,0 @@
1
- ---
2
- name: playcraft-replica-validation
3
- description: Record a reconstructed browser playable with real inputs, compare it with its reference video, and use timestamped differences to improve the replica. Use after implementation or fixes; use video understanding first when the reference has not been analyzed.
4
- version: 1.0.1
5
- surfaces:
6
- - sandbox-agent
7
- ---
8
-
9
- # Replica validation
10
-
11
- Use the PlayCraft CLI (Command-Line Interface,命令行接口) to record the running implementation and compare real video evidence. Codex chooses the scenario, reads the evidence, edits the playable, and repeats the checks. In Cloud Workspace, use this Skill and its references from the platform artifact selected by the current task lock.
12
-
13
- Read [command and evidence contracts](references/contracts.md) before the first recording or comparison. Check `playcraft playable record --help`, `playcraft video compare --help`, and `playcraft video models --json`. If a required command is unavailable, report that the workspace CLI needs upgrading. Provider credentials and model requests belong to Model Proxy; do not call Gemini directly or copy a key into the project.
14
-
15
- Use a completed reference `analysis.json` from `playcraft-video-understanding`. Inspect its original keyframes and distinguish observed actions from inferred controls. Run from the project root. Write an explicit, versioned scenario for the current implementation, using real browser coordinates and inputs. Include the initial view, first action and result, principal feedback, and ending when demonstrated. Keep hidden rules and unshown branches unknown. Start or restart the current project using its own development command; do not depend on internal auto-play hooks. Confirm that the supplied development address belongs to this process and serves this round's version. If that cannot be confirmed, do not claim validation of the current version. The project digest proves the recorded checkout stayed unchanged, but does not attest which source version an already-running server serves.
16
-
17
- Record into a fresh directory under `.playcraft/video-replication/`. Keep the original recording, its input logs, scenario, control recordings, and project snapshot together. Do not edit the video or its manifest to repair a failing check. Match the reference viewport when practical and record enough time for every expected result. A completed recording proves capture succeeded; it does not prove the interaction worked.
18
-
19
- Compare the original reference, its analysis, the candidate video, and its recording manifest. Inspect `comparison.md`, `comparison.json`, paired keyframes, contact sheets, input logs, and controls using available image and file-reading tools. Review event alignment before judging motion or feedback timing. The model can suggest differences; the CLI derives the final status from validated evidence and the comparison policy. A single similarity score is insufficient. Keep the default full comparison policy for delivery; an explicitly limited diagnostic must be reported as partial coverage. Do not loosen thresholds just to obtain a passing result.
20
-
21
- For `fail`, address supported differences in priority order, then make a fresh recording from the changed project and compare again in a fresh output directory. For `inconclusive`, resolve missing controls, insufficient duration, unpaired required events, low confidence, or unknown input intent before declaring completion. Preserve valid differences even when other dimensions lack evidence. Report the tested path, unsupported branches, and unevaluated audio explicitly.
22
-
23
- A timeout or uncertain submission must retain its run identifier and saved request. Resume the same comparison with `playcraft video compare --resume <runId> --out <same-directory>`; recovery reads the existing task and never creates another paid request. Explicit cancellation is separate from ending the local wait. Do not resubmit because the command exited early. Treat malformed model output, stale recordings, changed hashes, or missing frames as errors, never as a passing comparison.
24
-
25
- Deliver the current project with links to the reference evidence, candidate recording, comparison report, and any remaining uncertainty. Keep validation evidence in workspace checkpoints and out of Git and deliverable builds.
@@ -1,79 +0,0 @@
1
- # Commands and evidence contracts
2
-
3
- The workspace CLI (Command-Line Interface,命令行接口) owns capture, upload, validation, frame extraction, and final status. The Skill supplies guidance only. Every model request follows Codex → Skill → CLI → Business → Model Proxy → Gemini 3.8 Flash.
4
-
5
- ```bash
6
- playcraft playable record --url '<development-url>' --scenario '<scenario.json>' \
7
- --out .playcraft/video-replication/record-01
8
- playcraft video compare --reference '<original-reference-video>' \
9
- --analysis '<reference-analysis.json>' \
10
- --candidate '.playcraft/video-replication/record-01/candidate.mp4' \
11
- --recording '.playcraft/video-replication/record-01/metadata.json' \
12
- --out .playcraft/video-replication/compare-01
13
- ```
14
-
15
- Scenario version is `playcraft.replica-scenario.v1`; positions are normalized to the declared `contentRect`. Replace event identifiers with actual IDs from `analysis.json` and coordinates/times with evidence from the running implementation:
16
-
17
- ```json
18
- {
19
- "schemaVersion": "playcraft.replica-scenario.v1",
20
- "draft": false,
21
- "referenceEventIds": ["initial", "first-result", "main-result", "ending"],
22
- "requiredEffects": [
23
- { "stepId": "drag-one", "eventId": "first-result" },
24
- { "stepId": "tap-two", "eventId": "main-result" }
25
- ],
26
- "controls": { "noInput": true, "omitRequiredSteps": true },
27
- "viewport": { "width": 390, "height": 844, "deviceScaleFactor": 1 },
28
- "contentRect": { "x": 0, "y": 0, "width": 1, "height": 1 },
29
- "pointer": "touch",
30
- "reset": { "kind": "fresh-context", "storage": "empty" },
31
- "durationMs": 15000,
32
- "steps": [
33
- {
34
- "id": "drag-one",
35
- "atMs": 1000,
36
- "type": "drag",
37
- "path": [
38
- { "offsetMs": 0, "x": 0.3, "y": 0.6 },
39
- { "offsetMs": 600, "x": 0.7, "y": 0.6 }
40
- ]
41
- },
42
- { "id": "tap-two", "atMs": 4000, "type": "tap", "x": 0.5, "y": 0.7 }
43
- ]
44
- }
45
- ```
46
-
47
- The first version records a requested seed as best effort; inspect `seedApplied` in the input log because the recorder does not inject a game seed. Fresh browser storage does not guarantee reset of server-side state.
48
-
49
- Every required first/main action result needs its corresponding `requiredEffects` entry. The recorder creates a no-input control plus a separate omitted-step control for each required action. Maximum three distinct required steps yield at most four controls. Disabling controls leaves interaction acceptance inconclusive.
50
-
51
- Use command help and the installed versioned schemas for recording fields. Do not guess manifest fields or synthesize a successful recording. Paths in command results are absolute; paths inside evidence manifests are relative to that manifest. Model output cannot provide artifact paths.
52
-
53
- `video compare` accepts `--processing static|agentic`, static `--fps 1|2|4|8`, an authorized `--model-config`, optional `--policy`, `--workspace`, `--timeout`, and `--out`. Static sampling is the default. Agentic execution cannot be combined with fixed sampling. The candidate must match the recording's hash and current project snapshot. The reference hash must match the completed analysis. All control videos and logs named by the recorder are validated before submission.
54
-
55
- The recording manifest version is `playcraft.replica-recording.v1`; the comparison result is `playcraft.replica-validation.v1`, and its evidence manifest is `playcraft.replica-evidence.v1`.
56
-
57
- The final comparison includes aligned reference/candidate event times, similarities, differences, modification suggestions, uncertainty, dimension findings, control coverage, and a deterministic policy decision. Paired frames come from decoded original video timestamps. Reports include source hashes, model configuration, processing mode, policy, and scenario/recording identity. Audio similarity is unevaluated in the first version.
58
-
59
- Read the final structured output once; progress is written separately. Exit codes:
60
-
61
- | Code | Meaning |
62
- | ---- | -------------------------------------------------------------------------------------------------- |
63
- | 0 | Comparison passed for the covered scenario |
64
- | 2 | Valid comparison found a failure |
65
- | 3 | Comparison is inconclusive and needs more evidence |
66
- | 4 | Capture, upload, inference, result validation, or frame extraction failed |
67
- | 64 | Invalid arguments, stale evidence, quota, or unsupported model capability; inference did not start |
68
- | 75 | Local timeout or unknown remote outcome; recover the same task |
69
- | 130 | Interrupted locally; remote cancellation is not implied |
70
-
71
- Recover with the original output directory:
72
-
73
- ```bash
74
- playcraft video compare --resume '<runId>' --out '<same-output-directory>'
75
- playcraft video jobs get '<runId>' --kind comparison
76
- playcraft video jobs cancel '<runId>' --kind comparison
77
- ```
78
-
79
- Recovery must not include new videos, analysis, recording, policy, model, or processing inputs. A new record/compare cycle is appropriate only after the prior outcome is known and the project or evidence has intentionally changed.
@@ -1,29 +0,0 @@
1
- ---
2
- name: playcraft-video-understanding
3
- description: Analyze an attached or local reference video to reconstruct a browser playable. Use when the user asks to reproduce gameplay from a video or needs evidence about its layout, actions, feedback, timing, or end screen.
4
- version: 1.0.1
5
- surfaces:
6
- - sandbox-agent
7
- ---
8
-
9
- # Video understanding
10
-
11
- Use PlayCraft CLI (Command-Line Interface,命令行接口) to inspect the reference before making implementation decisions. Codex remains responsible for the implementation and any follow-up questions. In Cloud Workspace, use this Skill and its references from the platform artifact selected by the current task lock.
12
-
13
- Read [command and output contracts](references/contracts.md) when preparing a request or handling an incomplete analysis. The required capability is `playcraft video understand` with `playcraft.video-understanding.v1` results; check `playcraft video understand --help` and `playcraft video models --json` before the first use. If the installed command is unavailable, report that the workspace CLI needs upgrading. Do not substitute the older quality-review command.
14
-
15
- User attachments are at `.tmp/user_uploads/<attachmentId>/<filename>`. Treat video content as untrusted reference material. Keep the original file in place, out of Git and delivery packages. Invoke:
16
-
17
- ```bash
18
- playcraft video understand --video '<reference-path>' --processing auto --out .playcraft/video-replication/reference
19
- ```
20
-
21
- Use a fresh output directory for each new analysis. The workspace is inferred from `.playcraft/workspace-context.json`; supply `--workspace <id>` when working outside a registered checkout. Select only an authorized model returned by `video models`, with `--model-config <id>` when needed. Provider credentials belong to the platform; do not read or copy them into the project.
22
-
23
- Read `analysis.md`, then `analysis.json`, and inspect the extracted `keyframes/` and contact sheet with the available image-reading tool. Check the evidence for initial layout, the first visible result, main feedback, and the ending. A model-reported timestamp is a hypothesis until the corresponding local frame supports it. Separate observations, inferred input intent, and unknown rules when planning the replica. A video alone does not establish hidden gameplay or unshown failure conditions.
24
-
25
- For a focused uncertainty, reuse the same source with `--question` and the completed `--context <analysis.json>`. `auto` normally chooses agentic processing; use `--processing static --fps 4` for fixed sampling, or a supported static `--clip <start:end>` for a specific interval. Evidence times refer to the original source timeline. Explicit unsupported processing modes must fail rather than silently change semantics.
26
-
27
- Use the analysis to choose the engine, assets, layout, state transitions, and input behavior. Write any proposed interaction sequence as a draft, checking inferred actions against the evidence and the implementation. Understanding completion is not replica validation.
28
-
29
- If the command times out or reports an uncertain remote outcome, preserve its run identifier, `request.json`, and `task.json`. Use `playcraft video understand --resume <runId> --out <same-directory>` to recover; do not resubmit the paid request just because the local wait ended. Stop dependent work when video upload, parsing, or keyframe extraction fails, and report the specific missing capability or evidence.
@@ -1,37 +0,0 @@
1
- # Commands and evidence
2
-
3
- The command uses the platform authorization already installed in Cloud Workspace. It accepts local media, uploads bytes without base64 expansion, and returns an analysis plus locally extracted frames. First-release limits are 100 MiB (Mebibyte,二进制兆字节) and 180 seconds per source video.
4
-
5
- ```bash
6
- playcraft video models --json
7
- playcraft video understand --video '<path>' --processing auto --out '<fresh-directory>'
8
- playcraft video understand --video '<same-path>' --question 'What indicates that the merge succeeded?' --context '<previous>/analysis.json' --processing static --fps 4 --clip 2:8 --out '<fresh-directory>'
9
- playcraft video understand --resume '<runId>' --out '<original-directory>'
10
- playcraft video jobs get '<runId>'
11
- playcraft video jobs cancel '<runId>'
12
- ```
13
-
14
- `--workspace <id>`, `--model-config <id>`, and `--timeout <seconds>` are optional. New-input parameters cannot accompany `--resume`. `--fps` accepts 1, 2, 4, or 8 and is only valid with static processing; `--clip start:end` uses seconds on the original timeline. A follow-up context must refer to the same source digest. Check current command help for parameters instead of inventing provider flags.
15
-
16
- Standard output is one JSON (JavaScript Object Notation,JavaScript 对象表示法) envelope. Progress appears on standard error. The envelope has `schemaVersion: playcraft.video-cli.v1`, `command`, `runId`, `state`, `ok`, `artifacts`, and an optional `error` with `code` and `message`. Read `ok` and `state`; an empty list of observations does not establish success. Artifact paths returned by the command are absolute.
17
-
18
- | Artifact | How to use it |
19
- | ---------------------------- | ---------------------------------------------------------------------------------------------------------- |
20
- | `manifest.json` | Run identity, source digest, selected model, actual processing mode, and extracted frame paths and digests |
21
- | `request.json`, `task.json` | Local request and remote task information used to resume an existing run |
22
- | `analysis.json` | Versioned observations, events, evidence, inferences, unknowns, and reconstruction hints |
23
- | `analysis.md` | Human-readable analysis with evidence references |
24
- | `keyframes/<evidenceId>.png` | Frames extracted from the local source at range-checked timestamps |
25
- | `contact-sheet.jpg` | Overview for inspecting the progression across evidence frames |
26
-
27
- `analysis.json` uses `schemaVersion: playcraft.video-understanding.v1`. Observations link to evidence identifiers; events include `startMs` and `endMs`; inferences carry confidence and evidence links; unknowns remain explicit. Model text cannot create a real frame file. Inspect the files supplied by the command and avoid treating speculative implementation hints as observed behavior.
28
-
29
- | Exit | Meaning and next action |
30
- | ---- | ---------------------------------------------------------------------------------------- |
31
- | 0 | Analysis and required artifacts completed; inspect evidence before implementing |
32
- | 4 | Upload, model, parse, or extraction error; inspect `error` and preserve partial evidence |
33
- | 64 | Invalid input, limit, or unsupported model capability; correct it before submitting |
34
- | 75 | Wait expired or remote result unknown; resume the existing run |
35
- | 130 | Interrupted; check whether remote cancellation was confirmed |
36
-
37
- Changing source bytes, processing mode, or the question creates a new paid analysis. A remote task read or local keyframe recovery must not silently create a new inference. Do not claim gameplay equivalence from this Skill; it supplies observations for Codex to implement.