@playcraft/cli 0.1.49-beta → 0.1.51

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 (395) hide show
  1. package/README.md +3 -62
  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 -210
  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 -113
  50. package/dist/commands/video.js +0 -122
  51. package/dist/commands/workspace-runtime.js +0 -54
  52. package/dist/commands/workspace.js +0 -40
  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/error-diagnostics.js +0 -20
  75. package/dist/utils/tool-operation-journal.js +0 -124
  76. package/dist/utils/video-evidence-git.js +0 -81
  77. package/dist/video-replication/client.js +0 -234
  78. package/dist/video-replication/comparison-evidence.js +0 -149
  79. package/dist/video-replication/comparison-files.js +0 -57
  80. package/dist/video-replication/comparison-log.js +0 -72
  81. package/dist/video-replication/comparison-messages.js +0 -104
  82. package/dist/video-replication/comparison-output.js +0 -63
  83. package/dist/video-replication/comparison-preflight.js +0 -66
  84. package/dist/video-replication/comparison.js +0 -230
  85. package/dist/video-replication/diagnostics.js +0 -41
  86. package/dist/video-replication/evidence.js +0 -165
  87. package/dist/video-replication/media.js +0 -112
  88. package/dist/video-replication/messages.js +0 -97
  89. package/dist/workspace/messages.js +0 -67
  90. package/dist/workspace/transfer-client.js +0 -233
  91. package/dist/workspace/transfer-errors.js +0 -48
  92. package/dist/workspace-runtime/batch/content-changes.js +0 -30
  93. package/dist/workspace-runtime/batch/control.js +0 -86
  94. package/dist/workspace-runtime/batch/deliver.js +0 -173
  95. package/dist/workspace-runtime/batch/freeze.js +0 -101
  96. package/dist/workspace-runtime/batch/generate.js +0 -249
  97. package/dist/workspace-runtime/batch/git.js +0 -40
  98. package/dist/workspace-runtime/batch/occupancy.js +0 -8
  99. package/dist/workspace-runtime/batch/policy.js +0 -70
  100. package/dist/workspace-runtime/batch/prepare.js +0 -317
  101. package/dist/workspace-runtime/batch/processes.js +0 -72
  102. package/dist/workspace-runtime/batch/records.js +0 -113
  103. package/dist/workspace-runtime/batch/routes.js +0 -61
  104. package/dist/workspace-runtime/batch/source-candidate.js +0 -86
  105. package/dist/workspace-runtime/batch/source-capture.js +0 -146
  106. package/dist/workspace-runtime/batch/source-failure.js +0 -43
  107. package/dist/workspace-runtime/batch/source-publish.js +0 -130
  108. package/dist/workspace-runtime/codex/app-server.js +0 -1065
  109. package/dist/workspace-runtime/codex/compact.js +0 -46
  110. package/dist/workspace-runtime/codex/config-toml.js +0 -107
  111. package/dist/workspace-runtime/codex/delivery-tools.js +0 -144
  112. package/dist/workspace-runtime/codex/jsonrpc-stdio.js +0 -145
  113. package/dist/workspace-runtime/codex/launcher-client.js +0 -177
  114. package/dist/workspace-runtime/codex/loopback.js +0 -146
  115. package/dist/workspace-runtime/codex/mcp-config.js +0 -39
  116. package/dist/workspace-runtime/codex/model-broker.js +0 -263
  117. package/dist/workspace-runtime/codex/native-adapter.js +0 -9
  118. package/dist/workspace-runtime/codex/permissions.js +0 -79
  119. package/dist/workspace-runtime/codex/thread-image.js +0 -38
  120. package/dist/workspace-runtime/codex/tool-response.js +0 -10
  121. package/dist/workspace-runtime/codex/ui-instructions.js +0 -4
  122. package/dist/workspace-runtime/codex/ui-tools.js +0 -203
  123. package/dist/workspace-runtime/codex/work-mode.js +0 -15
  124. package/dist/workspace-runtime/main.js +0 -10
  125. package/dist/workspace-runtime/persistence/journal.js +0 -365
  126. package/dist/workspace-runtime/persistence/native-subagents.js +0 -162
  127. package/dist/workspace-runtime/persistence/sqlite-client.js +0 -77
  128. package/dist/workspace-runtime/persistence/sqlite-worker.js +0 -1161
  129. package/dist/workspace-runtime/persistence/stopped-execution.js +0 -77
  130. package/dist/workspace-runtime/previews/diagnostics.js +0 -44
  131. package/dist/workspace-runtime/previews/output.js +0 -36
  132. package/dist/workspace-runtime/previews/publish.js +0 -274
  133. package/dist/workspace-runtime/previews/single-file.js +0 -43
  134. package/dist/workspace-runtime/processes/managed-writes.js +0 -69
  135. package/dist/workspace-runtime/serve.js +0 -343
  136. package/dist/workspace-runtime/server/accept-operation.js +0 -61
  137. package/dist/workspace-runtime/server/auth.js +0 -21
  138. package/dist/workspace-runtime/server/connectivity.js +0 -209
  139. package/dist/workspace-runtime/server/desktop-host.js +0 -429
  140. package/dist/workspace-runtime/server/desktop-preview-config.js +0 -81
  141. package/dist/workspace-runtime/server/desktop-preview-service.js +0 -157
  142. package/dist/workspace-runtime/server/desktop-preview-static.js +0 -53
  143. package/dist/workspace-runtime/server/desktop-preview.js +0 -189
  144. package/dist/workspace-runtime/server/dispatch.js +0 -1393
  145. package/dist/workspace-runtime/server/endpoints.js +0 -21
  146. package/dist/workspace-runtime/server/execution-group.js +0 -90
  147. package/dist/workspace-runtime/server/http.js +0 -1316
  148. package/dist/workspace-runtime/server/lock.js +0 -53
  149. package/dist/workspace-runtime/server/operation-record.js +0 -33
  150. package/dist/workspace-runtime/server/rfb-watch-gate.js +0 -168
  151. package/dist/workspace-runtime/server/session-restore.js +0 -84
  152. package/dist/workspace-runtime/server/source-uploader.js +0 -138
  153. package/dist/workspace-runtime/server/submit-operation.js +0 -41
  154. package/dist/workspace-runtime/server/terminal-host.js +0 -366
  155. package/dist/workspace-runtime/server/types.js +0 -1
  156. package/dist/workspace-runtime/server/workspace-transfer.js +0 -168
  157. package/dist/workspace-runtime/verify-artifact.js +0 -59
  158. package/dist/workspace-runtime/workspaces/append-only-log.js +0 -62
  159. package/dist/workspace-runtime/workspaces/archive.js +0 -207
  160. package/dist/workspace-runtime/workspaces/artifact-events.js +0 -55
  161. package/dist/workspace-runtime/workspaces/artifact-files.js +0 -199
  162. package/dist/workspace-runtime/workspaces/artifact-receipts.js +0 -89
  163. package/dist/workspace-runtime/workspaces/artifacts.js +0 -282
  164. package/dist/workspace-runtime/workspaces/asset-library-import.js +0 -366
  165. package/dist/workspace-runtime/workspaces/attachment-content.js +0 -73
  166. package/dist/workspace-runtime/workspaces/attachments.js +0 -185
  167. package/dist/workspace-runtime/workspaces/batch-commit.js +0 -142
  168. package/dist/workspace-runtime/workspaces/binary-content.js +0 -65
  169. package/dist/workspace-runtime/workspaces/binary-write.js +0 -112
  170. package/dist/workspace-runtime/workspaces/cli-context.js +0 -23
  171. package/dist/workspace-runtime/workspaces/cli-credentials.js +0 -115
  172. package/dist/workspace-runtime/workspaces/composer-context.js +0 -147
  173. package/dist/workspace-runtime/workspaces/context-error.js +0 -2
  174. package/dist/workspace-runtime/workspaces/delivery-build.js +0 -331
  175. package/dist/workspace-runtime/workspaces/delivery-store.js +0 -168
  176. package/dist/workspace-runtime/workspaces/delivery.js +0 -64
  177. package/dist/workspace-runtime/workspaces/dependencies.js +0 -279
  178. package/dist/workspace-runtime/workspaces/developer-command.js +0 -98
  179. package/dist/workspace-runtime/workspaces/directories.js +0 -132
  180. package/dist/workspace-runtime/workspaces/directory-access.js +0 -37
  181. package/dist/workspace-runtime/workspaces/directory-attachments.js +0 -245
  182. package/dist/workspace-runtime/workspaces/directory-create.js +0 -57
  183. package/dist/workspace-runtime/workspaces/directory-maintenance.js +0 -111
  184. package/dist/workspace-runtime/workspaces/directory-stats.js +0 -49
  185. package/dist/workspace-runtime/workspaces/disk-snapshot.js +0 -31
  186. package/dist/workspace-runtime/workspaces/empty-template.js +0 -42
  187. package/dist/workspace-runtime/workspaces/file-content.js +0 -48
  188. package/dist/workspace-runtime/workspaces/files-search.js +0 -116
  189. package/dist/workspace-runtime/workspaces/files.js +0 -150
  190. package/dist/workspace-runtime/workspaces/git-credential-broker.js +0 -312
  191. package/dist/workspace-runtime/workspaces/git-credential-helper.js +0 -68
  192. package/dist/workspace-runtime/workspaces/git-direct-store.js +0 -155
  193. package/dist/workspace-runtime/workspaces/git.js +0 -670
  194. package/dist/workspace-runtime/workspaces/instructions.js +0 -56
  195. package/dist/workspace-runtime/workspaces/json5-edit.js +0 -170
  196. package/dist/workspace-runtime/workspaces/library-attachments.js +0 -268
  197. package/dist/workspace-runtime/workspaces/operation-log.js +0 -23
  198. package/dist/workspace-runtime/workspaces/optional-platform-skills.js +0 -14
  199. package/dist/workspace-runtime/workspaces/parameters.js +0 -272
  200. package/dist/workspace-runtime/workspaces/prepare.js +0 -278
  201. package/dist/workspace-runtime/workspaces/registry.js +0 -251
  202. package/dist/workspace-runtime/workspaces/remix-references.js +0 -301
  203. package/dist/workspace-runtime/workspaces/revisions.js +0 -31
  204. package/dist/workspace-runtime/workspaces/session-image.js +0 -104
  205. package/dist/workspace-runtime/workspaces/shared-git.js +0 -40
  206. package/dist/workspace-runtime/workspaces/skill-artifact-loader.js +0 -95
  207. package/dist/workspace-runtime/workspaces/skills.js +0 -191
  208. package/dist/workspace-runtime/workspaces/transfer-quota.js +0 -69
  209. package/dist/workspace-runtime/workspaces/transfer.js +0 -471
  210. package/dist/workspace-runtime/workspaces/turn-changes.js +0 -75
  211. package/dist/workspace-runtime/workspaces/video-attachments.js +0 -49
  212. package/project-template/.claude/skills/playcraft-replica-validation/SKILL.md +0 -25
  213. package/project-template/.claude/skills/playcraft-replica-validation/references/contracts.md +0 -79
  214. package/project-template/.claude/skills/playcraft-video-understanding/SKILL.md +0 -29
  215. package/project-template/.claude/skills/playcraft-video-understanding/references/contracts.md +0 -37
  216. package/project-template-v2/.claude/agents/artist.md +0 -82
  217. package/project-template-v2/.claude/agents/developer.md +0 -153
  218. package/project-template-v2/.claude/agents/game-designer.md +0 -264
  219. package/project-template-v2/.claude/agents/refs/artist-art-style-catalog.md +0 -533
  220. package/project-template-v2/.claude/agents/refs/artist-color-audio-recipes.md +0 -153
  221. package/project-template-v2/.claude/agents/refs/artist-dimension-axis.md +0 -27
  222. package/project-template-v2/.claude/agents/refs/artist-master-composite-recipes.md +0 -208
  223. package/project-template-v2/.claude/agents/refs/atom-skill-library.md +0 -81
  224. package/project-template-v2/.claude/agents/refs/developer-impl-cookbook.md +0 -432
  225. package/project-template-v2/.claude/agents/refs/framework-5-component-filter.md +0 -252
  226. package/project-template-v2/.claude/agents/refs/framework-game-feel-juice.md +0 -266
  227. package/project-template-v2/.claude/agents/refs/framework-mda.md +0 -147
  228. package/project-template-v2/.claude/agents/refs/game-designer-gameplay-sufficiency.md +0 -123
  229. package/project-template-v2/.claude/agents/refs/ta-3d-flip-recipe.md +0 -88
  230. package/project-template-v2/.claude/agents/refs/ta-atlas-deliverable-standard.md +0 -67
  231. package/project-template-v2/.claude/agents/refs/ta-batch-pipeline-recipes.md +0 -120
  232. package/project-template-v2/.claude/agents/refs/ta-image-generation-detail.md +0 -300
  233. package/project-template-v2/.claude/agents/refs/ta-image-ops-reference.md +0 -495
  234. package/project-template-v2/.claude/agents/refs/ta-pipeline-cookbook.md +0 -1141
  235. package/project-template-v2/.claude/agents/refs/ta-tools-reference.md +0 -111
  236. package/project-template-v2/.claude/agents/refs/ta-vfx-preset-catalog.md +0 -365
  237. package/project-template-v2/.claude/agents/refs/threejs-cannon-pitfalls.md +0 -412
  238. package/project-template-v2/.claude/agents/reviewer.md +0 -75
  239. package/project-template-v2/.claude/agents/technical-artist.md +0 -86
  240. package/project-template-v2/.claude/hooks/snapshot-milestone.mjs +0 -243
  241. package/project-template-v2/.claude/hooks/user-prompt.mjs +0 -133
  242. package/project-template-v2/.claude/settings.json +0 -33
  243. package/project-template-v2/.claude/skills/brainstorming/SKILL.md +0 -161
  244. package/project-template-v2/.claude/skills/brainstorming/scripts/frame-template.html +0 -270
  245. package/project-template-v2/.claude/skills/brainstorming/scripts/helper.js +0 -177
  246. package/project-template-v2/.claude/skills/brainstorming/scripts/server.cjs +0 -354
  247. package/project-template-v2/.claude/skills/brainstorming/scripts/start-server.sh +0 -148
  248. package/project-template-v2/.claude/skills/brainstorming/scripts/stop-server.sh +0 -56
  249. package/project-template-v2/.claude/skills/brainstorming/scripts/wait-for-selection.sh +0 -62
  250. package/project-template-v2/.claude/skills/brainstorming/spec-document-reviewer-prompt.md +0 -49
  251. package/project-template-v2/.claude/skills/brainstorming/visual-companion.md +0 -309
  252. package/project-template-v2/.claude/skills/playcraft-ad-psychology/SKILL.md +0 -182
  253. package/project-template-v2/.claude/skills/playcraft-art-style-guide/SKILL.md +0 -123
  254. package/project-template-v2/.claude/skills/playcraft-asset-state-sheet/SKILL.md +0 -205
  255. package/project-template-v2/.claude/skills/playcraft-audio-generation/SKILL.md +0 -280
  256. package/project-template-v2/.claude/skills/playcraft-batch-pipeline/SKILL.md +0 -184
  257. package/project-template-v2/.claude/skills/playcraft-build-optimizer/SKILL.md +0 -306
  258. package/project-template-v2/.claude/skills/playcraft-image-generation/SKILL.md +0 -298
  259. package/project-template-v2/.claude/skills/playcraft-image-generation/reference/build-sprite-sheet.template.mjs +0 -123
  260. package/project-template-v2/.claude/skills/playcraft-image-generation/reference/compare-style.template.mjs +0 -254
  261. package/project-template-v2/.claude/skills/playcraft-image-generation/reference/gen-batch-sprite.template.mjs +0 -324
  262. package/project-template-v2/.claude/skills/playcraft-image-generation/reference/gen-batch.template.mjs +0 -97
  263. package/project-template-v2/.claude/skills/playcraft-image-generation/reference/gen-edit-variants.template.mjs +0 -118
  264. package/project-template-v2/.claude/skills/playcraft-image-generation/reference/process-batch.template.mjs +0 -137
  265. package/project-template-v2/.claude/skills/playcraft-image-generation/reference/prompt-cookbook.md +0 -397
  266. package/project-template-v2/.claude/skills/playcraft-image-generation/reference/validate-sprite-sheet.template.mjs +0 -296
  267. package/project-template-v2/.claude/skills/playcraft-image-ops/SKILL.md +0 -122
  268. package/project-template-v2/.claude/skills/playcraft-image-processing/SKILL.md +0 -219
  269. package/project-template-v2/.claude/skills/playcraft-masking/SKILL.md +0 -373
  270. package/project-template-v2/.claude/skills/playcraft-playable-optimization/SKILL.md +0 -161
  271. package/project-template-v2/.claude/skills/playcraft-research/SKILL.md +0 -215
  272. package/project-template-v2/.claude/skills/playcraft-skill-recommender/SKILL.md +0 -382
  273. package/project-template-v2/.claude/skills/playcraft-sprite-generation/SKILL.md +0 -423
  274. package/project-template-v2/.claude/skills/playcraft-sprite-remix/SKILL.md +0 -158
  275. package/project-template-v2/.claude/skills/playcraft-sprite-sheet/SKILL.md +0 -100
  276. package/project-template-v2/.claude/skills/playcraft-storyboard/SKILL.md +0 -167
  277. package/project-template-v2/.claude/skills/playcraft-style-qa/SKILL.md +0 -270
  278. package/project-template-v2/.claude/skills/playcraft-text-rendering/SKILL.md +0 -236
  279. package/project-template-v2/.claude/skills/playcraft-vfx-animation/SKILL.md +0 -130
  280. package/project-template-v2/.claude/skills/playwright-cli/SKILL.md +0 -390
  281. package/project-template-v2/.claude/skills/playwright-cli/references/element-attributes.md +0 -23
  282. package/project-template-v2/.claude/skills/playwright-cli/references/playwright-tests.md +0 -39
  283. package/project-template-v2/.claude/skills/playwright-cli/references/request-mocking.md +0 -87
  284. package/project-template-v2/.claude/skills/playwright-cli/references/running-code.md +0 -240
  285. package/project-template-v2/.claude/skills/playwright-cli/references/session-management.md +0 -226
  286. package/project-template-v2/.claude/skills/playwright-cli/references/spec-driven-testing.md +0 -308
  287. package/project-template-v2/.claude/skills/playwright-cli/references/storage-state.md +0 -275
  288. package/project-template-v2/.claude/skills/playwright-cli/references/test-generation.md +0 -134
  289. package/project-template-v2/.claude/skills/playwright-cli/references/tracing.md +0 -142
  290. package/project-template-v2/.claude/skills/playwright-cli/references/video-recording.md +0 -153
  291. package/project-template-v2/.claude/skills/session-analyzer/SKILL.md +0 -386
  292. package/project-template-v2/.claude/skills/session-analyzer/scripts/execution-breakdown.mjs +0 -182
  293. package/project-template-v2/.claude/skills/session-analyzer/scripts/find-turns.mjs +0 -72
  294. package/project-template-v2/.claude/skills/session-analyzer/scripts/heavy-output.mjs +0 -121
  295. package/project-template-v2/.claude/skills/session-analyzer/scripts/resolve-session.mjs +0 -102
  296. package/project-template-v2/.claude/skills/session-analyzer/scripts/subagent-stats.mjs +0 -127
  297. package/project-template-v2/.claude/skills/session-analyzer/scripts/subagent-tool-timeline.mjs +0 -106
  298. package/project-template-v2/.claude/skills/session-analyzer/scripts/time-gaps.mjs +0 -128
  299. package/project-template-v2/.claude/skills/session-analyzer/scripts/turn-timeline.mjs +0 -67
  300. package/project-template-v2/.claude/snapshot.mjs +0 -263
  301. package/project-template-v2/.playcraft/skills.lock.json +0 -152
  302. package/project-template-v2/CLAUDE.md +0 -146
  303. package/project-template-v2/assets/audio/bgm/.gitkeep +0 -0
  304. package/project-template-v2/assets/audio/sfx/.gitkeep +0 -0
  305. package/project-template-v2/assets/bundles/.gitkeep +0 -0
  306. package/project-template-v2/assets/images/bg/.gitkeep +0 -0
  307. package/project-template-v2/assets/images/reference/.gitkeep +0 -0
  308. package/project-template-v2/assets/images/storyboard/.gitkeep +0 -0
  309. package/project-template-v2/assets/images/tiles/.gitkeep +0 -0
  310. package/project-template-v2/assets/images/ui/.gitkeep +0 -0
  311. package/project-template-v2/assets/images/vfx/.gitkeep +0 -0
  312. package/project-template-v2/assets/models/.gitkeep +0 -0
  313. package/project-template-v2/docs/harness/iteration-1/context-flow.md +0 -254
  314. package/project-template-v2/docs/harness/iteration-1/generate-flow.md +0 -91
  315. package/project-template-v2/docs/harness/iteration-1/ideate-flow.md +0 -214
  316. package/project-template-v2/docs/harness/iteration-1/optimize-flow.md +0 -75
  317. package/project-template-v2/docs/harness/iteration-1/wrapup-flow.md +0 -63
  318. package/project-template-v2/docs/harness/iteration-2/context-flow.md +0 -223
  319. package/project-template-v2/docs/harness/iteration-2/generate-flow.md +0 -129
  320. package/project-template-v2/docs/harness/iteration-2/ideate-flow.md +0 -267
  321. package/project-template-v2/docs/harness/iteration-2/optimize-flow.md +0 -164
  322. package/project-template-v2/docs/harness/iteration-2/wrapup-flow.md +0 -115
  323. package/project-template-v2/docs/harness/orchestrator-flow.md +0 -364
  324. package/project-template-v2/docs/project-state.json +0 -60
  325. package/project-template-v2/docs/project-state.md +0 -72
  326. package/project-template-v2/docs/standards/README.md +0 -225
  327. package/project-template-v2/docs/standards/agent-behavior-standards.md +0 -174
  328. package/project-template-v2/docs/standards/artifacts/design-brief.md +0 -19
  329. package/project-template-v2/docs/standards/artifacts/design.md +0 -22
  330. package/project-template-v2/docs/standards/artifacts/game-code.md +0 -41
  331. package/project-template-v2/docs/standards/artifacts/todo-list.md +0 -41
  332. package/project-template-v2/docs/standards/iter1-agent-behavior-standards.md +0 -343
  333. package/project-template-v2/game/index.ts +0 -18
  334. package/project-template-v2/globals.d.ts +0 -51
  335. package/project-template-v2/index.css +0 -34
  336. package/project-template-v2/index.html +0 -18
  337. package/project-template-v2/main.ts +0 -9
  338. package/project-template-v2/package.json +0 -46
  339. package/project-template-v2/skills/_shared/scripts/dispatch-clear.mjs +0 -31
  340. package/project-template-v2/skills/_shared/scripts/dispatch-set.mjs +0 -86
  341. package/project-template-v2/skills/_shared/scripts/dod-check.mjs +0 -153
  342. package/project-template-v2/skills/_shared/scripts/handoff-append.mjs +0 -70
  343. package/project-template-v2/skills/_shared/scripts/lib/validator-artifacts.mjs +0 -91
  344. package/project-template-v2/skills/_shared/scripts/pipeline/dod-config.mjs +0 -131
  345. package/project-template-v2/skills/_shared/scripts/pipeline/index.mjs +0 -80
  346. package/project-template-v2/skills/_shared/scripts/pipeline/iteration-1-core.mjs +0 -90
  347. package/project-template-v2/skills/_shared/scripts/pipeline/iteration-2-wrap.mjs +0 -93
  348. package/project-template-v2/skills/_shared/scripts/pipeline/iteration-3-visual.mjs +0 -23
  349. package/project-template-v2/skills/_shared/scripts/render-project-state.mjs +0 -230
  350. package/project-template-v2/skills/_shared/scripts/state-advance-stage.mjs +0 -58
  351. package/project-template-v2/skills/_shared/scripts/state-get.mjs +0 -339
  352. package/project-template-v2/skills/_shared/scripts/state-handoff.mjs +0 -39
  353. package/project-template-v2/skills/_shared/scripts/state-set.mjs +0 -94
  354. package/project-template-v2/skills/_shared/scripts/state-store.mjs +0 -783
  355. package/project-template-v2/skills/_shared/scripts/todo-add.mjs +0 -88
  356. package/project-template-v2/skills/_shared/scripts/todo-get.mjs +0 -130
  357. package/project-template-v2/skills/_shared/scripts/todo-remove.mjs +0 -33
  358. package/project-template-v2/skills/_shared/scripts/todo-set.mjs +0 -47
  359. package/project-template-v2/skills/_shared/scripts/verify-asset-code-sync.mjs +0 -268
  360. package/project-template-v2/skills/_shared/scripts/verify-env.mjs +0 -285
  361. package/project-template-v2/skills/_shared/scripts/verify-placeholders.mjs +0 -161
  362. package/project-template-v2/skills/playable-autoplay/SKILL.md +0 -176
  363. package/project-template-v2/skills/playable-autoplay/agents/openai.yaml +0 -4
  364. package/project-template-v2/skills/playable-debug/SKILL.md +0 -116
  365. package/project-template-v2/skills/playable-debug/agents/openai.yaml +0 -4
  366. package/project-template-v2/skills/playable-debug/references/debug-config.md +0 -40
  367. package/project-template-v2/skills/playable-record/SKILL.md +0 -140
  368. package/project-template-v2/skills/playable-record/scripts/lib/dev-server.mjs +0 -104
  369. package/project-template-v2/skills/playable-record/scripts/lib/record-audio-bridge.js +0 -141
  370. package/project-template-v2/skills/playable-record/scripts/record-playable.mjs +0 -425
  371. package/project-template-v2/skills/playable-record/scripts/verify-contract.mjs +0 -261
  372. package/project-template-v2/skills/playable-record/scripts/verify-firstwin.mjs +0 -398
  373. package/project-template-v2/skills/playable-record/scripts/verify-fusion.mjs +0 -322
  374. package/project-template-v2/skills/playable-record/scripts/verify-lifecycle.mjs +0 -105
  375. package/project-template-v2/skills/playable-record/scripts/verify-vlm-video.mjs +0 -230
  376. package/project-template-v2/skills/playable-validate/SKILL.md +0 -61
  377. package/project-template-v2/skills/playable-validate/validation-rules.md +0 -14
  378. package/project-template-v2/skills/playable-verify-ui/SKILL.md +0 -63
  379. package/project-template-v2/skills/playable-verify-ui/scripts/lib/__init__.py +0 -1
  380. package/project-template-v2/skills/playable-verify-ui/scripts/lib/config_expr.js +0 -218
  381. package/project-template-v2/skills/playable-verify-ui/scripts/lib/node_utils.js +0 -51
  382. package/project-template-v2/skills/playable-verify-ui/scripts/lib/profile_loader.py +0 -59
  383. package/project-template-v2/skills/playable-verify-ui/scripts/lib/report.py +0 -106
  384. package/project-template-v2/skills/playable-verify-ui/scripts/lib/runtime_contract.js +0 -116
  385. package/project-template-v2/skills/playable-verify-ui/scripts/verify_fetch_antipatterns.py +0 -59
  386. package/project-template-v2/skills/playable-verify-ui/scripts/verify_hardcoded_layout.js +0 -64
  387. package/project-template-v2/skills/playable-verify-ui/scripts/verify_runtime_contract.js +0 -58
  388. package/project-template-v2/skills/playable-verify-ui/scripts/verify_text_overlap.py +0 -333
  389. package/project-template-v2/ta-workspace/scripts/.gitkeep +0 -0
  390. package/project-template-v2/tsconfig.json +0 -20
  391. package/project-template-v2/vite.config.ts +0 -27
  392. package/project-templates/batch-locks/phaser.yaml +0 -922
  393. package/project-templates/batch-locks/threejs.yaml +0 -978
  394. package/project-templates/empty/AGENTS.md +0 -41
  395. package/project-templates/empty/gitignore +0 -33
