@playcraft/cli 0.1.24 → 0.1.26

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 (312) hide show
  1. package/README.md +38 -3
  2. package/dist/agent/local-backend.js +1 -1
  3. package/dist/auth/account-activation.js +157 -0
  4. package/dist/auth/messages.js +30 -0
  5. package/dist/cli-root-help.js +2 -0
  6. package/dist/commands/build.js +9 -4
  7. package/dist/commands/create.js +26 -33
  8. package/dist/commands/image.js +2 -6
  9. package/dist/commands/login.js +84 -42
  10. package/dist/commands/platform-skills.generated.js +19 -0
  11. package/dist/commands/playable.js +53 -0
  12. package/dist/commands/skills.js +2 -0
  13. package/dist/commands/tools-3d.js +11 -0
  14. package/dist/commands/tools-batch.js +87 -0
  15. package/dist/commands/tools-generation.js +8 -4
  16. package/dist/commands/tools.js +2 -0
  17. package/dist/commands/video.js +111 -0
  18. package/dist/commands/workspace-runtime.js +54 -0
  19. package/dist/config.js +59 -4
  20. package/dist/index.js +34 -3
  21. package/dist/project-skills/builtin-import.js +225 -0
  22. package/dist/project-skills/commands.js +77 -0
  23. package/dist/project-skills/lifecycle.js +28 -0
  24. package/dist/project-skills/local-run.js +112 -0
  25. package/dist/project-skills/local-store.js +221 -0
  26. package/dist/project-skills/messages.js +80 -0
  27. package/dist/project-skills/reconcile.js +62 -0
  28. package/dist/project-skills/remote-cache.js +51 -0
  29. package/dist/project-skills/validation.js +19 -0
  30. package/dist/recording/messages.js +40 -0
  31. package/dist/recording/project-digest.js +85 -0
  32. package/dist/recording/recorder.js +293 -0
  33. package/dist/recording/scenario.js +50 -0
  34. package/dist/remix/clone.js +2 -0
  35. package/dist/remix/init-template.js +2 -0
  36. package/dist/remix/pull.js +2 -0
  37. package/dist/remix/push.js +21 -1
  38. package/dist/utils/agent-api-client.js +185 -30
  39. package/dist/utils/environment-endpoints.js +17 -0
  40. package/dist/utils/playcraft-http-client.js +6 -4
  41. package/dist/utils/tool-operation-journal.js +124 -0
  42. package/dist/utils/video-evidence-git.js +81 -0
  43. package/dist/video-replication/client.js +222 -0
  44. package/dist/video-replication/comparison-evidence.js +149 -0
  45. package/dist/video-replication/comparison-files.js +57 -0
  46. package/dist/video-replication/comparison-log.js +72 -0
  47. package/dist/video-replication/comparison-messages.js +104 -0
  48. package/dist/video-replication/comparison-output.js +63 -0
  49. package/dist/video-replication/comparison-preflight.js +66 -0
  50. package/dist/video-replication/comparison.js +216 -0
  51. package/dist/video-replication/evidence.js +165 -0
  52. package/dist/video-replication/media.js +111 -0
  53. package/dist/video-replication/messages.js +89 -0
  54. package/dist/workspace-runtime/codex/app-server.js +636 -0
  55. package/dist/workspace-runtime/codex/config-toml.js +107 -0
  56. package/dist/workspace-runtime/codex/jsonrpc-stdio.js +123 -0
  57. package/dist/workspace-runtime/codex/launcher-client.js +163 -0
  58. package/dist/workspace-runtime/codex/loopback.js +143 -0
  59. package/dist/workspace-runtime/codex/model-broker.js +258 -0
  60. package/dist/workspace-runtime/codex/native-adapter.js +9 -0
  61. package/dist/workspace-runtime/main.js +10 -0
  62. package/dist/workspace-runtime/persistence/journal.js +355 -0
  63. package/dist/workspace-runtime/persistence/native-subagents.js +162 -0
  64. package/dist/workspace-runtime/persistence/sqlite-client.js +77 -0
  65. package/dist/workspace-runtime/persistence/sqlite-worker.js +1057 -0
  66. package/dist/workspace-runtime/previews/publish.js +259 -0
  67. package/dist/workspace-runtime/previews/single-file.js +43 -0
  68. package/dist/workspace-runtime/processes/managed-writes.js +69 -0
  69. package/dist/workspace-runtime/production/contracts.js +17 -0
  70. package/dist/workspace-runtime/production/controller.js +207 -0
  71. package/dist/workspace-runtime/production/dependencies.js +60 -0
  72. package/dist/workspace-runtime/production/native.js +90 -0
  73. package/dist/workspace-runtime/production/processes.js +86 -0
  74. package/dist/workspace-runtime/production/save-command.js +24 -0
  75. package/dist/workspace-runtime/production/source-copy.js +91 -0
  76. package/dist/workspace-runtime/production/stages.js +202 -0
  77. package/dist/workspace-runtime/production/storage.js +32 -0
  78. package/dist/workspace-runtime/serve.js +202 -0
  79. package/dist/workspace-runtime/server/auth.js +21 -0
  80. package/dist/workspace-runtime/server/connectivity.js +201 -0
  81. package/dist/workspace-runtime/server/desktop-host.js +429 -0
  82. package/dist/workspace-runtime/server/desktop-preview-config.js +81 -0
  83. package/dist/workspace-runtime/server/desktop-preview-service.js +157 -0
  84. package/dist/workspace-runtime/server/desktop-preview-static.js +53 -0
  85. package/dist/workspace-runtime/server/desktop-preview.js +189 -0
  86. package/dist/workspace-runtime/server/dispatch.js +903 -0
  87. package/dist/workspace-runtime/server/endpoints.js +19 -0
  88. package/dist/workspace-runtime/server/execution-group.js +85 -0
  89. package/dist/workspace-runtime/server/http.js +984 -0
  90. package/dist/workspace-runtime/server/lock.js +53 -0
  91. package/dist/workspace-runtime/server/rfb-watch-gate.js +168 -0
  92. package/dist/workspace-runtime/server/terminal-host.js +294 -0
  93. package/dist/workspace-runtime/server/types.js +1 -0
  94. package/dist/workspace-runtime/workspaces/artifact-events.js +55 -0
  95. package/dist/workspace-runtime/workspaces/artifact-files.js +195 -0
  96. package/dist/workspace-runtime/workspaces/artifact-receipts.js +89 -0
  97. package/dist/workspace-runtime/workspaces/artifacts.js +260 -0
  98. package/dist/workspace-runtime/workspaces/attachment-content.js +111 -0
  99. package/dist/workspace-runtime/workspaces/attachments.js +175 -0
  100. package/dist/workspace-runtime/workspaces/batch-commit.js +142 -0
  101. package/dist/workspace-runtime/workspaces/cli-context.js +23 -0
  102. package/dist/workspace-runtime/workspaces/cli-credentials.js +111 -0
  103. package/dist/workspace-runtime/workspaces/context-error.js +2 -0
  104. package/dist/workspace-runtime/workspaces/dependencies.js +264 -0
  105. package/dist/workspace-runtime/workspaces/developer-command.js +64 -0
  106. package/dist/workspace-runtime/workspaces/directories.js +113 -0
  107. package/dist/workspace-runtime/workspaces/directory-access.js +29 -0
  108. package/dist/workspace-runtime/workspaces/directory-maintenance.js +111 -0
  109. package/dist/workspace-runtime/workspaces/directory-stats.js +49 -0
  110. package/dist/workspace-runtime/workspaces/disk-snapshot.js +31 -0
  111. package/dist/workspace-runtime/workspaces/empty-template.js +40 -0
  112. package/dist/workspace-runtime/workspaces/files.js +120 -0
  113. package/dist/workspace-runtime/workspaces/git-credential-broker.js +312 -0
  114. package/dist/workspace-runtime/workspaces/git-credential-helper.js +68 -0
  115. package/dist/workspace-runtime/workspaces/git-direct-store.js +155 -0
  116. package/dist/workspace-runtime/workspaces/git.js +481 -0
  117. package/dist/workspace-runtime/workspaces/json5-edit.js +170 -0
  118. package/dist/workspace-runtime/workspaces/parameters.js +272 -0
  119. package/dist/workspace-runtime/workspaces/prepare.js +264 -0
  120. package/dist/workspace-runtime/workspaces/registry.js +200 -0
  121. package/dist/workspace-runtime/workspaces/revisions.js +31 -0
  122. package/dist/workspace-runtime/workspaces/skill-artifact-loader.js +91 -0
  123. package/dist/workspace-runtime/workspaces/skills.js +189 -0
  124. package/dist/workspace-runtime/workspaces/video-attachments.js +49 -0
  125. package/package.json +14 -5
  126. package/project-template/.claude/skills/playcraft-image-generation/SKILL.md +23 -61
  127. package/project-template/.claude/skills/playcraft-replica-validation/SKILL.md +25 -0
  128. package/project-template/.claude/skills/playcraft-replica-validation/references/contracts.md +79 -0
  129. package/project-template/.claude/skills/playcraft-video-understanding/SKILL.md +29 -0
  130. package/project-template/.claude/skills/playcraft-video-understanding/references/contracts.md +37 -0
  131. package/project-template-v2/.claude/agents/artist.md +82 -0
  132. package/project-template-v2/.claude/agents/developer.md +153 -0
  133. package/project-template-v2/.claude/agents/game-designer.md +264 -0
  134. package/project-template-v2/.claude/agents/refs/artist-art-style-catalog.md +533 -0
  135. package/project-template-v2/.claude/agents/refs/artist-color-audio-recipes.md +153 -0
  136. package/project-template-v2/.claude/agents/refs/artist-dimension-axis.md +27 -0
  137. package/project-template-v2/.claude/agents/refs/artist-master-composite-recipes.md +208 -0
  138. package/project-template-v2/.claude/agents/refs/atom-skill-library.md +81 -0
  139. package/project-template-v2/.claude/agents/refs/developer-impl-cookbook.md +432 -0
  140. package/project-template-v2/.claude/agents/refs/framework-5-component-filter.md +252 -0
  141. package/project-template-v2/.claude/agents/refs/framework-game-feel-juice.md +266 -0
  142. package/project-template-v2/.claude/agents/refs/framework-mda.md +147 -0
  143. package/project-template-v2/.claude/agents/refs/game-designer-gameplay-sufficiency.md +123 -0
  144. package/project-template-v2/.claude/agents/refs/ta-3d-flip-recipe.md +88 -0
  145. package/project-template-v2/.claude/agents/refs/ta-atlas-deliverable-standard.md +67 -0
  146. package/project-template-v2/.claude/agents/refs/ta-batch-pipeline-recipes.md +120 -0
  147. package/project-template-v2/.claude/agents/refs/ta-image-generation-detail.md +300 -0
  148. package/project-template-v2/.claude/agents/refs/ta-image-ops-reference.md +495 -0
  149. package/project-template-v2/.claude/agents/refs/ta-pipeline-cookbook.md +1141 -0
  150. package/project-template-v2/.claude/agents/refs/ta-tools-reference.md +111 -0
  151. package/project-template-v2/.claude/agents/refs/ta-vfx-preset-catalog.md +365 -0
  152. package/project-template-v2/.claude/agents/refs/threejs-cannon-pitfalls.md +412 -0
  153. package/project-template-v2/.claude/agents/reviewer.md +75 -0
  154. package/project-template-v2/.claude/agents/technical-artist.md +86 -0
  155. package/project-template-v2/.claude/hooks/snapshot-milestone.mjs +243 -0
  156. package/project-template-v2/.claude/hooks/user-prompt.mjs +133 -0
  157. package/project-template-v2/.claude/settings.json +33 -0
  158. package/project-template-v2/.claude/skills/brainstorming/SKILL.md +161 -0
  159. package/project-template-v2/.claude/skills/brainstorming/scripts/frame-template.html +270 -0
  160. package/project-template-v2/.claude/skills/brainstorming/scripts/helper.js +177 -0
  161. package/project-template-v2/.claude/skills/brainstorming/scripts/server.cjs +354 -0
  162. package/project-template-v2/.claude/skills/brainstorming/scripts/start-server.sh +148 -0
  163. package/project-template-v2/.claude/skills/brainstorming/scripts/stop-server.sh +56 -0
  164. package/project-template-v2/.claude/skills/brainstorming/scripts/wait-for-selection.sh +62 -0
  165. package/project-template-v2/.claude/skills/brainstorming/spec-document-reviewer-prompt.md +49 -0
  166. package/project-template-v2/.claude/skills/brainstorming/visual-companion.md +309 -0
  167. package/project-template-v2/.claude/skills/playcraft-ad-psychology/SKILL.md +182 -0
  168. package/project-template-v2/.claude/skills/playcraft-art-style-guide/SKILL.md +123 -0
  169. package/project-template-v2/.claude/skills/playcraft-asset-state-sheet/SKILL.md +205 -0
  170. package/project-template-v2/.claude/skills/playcraft-audio-generation/SKILL.md +280 -0
  171. package/project-template-v2/.claude/skills/playcraft-batch-pipeline/SKILL.md +184 -0
  172. package/project-template-v2/.claude/skills/playcraft-build-optimizer/SKILL.md +306 -0
  173. package/project-template-v2/.claude/skills/playcraft-image-generation/SKILL.md +298 -0
  174. package/project-template-v2/.claude/skills/playcraft-image-generation/reference/build-sprite-sheet.template.mjs +123 -0
  175. package/project-template-v2/.claude/skills/playcraft-image-generation/reference/compare-style.template.mjs +254 -0
  176. package/project-template-v2/.claude/skills/playcraft-image-generation/reference/gen-batch-sprite.template.mjs +324 -0
  177. package/project-template-v2/.claude/skills/playcraft-image-generation/reference/gen-batch.template.mjs +97 -0
  178. package/project-template-v2/.claude/skills/playcraft-image-generation/reference/gen-edit-variants.template.mjs +118 -0
  179. package/project-template-v2/.claude/skills/playcraft-image-generation/reference/process-batch.template.mjs +137 -0
  180. package/project-template-v2/.claude/skills/playcraft-image-generation/reference/prompt-cookbook.md +397 -0
  181. package/project-template-v2/.claude/skills/playcraft-image-generation/reference/validate-sprite-sheet.template.mjs +296 -0
  182. package/project-template-v2/.claude/skills/playcraft-image-ops/SKILL.md +122 -0
  183. package/project-template-v2/.claude/skills/playcraft-image-processing/SKILL.md +219 -0
  184. package/project-template-v2/.claude/skills/playcraft-masking/SKILL.md +373 -0
  185. package/project-template-v2/.claude/skills/playcraft-playable-optimization/SKILL.md +161 -0
  186. package/project-template-v2/.claude/skills/playcraft-research/SKILL.md +215 -0
  187. package/project-template-v2/.claude/skills/playcraft-skill-recommender/SKILL.md +382 -0
  188. package/project-template-v2/.claude/skills/playcraft-sprite-generation/SKILL.md +423 -0
  189. package/project-template-v2/.claude/skills/playcraft-sprite-remix/SKILL.md +158 -0
  190. package/project-template-v2/.claude/skills/playcraft-sprite-sheet/SKILL.md +100 -0
  191. package/project-template-v2/.claude/skills/playcraft-storyboard/SKILL.md +167 -0
  192. package/project-template-v2/.claude/skills/playcraft-style-qa/SKILL.md +270 -0
  193. package/project-template-v2/.claude/skills/playcraft-text-rendering/SKILL.md +236 -0
  194. package/project-template-v2/.claude/skills/playcraft-vfx-animation/SKILL.md +130 -0
  195. package/project-template-v2/.claude/skills/playwright-cli/SKILL.md +390 -0
  196. package/project-template-v2/.claude/skills/playwright-cli/references/element-attributes.md +23 -0
  197. package/project-template-v2/.claude/skills/playwright-cli/references/playwright-tests.md +39 -0
  198. package/project-template-v2/.claude/skills/playwright-cli/references/request-mocking.md +87 -0
  199. package/project-template-v2/.claude/skills/playwright-cli/references/running-code.md +240 -0
  200. package/project-template-v2/.claude/skills/playwright-cli/references/session-management.md +226 -0
  201. package/project-template-v2/.claude/skills/playwright-cli/references/spec-driven-testing.md +308 -0
  202. package/project-template-v2/.claude/skills/playwright-cli/references/storage-state.md +275 -0
  203. package/project-template-v2/.claude/skills/playwright-cli/references/test-generation.md +134 -0
  204. package/project-template-v2/.claude/skills/playwright-cli/references/tracing.md +142 -0
  205. package/project-template-v2/.claude/skills/playwright-cli/references/video-recording.md +153 -0
  206. package/project-template-v2/.claude/skills/session-analyzer/SKILL.md +386 -0
  207. package/project-template-v2/.claude/skills/session-analyzer/scripts/execution-breakdown.mjs +182 -0
  208. package/project-template-v2/.claude/skills/session-analyzer/scripts/find-turns.mjs +72 -0
  209. package/project-template-v2/.claude/skills/session-analyzer/scripts/heavy-output.mjs +121 -0
  210. package/project-template-v2/.claude/skills/session-analyzer/scripts/resolve-session.mjs +102 -0
  211. package/project-template-v2/.claude/skills/session-analyzer/scripts/subagent-stats.mjs +127 -0
  212. package/project-template-v2/.claude/skills/session-analyzer/scripts/subagent-tool-timeline.mjs +106 -0
  213. package/project-template-v2/.claude/skills/session-analyzer/scripts/time-gaps.mjs +128 -0
  214. package/project-template-v2/.claude/skills/session-analyzer/scripts/turn-timeline.mjs +67 -0
  215. package/project-template-v2/.claude/snapshot.mjs +263 -0
  216. package/project-template-v2/.playcraft/skills.lock.json +152 -0
  217. package/project-template-v2/CLAUDE.md +146 -0
  218. package/project-template-v2/assets/audio/bgm/.gitkeep +0 -0
  219. package/project-template-v2/assets/audio/sfx/.gitkeep +0 -0
  220. package/project-template-v2/assets/bundles/.gitkeep +0 -0
  221. package/project-template-v2/assets/images/bg/.gitkeep +0 -0
  222. package/project-template-v2/assets/images/reference/.gitkeep +0 -0
  223. package/project-template-v2/assets/images/storyboard/.gitkeep +0 -0
  224. package/project-template-v2/assets/images/tiles/.gitkeep +0 -0
  225. package/project-template-v2/assets/images/ui/.gitkeep +0 -0
  226. package/project-template-v2/assets/images/vfx/.gitkeep +0 -0
  227. package/project-template-v2/assets/models/.gitkeep +0 -0
  228. package/project-template-v2/docs/harness/iteration-1/context-flow.md +254 -0
  229. package/project-template-v2/docs/harness/iteration-1/generate-flow.md +91 -0
  230. package/project-template-v2/docs/harness/iteration-1/ideate-flow.md +214 -0
  231. package/project-template-v2/docs/harness/iteration-1/optimize-flow.md +75 -0
  232. package/project-template-v2/docs/harness/iteration-1/wrapup-flow.md +63 -0
  233. package/project-template-v2/docs/harness/iteration-2/context-flow.md +223 -0
  234. package/project-template-v2/docs/harness/iteration-2/generate-flow.md +129 -0
  235. package/project-template-v2/docs/harness/iteration-2/ideate-flow.md +267 -0
  236. package/project-template-v2/docs/harness/iteration-2/optimize-flow.md +164 -0
  237. package/project-template-v2/docs/harness/iteration-2/wrapup-flow.md +115 -0
  238. package/project-template-v2/docs/harness/orchestrator-flow.md +364 -0
  239. package/project-template-v2/docs/project-state.json +60 -0
  240. package/project-template-v2/docs/project-state.md +72 -0
  241. package/project-template-v2/docs/standards/README.md +225 -0
  242. package/project-template-v2/docs/standards/agent-behavior-standards.md +174 -0
  243. package/project-template-v2/docs/standards/artifacts/design-brief.md +19 -0
  244. package/project-template-v2/docs/standards/artifacts/design.md +22 -0
  245. package/project-template-v2/docs/standards/artifacts/game-code.md +41 -0
  246. package/project-template-v2/docs/standards/artifacts/todo-list.md +41 -0
  247. package/project-template-v2/docs/standards/iter1-agent-behavior-standards.md +343 -0
  248. package/project-template-v2/game/index.ts +18 -0
  249. package/project-template-v2/globals.d.ts +51 -0
  250. package/project-template-v2/index.css +34 -0
  251. package/project-template-v2/index.html +18 -0
  252. package/project-template-v2/main.ts +9 -0
  253. package/project-template-v2/package.json +46 -0
  254. package/project-template-v2/skills/_shared/scripts/dispatch-clear.mjs +31 -0
  255. package/project-template-v2/skills/_shared/scripts/dispatch-set.mjs +86 -0
  256. package/project-template-v2/skills/_shared/scripts/dod-check.mjs +153 -0
  257. package/project-template-v2/skills/_shared/scripts/handoff-append.mjs +70 -0
  258. package/project-template-v2/skills/_shared/scripts/lib/validator-artifacts.mjs +91 -0
  259. package/project-template-v2/skills/_shared/scripts/pipeline/dod-config.mjs +131 -0
  260. package/project-template-v2/skills/_shared/scripts/pipeline/index.mjs +80 -0
  261. package/project-template-v2/skills/_shared/scripts/pipeline/iteration-1-core.mjs +90 -0
  262. package/project-template-v2/skills/_shared/scripts/pipeline/iteration-2-wrap.mjs +93 -0
  263. package/project-template-v2/skills/_shared/scripts/pipeline/iteration-3-visual.mjs +23 -0
  264. package/project-template-v2/skills/_shared/scripts/render-project-state.mjs +230 -0
  265. package/project-template-v2/skills/_shared/scripts/state-advance-stage.mjs +58 -0
  266. package/project-template-v2/skills/_shared/scripts/state-get.mjs +339 -0
  267. package/project-template-v2/skills/_shared/scripts/state-handoff.mjs +39 -0
  268. package/project-template-v2/skills/_shared/scripts/state-set.mjs +94 -0
  269. package/project-template-v2/skills/_shared/scripts/state-store.mjs +783 -0
  270. package/project-template-v2/skills/_shared/scripts/todo-add.mjs +88 -0
  271. package/project-template-v2/skills/_shared/scripts/todo-get.mjs +130 -0
  272. package/project-template-v2/skills/_shared/scripts/todo-remove.mjs +33 -0
  273. package/project-template-v2/skills/_shared/scripts/todo-set.mjs +47 -0
  274. package/project-template-v2/skills/_shared/scripts/verify-asset-code-sync.mjs +268 -0
  275. package/project-template-v2/skills/_shared/scripts/verify-env.mjs +285 -0
  276. package/project-template-v2/skills/_shared/scripts/verify-placeholders.mjs +161 -0
  277. package/project-template-v2/skills/playable-autoplay/SKILL.md +176 -0
  278. package/project-template-v2/skills/playable-autoplay/agents/openai.yaml +4 -0
  279. package/project-template-v2/skills/playable-debug/SKILL.md +116 -0
  280. package/project-template-v2/skills/playable-debug/agents/openai.yaml +4 -0
  281. package/project-template-v2/skills/playable-debug/references/debug-config.md +40 -0
  282. package/project-template-v2/skills/playable-record/SKILL.md +140 -0
  283. package/project-template-v2/skills/playable-record/scripts/lib/dev-server.mjs +104 -0
  284. package/project-template-v2/skills/playable-record/scripts/lib/record-audio-bridge.js +141 -0
  285. package/project-template-v2/skills/playable-record/scripts/record-playable.mjs +425 -0
  286. package/project-template-v2/skills/playable-record/scripts/verify-contract.mjs +261 -0
  287. package/project-template-v2/skills/playable-record/scripts/verify-firstwin.mjs +398 -0
  288. package/project-template-v2/skills/playable-record/scripts/verify-fusion.mjs +322 -0
  289. package/project-template-v2/skills/playable-record/scripts/verify-lifecycle.mjs +105 -0
  290. package/project-template-v2/skills/playable-record/scripts/verify-vlm-video.mjs +230 -0
  291. package/project-template-v2/skills/playable-validate/SKILL.md +61 -0
  292. package/project-template-v2/skills/playable-validate/validation-rules.md +14 -0
  293. package/project-template-v2/skills/playable-verify-ui/SKILL.md +63 -0
  294. package/project-template-v2/skills/playable-verify-ui/scripts/lib/__init__.py +1 -0
  295. package/project-template-v2/skills/playable-verify-ui/scripts/lib/config_expr.js +218 -0
  296. package/project-template-v2/skills/playable-verify-ui/scripts/lib/node_utils.js +51 -0
  297. package/project-template-v2/skills/playable-verify-ui/scripts/lib/profile_loader.py +59 -0
  298. package/project-template-v2/skills/playable-verify-ui/scripts/lib/report.py +106 -0
  299. package/project-template-v2/skills/playable-verify-ui/scripts/lib/runtime_contract.js +116 -0
  300. package/project-template-v2/skills/playable-verify-ui/scripts/verify_fetch_antipatterns.py +59 -0
  301. package/project-template-v2/skills/playable-verify-ui/scripts/verify_hardcoded_layout.js +64 -0
  302. package/project-template-v2/skills/playable-verify-ui/scripts/verify_runtime_contract.js +58 -0
  303. package/project-template-v2/skills/playable-verify-ui/scripts/verify_text_overlap.py +333 -0
  304. package/project-template-v2/ta-workspace/scripts/.gitkeep +0 -0
  305. package/project-template-v2/tsconfig.json +20 -0
  306. package/project-template-v2/vite.config.ts +27 -0
  307. package/project-templates/batch-locks/phaser.yaml +922 -0
  308. package/project-templates/batch-locks/threejs.yaml +978 -0
  309. package/project-templates/empty/AGENTS.md +25 -0
  310. package/project-templates/empty/gitignore +33 -0
  311. package/project-templates/phaser/.claude/skills/playcraft-image-generation/SKILL.md +23 -61
  312. package/project-templates/threejs/.claude/skills/playcraft-image-generation/SKILL.md +23 -61
