@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,149 +0,0 @@
1
- import { readFile, writeFile } from 'node:fs/promises';
2
- import { join } from 'node:path';
3
- import sharp from 'sharp';
4
- import { replicaValidationResultSchema, validateReplicaComparisonContent, } from '@playcraft/common/agent-tools';
5
- import { openVideo, runMediaTool, verifyVideo } from './media.js';
6
- import { comparisonOutputPath, writeComparisonArtifact, writeComparisonJson } from './comparison-output.js';
7
- import { comparisonMessage as t, ComparisonCliError } from './comparison-messages.js';
8
- import { comparisonSha256 } from './comparison-files.js';
9
- const xml = (text) => text.replace(/[&<>"']/g, value => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&apos;' })[value]);
10
- const md = (text) => text.replace(/([\\`*_[\]<>])/g, '\\$1');
11
- const reasonMessages = {
12
- 'video.eventPairMissing': 'reasonEventPairMissing', 'video.dimensionUncertain': 'reasonDimensionUncertain',
13
- 'video.dimensionEventMissing': 'reasonDimensionEventMissing', 'video.progressionEvidenceMissing': 'reasonProgressionEvidenceMissing',
14
- 'video.timingPrecisionInsufficient': 'reasonTimingPrecisionInsufficient', 'video.timingFailureUnverified': 'reasonTimingFailureUnverified',
15
- 'video.requiredEffectsMissing': 'reasonRequiredEffectsMissing', 'video.controlRuntimeUncertain': 'reasonControlRuntimeUncertain',
16
- 'video.controlEvidenceMissing': 'reasonControlEvidenceMissing', 'video.requestsFailed': 'reasonRequestsFailed',
17
- };
18
- const renderReason = (reason) => {
19
- const key = reason.startsWith('video.referenceKindMissing:') ? 'reasonReferenceKindMissing'
20
- : Object.prototype.hasOwnProperty.call(reasonMessages, reason) ? reasonMessages[reason] : undefined;
21
- return md(key ? `${t(key)} (${reason})` : reason);
22
- };
23
- async function tile(frame, out) {
24
- const image = await sharp(join(out, frame.path)).resize(260, 190, { fit: 'contain', background: '#f4f4f4' }).png().toBuffer();
25
- const label = Buffer.from(`<svg width="260" height="34"><rect width="100%" height="100%" fill="white"/><text x="8" y="14" font-size="11">${xml(frame.inputId)} · ${xml(frame.evidenceId)}</text><text x="8" y="29" font-size="11">${(frame.timeMs / 1000).toFixed(3)}s</text></svg>`);
26
- return sharp({ create: { width: 260, height: 224, channels: 3, background: '#fff' } }).composite([{ input: image, top: 0, left: 0 }, { input: label, top: 190, left: 0 }]).png().toBuffer();
27
- }
28
- export function renderComparisonReport(comparison, frames) {
29
- const frameMap = new Map(frames.map(frame => [frame.evidenceId, frame]));
30
- const refs = (ids) => ids.map(id => `[${md(id)}](${frameMap.get(id)?.path})`).join(', ');
31
- const list = (values) => values.length ? values.map(md).join(', ') : t('reportNone');
32
- const timingStatus = comparison.timingChecks.some(check => check.status === 'fail') ? 'fail'
33
- : !comparison.timingChecks.length || comparison.timingChecks.some(check => check.status === 'inconclusive') ? 'inconclusive' : 'pass';
34
- const lines = [`# ${t('summary')}: ${comparison.status}`, '', t('coverageNotice'), '',
35
- `## ${t('decision')}`, '', `- ${t('reportStatus')}: **${comparison.status}**`, `- ${t('reportScope')}: ${comparison.scope}`,
36
- `- ${t('reportPolicy')}: ${comparison.policySha256}`, `- ${t('reportRecording')}: ${comparison.recordingHash}`, ''];
37
- lines.push(t(comparison.status === 'pass' ? 'policyPassedNotice' : 'policyNotPassedNotice'), '', `## ${t('coverage')}`, '', t('deterministicNotice'), '', `- ${t('reportRequired')}: ${list(comparison.coverage.requiredEventIds)}`, `- ${t('reportEvaluated')}: ${list(comparison.coverage.evaluatedEventIds)}`, `- ${t('reportMissing')}: ${list(comparison.coverage.missingEventIds)}`, `- ${t('reportRequiredKinds')}: ${list(comparison.coverage.requiredKinds)}`, `- ${t('reportMissingKinds')}: ${list(comparison.coverage.missingKinds)}`, '', `### ${t('reportMissingEvidence')}`, '');
38
- if (comparison.missingEvidence.some(item => item.dimension === 'interaction'))
39
- lines.push(t('interactionUnverified'), '');
40
- for (const item of comparison.missingEvidence)
41
- lines.push(`- ${item.eventId ? `${md(item.eventId)}: ` : ''}${item.dimension ? `${item.dimension}: ` : ''}${renderReason(item.reason)}`);
42
- if (!comparison.missingEvidence.length)
43
- lines.push(`- ${t('reportNone')}`);
44
- lines.push('', `### ${t('reportTimingChecks')}: ${timingStatus}`, '');
45
- if (timingStatus === 'inconclusive')
46
- lines.push(t('timingUnverified'), '');
47
- for (const check of comparison.timingChecks)
48
- lines.push(`- ${t('reportTiming')} ${md(check.eventId)} (${check.measurement}): ${check.status}; ${t('reportReference')} ${check.referenceRangeMs.join('–')} ms; ${t('reportCandidate')} ${check.candidateRangeMs.join('–')} ms; ${t('reportTolerance')} ${check.toleranceMs} ms`);
49
- lines.push('', `## ${t('modelObservations')}`, '', t('modelObservationNotice'), '', `- ${t('reportModel')}: ${comparison.model.provider}/${comparison.model.model} (${comparison.model.processing})`, '', `### ${t('modelSummary')}`, '', md(comparison.summary), '', `### ${t('modelDimensions')}`, '');
50
- for (const [dimension, judgment] of Object.entries(comparison.dimensions))
51
- lines.push(`- **${dimension}: ${judgment.status}** (${judgment.confidence}) — ${md(judgment.reason)} ${refs(judgment.evidenceIds)}`);
52
- lines.push('', `## ${t('differences')}`, '');
53
- for (const finding of comparison.findings)
54
- lines.push(`### ${md(finding.id)} · ${finding.severity} · ${finding.dimension}`, '', `- ${t('reportExpected')}: ${md(finding.expected)}`, `- ${t('reportObserved')}: ${md(finding.observed)}`, `- ${t('reportSuggestedFix')}: ${md(finding.suggestedFix)}`, `- ${t('reportConfidence')}: ${finding.confidence}; ${t('reportEvidence')}: ${refs(finding.evidenceIds)}`, '');
55
- lines.push(`## ${t('alignment')}`, '');
56
- for (const alignment of comparison.alignments) {
57
- const reference = frameMap.get(alignment.referenceEvidenceId);
58
- const candidate = frameMap.get(alignment.candidateEvidenceId);
59
- lines.push(`- **${md(alignment.eventId)}** (${alignment.confidence}): ${t('reportReference')} ${reference?.timeMs} ms ↔ ${t('reportCandidate')} ${candidate?.timeMs} ms; ${refs([alignment.referenceEvidenceId, alignment.candidateEvidenceId])}`);
60
- }
61
- lines.push('', `## ${t('controls')}`, '');
62
- for (const check of comparison.controlChecks)
63
- lines.push(`- ${md(check.stepId)} → ${md(check.eventId)}; ${md(check.controlInputId)}: ${check.effect} (${check.confidence}); ${refs([check.candidateEvidenceId, check.controlEvidenceId])}`);
64
- lines.push('', `## ${t('evidence')}`, '');
65
- for (const frame of frames)
66
- lines.push(`- ${md(frame.evidenceId)} · ${md(frame.inputId)}: ${t('reportRequested')} ${frame.requestedTimeMs} ms, ${t('reportDecoded')} ${frame.timeMs} ms; [${t('reportFrame')}](${frame.path})`);
67
- return `${lines.join('\n')}\n`;
68
- }
69
- /** Extract real source frames for each validated input; model output never supplies local paths. */
70
- export async function materializeComparisonEvidence(input) {
71
- let comparison;
72
- try {
73
- comparison = replicaValidationResultSchema.parse(input.comparison);
74
- validateReplicaComparisonContent({ summary: comparison.summary, evidence: comparison.evidence, alignments: comparison.alignments,
75
- dimensions: comparison.dimensions, findings: comparison.findings, controlChecks: comparison.controlChecks, timings: comparison.timings, missingEvidence: comparison.missingEvidence }, { inputs: comparison.inputs, analysis: input.request.analysis, recording: input.request.recording, policy: input.request.policy, processing: input.request.processing, fps: input.request.fps });
76
- }
77
- catch {
78
- throw new ComparisonCliError('comparisonResult');
79
- }
80
- // Validate every destination before replacing any prior artifact.
81
- for (const path of [...comparison.evidence.map(item => `keyframes/${item.inputId}/${item.id}.png`), 'comparison.json', 'comparison.md', 'manifest.json', 'contact-sheet.jpg', 'paired-contact-sheet.jpg'])
82
- await comparisonOutputPath(input.out, path);
83
- const frames = [];
84
- for (const source of comparison.inputs) {
85
- const local = input.inputs.find(item => item.id === source.id && item.role === source.role);
86
- if (!local)
87
- throw new ComparisonCliError('comparisonResult');
88
- const evidence = comparison.evidence.filter(item => item.inputId === source.id);
89
- if (!evidence.length)
90
- continue;
91
- const file = await openVideo(local.video.path);
92
- try {
93
- await verifyVideo(file, source.source);
94
- const output = await runMediaTool('ffprobe', ['-v', 'error', '-protocol_whitelist', 'file,pipe', '-format_whitelist', 'mov,matroska,webm',
95
- '-select_streams', 'v:0', '-show_entries', 'frame=best_effort_timestamp_time', '-of', 'csv=p=0', '/dev/fd/3'], file.fd, input.signal);
96
- const raw = output.split('\n').map(line => Number.parseFloat(line) * 1000).filter(Number.isFinite);
97
- const origin = raw[0] ?? 0;
98
- const times = raw.map(time => time - origin).filter(time => time >= 0 && time < source.source.durationMs);
99
- if (!times.length)
100
- throw new ComparisonCliError('comparisonResult');
101
- for (const item of evidence) {
102
- input.signal?.throwIfAborted();
103
- const requestedTimeMs = (item.startMs + item.endMs) / 2;
104
- const timeMs = times.reduce((best, time) => Math.abs(time - requestedTimeMs) < Math.abs(best - requestedTimeMs) ? time : best);
105
- const path = `keyframes/${source.id}/${item.id}.png`;
106
- await writeComparisonArtifact(input.out, path, temporary => runMediaTool('ffmpeg', ['-v', 'error', '-nostdin', '-y', '-protocol_whitelist', 'file,pipe', '-format_whitelist', 'mov,matroska,webm',
107
- '-ss', (Math.max(0, timeMs - 0.01) / 1000).toFixed(6), '-i', '/dev/fd/3', '-map', '0:v:0', '-frames:v', '1',
108
- '-vf', 'scale=min(1280\\,iw):-2', '-threads', '1', temporary], file.fd, input.signal));
109
- const bytes = await readFile(join(input.out, path));
110
- const metadata = await sharp(bytes).metadata();
111
- if (!metadata.width || !metadata.height)
112
- throw new ComparisonCliError('comparisonResult');
113
- frames.push({ evidenceId: item.id, inputId: source.id, requestedTimeMs, timeMs, path, sha256: comparisonSha256(bytes) });
114
- }
115
- await verifyVideo(file, source.source);
116
- }
117
- finally {
118
- await file.close();
119
- }
120
- }
121
- if (frames.length !== comparison.evidence.length)
122
- throw new ComparisonCliError('comparisonResult');
123
- const tiles = await Promise.all(frames.map(frame => tile(frame, input.out)));
124
- const columns = Math.min(4, tiles.length);
125
- const contactSheet = join(input.out, 'contact-sheet.jpg');
126
- await writeComparisonArtifact(input.out, 'contact-sheet.jpg', temporary => sharp({ create: { width: columns * 260, height: Math.ceil(tiles.length / columns) * 224, channels: 3, background: '#ddd' } })
127
- .composite(tiles.map((value, index) => ({ input: value, left: index % columns * 260, top: Math.floor(index / columns) * 224 }))).jpeg({ quality: 85 }).toFile(temporary));
128
- const pairs = comparison.alignments.flatMap(alignment => [alignment.referenceEvidenceId, alignment.candidateEvidenceId]);
129
- const selected = pairs.length ? pairs.map(id => tiles[frames.findIndex(frame => frame.evidenceId === id)])
130
- : ['reference', 'candidate'].flatMap(inputId => { const index = frames.findIndex(frame => frame.inputId === inputId); return index >= 0 ? [tiles[index]] : []; });
131
- if (!selected.length)
132
- throw new ComparisonCliError('comparisonResult');
133
- const pairedContactSheet = join(input.out, 'paired-contact-sheet.jpg');
134
- await writeComparisonArtifact(input.out, 'paired-contact-sheet.jpg', temporary => sharp({ create: { width: 520, height: Math.ceil(selected.length / 2) * 224, channels: 3, background: '#ddd' } })
135
- .composite(selected.map((value, index) => ({ input: value, left: index % 2 * 260, top: Math.floor(index / 2) * 224 }))).jpeg({ quality: 85 }).toFile(temporary));
136
- const resultPath = join(input.out, 'comparison.json');
137
- const markdown = join(input.out, 'comparison.md');
138
- await writeComparisonJson(input.out, 'comparison.json', comparison);
139
- await writeComparisonArtifact(input.out, 'comparison.md', temporary => writeFile(temporary, renderComparisonReport(comparison, frames), { mode: 0o600, flag: 'wx' }));
140
- const files = [];
141
- for (const path of ['comparison.json', 'comparison.md', 'contact-sheet.jpg', 'paired-contact-sheet.jpg'])
142
- files.push({ path, sha256: comparisonSha256(await readFile(join(input.out, path))) });
143
- const manifest = join(input.out, 'manifest.json');
144
- await writeComparisonJson(input.out, 'manifest.json', { schemaVersion: 'playcraft.replica-evidence.v1', runId: comparison.runId, state: 'completed', status: comparison.status,
145
- recordingHash: comparison.recordingHash, policySha256: comparison.policySha256, model: comparison.model, inputs: comparison.inputs,
146
- comparison: 'comparison.json', markdown: 'comparison.md', contactSheet: 'contact-sheet.jpg', pairedContactSheet: 'paired-contact-sheet.jpg',
147
- files, frames, frameTimeSemantics: 'nearest-decoded-original-video-frame', audio: 'not_evaluated' });
148
- return { manifest, comparison: resultPath, markdown, contactSheet, pairedContactSheet, keyframes: frames.map(frame => join(input.out, frame.path)) };
149
- }
@@ -1,57 +0,0 @@
1
- import { createHash } from 'node:crypto';
2
- import { lstat, open, realpath } from 'node:fs/promises';
3
- import { isAbsolute, relative, resolve, sep } from 'node:path';
4
- import { ComparisonCliError } from './comparison-messages.js';
5
- export function comparisonSha256(value) {
6
- return createHash('sha256').update(value).digest('hex');
7
- }
8
- export async function readComparisonJson(path, maxBytes = 2 * 1024 * 1024) {
9
- const file = await open(path, 'r');
10
- try {
11
- const stat = await file.stat();
12
- if (!stat.isFile() || stat.size <= 0 || stat.size > maxBytes)
13
- throw new ComparisonCliError('invalidRecording', 64);
14
- return JSON.parse(await file.readFile('utf8'));
15
- }
16
- finally {
17
- await file.close();
18
- }
19
- }
20
- /** Only recorder-owned, regular files under the exact recording directory may become evidence. */
21
- export async function recordingArtifactPath(root, path) {
22
- if (!path || isAbsolute(path) || path.includes('\\') || path.split('/').some(part => !part || part === '.' || part === '..'))
23
- throw new ComparisonCliError('invalidRecording', 64);
24
- const base = await realpath(root);
25
- const target = resolve(base, path);
26
- const local = relative(base, target);
27
- if (!local || local === '..' || local.startsWith(`..${sep}`) || isAbsolute(local))
28
- throw new ComparisonCliError('invalidRecording', 64);
29
- let current = base;
30
- for (const part of local.split(sep)) {
31
- current = resolve(current, part);
32
- const stat = await lstat(current);
33
- if (stat.isSymbolicLink())
34
- throw new ComparisonCliError('invalidRecording', 64);
35
- }
36
- if (await realpath(target) !== target || !(await lstat(target)).isFile())
37
- throw new ComparisonCliError('invalidRecording', 64);
38
- return target;
39
- }
40
- export async function verifyRecordingArtifact(root, artifact, maxBytes = 8 * 1024 * 1024) {
41
- const path = await recordingArtifactPath(root, artifact.path);
42
- const file = await open(path, 'r');
43
- try {
44
- const stat = await file.stat();
45
- if (!stat.isFile() || stat.size > maxBytes)
46
- throw new ComparisonCliError('invalidRecording', 64);
47
- const hash = createHash('sha256');
48
- for await (const chunk of file.createReadStream({ autoClose: false, start: 0 }))
49
- hash.update(chunk);
50
- if (hash.digest('hex') !== artifact.sha256)
51
- throw new ComparisonCliError('invalidRecording', 64);
52
- }
53
- finally {
54
- await file.close();
55
- }
56
- return path;
57
- }
@@ -1,72 +0,0 @@
1
- import { canonicalReplicaJson } from '@playcraft/common/agent-tools';
2
- import { ComparisonCliError } from './comparison-messages.js';
3
- /** Completed capture and every real/omitted scenario step must be represented in the hashed log. */
4
- export function validateComparisonInputLog(records, scenario, artifact) {
5
- const invalid = () => { throw new ComparisonCliError('invalidRecording', 64); };
6
- const finiteTime = (value) => typeof value === 'number' && Number.isFinite(value) && value >= 0;
7
- if (records.some(value => value === null || typeof value !== 'object' || Array.isArray(value)))
8
- invalid();
9
- const log = records;
10
- const starts = log.filter(record => record.kind === 'recording-start'), ends = log.filter(record => record.kind === 'recording-end');
11
- if (starts.length !== 1 || ends.length !== 1 || log[0] !== starts[0] || log.at(-1) !== ends[0])
12
- invalid();
13
- const start = starts[0], end = ends[0];
14
- if (start.plannedAtMs !== 0 || start.actualAtMs !== 0 || start.device !== scenario.pointer || canonicalReplicaJson(start.reset) !== canonicalReplicaJson(scenario.reset)
15
- || end.plannedAtMs !== scenario.durationMs || !finiteTime(end.actualAtMs) || end.actualAtMs < scenario.durationMs || end.status !== 'completed')
16
- invalid();
17
- if (log.some(record => record.status === 'failed' || !finiteTime(record.actualAtMs) || !finiteTime(record.plannedAtMs)))
18
- invalid();
19
- const finals = log.filter(record => ['tap', 'drag'].includes(String(record.kind)) && ['dispatched', 'omitted'].includes(String(record.status)));
20
- if (finals.length !== scenario.steps.length)
21
- invalid();
22
- let previousEnd = -1;
23
- for (const step of scenario.steps) {
24
- const matching = finals.filter(record => record.stepId === step.id);
25
- if (matching.length !== 1)
26
- invalid();
27
- const record = matching[0], omitted = artifact.omittedStepIds.includes(step.id);
28
- if (record.kind !== step.type || record.plannedAtMs !== step.atMs || record.device !== scenario.pointer || record.status !== (omitted ? 'omitted' : 'dispatched')
29
- || !finiteTime(record.actualAtMs) || record.actualAtMs + 2 < step.atMs || record.actualAtMs < previousEnd)
30
- invalid();
31
- const expected = (step.type === 'tap' ? [{ offsetMs: 0, x: step.x, y: step.y }] : step.path).map(point => ({ ...point, actual: {
32
- x: (scenario.contentRect.x + point.x * scenario.contentRect.width) * scenario.viewport.width,
33
- y: (scenario.contentRect.y + point.y * scenario.contentRect.height) * scenario.viewport.height,
34
- } }));
35
- if (canonicalReplicaJson(record.points) !== canonicalReplicaJson(expected))
36
- invalid();
37
- if (!omitted) {
38
- if (!finiteTime(record.completedAtMs) || record.completedAtMs < record.actualAtMs
39
- || record.completedAtMs > artifact.video.source.durationMs + artifact.timing.uncertaintyMs)
40
- invalid();
41
- const pointers = log.filter(pointer => pointer.stepId === step.id && pointer.kind === 'pointer' && pointer.status === 'dispatched');
42
- if (step.type === 'tap' ? !pointers.some(pointer => pointer.phase === 'tap') : !['start', 'move', 'end'].every(phase => pointers.some(pointer => pointer.phase === phase)))
43
- invalid();
44
- previousEnd = record.completedAtMs;
45
- }
46
- else
47
- previousEnd = record.actualAtMs;
48
- }
49
- }
50
- /** Runtime counters must agree with the hashed console and browser event evidence. */
51
- export function validateComparisonConsoleLog(records, artifact) {
52
- const counts = { pageErrors: 0, consoleErrors: 0, failedRequests: 0 };
53
- for (const value of records) {
54
- if (value === null || typeof value !== 'object' || Array.isArray(value))
55
- throw new ComparisonCliError('invalidRecording', 64);
56
- const record = value;
57
- if (typeof record.atMs !== 'number' || !Number.isFinite(record.atMs) || record.atMs < 0)
58
- throw new ComparisonCliError('invalidRecording', 64);
59
- if (record.kind === 'console') {
60
- if (record.level === 'error')
61
- counts.consoleErrors++;
62
- }
63
- else if (['pageerror', 'popup', 'dialog'].includes(String(record.kind)))
64
- counts.pageErrors++;
65
- else if (record.kind === 'requestfailed')
66
- counts.failedRequests++;
67
- else
68
- throw new ComparisonCliError('invalidRecording', 64);
69
- }
70
- if (canonicalReplicaJson(counts) !== canonicalReplicaJson(artifact.runtime))
71
- throw new ComparisonCliError('invalidRecording', 64);
72
- }
@@ -1,104 +0,0 @@
1
- const messages = {
2
- en: {
3
- compare: 'Compare a recorded playable with its reference and extract paired evidence',
4
- processing: 'static | agentic (defaults to static)',
5
- reference: 'Original local reference video', analysis: 'Completed reference analysis.json',
6
- candidate: 'Locally recorded candidate video', recording: 'Recording metadata.json with input and control evidence',
7
- policy: 'Optional versioned comparison policy JSON', kind: 'understanding | comparison (defaults to understanding)',
8
- invalidComparison: 'Comparison arguments or policy are invalid.',
9
- unsafeOutput: 'The comparison output contains a linked or invalid directory or artifact. Choose a safe output directory.',
10
- invalidRecording: 'Recording metadata, source hashes, input logs, or controls are invalid.',
11
- staleRecording: 'The project changed after recording. Make a fresh recording before comparison.',
12
- comparisonContext: 'The reference analysis does not match the reference video.',
13
- comparisonResume: 'The saved comparison request is missing or does not match this run and workspace.',
14
- comparisonUnavailable: 'No authorized model supports the requested comparison.',
15
- comparisonResult: 'The comparison service returned invalid or incomplete evidence.',
16
- comparisonFailed: 'Comparison failed. Inspect its saved request and task before retrying.',
17
- summary: 'Comparison', decision: 'Policy decision', alignment: 'Model event alignment',
18
- differences: 'Model-observed differences and modification suggestions', coverage: 'Coverage and uncertainty',
19
- evidence: 'Paired evidence', controls: 'Model interpretation of interaction controls',
20
- reportStatus: 'Result', reportRequiredKinds: 'Required event kinds', reportMissingKinds: 'Missing event kinds',
21
- reportMissingEvidence: 'Missing or uncertain evidence', reportTimingChecks: 'Deterministic timing checks', reportNone: 'none',
22
- modelObservations: 'Model observations', modelSummary: 'Model summary', modelDimensions: 'Model dimension judgments',
23
- modelObservationNotice: 'These are model observations and interpretations. A model pass does not override the policy decision or missing evidence above.',
24
- policyPassedNotice: 'Policy acceptance passed for this recorded scenario.',
25
- policyNotPassedNotice: 'Policy acceptance has not passed. Gameplay equivalence has not been established.',
26
- deterministicNotice: 'Coverage and timing checks below are deterministic. Missing evidence remains unverified even when a model observation says pass.',
27
- interactionUnverified: 'Interaction is not fully verified: required causal or control evidence is missing or uncertain.',
28
- timingUnverified: 'Timing is not fully verified: evidence precision or coverage is insufficient for the policy tolerance.',
29
- reasonReferenceKindMissing: 'Reference analysis lacks a required event kind.',
30
- reasonEventPairMissing: 'Reference and candidate lack a sufficiently confident event pair.',
31
- reasonDimensionUncertain: 'The model judgment for this dimension is uncertain or below the required confidence.',
32
- reasonDimensionEventMissing: 'This dimension does not cover a required event.',
33
- reasonProgressionEvidenceMissing: 'Evidence for event progression timing is missing or insufficient.',
34
- reasonTimingPrecisionInsufficient: 'Timing precision or coverage cannot resolve the policy tolerance.',
35
- reasonTimingFailureUnverified: 'The model timing failure is not confirmed by deterministic timing checks.',
36
- reasonRequiredEffectsMissing: 'Required gameplay results are not linked to recorded input steps.',
37
- reasonControlRuntimeUncertain: 'A control recording contains runtime errors or failed requests.',
38
- reasonControlEvidenceMissing: 'Required control evidence is missing, uncertain, or does not cover the candidate result window.',
39
- reasonRequestsFailed: 'The candidate recording contains failed network requests.',
40
- reportScope: 'Scope', reportModel: 'Model', reportPolicy: 'Policy', reportRecording: 'Recording',
41
- reportExpected: 'Expected', reportObserved: 'Observed', reportSuggestedFix: 'Suggested fix',
42
- reportConfidence: 'Confidence', reportEvidence: 'evidence', reportReference: 'reference', reportCandidate: 'candidate',
43
- reportRequired: 'Required', reportEvaluated: 'Evaluated', reportMissing: 'Missing', reportTiming: 'Timing',
44
- reportTolerance: 'tolerance', reportRequested: 'requested', reportDecoded: 'decoded', reportFrame: 'frame',
45
- coverageNotice: 'This result covers only the recorded scenario. Audio and unshown gameplay branches were not evaluated.',
46
- },
47
- zh: {
48
- compare: '比较复刻玩法录像与参考视频,并抽取成对证据',
49
- processing: 'static | agentic,默认 static',
50
- reference: '本地原始参考视频', analysis: '已完成的参考分析 analysis.json',
51
- candidate: '本地录制的候选视频', recording: '包含输入和对照证据的录制 metadata.json',
52
- policy: '可选的版本化比较策略 JSON', kind: 'understanding | comparison,默认 understanding',
53
- invalidComparison: '比较参数或策略无效。',
54
- unsafeOutput: '比较输出目录或制品包含符号链接或无效文件,请使用安全的输出目录。',
55
- invalidRecording: '录制元数据、视频摘要、输入日志或对照证据无效。',
56
- staleRecording: '工程在录制后发生了变化,请重新录制再比较。',
57
- comparisonContext: '参考分析与参考视频不一致。',
58
- comparisonResume: '比较请求不存在,或与任务、工作区不一致。',
59
- comparisonUnavailable: '没有有权使用且支持所需比较模式的模型。',
60
- comparisonResult: '比较服务返回的证据无效或不完整。',
61
- comparisonFailed: '比较失败,请先核对已保存的请求和任务再重试。',
62
- summary: '比较报告', decision: '策略结论', alignment: '模型事件配对',
63
- differences: '模型观察到的差异和修改建议', coverage: '覆盖范围与未知项', evidence: '成对证据',
64
- controls: '模型对交互对照的解释', coverageNotice: '结论仅覆盖本次录制场景;音频及未展示的玩法分支尚未评估。',
65
- reportStatus: '结论', reportRequiredKinds: '必需事件类型', reportMissingKinds: '缺失事件类型',
66
- reportMissingEvidence: '缺失或不确定的证据', reportTimingChecks: '确定性时序检查', reportNone: '无',
67
- modelObservations: '模型观察', modelSummary: '模型摘要', modelDimensions: '模型维度判断',
68
- modelObservationNotice: '以下内容是模型的观察和解释。模型判断为 pass,不会覆盖前面的策略结论或证据缺口。',
69
- policyPassedNotice: '本次录制场景已通过策略验收。',
70
- policyNotPassedNotice: '策略验收尚未通过,玩法等价性尚未得到验证。',
71
- deterministicNotice: '以下覆盖范围和时序检查由确定性规则计算。即使模型判断为 pass,缺失证据的部分仍未得到验证。',
72
- interactionUnverified: '交互尚未完整验证:必需的因果或对照证据缺失或不确定。',
73
- timingUnverified: '时序尚未完整验证:证据精度或覆盖不足,无法满足策略容差。',
74
- reasonReferenceKindMissing: '参考分析缺少必需的事件类型。',
75
- reasonEventPairMissing: '参考视频与候选视频缺少达到置信度要求的事件配对。',
76
- reasonDimensionUncertain: '模型对该维度的判断不确定,或未达到置信度要求。',
77
- reasonDimensionEventMissing: '该维度尚未覆盖必需事件。',
78
- reasonProgressionEvidenceMissing: '事件进展的时序证据缺失或不足。',
79
- reasonTimingPrecisionInsufficient: '时序证据的精度或覆盖不足,无法按策略容差得出结论。',
80
- reasonTimingFailureUnverified: '模型报告的时序失败尚未得到确定性时序检查的确认。',
81
- reasonRequiredEffectsMissing: '必需的玩法结果尚未关联到录制输入步骤。',
82
- reasonControlRuntimeUncertain: '对照录制存在运行错误或网络请求失败。',
83
- reasonControlEvidenceMissing: '必需的对照证据缺失、不确定,或未覆盖候选结果的时间窗口。',
84
- reasonRequestsFailed: '候选录制存在网络请求失败。',
85
- reportScope: '范围', reportModel: '模型', reportPolicy: '策略', reportRecording: '录制',
86
- reportExpected: '预期', reportObserved: '实际', reportSuggestedFix: '修改建议',
87
- reportConfidence: '置信度', reportEvidence: '证据', reportReference: '参考', reportCandidate: '候选',
88
- reportRequired: '必需', reportEvaluated: '已评估', reportMissing: '缺失', reportTiming: '时序',
89
- reportTolerance: '容差', reportRequested: '请求时刻', reportDecoded: '解码时刻', reportFrame: '帧',
90
- },
91
- };
92
- export function comparisonMessage(key) {
93
- const locale = process.env.PLAYCRAFT_LANGUAGE || process.env.LANG || 'en';
94
- return messages[locale.startsWith('zh') ? 'zh' : 'en'][key];
95
- }
96
- export class ComparisonCliError extends Error {
97
- code;
98
- exitCode;
99
- constructor(code, exitCode = 4) {
100
- super(comparisonMessage(code));
101
- this.code = code;
102
- this.exitCode = exitCode;
103
- }
104
- }
@@ -1,63 +0,0 @@
1
- import { randomUUID } from 'node:crypto';
2
- import { lstat, mkdir, realpath, rename, rm, writeFile } from 'node:fs/promises';
3
- import { dirname, extname, isAbsolute, join, parse, resolve, sep } from 'node:path';
4
- import { ComparisonCliError } from './comparison-messages.js';
5
- export async function comparisonOutputRoot(root, create = false) {
6
- const absolute = resolve(root), prefix = parse(absolute).root;
7
- let current = prefix;
8
- for (const part of absolute.slice(prefix.length).split(sep).filter(Boolean)) {
9
- current = join(current, part);
10
- if (create)
11
- await mkdir(current, { mode: 0o700 }).catch(error => { if (error.code !== 'EEXIST')
12
- throw error; });
13
- const stat = await lstat(current);
14
- if (stat.isSymbolicLink()) {
15
- // macOS exposes system-managed temporary directories through these aliases.
16
- const systemAlias = process.platform === 'darwin' && ['/tmp', '/var'].includes(current)
17
- && await realpath(current) === `/private${current}`;
18
- if (!systemAlias)
19
- throw new ComparisonCliError('unsafeOutput', 64);
20
- current = await realpath(current);
21
- }
22
- else if (!stat.isDirectory())
23
- throw new ComparisonCliError('unsafeOutput', 64);
24
- }
25
- return realpath(current);
26
- }
27
- /** Refuse linked output directories and files, including artifacts left before resume. */
28
- export async function comparisonOutputPath(root, relative) {
29
- if (isAbsolute(relative) || relative.includes('\\') || relative.split('/').some(part => !part || part === '.' || part === '..'))
30
- throw new ComparisonCliError('unsafeOutput', 64);
31
- let current = await comparisonOutputRoot(root);
32
- const parts = relative.split('/');
33
- for (const part of parts.slice(0, -1)) {
34
- current = join(current, part);
35
- await mkdir(current, { mode: 0o700 }).catch(error => { if (error.code !== 'EEXIST')
36
- throw error; });
37
- const stat = await lstat(current);
38
- if (!stat.isDirectory() || stat.isSymbolicLink())
39
- throw new ComparisonCliError('unsafeOutput', 64);
40
- }
41
- const path = join(current, parts.at(-1));
42
- const stat = await lstat(path).catch(error => { if (error.code === 'ENOENT')
43
- return undefined; throw error; });
44
- if (stat && (!stat.isFile() || stat.isSymbolicLink()))
45
- throw new ComparisonCliError('unsafeOutput', 64);
46
- return path;
47
- }
48
- /** Writers use a private random sibling and replace the destination without following it. */
49
- export async function writeComparisonArtifact(root, relative, writer) {
50
- const target = await comparisonOutputPath(root, relative);
51
- const temporary = join(dirname(target), `.comparison-${randomUUID()}.tmp${extname(target)}`);
52
- try {
53
- await writer(temporary);
54
- await comparisonOutputPath(root, relative);
55
- await rename(temporary, target);
56
- }
57
- finally {
58
- await rm(temporary, { force: true });
59
- }
60
- }
61
- export async function writeComparisonJson(root, relative, value) {
62
- await writeComparisonArtifact(root, relative, temporary => writeFile(temporary, `${JSON.stringify(value, null, 2)}\n`, { mode: 0o600, flag: 'wx' }));
63
- }
@@ -1,66 +0,0 @@
1
- import { readFile } from 'node:fs/promises';
2
- import { dirname, resolve } from 'node:path';
3
- import { canonicalReplicaJson, replicaRecordingMetadataSchema, videoUnderstandingAnalysisSchema, validateVideoUnderstandingContent, } from '@playcraft/common/agent-tools';
4
- import { computeProjectInputDigest } from '../recording/project-digest.js';
5
- import { probeVideo } from './media.js';
6
- import { ComparisonCliError } from './comparison-messages.js';
7
- import { validateComparisonConsoleLog, validateComparisonInputLog } from './comparison-log.js';
8
- import { comparisonSha256, readComparisonJson, recordingArtifactPath, verifyRecordingArtifact } from './comparison-files.js';
9
- function sameSource(actual, expected) {
10
- return actual.sha256 === expected.sha256 && actual.sizeBytes === expected.sizeBytes && actual.mimeType === expected.mimeType
11
- && Math.abs(actual.durationMs - expected.durationMs) < 1 && actual.width === expected.width && actual.height === expected.height;
12
- }
13
- /** Validate hashes and the live checkout before any managed upload or model submission. */
14
- export async function preflightComparison(input) {
15
- const probe = input.probe ?? probeVideo;
16
- let recording;
17
- let analysis;
18
- try {
19
- recording = replicaRecordingMetadataSchema.parse(await readComparisonJson(input.recording));
20
- analysis = videoUnderstandingAnalysisSchema.parse(await readComparisonJson(input.analysis));
21
- validateVideoUnderstandingContent({ summary: analysis.summary, observations: analysis.observations, events: analysis.events, evidence: analysis.evidence, inferences: analysis.inferences, unknowns: analysis.unknowns, reconstructionHints: analysis.reconstructionHints }, analysis.source);
22
- }
23
- catch {
24
- throw new ComparisonCliError('invalidRecording', 64);
25
- }
26
- if (comparisonSha256(canonicalReplicaJson(recording.scenario)) !== recording.scenarioSha256)
27
- throw new ComparisonCliError('invalidRecording', 64);
28
- const cwd = resolve(input.cwd ?? process.cwd());
29
- const root = dirname(resolve(input.recording));
30
- const current = await computeProjectInputDigest(cwd, [root, resolve(input.out)]);
31
- if (resolve(recording.project.root) !== current.root || recording.project.sha256 !== current.sha256 || recording.project.fileCount !== current.fileCount)
32
- throw new ComparisonCliError('staleRecording', 64);
33
- const reference = await probe(resolve(input.reference), input.signal);
34
- if (!sameSource(reference.source, analysis.source))
35
- throw new ComparisonCliError('comparisonContext', 64);
36
- const candidatePath = await recordingArtifactPath(root, recording.candidate.video.path);
37
- const candidate = await probe(resolve(input.candidate), input.signal);
38
- if (candidate.path !== candidatePath || !sameSource(candidate.source, recording.candidate.video.source))
39
- throw new ComparisonCliError('invalidRecording', 64);
40
- const inputs = [{ id: 'reference', role: 'reference', video: reference }, { id: recording.candidate.id, role: 'candidate', video: candidate }];
41
- for (const artifact of [recording.candidate, ...recording.controls]) {
42
- input.signal.throwIfAborted();
43
- const events = await verifyRecordingArtifact(root, artifact.events);
44
- const consolePath = await verifyRecordingArtifact(root, artifact.console);
45
- for (const shot of artifact.screenshots ?? [])
46
- await verifyRecordingArtifact(root, shot, 16 * 1024 * 1024);
47
- // Logs must contain structured records; an empty candidate input log cannot establish real interaction.
48
- const text = await readFile(events, 'utf8');
49
- try {
50
- const records = text.trim() ? text.trim().split('\n').map(line => JSON.parse(line)) : [];
51
- validateComparisonInputLog(records, recording.scenario, artifact);
52
- const consoleText = await readFile(consolePath, 'utf8');
53
- validateComparisonConsoleLog(consoleText.trim() ? consoleText.trim().split('\n').map(line => JSON.parse(line)) : [], artifact);
54
- }
55
- catch {
56
- throw new ComparisonCliError('invalidRecording', 64);
57
- }
58
- if (artifact.kind !== 'candidate') {
59
- const video = await probe(await recordingArtifactPath(root, artifact.video.path), input.signal);
60
- if (!sameSource(video.source, artifact.video.source))
61
- throw new ComparisonCliError('invalidRecording', 64);
62
- inputs.push({ id: artifact.id, role: 'control', video });
63
- }
64
- }
65
- return { recording, analysis, inputs };
66
- }