@@ -1,471 +0,0 @@
1
- import { spawn, spawnSync } from 'node:child_process';
2
- import { createHash, randomUUID } from 'node:crypto';
3
- import { constants, createWriteStream } from 'node:fs';
4
- import { chmod, copyFile, lstat, mkdir, mkdtemp, open, readdir, realpath, rename, rm, rmdir, stat, writeFile } from 'node:fs/promises';
5
- import { tmpdir } from 'node:os';
6
- import { dirname, join, resolve, sep } from 'node:path';
7
- import { Transform } from 'node:stream';
8
- import { pipeline } from 'node:stream/promises';
9
- import { isCredentialFile } from './git.js';
10
- const EXCLUDED = new Set(['.git', 'node_modules', '.playcraft-workspace.json', '.playcraft.json']);
11
- const GIT_TIMEOUT_MS = 15 * 60_000;
12
- const GIT_OPTIONS = ['-c', 'core.hooksPath=/dev/null', '-c', 'commit.gpgsign=false', '-c', 'core.autocrlf=false',
13
- '-c', 'user.name=PlayCraft Workspace', '-c', 'user.email=workspace@playcraft.local'];
14
- const SNAPSHOT_MESSAGE = 'PlayCraft workspace snapshot v1';
15
- export const RAW_ATTRIBUTES = '* -text -eol -working-tree-encoding -filter -ident\n';
16
- export class WorkspaceTransferError extends Error {
17
- code;
18
- details;
19
- constructor(code, details) {
20
- super(code);
21
- this.code = code;
22
- this.details = details;
23
- }
24
- }
25
- function gitEnvironment() {
26
- return { PATH: process.env.PATH, HOME: tmpdir(), GIT_CONFIG_NOSYSTEM: '1', GIT_CONFIG_GLOBAL: '/dev/null',
27
- GIT_TERMINAL_PROMPT: '0', GIT_ALLOW_PROTOCOL: 'file', GIT_AUTHOR_NAME: 'PlayCraft Workspace',
28
- GIT_AUTHOR_EMAIL: 'workspace@playcraft.local', GIT_COMMITTER_NAME: 'PlayCraft Workspace',
29
- GIT_COMMITTER_EMAIL: 'workspace@playcraft.local', GIT_AUTHOR_DATE: '2000-01-01T00:00:00Z',
30
- GIT_COMMITTER_DATE: '2000-01-01T00:00:00Z' };
31
- }
32
- function excluded(path) {
33
- const parts = path.split('/');
34
- return parts.some(part => EXCLUDED.has(part.toLowerCase()) || /^\.playcraft-(?:credential|transfer)-.*\.tmp$/i.test(part)) ||
35
- parts.some((part, index) => part === '.playcraft' && /^(?:workspace-context(?:\..*)?|video-replication)$/.test(parts[index + 1] ?? '')) ||
36
- isCredentialFile(path);
37
- }
38
- export function transferGit(root, args) {
39
- const result = spawnSync('git', [...GIT_OPTIONS, ...args], { cwd: root, encoding: 'utf8', env: gitEnvironment(), timeout: GIT_TIMEOUT_MS, maxBuffer: 8 * 1024 * 1024 });
40
- if (result.status !== 0) {
41
- const code = result.error?.code;
42
- throw new WorkspaceTransferError(code === 'ETIMEDOUT' ? 'workspace_transfer_git_timeout'
43
- : code === 'ENOENT' ? 'workspace_transfer_git_missing' : 'workspace_transfer_git_failed');
44
- }
45
- return result.stdout.trim();
46
- }
47
- /** Large object operations write directly to disk and remain cancellable. */
48
- export async function runTransferGit(root, args, options = {}) {
49
- options.signal?.throwIfAborted();
50
- return new Promise((resolveResult, reject) => {
51
- const child = spawn('git', [...GIT_OPTIONS, ...args], { cwd: root, env: gitEnvironment(), stdio: ['ignore', 'pipe', 'pipe'] });
52
- let stdout = '', stderr = '', failure, timedOut = false;
53
- const abort = () => { failure = options.signal?.reason ?? new Error('workspace_transfer_cancelled'); child.kill('SIGKILL'); };
54
- const timeout = setTimeout(() => { timedOut = true; child.kill('SIGKILL'); }, GIT_TIMEOUT_MS);
55
- options.signal?.addEventListener('abort', abort, { once: true });
56
- child.stdout.setEncoding('utf8');
57
- child.stderr.setEncoding('utf8');
58
- child.stdout.on('data', (chunk) => {
59
- try {
60
- if (options.onStdout)
61
- options.onStdout(chunk);
62
- else {
63
- stdout += chunk;
64
- if (stdout.length > 8 * 1024 * 1024)
65
- throw new WorkspaceTransferError('workspace_transfer_git_output_too_large');
66
- }
67
- }
68
- catch (error) {
69
- failure = error;
70
- child.kill('SIGKILL');
71
- }
72
- });
73
- child.stderr.on('data', (chunk) => { if (stderr.length < 4096)
74
- stderr += chunk.slice(0, 4096 - stderr.length); });
75
- child.once('error', error => {
76
- failure = new WorkspaceTransferError(error.code === 'ENOENT'
77
- ? 'workspace_transfer_git_missing' : 'workspace_transfer_git_failed');
78
- });
79
- child.once('close', code => {
80
- clearTimeout(timeout);
81
- options.signal?.removeEventListener('abort', abort);
82
- if (failure)
83
- reject(failure);
84
- else if (timedOut)
85
- reject(new WorkspaceTransferError('workspace_transfer_git_timeout', { operation: args[0] }));
86
- else if (code !== 0)
87
- reject(new WorkspaceTransferError(/no space left|disk full/i.test(stderr)
88
- ? 'workspace_transfer_disk_full' : 'workspace_transfer_git_failed', { operation: args[0], exitCode: code }));
89
- else
90
- resolveResult(stdout.trim());
91
- });
92
- });
93
- }
94
- async function copySnapshot(source, target, signal, hashOnly = false) {
95
- let bytes = 0, files = 0;
96
- async function copy(directory, prefix) {
97
- signal?.throwIfAborted();
98
- const treeParts = [];
99
- const directoryInfo = await lstat(directory);
100
- const directoryReal = await realpath(directory);
101
- if (!directoryInfo.isDirectory() || directoryInfo.isSymbolicLink() ||
102
- (directoryReal !== source && !directoryReal.startsWith(`${source}${sep}`)))
103
- throw new WorkspaceTransferError('workspace_transfer_conflict');
104
- for (const entry of await readdir(directory, { withFileTypes: true })) {
105
- const path = prefix ? `${prefix}/${entry.name}` : entry.name;
106
- if (excluded(path))
107
- continue;
108
- signal?.throwIfAborted();
109
- const from = join(directory, entry.name), to = target ? join(target, path) : undefined;
110
- const info = await lstat(from);
111
- if (info.isSymbolicLink())
112
- continue;
113
- if (info.isDirectory()) {
114
- if (to)
115
- await mkdir(to, { recursive: true });
116
- const child = await copy(from, path);
117
- if (child && child !== EMPTY_TREE)
118
- treeParts.push({ name: entry.name, directory: true,
119
- value: Buffer.concat([Buffer.from(`40000 ${entry.name}\0`), Buffer.from(child, 'hex')]) });
120
- }
121
- else if (info.isFile()) {
122
- bytes += info.size;
123
- files++;
124
- if (!(await realpath(from)).startsWith(`${source}${sep}`))
125
- throw new WorkspaceTransferError('workspace_transfer_conflict');
126
- if (!to && !hashOnly)
127
- continue;
128
- const handle = await open(from, constants.O_RDONLY | constants.O_NOFOLLOW);
129
- try {
130
- const before = await handle.stat();
131
- if (!before.isFile() || before.dev !== info.dev || before.ino !== info.ino || before.size !== info.size ||
132
- before.mtimeMs !== info.mtimeMs || before.ctimeMs !== info.ctimeMs)
133
- throw new WorkspaceTransferError('workspace_transfer_conflict');
134
- let size = 0;
135
- const hash = hashOnly ? createHash('sha1').update(`blob ${before.size}\0`) : undefined;
136
- if (to) {
137
- const guard = new Transform({ transform(chunk, _encoding, callback) {
138
- size += chunk.length;
139
- callback(size > before.size ? new WorkspaceTransferError('workspace_transfer_conflict') : null, chunk);
140
- } });
141
- await pipeline(handle.createReadStream({ autoClose: false }), guard, createWriteStream(to, { flags: 'wx', mode: 0o600 }), { signal });
142
- }
143
- else {
144
- for await (const chunk of handle.createReadStream({ autoClose: false })) {
145
- signal?.throwIfAborted();
146
- size += chunk.length;
147
- if (size > before.size)
148
- throw new WorkspaceTransferError('workspace_transfer_conflict');
149
- hash.update(chunk);
150
- }
151
- }
152
- const after = await handle.stat();
153
- if (size !== before.size || after.size !== before.size || after.mtimeMs !== before.mtimeMs || after.ctimeMs !== before.ctimeMs)
154
- throw new WorkspaceTransferError('workspace_transfer_conflict');
155
- if (hash)
156
- treeParts.push({ name: entry.name, directory: false,
157
- value: Buffer.concat([Buffer.from(`${info.mode & 0o111 ? '100755' : '100644'} ${entry.name}\0`), hash.digest()]) });
158
- }
159
- finally {
160
- await handle.close();
161
- }
162
- if (to)
163
- await chmod(to, info.mode & 0o111 ? 0o755 : 0o644);
164
- }
165
- }
166
- const after = await lstat(directory);
167
- if (after.dev !== directoryInfo.dev || after.ino !== directoryInfo.ino || after.mtimeMs !== directoryInfo.mtimeMs || after.ctimeMs !== directoryInfo.ctimeMs)
168
- throw new WorkspaceTransferError('workspace_transfer_conflict');
169
- if (hashOnly) {
170
- treeParts.sort((left, right) => Buffer.compare(Buffer.from(left.name + (left.directory ? '/' : '\0')), Buffer.from(right.name + (right.directory ? '/' : '\0'))));
171
- const length = treeParts.reduce((total, part) => total + part.value.length, 0);
172
- const hash = createHash('sha1').update(`tree ${length}\0`);
173
- for (const part of treeParts)
174
- hash.update(part.value);
175
- return hash.digest('hex');
176
- }
177
- }
178
- const tree = await copy(source, '');
179
- return { bytes, files, ...(tree ? { tree } : {}) };
180
- }
181
- const EMPTY_TREE = createHash('sha1').update('tree 0\0').digest('hex');
182
- export async function inspectWorkspaceTransfer(root, options = {}) {
183
- return copySnapshot(await realpath(root), undefined, options.signal);
184
- }
185
- export async function inspectWorkspaceTree(root, options = {}) {
186
- const snapshot = await copySnapshot(await realpath(root), undefined, options.signal, true);
187
- return { bytes: snapshot.bytes, files: snapshot.files, tree: snapshot.tree };
188
- }
189
- async function matchesFile(root, path, expected) {
190
- const target = await safeTarget(root, path);
191
- const info = await lstat(target).catch(error => { if (['ENOENT', 'ENOTDIR'].includes(error.code))
192
- return undefined; throw error; });
193
- if (!expected)
194
- return !info || info.isDirectory();
195
- if (!info?.isFile() || Boolean(info.mode & 0o111) !== expected.executable)
196
- return false;
197
- const handle = await open(target, constants.O_RDONLY | constants.O_NOFOLLOW);
198
- try {
199
- const stat = await handle.stat();
200
- const hash = createHash(expected.sha.length === 64 ? 'sha256' : 'sha1');
201
- hash.update(`blob ${stat.size}\0`);
202
- for await (const chunk of handle.createReadStream({ autoClose: false }))
203
- hash.update(chunk);
204
- return hash.digest('hex') === expected.sha;
205
- }
206
- finally {
207
- await handle.close();
208
- }
209
- }
210
- async function removeEmptyParents(root, target) {
211
- let parent = dirname(target);
212
- while (parent !== root) {
213
- if ((await readdir(parent)).length > 0)
214
- break;
215
- await rmdir(parent);
216
- parent = dirname(parent);
217
- }
218
- }
219
- async function installFile(root, path, source, executable) {
220
- const target = await safeTarget(root, path);
221
- const existing = await lstat(target).catch(error => { if (error.code === 'ENOENT')
222
- return undefined; throw error; });
223
- if (existing?.isDirectory())
224
- await rmdir(target);
225
- await mkdir(dirname(target), { recursive: true });
226
- const stage = join(dirname(target), `.playcraft-transfer-${randomUUID()}.tmp`);
227
- try {
228
- await copyFile(source, stage);
229
- await chmod(stage, executable ? 0o755 : 0o644);
230
- await rename(stage, target);
231
- }
232
- catch (error) {
233
- await rm(stage, { force: true }).catch(() => undefined);
234
- await removeEmptyParents(root, target).catch(() => undefined);
235
- throw error;
236
- }
237
- finally {
238
- await rm(stage, { force: true }).catch(() => undefined);
239
- }
240
- }
241
- /** A fresh history contains only the current portable files, including uncommitted changes. */
242
- export async function createWorkspaceTransfer(root, options = {}) {
243
- const temporary = await mkdtemp(join(tmpdir(), 'playcraft-transfer-'));
244
- const cleanup = () => rm(temporary, { recursive: true, force: true });
245
- try {
246
- const checkout = join(temporary, 'checkout');
247
- await mkdir(checkout);
248
- const stats = await copySnapshot(await realpath(root), checkout, options.signal);
249
- await runTransferGit(checkout, ['init', '--initial-branch=main'], options);
250
- // Portable snapshots preserve bytes even when project attributes request content conversion.
251
- await writeFile(join(checkout, '.git', 'info', 'attributes'), RAW_ATTRIBUTES);
252
- await runTransferGit(checkout, ['add', '--force', '--all'], options);
253
- const tree = await runTransferGit(checkout, ['write-tree'], options);
254
- const head = await canonicalCommit(checkout, tree, options.signal);
255
- await runTransferGit(checkout, ['update-ref', 'refs/heads/main', head], options);
256
- const bundlePath = join(temporary, 'workspace.bundle');
257
- if (!options.skipBundle)
258
- await runTransferGit(checkout, ['bundle', 'create', bundlePath, 'HEAD'], options);
259
- return { bundlePath, tree, head, stats, cleanup };
260
- }
261
- catch (error) {
262
- await cleanup();
263
- throw error;
264
- }
265
- }
266
- async function canonicalCommit(root, tree, signal) {
267
- return runTransferGit(root, ['commit-tree', tree, '-m', SNAPSHOT_MESSAGE], { signal });
268
- }
269
- /** Only objects introduced since the last cloud snapshot are packed; local history stays local. */
270
- export async function createWorkspacePushTransfer(root, expectedTree, options = {}) {
271
- if (!/^[a-f0-9]{40,64}$/.test(expectedTree))
272
- throw new WorkspaceTransferError('invalid_workspace_transfer');
273
- const temporary = await mkdtemp(join(tmpdir(), 'playcraft-push-'));
274
- const cleanup = () => rm(temporary, { recursive: true, force: true });
275
- try {
276
- const checkout = join(temporary, 'checkout');
277
- await runTransferGit(temporary, ['clone', '--shared', '--no-checkout', '--', await realpath(root), checkout], options);
278
- const tree = await runTransferGit(checkout, ['rev-parse', '--verify', 'HEAD^{tree}'], options);
279
- const stats = summarizeEntries(await treeEntries(checkout, tree, options.signal));
280
- try {
281
- await runTransferGit(checkout, ['cat-file', '-e', `${expectedTree}^{tree}`], options);
282
- }
283
- catch (error) {
284
- if (error instanceof WorkspaceTransferError && error.code === 'workspace_transfer_git_failed')
285
- throw new WorkspaceTransferError('workspace_transfer_base_missing');
286
- throw error;
287
- }
288
- const base = await canonicalCommit(checkout, expectedTree, options.signal);
289
- const head = await runTransferGit(checkout, ['commit-tree', tree, '-p', base, '-m', SNAPSHOT_MESSAGE], options);
290
- const transferRef = `refs/heads/playcraft-transfer-${tree}`;
291
- await runTransferGit(checkout, ['update-ref', transferRef, head], options);
292
- await runTransferGit(checkout, ['symbolic-ref', 'HEAD', transferRef], options);
293
- const bundlePath = join(temporary, 'workspace.bundle');
294
- await runTransferGit(checkout, ['bundle', 'create', bundlePath, 'HEAD', transferRef, `^${base}`], options);
295
- return { bundlePath, tree, head, stats, cleanup };
296
- }
297
- catch (error) {
298
- await cleanup();
299
- throw error;
300
- }
301
- }
302
- async function treeEntries(root, ref = 'HEAD', signal) {
303
- const entries = [];
304
- const normalized = new Set();
305
- let pending = '';
306
- const parse = (line) => {
307
- const match = /^(100644|100755) blob ([a-f0-9]{40,64})\s+(\d+)\t(.+)$/s.exec(line);
308
- if (!match)
309
- throw new WorkspaceTransferError('invalid_workspace_transfer');
310
- const path = match[4];
311
- if (path.length > 1024 || path.includes('\\') || path.startsWith('/') || /[\x00-\x1f\x7f]/.test(path) ||
312
- path.split('/').some(part => !part || part === '.' || part === '..') || excluded(path))
313
- throw new WorkspaceTransferError('invalid_workspace_transfer');
314
- const size = Number(match[3]);
315
- if (!Number.isSafeInteger(size) || normalized.has(path.toLowerCase()))
316
- throw new WorkspaceTransferError('invalid_workspace_transfer');
317
- normalized.add(path.toLowerCase());
318
- entries.push({ path, sha: match[2], executable: match[1] === '100755', size });
319
- };
320
- await runTransferGit(root, ['ls-tree', '-r', '-z', '-l', ref], { signal, onStdout(chunk) {
321
- pending += chunk;
322
- let end;
323
- while ((end = pending.indexOf('\0')) !== -1) {
324
- parse(pending.slice(0, end));
325
- pending = pending.slice(end + 1);
326
- }
327
- if (pending.length > 1200)
328
- throw new WorkspaceTransferError('invalid_workspace_transfer');
329
- } });
330
- if (pending)
331
- throw new WorkspaceTransferError('invalid_workspace_transfer');
332
- return entries;
333
- }
334
- function summarizeEntries(entries) {
335
- const bytes = entries.reduce((total, entry) => total + entry.size, 0);
336
- if (!Number.isSafeInteger(bytes))
337
- throw new WorkspaceTransferError('invalid_workspace_transfer');
338
- return { bytes, files: entries.length };
339
- }
340
- export async function inspectWorkspaceCommit(root, ref = 'HEAD', options = {}) {
341
- const tree = await runTransferGit(root, ['rev-parse', '--verify', '--end-of-options', `${ref}^{tree}`], options);
342
- return summarizeEntries(await treeEntries(root, tree, options.signal));
343
- }
344
- async function safeTarget(root, path) {
345
- const target = resolve(root, path);
346
- if (!target.startsWith(`${root}${sep}`))
347
- throw new WorkspaceTransferError('invalid_workspace_transfer');
348
- let current = root;
349
- for (const segment of path.split('/')) {
350
- current = join(current, segment);
351
- const info = await lstat(current).catch(error => { if (['ENOENT', 'ENOTDIR'].includes(error.code))
352
- return undefined; throw error; });
353
- if (info?.isSymbolicLink())
354
- throw new WorkspaceTransferError('workspace_transfer_symlink');
355
- }
356
- return target;
357
- }
358
- /** Compare the entire checkout before applying local commits, so cloud edits are never overwritten. */
359
- export async function applyWorkspaceTransfer(root, bundle, expectedTree, options = {}) {
360
- if (!/^[a-f0-9]{40,64}$/.test(expectedTree))
361
- throw new WorkspaceTransferError('invalid_workspace_transfer');
362
- options.signal?.throwIfAborted();
363
- const rootReal = await realpath(root);
364
- const temporary = await mkdtemp(join(tmpdir(), 'playcraft-import-'));
365
- let current;
366
- try {
367
- const bundlePath = typeof bundle === 'string' ? resolve(bundle) : join(temporary, 'incoming.bundle');
368
- const incoming = join(temporary, 'checkout');
369
- if (typeof bundle !== 'string')
370
- await writeFile(bundlePath, bundle, { mode: 0o600 });
371
- const bundleInfo = await stat(bundlePath);
372
- if (!bundleInfo.isFile() || !bundleInfo.size)
373
- throw new WorkspaceTransferError('invalid_workspace_transfer');
374
- const advertised = await runTransferGit(temporary, ['bundle', 'list-heads', bundlePath], options);
375
- const targets = advertised.split('\n').map(line => /^[a-f0-9]{40,64} refs\/heads\/playcraft-transfer-([a-f0-9]{40,64})$/.exec(line)?.[1]).filter(Boolean);
376
- if (targets.length > 1)
377
- throw new WorkspaceTransferError('invalid_workspace_transfer');
378
- const targetTree = targets[0];
379
- current = await createWorkspaceTransfer(rootReal, { ...options, skipBundle: true });
380
- // A lost response can be retried after the original prerequisite has left the cloud snapshot.
381
- if (targetTree === current.tree) {
382
- await options.validateSnapshot?.(current.stats, current.stats, bundleInfo.size);
383
- return { tree: current.tree, head: current.head, changedPaths: [] };
384
- }
385
- const baseline = join(dirname(current.bundlePath), 'checkout');
386
- await runTransferGit(temporary, ['clone', '--shared', '--no-checkout', '--', baseline, incoming], options);
387
- await writeFile(join(incoming, '.git', 'info', 'attributes'), RAW_ATTRIBUTES);
388
- try {
389
- await runTransferGit(incoming, ['fetch', '--no-tags', '--', bundlePath, 'HEAD'], options);
390
- }
391
- catch (error) {
392
- if (current.tree !== expectedTree)
393
- throw new WorkspaceTransferError('workspace_transfer_conflict');
394
- if (error instanceof WorkspaceTransferError && error.code === 'workspace_transfer_git_failed')
395
- throw new WorkspaceTransferError('invalid_workspace_transfer');
396
- throw error;
397
- }
398
- const entries = await treeEntries(incoming, 'FETCH_HEAD', options.signal);
399
- const tree = await runTransferGit(incoming, ['rev-parse', 'FETCH_HEAD^{tree}'], options);
400
- const head = await runTransferGit(incoming, ['rev-parse', 'FETCH_HEAD'], options);
401
- if (targetTree && targetTree !== tree)
402
- throw new WorkspaceTransferError('invalid_workspace_transfer');
403
- await options.validateSnapshot?.(summarizeEntries(entries), current.stats, bundleInfo.size);
404
- if (current.tree === tree)
405
- return { tree, head, changedPaths: [] };
406
- if (current.tree !== expectedTree)
407
- throw new WorkspaceTransferError('workspace_transfer_conflict');
408
- const previous = new Map((await treeEntries(baseline, 'HEAD', options.signal)).map(entry => [entry.path, entry]));
409
- const next = new Map(entries.map(entry => [entry.path, entry]));
410
- const changedPaths = [...new Set([...previous.keys(), ...next.keys()])]
411
- .filter(path => previous.get(path)?.sha !== next.get(path)?.sha || previous.get(path)?.executable !== next.get(path)?.executable);
412
- for (const path of changedPaths) {
413
- const target = await safeTarget(rootReal, path);
414
- const info = await lstat(target).catch(error => { if (['ENOENT', 'ENOTDIR'].includes(error.code))
415
- return undefined; throw error; });
416
- if (next.has(path) && info?.isDirectory()) {
417
- const children = await readdir(target, { recursive: true });
418
- for (const child of children) {
419
- const childPath = `${path}/${child.split(sep).join('/')}`;
420
- const childInfo = await lstat(join(target, child));
421
- if (!childInfo.isDirectory() && (!previous.has(childPath) || next.has(childPath)))
422
- throw new WorkspaceTransferError('workspace_transfer_conflict');
423
- }
424
- }
425
- }
426
- await runTransferGit(incoming, ['checkout', '--force', '--detach', head], options);
427
- const recheck = await copySnapshot(rootReal, undefined, options.signal, true);
428
- if (recheck.tree !== expectedTree)
429
- throw new WorkspaceTransferError('workspace_transfer_conflict');
430
- const applied = [];
431
- try {
432
- // Deleting files first also permits a file to become a directory, or the reverse.
433
- for (const path of changedPaths.filter(path => !next.has(path)).sort((a, b) => b.length - a.length)) {
434
- options.signal?.throwIfAborted();
435
- if (!await matchesFile(rootReal, path, previous.get(path)))
436
- throw new WorkspaceTransferError('workspace_transfer_conflict');
437
- await rm(await safeTarget(rootReal, path), { force: true });
438
- applied.push(path);
439
- await removeEmptyParents(rootReal, join(rootReal, path));
440
- }
441
- for (const path of changedPaths.filter(path => next.has(path))) {
442
- options.signal?.throwIfAborted();
443
- if (!await matchesFile(rootReal, path, previous.get(path)))
444
- throw new WorkspaceTransferError('workspace_transfer_conflict');
445
- const entry = next.get(path);
446
- await installFile(rootReal, path, join(incoming, path), entry.executable);
447
- applied.push(path);
448
- }
449
- }
450
- catch (error) {
451
- // Roll back only our writes; never replace another writer's intervening content.
452
- for (const path of applied.reverse()) {
453
- if (!await matchesFile(rootReal, path, next.get(path)))
454
- throw new WorkspaceTransferError('workspace_transfer_rollback_conflict');
455
- const old = previous.get(path);
456
- if (old)
457
- await installFile(rootReal, path, join(baseline, path), old.executable);
458
- else {
459
- await rm(await safeTarget(rootReal, path), { force: true });
460
- await removeEmptyParents(rootReal, join(rootReal, path));
461
- }
462
- }
463
- throw error;
464
- }
465
- return { tree, head, changedPaths };
466
- }
467
- finally {
468
- await current?.cleanup();
469
- await rm(temporary, { recursive: true, force: true });
470
- }
471
- }
@@ -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.