package/README.md CHANGED
@@ -7,11 +7,11 @@
7
7
 
8
8
  PlayCraft CLI(Command-Line Interface,命令行工具)支持本地代码与云端编辑器实时同步,以及将 PlayCanvas/PlayCraft 项目打包为多平台 Playable Ads。
9
9
 
10
- ## 云端工作台 Runtime(计划能力)
10
+ ## 云端工作台 Runtime
11
11
 
12
- 云端工作台计划以 `@playcraft/cli` 承载自定义 AGS(Agent Sandbox,Agent 沙箱服务)内的常驻运行服务,新增 `playcraft workspace-runtime serve` 命令,管理多个 Workspace、一个 Codex App Server、文件/Git/终端/预览及持久恢复。该命令尚未实现,当前发布版本不可调用;现有 `playcraft start` 仍用于单项目本地开发。
12
+ `playcraft workspace-runtime serve --config <json>` 已注册。它前台常驻、跳过升级检查,读取 Runtime Server 下发的引导配置,只绑定回环并校验 control token。`runtime.ensure` 会执行到终态;设置 `PLAYCRAFT_NATIVE_ADAPTER=loopback` 或测试注入 loopback 适配器后,可跑会话 create/turn/interrupt/审批,以及本地 journal 检查点/恢复/销毁。工作空间相对路径的 `file.write` 与文件检查点不依赖 loopback。相同幂等键上的 `uncertain` 操作不会新开 turn。这不是 Codex App Server。现有 `playcraft start` 仍用于单项目本地开发,二者不要混用。
13
13
 
14
- 新服务采用固定版本、前台非交互运行,由镜像监管器启动;独立 Runtime Server 在沙箱外部署。具体代码归属、进程权限、发布与恢复契约见 [云端工作台后端架构方案](../../docs/architecture/backend/cloud-workspace/backend-architecture.md#cli-runtime)。
14
+ 实现与模块边界见 [Sandbox Agent Server](./src/workspace-runtime/README.md);契约见 [云端工作台后端架构方案](../../docs/architecture/backend/cloud-workspace/backend-architecture.md#cli-runtime)。
15
15
 
16
16
  ## ✨ 特性
17
17
 
@@ -141,6 +141,17 @@ playcraft login --url https://your-playcraft.example.com
141
141
 
142
142
  登录成功后,`tools`、`recommend`、`fix-ids` 等所有需要后端 API 的命令均无需额外配置,直接使用。
143
143
 
144
+ 账号提示待激活时,使用同一目标环境及已保存的个人访问令牌主动激活:
145
+
146
+ ```bash
147
+ playcraft login --activate
148
+ playcraft login --activate --url https://your-playcraft.example.com
149
+ ```
150
+
151
+ 也可通过 `PLAYCRAFT_TOKEN` 提供个人访问令牌。激活只发送一次请求,成功后重新执行原业务命令;该动作不会打开浏览器或续签令牌。目标服务须已接入身份服务的激活入口;封禁、停用账号及沙箱执行令牌不能用于激活。
152
+
153
+ 若本地、全局或环境变量配置冲突,激活会要求明确指定 `--url`、所需的 `PLAYCRAFT_ENV` 和对应的个人访问令牌。
154
+
144
155
  ### 沙箱环境(CI / Agent)
145
156
 
146
157
  在沙箱或 CI 中可通过 `.playcraft.json` 或环境变量配置:
@@ -188,6 +199,30 @@ echo '{"token":"<sandbox-jwt>","apiUrl":"https://..."}' > .playcraft.json
188
199
  | `recommend improvements` | Tag 改进建议 | `playcraft recommend improvements --channel Facebook` |
189
200
  | `recommend random` | 随机 Tag 组合推荐 | `playcraft recommend random --channel Google` |
190
201
 
202
+ ### 业务工具请求恢复
203
+
204
+ 业务工具写请求要求服务支持持久操作协议。命令行先保存请求编号、路径和输入摘要到 `~/.playcraft/tool-requests/`,再发送请求;记录不包含凭证或请求正文。可用绝对路径环境变量 `PLAYCRAFT_TOOL_REQUEST_DIR` 指定可持久保留的目录。
205
+
206
+ 原样重跑同一条命令(路径与参数都相同)时,命令行按上一次的结果决定是否沿用原请求编号:
207
+
208
+ | 上一次的结果 | 原样重跑时 |
209
+ | -------------------------------------------------------------------- | -------------------------------------------------------- |
210
+ | 成功(2xx) | 新操作 |
211
+ | 请求被拒绝(4xx,下列 409 除外):请求没有被执行 | 新操作 |
212
+ | 服务端错误(5xx)、超时或连接中断:结果未知 | 沿用原编号,不会另起一次操作;服务端按下文的恢复方式处理 |
213
+ | 409 `business_operation_in_progress`:原操作仍在执行 | 沿用原编号,稍后重跑取得结果 |
214
+ | 409 `business_operation_requires_reconciliation`:结果未知,需要核对 | 沿用原编号,不会再执行 |
215
+
216
+ 沿用原编号时,服务端按原操作的状态处理:已完成则返回原结果;仍在执行则返回 409 `business_operation_in_progress`;没有拿到结果就结束时,按该工具声明的恢复方式决定,可以安全重做的工具(生图、构建、发布到 COS 等)用原编号再执行一次,其余返回 409 `business_operation_requires_reconciliation`。原调用的执行租约有效期间(执行中每 20 秒续期,60 秒未续即过期),同一编号不会被再次执行;续期因服务或数据库故障中断超过 60 秒时,重做可能与仍在运行的原调用重叠。
217
+
218
+ 需要核对的操作,只有确认它没有生效后,才可设置 `PLAYCRAFT_TOOL_NEW_REQUEST=1` 强制发起新操作;不要删除记录来绕过。也可以显式指定编号,只查询原操作的确定结果(不会重新执行;声明了恢复方式的工具若上次报错,返回那次的错误)。只查询时即使是可以安全重做的工具,没有结果也返回需要核对;去掉 `PLAYCRAFT_TOOL_REQUEST_ID` 原样重跑即可续做:
219
+
220
+ ```bash
221
+ PLAYCRAFT_TOOL_REQUEST_ID=<错误中显示的请求编号> playcraft tools build-project --project-id <原项目编号>
222
+ ```
223
+
224
+ 能力检查失败时命令会明确说明尚未发送操作。服务不兼容时命令会在发送业务请求前停止;沙箱命令行与 Business 须排空旧调用后同步升级。
225
+
191
226
  ### 构建相关
192
227
 
193
228
  | 命令 | 说明 | 示例 |
@@ -2,7 +2,7 @@ import express from 'express';
2
2
  import crypto from 'node:crypto';
3
3
  import path from 'node:path';
4
4
  import fs from 'node:fs/promises';
5
- import { eq, inArray } from 'drizzle-orm';
5
+ import { eq, inArray } from '@playcraft/common/database/operators';
6
6
  import pc from 'picocolors';
7
7
  import { createSqliteAdapter } from '@playcraft/common/database';
8
8
  import { AssetModel, ProjectModel, SceneModel } from '@playcraft/common/models';
@@ -0,0 +1,157 @@
1
+ import { existsSync, readFileSync } from 'node:fs';
2
+ import { homedir } from 'node:os';
3
+ import { join } from 'node:path';
4
+ import { loadGlobalConfig } from '../config.js';
5
+ import { accountActivationMessage } from './messages.js';
6
+ export const ACCOUNT_PENDING_ACTIVATION = 'account_pending_activation';
7
+ export class AccountActivationError extends Error {
8
+ code;
9
+ status;
10
+ retryable = false;
11
+ constructor(code, message, status) {
12
+ super(message);
13
+ this.code = code;
14
+ this.status = status;
15
+ this.name = 'AccountActivationError';
16
+ }
17
+ }
18
+ export function responseErrorCode(payload) {
19
+ if (!payload || typeof payload !== 'object')
20
+ return undefined;
21
+ const { error, code } = payload;
22
+ return typeof error?.code === 'string' ? error.code : typeof code === 'string' ? code : undefined;
23
+ }
24
+ export function pendingAccountActivationError(status, payload) {
25
+ return status === 403 && responseErrorCode(payload) === ACCOUNT_PENDING_ACTIVATION
26
+ ? new AccountActivationError(ACCOUNT_PENDING_ACTIVATION, `${ACCOUNT_PENDING_ACTIVATION}: ${accountActivationMessage('pending')}`, status)
27
+ : undefined;
28
+ }
29
+ export function parseErrorPayload(text) {
30
+ try {
31
+ return JSON.parse(text);
32
+ }
33
+ catch {
34
+ return undefined;
35
+ }
36
+ }
37
+ function localActivationConfig() {
38
+ for (const path of [
39
+ join(process.cwd(), '.playcraft.json'),
40
+ '/project/.playcraft.json',
41
+ join(homedir(), '.playcraft.json'),
42
+ ]) {
43
+ if (!existsSync(path))
44
+ continue;
45
+ try {
46
+ const config = JSON.parse(readFileSync(path, 'utf8'));
47
+ if (config && typeof config === 'object' && !Array.isArray(config))
48
+ return config;
49
+ }
50
+ catch {
51
+ // Follow the existing local credential lookup when a file is unreadable.
52
+ }
53
+ }
54
+ return {};
55
+ }
56
+ function stringValue(value) {
57
+ return typeof value === 'string' && value ? value : undefined;
58
+ }
59
+ function activationBaseUrl(raw) {
60
+ try {
61
+ const url = new URL(raw);
62
+ if (!['http:', 'https:'].includes(url.protocol) ||
63
+ url.username ||
64
+ url.password ||
65
+ url.search ||
66
+ url.hash)
67
+ throw new Error();
68
+ url.pathname = url.pathname.replace(/\/+$/, '').replace(/\/api$/, '');
69
+ return url.toString().replace(/\/+$/, '');
70
+ }
71
+ catch {
72
+ throw new AccountActivationError('INVALID_ACTIVATION_TARGET', accountActivationMessage('invalidTarget'));
73
+ }
74
+ }
75
+ function savedTokenForTarget(config, rawUrl, target, environment) {
76
+ if (!rawUrl || (stringValue(config.env)?.trim() || '') !== environment)
77
+ return undefined;
78
+ try {
79
+ return activationBaseUrl(rawUrl) === target ? stringValue(config.token) : undefined;
80
+ }
81
+ catch {
82
+ return undefined;
83
+ }
84
+ }
85
+ function normalizedSavedUrl(raw) {
86
+ try {
87
+ return raw ? activationBaseUrl(raw) : undefined;
88
+ }
89
+ catch {
90
+ return undefined;
91
+ }
92
+ }
93
+ export async function activateAccount(options, defaultBaseUrl) {
94
+ const local = localActivationConfig();
95
+ const global = loadGlobalConfig();
96
+ const localUrl = stringValue(local.apiUrl);
97
+ const globalUrl = stringValue(global.backendUrl) || stringValue(global.url);
98
+ const explicitUrl = options.url || process.env.PLAYCRAFT_API_URL || process.env.PLAYCRAFT_URL;
99
+ const target = activationBaseUrl(explicitUrl || localUrl || globalUrl || defaultBaseUrl);
100
+ const explicitToken = options.token || process.env.PLAYCRAFT_TOKEN || process.env.PLAYCRAFT_SANDBOX_TOKEN;
101
+ const savedUrls = [normalizedSavedUrl(localUrl), normalizedSavedUrl(globalUrl)].filter(Boolean);
102
+ const savedTokens = [stringValue(local.token), stringValue(global.token)].filter(Boolean);
103
+ const environmentsConflict = savedUrls.length === 2 &&
104
+ savedUrls[0] === savedUrls[1] &&
105
+ (stringValue(local.env)?.trim() || '') !== (stringValue(global.env)?.trim() || '');
106
+ const environmentTokensConflict = process.env.PLAYCRAFT_TOKEN &&
107
+ process.env.PLAYCRAFT_SANDBOX_TOKEN &&
108
+ process.env.PLAYCRAFT_TOKEN !== process.env.PLAYCRAFT_SANDBOX_TOKEN;
109
+ const environmentUrlsConflict = process.env.PLAYCRAFT_API_URL &&
110
+ process.env.PLAYCRAFT_URL &&
111
+ normalizedSavedUrl(process.env.PLAYCRAFT_API_URL) !==
112
+ normalizedSavedUrl(process.env.PLAYCRAFT_URL);
113
+ if ((!explicitUrl && new Set(savedUrls).size > 1) ||
114
+ (!explicitToken && new Set(savedTokens).size > 1) ||
115
+ (environmentsConflict && !process.env.PLAYCRAFT_ENV) ||
116
+ (environmentTokensConflict && !options.token) ||
117
+ (environmentUrlsConflict && !options.url)) {
118
+ throw new AccountActivationError('AMBIGUOUS_ACTIVATION_TARGET', accountActivationMessage('ambiguous'));
119
+ }
120
+ const environment = (process.env.PLAYCRAFT_ENV ||
121
+ (!explicitUrl ? (localUrl ? stringValue(local.env) : stringValue(global.env)) : '') ||
122
+ '').trim();
123
+ const token = explicitToken ||
124
+ savedTokenForTarget(local, localUrl, target, environment) ||
125
+ savedTokenForTarget(global, globalUrl, target, environment);
126
+ if (!token)
127
+ throw new AccountActivationError('AUTH_REQUIRED', accountActivationMessage('missingToken'));
128
+ if (!/^playcraft_pat_[A-Za-z0-9_-]{32}$/.test(token)) {
129
+ throw new AccountActivationError('INVALID_ACTIVATION_TOKEN', accountActivationMessage('invalidToken'));
130
+ }
131
+ let response;
132
+ try {
133
+ response = await fetch(`${target}/api/settings/personal-access-tokens/activate`, {
134
+ method: 'POST',
135
+ redirect: 'error',
136
+ signal: AbortSignal.timeout(30_000),
137
+ headers: {
138
+ Authorization: `Bearer ${token}`,
139
+ ...(environment ? { 'playcraft-env': environment } : {}),
140
+ },
141
+ });
142
+ }
143
+ catch {
144
+ throw new AccountActivationError('ACTIVATION_UNCONFIRMED', accountActivationMessage('unavailable'));
145
+ }
146
+ if (!response.ok) {
147
+ await response.body?.cancel().catch(() => { });
148
+ throw new AccountActivationError('ACTIVATION_FAILED', accountActivationMessage('failed'), response.status);
149
+ }
150
+ const result = await response.json().catch(() => undefined);
151
+ if (!result ||
152
+ typeof result !== 'object' ||
153
+ result.accountStatus !== 'active') {
154
+ throw new AccountActivationError('ACTIVATION_UNCONFIRMED', accountActivationMessage('unavailable'), response.status);
155
+ }
156
+ console.log(accountActivationMessage('success'));
157
+ }
@@ -0,0 +1,30 @@
1
+ const messages = {
2
+ en: {
3
+ activateOption: 'Explicitly activate the account using the current personal access token',
4
+ tokenOption: 'Personal access token for --activate (or use PLAYCRAFT_TOKEN)',
5
+ pending: 'Account activation is required. Run `playcraft login --activate` with the same token and target environment, then rerun your command.',
6
+ success: 'Account activated. Rerun your original command to continue.',
7
+ missingToken: 'No personal access token matches this target environment. Provide PLAYCRAFT_TOKEN or log in to this environment first.',
8
+ invalidToken: 'Account activation requires a personal access token. A sandbox token cannot activate an account.',
9
+ ambiguous: 'Activation credentials or target environments conflict. Specify --url, PLAYCRAFT_ENV when needed, and an explicit personal access token using --token or PLAYCRAFT_TOKEN.',
10
+ invalidTarget: 'Use a web service base address (http:// or https://) without credentials, a query, or a fragment.',
11
+ failed: 'Account activation failed. Check the target environment and token before trying again.',
12
+ unavailable: 'Account activation could not be confirmed. Check your account status before explicitly trying again.',
13
+ },
14
+ zh: {
15
+ activateOption: '明确使用当前个人访问令牌激活账号',
16
+ tokenOption: '用于 --activate 的个人访问令牌(也可使用 PLAYCRAFT_TOKEN)',
17
+ pending: '账号需要激活。请使用同一令牌和目标环境主动运行 `playcraft login --activate`,然后重新执行原命令。',
18
+ success: '账号已激活,请重新执行原命令继续。',
19
+ missingToken: '没有与目标环境匹配的个人访问令牌。请提供 PLAYCRAFT_TOKEN,或先登录此环境。',
20
+ invalidToken: '激活账号需要个人访问令牌,沙箱令牌不能用于激活账号。',
21
+ ambiguous: '激活凭据或目标环境存在冲突。请明确指定 --url、所需的 PLAYCRAFT_ENV,并通过 --token 或 PLAYCRAFT_TOKEN 提供个人访问令牌。',
22
+ invalidTarget: '激活目标必须是网页服务基址(http:// 或 https://),不能包含凭据、查询参数或片段。',
23
+ failed: '账号激活失败,请检查目标环境和令牌后再尝试。',
24
+ unavailable: '无法确认账号是否已激活。请先检查账号状态,再主动决定是否重试。',
25
+ },
26
+ };
27
+ export function accountActivationMessage(key) {
28
+ const locale = process.env.PLAYCRAFT_LANGUAGE || process.env.LANG || 'en';
29
+ return messages[locale.startsWith('zh') ? 'zh' : 'en'][key];
30
+ }
@@ -5,8 +5,10 @@ export function getCliTopicsHelpText() {
5
5
  '',
6
6
  ' 本地开发 create, init, start, stop, status, logs, config, sync, fix-ids',
7
7
  ' 素材 tools generate-* | image <子命令> | audio <子命令> | prefab <子命令>',
8
+ ' 视频理解 video models | video understand | video jobs get/cancel',
8
9
  ' 平台 build, build-base, build-playable, build-all, analyze, inspect, upgrade',
9
10
  ' tools 除 generate-* 外(Git / 沙箱构建 / 发布 / 列表 / Prefab 等,需后端 API)',
11
+ ' 沙箱常驻 workspace-runtime serve(Cloud Workspace,由 Runtime 引导配置启动)',
10
12
  '',
11
13
  '也可运行: playcraft topics',
12
14
  '',
@@ -450,6 +450,7 @@ export async function buildCommand(projectPath, options) {
450
450
  const shouldRunPlayableOnly = options.mode === 'playable';
451
451
  // 验证平台
452
452
  const validPlatforms = [
453
+ 'playcraft',
453
454
  'facebook',
454
455
  'snapchat',
455
456
  'ironsource',
@@ -473,6 +474,7 @@ export async function buildCommand(projectPath, options) {
473
474
  // 注:Facebook 在 Phaser(playable-scripts devkit)下的产物格式由其 devkit 输入配置决定(恒为 ZIP),
474
475
  // 平台侧不在此处做引擎判断。
475
476
  const platformFormatConfig = {
477
+ playcraft: { formats: ['html'], default: 'html' },
476
478
  facebook: { formats: ['html', 'zip'], default: 'html' },
477
479
  applovin: { formats: ['html'], default: 'html' },
478
480
  ironsource: { formats: ['html'], default: 'html' },
@@ -584,8 +586,11 @@ export async function buildCommand(projectPath, options) {
584
586
  console.log(pc.dim(' 无主题目录,跳过主题选择'));
585
587
  }
586
588
  }
587
- // 输入商店跳转地址(CTA)- 如果配置文件中没有则必填
588
- if (!options.storeUrls) {
589
+ // 输入商店跳转地址(CTA)- 如果配置文件中没有则必填;通用预览(playcraft)不面向渠道,可不填
590
+ if (selectedPlatform === 'playcraft' && !options.storeUrls) {
591
+ console.log(pc.dim('\nℹ️ 通用预览不需要商店地址'));
592
+ }
593
+ else if (!options.storeUrls) {
589
594
  console.log(pc.cyan('\n🔗 商店跳转地址(CTA 按钮目标)'));
590
595
  console.log(pc.dim(' iOS 和 Android 地址均为必填'));
591
596
  const storeUrlAnswer = await inquirer.prompt([
@@ -636,8 +641,8 @@ export async function buildCommand(projectPath, options) {
636
641
  // 未指定格式,使用默认值
637
642
  options.format = formatConfig.default;
638
643
  }
639
- // 如果命令行未传入 storeUrls,交互式输入(必填)
640
- if (!options.storeUrls) {
644
+ // 如果命令行未传入 storeUrls,交互式输入(必填);通用预览(playcraft)不面向渠道,可不填
645
+ if (!options.storeUrls && options.platform !== 'playcraft') {
641
646
  spinner.stop();
642
647
  console.log(pc.cyan('\n🔗 商店跳转地址(CTA 按钮目标)'));
643
648
  console.log(pc.dim(' iOS 和 Android 地址均为必填'));
@@ -1,3 +1,4 @@
1
+ import { prepareLocalSkillLifecycle } from '../project-skills/lifecycle.js';
1
2
  import { execFileSync, execSync } from 'child_process';
2
3
  import inquirer from 'inquirer';
3
4
  import fs from 'fs/promises';
@@ -6,8 +7,11 @@ import { fileURLToPath } from 'url';
6
7
  import pc from 'picocolors';
7
8
  import ora from 'ora';
8
9
  import { resolveCreateTemplate, resolveCreateTemplateDir, } from '../scaffold/templates.js';
10
+ import { PLATFORM_SKILL_DIRS } from './platform-skills.generated.js';
9
11
  const __filename = fileURLToPath(import.meta.url);
10
12
  const __dirname = path.dirname(__filename);
13
+ /** `@playcraft/skills` 包内 Platform Skill 的目录名(与 Atom Skill 的 `skills/` 区分)。 */
14
+ const PLATFORM_SKILLS_DIRNAME = 'playcraft-skills';
11
15
  /** 生成一个本地占位 Project ID(99xxxxxx),后续可通过 playcraft config set 更新为真实 ID */
12
16
  function generateMockProjectId() {
13
17
  const suffix = Math.floor(Math.random() * 90_000_000) + 10_000_000;
@@ -849,7 +853,7 @@ function extractSkillBodyForCursor(content) {
849
853
  * @param cliCommandsDir CLI 命令所在目录(用于解析 skills 包位置)
850
854
  * @param configDir 配置目录名(如 '.claude' 或 '.codebuddy')
851
855
  */
852
- async function setupPlatformSkillLinksInner(targetDir, cliCommandsDir, configDir) {
856
+ export async function setupPlatformSkillLinksInner(targetDir, cliCommandsDir, configDir) {
853
857
  const skillsLinkDir = path.join(targetDir, configDir, 'skills');
854
858
  await fs.mkdir(skillsLinkDir, { recursive: true });
855
859
  // 以下 Skill 在项目模板中以静态目录形式存在,不创建软连接覆盖它们。
@@ -868,11 +872,12 @@ async function setupPlatformSkillLinksInner(targetDir, cliCommandsDir, configDir
868
872
  'playcraft-image-generation',
869
873
  'playcraft-audio-generation',
870
874
  ]);
871
- // 尝试从 CLI 安装包的位置定位 @playcraft/skills/skills 目录
875
+ // Platform Skill 住在 playcraft-skills/,与 Atom Skill 的 skills/ 是两个目录。
876
+ // 尝试从 CLI 安装包的位置定位它(monorepo 内或与 CLI 平级安装时可解析)。
872
877
  const candidateDirs = [
873
- path.resolve(cliCommandsDir, '..', '..', '..', '..', 'skills', 'skills'),
874
- path.resolve(cliCommandsDir, '..', '..', '..', 'skills', 'skills'),
875
- path.resolve(cliCommandsDir, '..', '..', 'skills', 'skills'),
878
+ path.resolve(cliCommandsDir, '..', '..', '..', '..', 'skills', PLATFORM_SKILLS_DIRNAME),
879
+ path.resolve(cliCommandsDir, '..', '..', '..', 'skills', PLATFORM_SKILLS_DIRNAME),
880
+ path.resolve(cliCommandsDir, '..', '..', 'skills', PLATFORM_SKILLS_DIRNAME),
876
881
  ];
877
882
  let resolvedSkillsDir = null;
878
883
  for (const dir of candidateDirs) {
@@ -885,18 +890,24 @@ async function setupPlatformSkillLinksInner(targetDir, cliCommandsDir, configDir
885
890
  // 继续下一个候选
886
891
  }
887
892
  }
888
- // 相对路径(指向 npm install 后的 node_modules)始终创建,作为安装后激活的备用
889
- const relativeSkillsBase = path.join('..', '..', 'node_modules', '@playcraft', 'skills', 'skills');
893
+ // 相对路径(指向 npm install 后的 node_modules)作为安装后激活的备用
894
+ const relativeSkillsBase = path.join('..', '..', 'node_modules', '@playcraft', 'skills', PLATFORM_SKILLS_DIRNAME);
890
895
  if (resolvedSkillsDir) {
891
- // 能立即解析 → 创建指向绝对路径的软连接,当下即可使用
896
+ // 能立即解析 → 以真实目录为准创建绝对路径软连接,当下即可使用。
897
+ // 按「是否含 SKILL.md」判定而非 playcraft- 前缀:sound-effects 无前缀但同样是平台技能。
892
898
  const entries = await fs.readdir(resolvedSkillsDir, { withFileTypes: true });
893
899
  for (const entry of entries) {
894
- if (!entry.name.startsWith('playcraft-') || staticSkills.has(entry.name))
900
+ if (!entry.isDirectory() || staticSkills.has(entry.name))
895
901
  continue;
896
- const linkPath = path.join(skillsLinkDir, entry.name);
897
902
  const targetPath = path.join(resolvedSkillsDir, entry.name);
898
903
  try {
899
- await fs.symlink(targetPath, linkPath, 'dir');
904
+ await fs.access(path.join(targetPath, 'SKILL.md'));
905
+ }
906
+ catch {
907
+ continue;
908
+ }
909
+ try {
910
+ await fs.symlink(targetPath, path.join(skillsLinkDir, entry.name), 'dir');
900
911
  }
901
912
  catch {
902
913
  // 已存在则跳过
@@ -904,28 +915,9 @@ async function setupPlatformSkillLinksInner(targetDir, cliCommandsDir, configDir
904
915
  }
905
916
  }
906
917
  else {
907
- // 无法立即解析 → 创建指向 node_modules 的相对路径软连接,npm install 后自动激活
908
- const knownPlatformTools = [
909
- 'playcraft-asset-management',
910
- 'playcraft-audio-generation',
911
- 'playcraft-build',
912
- 'playcraft-code-editor',
913
- 'playcraft-create-remix',
914
- 'playcraft-deploy',
915
- 'playcraft-image-generation',
916
- 'playcraft-image-processing',
917
- 'playcraft-platform-intro',
918
- 'playcraft-prefab',
919
- 'playcraft-project-management',
920
- 'playcraft-remix-workflow',
921
- 'playcraft-save',
922
- 'playcraft-skill-recommender',
923
- 'playcraft-sprite-generation',
924
- 'playcraft-sprite-remix',
925
- 'playcraft-sprite-sheet',
926
- 'playcraft-3d-flip-sprite',
927
- ];
928
- for (const name of knownPlatformTools) {
918
+ // 无法立即解析 → 指向 node_modules 的相对路径软连接,npm install 后自动激活。
919
+ // 清单由 scripts/generate-platform-skills.mjs 从真实目录生成并由 CI 校验,不要手写。
920
+ for (const name of PLATFORM_SKILL_DIRS) {
929
921
  if (staticSkills.has(name))
930
922
  continue;
931
923
  const linkPath = path.join(skillsLinkDir, name);
@@ -1289,6 +1281,7 @@ export async function createCommand(projectName, options) {
1289
1281
  smokeSpinner.warn(`Harness 自检跳过: ${message}`);
1290
1282
  }
1291
1283
  }
1284
+ await prepareLocalSkillLifecycle(targetDir, 'create');
1292
1285
  const gitSpinner = ora(isWorktreeMode ? '提交项目分支...' : '初始化 Git 仓库...').start();
1293
1286
  try {
1294
1287
  if (isWorktreeMode) {
@@ -677,9 +677,7 @@ async function runBatchStep(cmd, args, inputPath, outputPath) {
677
677
  const meta = await sharp(inputPath).metadata();
678
678
  const targetW = scale ? Math.round((meta.width ?? 64) * scale) : w;
679
679
  const targetH = scale ? Math.round((meta.height ?? 64) * scale) : h;
680
- await safeToFile(sharp(inputPath).resize(targetW, targetH, {
681
- fit, background: { r: 0, g: 0, b: 0, alpha: 0 },
682
- }), outputPath, inputPath);
680
+ await safeToFile(sharp(inputPath).resize(targetW, targetH, { fit }), outputPath, inputPath);
683
681
  break;
684
682
  }
685
683
  case 'crop': {
@@ -838,9 +836,7 @@ export function registerImageCommands(program) {
838
836
  process.exit(1);
839
837
  }
840
838
  const fit = parseResizeFitOption(opts.fit);
841
- await safeToFile(sharp(opts.input).resize(targetW, targetH, {
842
- fit, background: { r: 0, g: 0, b: 0, alpha: 0 },
843
- }), opts.output, opts.input);
839
+ await safeToFile(sharp(opts.input).resize(targetW, targetH, { fit }), opts.output, opts.input);
844
840
  const outSz = statSync(resolveSandboxPath(opts.output)).size;
845
841
  console.log(`Resized ${w}x${h} → ${targetW ?? '?'}x${targetH ?? '?'} — ${opts.output} (${sizeLabelBytes(outSz)})`);
846
842
  }