@playcraft/cli 0.0.44 → 0.0.47

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 (992) hide show
  1. package/dist/atom-tree/build-skeleton.js +180 -0
  2. package/dist/atom-tree/fusion-hints.js +86 -0
  3. package/dist/atom-tree/selection.js +402 -0
  4. package/dist/commands/build-all.js +33 -5
  5. package/dist/commands/build.js +7 -1
  6. package/dist/commands/create.js +774 -50
  7. package/dist/commands/image.js +11 -0
  8. package/dist/commands/prad.js +61 -0
  9. package/dist/commands/remix.js +20 -0
  10. package/dist/commands/skills.js +961 -185
  11. package/dist/commands/tools-detect.js +56 -0
  12. package/dist/commands/tools-generation.js +21 -0
  13. package/dist/commands/tools-video-review.js +100 -0
  14. package/dist/commands/tools.js +4 -0
  15. package/dist/index.js +2 -0
  16. package/dist/prad/atom-ref.js +23 -0
  17. package/dist/prad/check.js +377 -0
  18. package/dist/prad/check.test.js +49 -0
  19. package/dist/prad/explain.js +109 -0
  20. package/dist/prad/load-spec.js +23 -0
  21. package/dist/prad/paths.js +83 -0
  22. package/dist/prad/skills-index.js +60 -0
  23. package/dist/remix/init-template.js +80 -0
  24. package/dist/scaffold/compose.js +211 -0
  25. package/engine-packs/phaser/game/adapters/phaser.ts +52 -0
  26. package/engine-packs/phaser/game/config/game-config.ts +46 -0
  27. package/engine-packs/phaser/game/contract/publish.ts +141 -0
  28. package/engine-packs/phaser/game/contract/types.ts +34 -0
  29. package/engine-packs/phaser/game/index.ts +81 -0
  30. package/engine-packs/phaser/game/runtime-contract.ts +28 -0
  31. package/engine-packs/phaser/manifest.json +1 -0
  32. package/engine-packs/phaser/package.deps.json +1 -0
  33. package/engine-packs/threejs/game/config/.gitkeep +0 -0
  34. package/engine-packs/threejs/game/config/game-config.ts +46 -0
  35. package/engine-packs/threejs/game/contract/publish.ts +141 -0
  36. package/engine-packs/threejs/game/contract/types.ts +34 -0
  37. package/engine-packs/threejs/game/gameplay/.gitkeep +0 -0
  38. package/engine-packs/threejs/game/index.ts +76 -0
  39. package/engine-packs/threejs/game/runtime-contract.ts +15 -0
  40. package/engine-packs/threejs/game/scenes/.gitkeep +0 -0
  41. package/engine-packs/threejs/manifest.json +1 -0
  42. package/engine-packs/threejs/package.deps.json +8 -0
  43. package/package.json +18 -4
  44. package/project-template/.claude/agents/artist.md +101 -0
  45. package/project-template/.claude/agents/developer.md +90 -93
  46. package/project-template/.claude/agents/game-designer.md +192 -0
  47. package/project-template/.claude/agents/refs/README.md +57 -51
  48. package/project-template/.claude/agents/refs/{designer-color-audio-recipes.md → artist-color-audio-recipes.md} +2 -2
  49. package/project-template/.claude/agents/refs/{designer-deliverable-spec.md → artist-deliverable-spec.md} +57 -57
  50. package/project-template/.claude/agents/refs/{designer-dimension-axis.md → artist-dimension-axis.md} +5 -5
  51. package/project-template/.claude/agents/refs/{designer-handoff-v2-checklist.md → artist-handoff-v2-checklist.md} +15 -15
  52. package/project-template/.claude/agents/refs/artist-legacy-fallback.md +14 -0
  53. package/project-template/.claude/agents/refs/{designer-master-composite-recipes.md → artist-master-composite-recipes.md} +3 -3
  54. package/project-template/.claude/agents/refs/artist-style-exploration-flow.md +55 -0
  55. package/project-template/.claude/agents/refs/developer-dev-handoff.md +83 -40
  56. package/project-template/.claude/agents/refs/developer-impl-cookbook.md +365 -67
  57. package/project-template/.claude/agents/refs/developer-legacy-fallback.md +15 -0
  58. package/project-template/.claude/agents/refs/developer-phase1-flow.md +131 -85
  59. package/project-template/.claude/agents/refs/framework-5-component-filter.md +252 -0
  60. package/project-template/.claude/agents/refs/framework-game-feel-juice.md +266 -0
  61. package/project-template/.claude/agents/refs/framework-mda.md +147 -0
  62. package/project-template/.claude/agents/refs/game-designer-gameplay-sufficiency.md +123 -0
  63. package/project-template/.claude/agents/refs/game-designer-legacy-fallback.md +15 -0
  64. package/project-template/.claude/agents/refs/game-designer-workflow-detail.md +69 -0
  65. package/project-template/.claude/agents/refs/implementation-plan-guide.md +316 -0
  66. package/project-template/.claude/agents/refs/reviewer-convergence-eval.md +247 -33
  67. package/project-template/.claude/agents/refs/reviewer-legacy-fallback.md +14 -0
  68. package/project-template/.claude/agents/refs/skill-selector-guide.md +465 -0
  69. package/project-template/.claude/agents/refs/skill-value-layers.md +145 -0
  70. package/project-template/.claude/agents/refs/storyboard-generation-guide.md +110 -0
  71. package/project-template/.claude/agents/refs/ta-atlas-deliverable-standard.md +4 -4
  72. package/project-template/.claude/agents/refs/ta-pipeline-cookbook.md +101 -104
  73. package/project-template/.claude/agents/refs/technical-artist-legacy-fallback.md +14 -0
  74. package/project-template/.claude/agents/reviewer.md +81 -95
  75. package/project-template/.claude/agents/technical-artist.md +71 -95
  76. package/project-template/.claude/hooks/README.md +83 -35
  77. package/project-template/.claude/hooks/asr-coverage-validate.mjs +19 -19
  78. package/project-template/.claude/hooks/block-developer-preflight.mjs +185 -0
  79. package/project-template/.claude/hooks/block-ring1-tasklist.mjs +95 -0
  80. package/project-template/.claude/hooks/gate-dispatcher.mjs +78 -0
  81. package/project-template/.claude/hooks/gate-runner.mjs +557 -0
  82. package/project-template/.claude/hooks/gate.mjs +170 -0
  83. package/project-template/.claude/hooks/snapshot-milestone.mjs +246 -0
  84. package/project-template/.claude/hooks/user-prompt.mjs +193 -0
  85. package/project-template/.claude/hooks/validate-atom-tree-selection.mjs +281 -0
  86. package/project-template/.claude/hooks/validate-workflow-stop.mjs +1535 -95
  87. package/project-template/.claude/settings.json +54 -9
  88. package/project-template/.claude/skills/brainstorming/SKILL.md +161 -0
  89. package/project-template/.claude/skills/brainstorming/scripts/frame-template.html +270 -0
  90. package/project-template/.claude/skills/brainstorming/scripts/helper.js +177 -0
  91. package/project-template/.claude/skills/brainstorming/scripts/server.cjs +354 -0
  92. package/project-template/.claude/skills/brainstorming/scripts/start-server.sh +148 -0
  93. package/project-template/.claude/skills/brainstorming/scripts/stop-server.sh +56 -0
  94. package/project-template/.claude/skills/brainstorming/scripts/wait-for-selection.sh +62 -0
  95. package/project-template/.claude/skills/brainstorming/spec-document-reviewer-prompt.md +49 -0
  96. package/project-template/.claude/skills/brainstorming/visual-companion.md +309 -0
  97. package/project-template/.claude/skills/playcraft-ad-psychology/SKILL.md +3 -3
  98. package/project-template/.claude/skills/playcraft-art-style-guide/SKILL.md +3 -3
  99. package/project-template/.claude/skills/playcraft-asset-state-sheet/SKILL.md +35 -25
  100. package/project-template/.claude/skills/playcraft-audio-generation/SKILL.md +15 -15
  101. package/project-template/.claude/skills/playcraft-build-optimizer/SKILL.md +5 -5
  102. package/project-template/.claude/skills/playcraft-image-generation/SKILL.md +5 -5
  103. package/project-template/.claude/skills/playcraft-image-generation/reference/compare-style.template.mjs +5 -5
  104. package/project-template/.claude/skills/playcraft-image-generation/reference/gen-batch-sprite.template.mjs +116 -27
  105. package/project-template/.claude/skills/playcraft-image-generation/reference/prompt-cookbook.md +1 -1
  106. package/project-template/.claude/skills/playcraft-image-processing/SKILL.md +219 -0
  107. package/project-template/.claude/skills/playcraft-playable-optimization/SKILL.md +161 -0
  108. package/project-template/.claude/skills/playcraft-research/SKILL.md +24 -21
  109. package/project-template/.claude/skills/playcraft-skill-recommender/SKILL.md +331 -0
  110. package/project-template/.claude/skills/playcraft-sprite-generation/SKILL.md +48 -48
  111. package/project-template/.claude/skills/playcraft-sprite-remix/SKILL.md +158 -0
  112. package/project-template/.claude/skills/playcraft-sprite-sheet/SKILL.md +100 -0
  113. package/project-template/.claude/skills/playcraft-storyboard/SKILL.md +4 -4
  114. package/project-template/.claude/skills/playcraft-style-qa/SKILL.md +45 -45
  115. package/project-template/.claude/skills/playcraft-text-rendering/SKILL.md +27 -27
  116. package/project-template/.claude/skills/session-analyzer/SKILL.md +386 -0
  117. package/project-template/.claude/skills/session-analyzer/scripts/execution-breakdown.mjs +182 -0
  118. package/project-template/.claude/skills/session-analyzer/scripts/find-turns.mjs +72 -0
  119. package/project-template/.claude/skills/session-analyzer/scripts/heavy-output.mjs +121 -0
  120. package/project-template/.claude/skills/session-analyzer/scripts/resolve-session.mjs +103 -0
  121. package/project-template/.claude/skills/session-analyzer/scripts/subagent-stats.mjs +127 -0
  122. package/project-template/.claude/skills/session-analyzer/scripts/subagent-tool-timeline.mjs +106 -0
  123. package/project-template/.claude/skills/session-analyzer/scripts/time-gaps.mjs +128 -0
  124. package/project-template/.claude/skills/session-analyzer/scripts/turn-timeline.mjs +67 -0
  125. package/project-template/.claude/snapshot.mjs +350 -0
  126. package/project-template/.codebuddy/settings.json +53 -0
  127. package/project-template/.cursor/hooks.json +1 -1
  128. package/project-template/.cursor/rules/playcraft-orchestrator.mdc +91 -86
  129. package/project-template/.cursor/rules/playcraft-subagent-boundary.mdc +16 -5
  130. package/project-template/CHANGELOG.md +105 -0
  131. package/project-template/CLAUDE.md +58 -254
  132. package/project-template/docs/DESIGN.md +65 -0
  133. package/project-template/docs/intent-clarifications.md +58 -0
  134. package/project-template/docs/pipeline-overview.md +69 -0
  135. package/project-template/docs/project-state.md +211 -0
  136. package/project-template/docs/team/agent-conduct.md +51 -105
  137. package/project-template/docs/team/agent-runtime-matrix.md +60 -74
  138. package/project-template/docs/team/atom-tree-format.md +169 -0
  139. package/project-template/docs/team/collaboration.md +319 -225
  140. package/project-template/docs/team/core-model.md +80 -46
  141. package/project-template/docs/team/game-designer-migration-todo.md +8 -0
  142. package/project-template/docs/team/harness-legacy-deprecation.md +23 -0
  143. package/project-template/docs/team/platform-capabilities.md +16 -13
  144. package/project-template/docs/team/workflow-changelog.md +95 -23
  145. package/project-template/docs/team/workflow-consistency-checklist.md +93 -45
  146. package/project-template/focus-lab/.gitkeep +0 -0
  147. package/project-template/game/.playcraft-scaffold-pending +0 -0
  148. package/project-template/game/index.ts +18 -0
  149. package/project-template/globals.d.ts +51 -0
  150. package/project-template/index.css +34 -0
  151. package/project-template/index.html +18 -0
  152. package/project-template/main.ts +9 -0
  153. package/project-template/package.json +79 -0
  154. package/project-template/skills/README.md +52 -0
  155. package/project-template/skills/_shared/agent-prep.md +136 -0
  156. package/project-template/skills/_shared/requirements.txt +1 -0
  157. package/project-template/skills/_shared/scripts/__pycache__/placeholder_audio_lib.cpython-311.pyc +0 -0
  158. package/project-template/skills/_shared/scripts/__pycache__/placeholder_lib.cpython-311.pyc +0 -0
  159. package/project-template/skills/_shared/scripts/build-evidence-gate.mjs +87 -0
  160. package/project-template/skills/_shared/scripts/context-budget.mjs +215 -0
  161. package/project-template/skills/_shared/scripts/create-playable.mjs +140 -0
  162. package/project-template/skills/_shared/scripts/dev-start.mjs +151 -0
  163. package/project-template/skills/_shared/scripts/disclosure-config.mjs +480 -0
  164. package/project-template/skills/_shared/scripts/ensure-project.mjs +532 -0
  165. package/project-template/skills/_shared/scripts/gen-placeholder-audio.py +345 -0
  166. package/project-template/skills/_shared/scripts/gen-placeholder.py +244 -0
  167. package/project-template/skills/_shared/scripts/harness-smoke.mjs +257 -0
  168. package/project-template/skills/_shared/scripts/lib/validator-artifacts.mjs +129 -0
  169. package/project-template/skills/_shared/scripts/placeholder-pack-sheet.mjs +67 -0
  170. package/project-template/skills/_shared/scripts/placeholder_audio_lib.py +142 -0
  171. package/project-template/skills/_shared/scripts/placeholder_lib.py +993 -0
  172. package/project-template/skills/_shared/scripts/render-project-state.mjs +278 -0
  173. package/project-template/skills/_shared/scripts/ring-config.mjs +252 -0
  174. package/project-template/skills/_shared/scripts/skills-check.mjs +176 -0
  175. package/project-template/skills/_shared/scripts/slice-set.mjs +77 -0
  176. package/project-template/skills/_shared/scripts/state-advance-ring.mjs +63 -0
  177. package/project-template/skills/_shared/scripts/state-get.mjs +215 -0
  178. package/project-template/skills/_shared/scripts/state-handoff.mjs +120 -0
  179. package/project-template/skills/_shared/scripts/state-set.mjs +113 -0
  180. package/project-template/skills/_shared/scripts/state-store.mjs +893 -0
  181. package/project-template/skills/_shared/scripts/sync-template-skills.mjs +112 -0
  182. package/project-template/skills/_shared/scripts/verify-asset-code-sync.mjs +265 -0
  183. package/project-template/skills/_shared/scripts/verify-atom-conformance.mjs +181 -0
  184. package/project-template/skills/_shared/scripts/verify-env.mjs +285 -0
  185. package/project-template/skills/_shared/scripts/verify-placeholders.mjs +161 -0
  186. package/project-template/skills/_shared/scripts/workflow-doctor.mjs +348 -0
  187. package/project-template/skills/playable-animate/SKILL.md +52 -0
  188. package/project-template/skills/playable-animate/agents/openai.yaml +4 -0
  189. package/project-template/skills/playable-animate/references/board-refresh.md +30 -0
  190. package/project-template/skills/playable-animate/references/diagnosis-checklist.md +25 -0
  191. package/project-template/skills/playable-animate/references/entrance-sequencing.md +33 -0
  192. package/project-template/skills/playable-animate/references/interaction-timing.md +23 -0
  193. package/project-template/skills/playable-art/SKILL.md +203 -0
  194. package/project-template/skills/playable-art/agents/openai.yaml +4 -0
  195. package/project-template/skills/playable-art/references/alpha-postprocess.md +46 -0
  196. package/project-template/skills/playable-art/references/cta-art-spec.md +59 -0
  197. package/project-template/skills/playable-art/references/layout-proportions.md +30 -0
  198. package/project-template/skills/playable-art/references/prompt-patterns.md +83 -0
  199. package/project-template/skills/playable-art/references/reference-comparison.md +30 -0
  200. package/project-template/skills/playable-art/references/sheet-import-v2.md +91 -0
  201. package/project-template/skills/playable-art/scripts/__pycache__/import-gpt-art-sheets-v2.cpython-311.pyc +0 -0
  202. package/project-template/skills/playable-art/scripts/__pycache__/import_gpt_art_pipeline.cpython-311.pyc +0 -0
  203. package/project-template/skills/playable-art/scripts/__pycache__/review-processed-assets.cpython-311.pyc +0 -0
  204. package/project-template/skills/playable-art/scripts/build-atlas.py +332 -0
  205. package/project-template/skills/playable-art/scripts/chroma-key.py +50 -0
  206. package/project-template/skills/playable-art/scripts/extract-alpha-split.py +65 -0
  207. package/project-template/skills/playable-art/scripts/extract-dual-bg-alpha.py +118 -0
  208. package/project-template/skills/playable-art/scripts/import-gpt-art-sheets-v2.py +403 -0
  209. package/project-template/skills/playable-art/scripts/import-gpt-art-sheets.py +280 -0
  210. package/project-template/skills/playable-art/scripts/import-gpt-tile-sheet.py +114 -0
  211. package/project-template/skills/playable-art/scripts/import_gpt_art_pipeline.py +321 -0
  212. package/project-template/skills/playable-art/scripts/import_gpt_art_sheets_v2_test.py +126 -0
  213. package/project-template/skills/playable-art/scripts/placeholder_common.py +98 -0
  214. package/project-template/skills/playable-art/scripts/remove-bg.py +91 -0
  215. package/project-template/skills/playable-art/scripts/review-processed-assets.py +468 -0
  216. package/project-template/skills/playable-art/scripts/review_processed_assets_test.py +116 -0
  217. package/project-template/skills/playable-art/scripts/verify-alpha-clean.py +105 -0
  218. package/project-template/skills/playable-debug/SKILL.md +116 -0
  219. package/project-template/skills/playable-debug/agents/openai.yaml +4 -0
  220. package/project-template/skills/playable-debug/references/debug-config.md +40 -0
  221. package/project-template/skills/playable-focus/SKILL.md +148 -0
  222. package/project-template/skills/playable-focus/scripts/bootstrap-playground.mjs +84 -0
  223. package/project-template/skills/playable-focus/scripts/extract-focus-frames.mjs +87 -0
  224. package/project-template/skills/playable-focus/scripts/generate-focus-reports.mjs +121 -0
  225. package/project-template/skills/playable-focus/scripts/playground.mjs +390 -0
  226. package/project-template/skills/playable-focus/templates/focus-lab/frame.html +39 -0
  227. package/project-template/skills/playable-focus/templates/focus-lab/harness/clean-stage.js +114 -0
  228. package/project-template/skills/playable-focus/templates/focus-lab/harness/frame-lab-scene.js +94 -0
  229. package/project-template/skills/playable-focus/templates/focus-lab/harness/particle-lab-scene.js +224 -0
  230. package/project-template/skills/playable-focus/templates/focus-lab/model.html +16 -0
  231. package/project-template/skills/playable-focus/templates/focus-lab/particle.html +38 -0
  232. package/project-template/skills/playable-focus/templates/focus-lab/shatter.html +47 -0
  233. package/project-template/skills/playable-focus/templates/playground/registry.template.json +55 -0
  234. package/project-template/skills/playable-focus/templates/playground/vfx-catalog.template.json +280 -0
  235. package/project-template/skills/playable-fx-decompose/SKILL.md +212 -0
  236. package/project-template/skills/playable-fx-decompose/examples.md +89 -0
  237. package/project-template/skills/playable-fx-decompose/reference.md +201 -0
  238. package/project-template/skills/playable-fx-decompose/requirements.txt +2 -0
  239. package/project-template/skills/playable-fx-decompose/scripts/build-blank-tile-crack.py +192 -0
  240. package/project-template/skills/playable-fx-decompose/scripts/fix-shard-debris-frames.py +60 -0
  241. package/project-template/skills/playable-fx-decompose/scripts/generate-shard-debris.py +276 -0
  242. package/project-template/skills/playable-fx-peak/SKILL.md +237 -0
  243. package/project-template/skills/playable-fx-peak/requirements.txt +2 -0
  244. package/project-template/skills/playable-fx-peak/scripts/fix-frame-edges.py +70 -0
  245. package/project-template/skills/playable-fx-peak/scripts/repack-frame-sequence.py +526 -0
  246. package/project-template/skills/playable-fx-peak/scripts/validate-frame-anchors.py +130 -0
  247. package/project-template/skills/playable-placeholder/SKILL.md +239 -0
  248. package/project-template/skills/playable-record/SKILL.md +166 -0
  249. package/project-template/skills/playable-record/scripts/lib/build-artifact.mjs +92 -0
  250. package/project-template/skills/playable-record/scripts/lib/dev-server.mjs +104 -0
  251. package/project-template/skills/playable-record/scripts/lib/record-audio-bridge.js +141 -0
  252. package/project-template/skills/playable-record/scripts/lib/slice-structure-verify.mjs +199 -0
  253. package/project-template/skills/playable-record/scripts/record-playable.mjs +583 -0
  254. package/project-template/skills/playable-record/scripts/verify-build.mjs +185 -0
  255. package/project-template/skills/playable-record/scripts/verify-contract.mjs +261 -0
  256. package/project-template/skills/playable-record/scripts/verify-firstwin.mjs +398 -0
  257. package/project-template/skills/playable-record/scripts/verify-fusion.mjs +320 -0
  258. package/project-template/skills/playable-record/scripts/verify-lifecycle.mjs +78 -0
  259. package/project-template/skills/playable-record/scripts/verify-slice-artist.mjs +53 -0
  260. package/project-template/skills/playable-record/scripts/verify-slice-ta.mjs +53 -0
  261. package/project-template/skills/playable-record/scripts/verify-slice.mjs +142 -0
  262. package/project-template/skills/playable-record/scripts/verify-visual-ring.mjs +250 -0
  263. package/project-template/skills/playable-record/scripts/verify-visual-ring1.mjs +190 -0
  264. package/project-template/skills/playable-record/scripts/verify-vlm-video.mjs +227 -0
  265. package/project-template/skills/playable-validate/SKILL.md +260 -0
  266. package/project-template/skills/playable-validate/references/checklist-detail.md +36 -0
  267. package/project-template/skills/playable-validate/references/single-html-bundle.md +76 -0
  268. package/project-template/skills/playable-validate/validation-rules.md +200 -0
  269. package/project-template/skills/playable-verify-ui/SKILL.md +295 -0
  270. package/project-template/skills/playable-verify-ui/examples/asset-manifest.example.json +36 -0
  271. package/project-template/skills/playable-verify-ui/examples/mahjong-match.profile.yaml +334 -0
  272. package/project-template/skills/playable-verify-ui/profiles/template.profile.yaml +84 -0
  273. package/project-template/skills/playable-verify-ui/reference.md +165 -0
  274. package/project-template/skills/playable-verify-ui/references/phaser-patterns.md +67 -0
  275. package/project-template/skills/playable-verify-ui/references/scaling-decision-tree.md +51 -0
  276. package/project-template/skills/playable-verify-ui/requirements.txt +7 -0
  277. package/project-template/skills/playable-verify-ui/schema/asset-manifest.schema.json +68 -0
  278. package/project-template/skills/playable-verify-ui/schema/playable-ui-profile.schema.json +171 -0
  279. package/project-template/skills/playable-verify-ui/scripts/__pycache__/asset_manifest.cpython-311.pyc +0 -0
  280. package/project-template/skills/playable-verify-ui/scripts/__pycache__/verify_slice_plan.cpython-311.pyc +0 -0
  281. package/project-template/skills/playable-verify-ui/scripts/__pycache__/verify_vision_evidence.cpython-311.pyc +0 -0
  282. package/project-template/skills/playable-verify-ui/scripts/asset_manifest.py +231 -0
  283. package/project-template/skills/playable-verify-ui/scripts/lib/__init__.py +1 -0
  284. package/project-template/skills/playable-verify-ui/scripts/lib/config_expr.js +218 -0
  285. package/project-template/skills/playable-verify-ui/scripts/lib/node_utils.js +51 -0
  286. package/project-template/skills/playable-verify-ui/scripts/lib/profile_loader.py +59 -0
  287. package/project-template/skills/playable-verify-ui/scripts/lib/report.py +106 -0
  288. package/project-template/skills/playable-verify-ui/scripts/lib/runtime_contract.js +116 -0
  289. package/project-template/skills/playable-verify-ui/scripts/measure_frame_content.py +77 -0
  290. package/project-template/skills/playable-verify-ui/scripts/run_all.py +265 -0
  291. package/project-template/skills/playable-verify-ui/scripts/verify_asset_sizes.py +77 -0
  292. package/project-template/skills/playable-verify-ui/scripts/verify_assets_present.py +62 -0
  293. package/project-template/skills/playable-verify-ui/scripts/verify_atlas_budget.py +84 -0
  294. package/project-template/skills/playable-verify-ui/scripts/verify_atlas_frames.py +79 -0
  295. package/project-template/skills/playable-verify-ui/scripts/verify_bg_clean.py +365 -0
  296. package/project-template/skills/playable-verify-ui/scripts/verify_doc_asset_sync.py +90 -0
  297. package/project-template/skills/playable-verify-ui/scripts/verify_fetch_antipatterns.py +59 -0
  298. package/project-template/skills/playable-verify-ui/scripts/verify_game_config_load.js +54 -0
  299. package/project-template/skills/playable-verify-ui/scripts/verify_hardcoded_layout.js +64 -0
  300. package/project-template/skills/playable-verify-ui/scripts/verify_layout_regions.js +110 -0
  301. package/project-template/skills/playable-verify-ui/scripts/verify_profile_schema.py +37 -0
  302. package/project-template/skills/playable-verify-ui/scripts/verify_ring1_visual.py +211 -0
  303. package/project-template/skills/playable-verify-ui/scripts/verify_runtime_contract.js +58 -0
  304. package/project-template/skills/playable-verify-ui/scripts/verify_script_load_order.js +73 -0
  305. package/project-template/skills/playable-verify-ui/scripts/verify_slice_plan.py +238 -0
  306. package/project-template/skills/playable-verify-ui/scripts/verify_slice_plan_test.py +125 -0
  307. package/project-template/skills/playable-verify-ui/scripts/verify_text_overlap.py +333 -0
  308. package/project-template/skills/playable-verify-ui/scripts/verify_tile_consistency.py +323 -0
  309. package/project-template/skills/playable-verify-ui/scripts/verify_vision_evidence.py +894 -0
  310. package/project-template/skills/playable-verify-ui/scripts/verify_vision_evidence_test.py +122 -0
  311. package/project-template/skills/playable-verify-ui/scripts/verify_visual.py +422 -0
  312. package/project-template/templates/{designer-log.template.md → artist-log.template.md} +26 -31
  313. package/project-template/templates/atom-tree.template.json +133 -0
  314. package/project-template/templates/design-brief.template.md +59 -162
  315. package/project-template/templates/design-lens-checklist.reference.md +14 -14
  316. package/project-template/templates/design-methodology.md +25 -25
  317. package/project-template/templates/design.template.md +50 -0
  318. package/project-template/templates/developer-log.template.md +187 -63
  319. package/project-template/templates/five-axis-framework.md +89 -44
  320. package/project-template/templates/gen-placeholder-audio.template.mjs +63 -0
  321. package/project-template/templates/gen-placeholders.template.mjs +81 -0
  322. package/project-template/templates/intent-clarifications.template.md +7 -7
  323. package/project-template/templates/layout-spec.template.md +32 -20
  324. package/project-template/templates/plan.template.md +80 -0
  325. package/project-template/templates/project-state.template.md +90 -191
  326. package/project-template/templates/review-report.template.md +208 -24
  327. package/project-template/templates/style-exploration.template.md +8 -8
  328. package/project-template/templates/ta-log.template.md +58 -35
  329. package/project-template/tsconfig.json +20 -0
  330. package/project-template/vite.config.ts +27 -0
  331. package/project-templates/phaser/.claude/agents/artist.md +101 -0
  332. package/project-templates/phaser/.claude/agents/developer.md +123 -0
  333. package/project-templates/phaser/.claude/agents/game-designer.md +192 -0
  334. package/project-templates/phaser/.claude/agents/refs/README.md +77 -0
  335. package/project-templates/phaser/.claude/agents/refs/artist-art-style-catalog.md +533 -0
  336. package/project-templates/phaser/.claude/agents/refs/artist-color-audio-recipes.md +153 -0
  337. package/project-templates/phaser/.claude/agents/refs/artist-deliverable-spec.md +230 -0
  338. package/project-templates/phaser/.claude/agents/refs/artist-dimension-axis.md +27 -0
  339. package/project-templates/phaser/.claude/agents/refs/artist-handoff-v2-checklist.md +76 -0
  340. package/project-templates/phaser/.claude/agents/refs/artist-legacy-fallback.md +14 -0
  341. package/project-templates/phaser/.claude/agents/refs/artist-master-composite-recipes.md +208 -0
  342. package/project-templates/phaser/.claude/agents/refs/artist-style-exploration-flow.md +55 -0
  343. package/project-templates/phaser/.claude/agents/refs/developer-dev-handoff.md +152 -0
  344. package/project-templates/phaser/.claude/agents/refs/developer-impl-cookbook.md +432 -0
  345. package/project-templates/phaser/.claude/agents/refs/developer-legacy-fallback.md +15 -0
  346. package/project-templates/phaser/.claude/agents/refs/developer-phase1-flow.md +404 -0
  347. package/project-templates/phaser/.claude/agents/refs/framework-5-component-filter.md +252 -0
  348. package/project-templates/phaser/.claude/agents/refs/framework-game-feel-juice.md +266 -0
  349. package/project-templates/phaser/.claude/agents/refs/framework-mda.md +147 -0
  350. package/project-templates/phaser/.claude/agents/refs/game-designer-gameplay-sufficiency.md +123 -0
  351. package/project-templates/phaser/.claude/agents/refs/game-designer-legacy-fallback.md +15 -0
  352. package/project-templates/phaser/.claude/agents/refs/game-designer-workflow-detail.md +69 -0
  353. package/project-templates/phaser/.claude/agents/refs/implementation-plan-guide.md +309 -0
  354. package/project-templates/phaser/.claude/agents/refs/reviewer-convergence-eval.md +350 -0
  355. package/project-templates/phaser/.claude/agents/refs/reviewer-legacy-fallback.md +14 -0
  356. package/project-templates/phaser/.claude/agents/refs/reviewer-six-dimension-eval.md +6 -0
  357. package/project-templates/phaser/.claude/agents/refs/skill-selector-guide.md +465 -0
  358. package/project-templates/phaser/.claude/agents/refs/skill-value-layers.md +145 -0
  359. package/project-templates/phaser/.claude/agents/refs/storyboard-generation-guide.md +110 -0
  360. package/project-templates/phaser/.claude/agents/refs/ta-3d-flip-recipe.md +88 -0
  361. package/project-templates/phaser/.claude/agents/refs/ta-atlas-deliverable-standard.md +67 -0
  362. package/project-templates/phaser/.claude/agents/refs/ta-batch-pipeline-recipes.md +120 -0
  363. package/project-templates/phaser/.claude/agents/refs/ta-image-generation-detail.md +300 -0
  364. package/project-templates/phaser/.claude/agents/refs/ta-image-ops-reference.md +495 -0
  365. package/project-templates/phaser/.claude/agents/refs/ta-pipeline-cookbook.md +1141 -0
  366. package/project-templates/phaser/.claude/agents/refs/ta-tools-reference.md +111 -0
  367. package/project-templates/phaser/.claude/agents/refs/ta-vfx-preset-catalog.md +365 -0
  368. package/project-templates/phaser/.claude/agents/refs/technical-artist-legacy-fallback.md +14 -0
  369. package/project-templates/phaser/.claude/agents/reviewer.md +116 -0
  370. package/project-templates/phaser/.claude/agents/technical-artist.md +100 -0
  371. package/project-templates/phaser/.claude/hooks/README.md +120 -0
  372. package/project-templates/phaser/.claude/hooks/asr-coverage-validate.mjs +381 -0
  373. package/project-templates/phaser/.claude/hooks/block-developer-preflight.mjs +185 -0
  374. package/project-templates/phaser/.claude/hooks/block-ring1-tasklist.mjs +95 -0
  375. package/project-templates/phaser/.claude/hooks/gate-dispatcher.mjs +78 -0
  376. package/project-templates/phaser/.claude/hooks/gate-runner.mjs +557 -0
  377. package/project-templates/phaser/.claude/hooks/gate.mjs +171 -0
  378. package/project-templates/phaser/.claude/hooks/snapshot-milestone.mjs +247 -0
  379. package/project-templates/phaser/.claude/hooks/user-prompt.mjs +194 -0
  380. package/project-templates/phaser/.claude/hooks/validate-atom-tree-selection.mjs +281 -0
  381. package/project-templates/phaser/.claude/hooks/validate-workflow-stop.mjs +1889 -0
  382. package/project-templates/phaser/.claude/settings.json +81 -0
  383. package/project-templates/phaser/.claude/skills/brainstorming/SKILL.md +161 -0
  384. package/project-templates/phaser/.claude/skills/brainstorming/scripts/frame-template.html +270 -0
  385. package/project-templates/phaser/.claude/skills/brainstorming/scripts/helper.js +177 -0
  386. package/project-templates/phaser/.claude/skills/brainstorming/scripts/server.cjs +354 -0
  387. package/project-templates/phaser/.claude/skills/brainstorming/scripts/start-server.sh +148 -0
  388. package/project-templates/phaser/.claude/skills/brainstorming/scripts/stop-server.sh +56 -0
  389. package/project-templates/phaser/.claude/skills/brainstorming/scripts/wait-for-selection.sh +62 -0
  390. package/project-templates/phaser/.claude/skills/brainstorming/spec-document-reviewer-prompt.md +49 -0
  391. package/project-templates/phaser/.claude/skills/brainstorming/visual-companion.md +309 -0
  392. package/project-templates/phaser/.claude/skills/playcraft-ad-psychology/SKILL.md +182 -0
  393. package/project-templates/phaser/.claude/skills/playcraft-art-style-guide/SKILL.md +123 -0
  394. package/project-templates/phaser/.claude/skills/playcraft-asset-state-sheet/SKILL.md +205 -0
  395. package/project-templates/phaser/.claude/skills/playcraft-audio-generation/SKILL.md +280 -0
  396. package/project-templates/phaser/.claude/skills/playcraft-batch-pipeline/SKILL.md +184 -0
  397. package/project-templates/phaser/.claude/skills/playcraft-build-optimizer/SKILL.md +306 -0
  398. package/project-templates/phaser/.claude/skills/playcraft-image-generation/SKILL.md +298 -0
  399. package/project-templates/phaser/.claude/skills/playcraft-image-generation/reference/build-sprite-sheet.template.mjs +123 -0
  400. package/project-templates/phaser/.claude/skills/playcraft-image-generation/reference/compare-style.template.mjs +254 -0
  401. package/project-templates/phaser/.claude/skills/playcraft-image-generation/reference/gen-batch-sprite.template.mjs +324 -0
  402. package/project-templates/phaser/.claude/skills/playcraft-image-generation/reference/gen-batch.template.mjs +97 -0
  403. package/project-templates/phaser/.claude/skills/playcraft-image-generation/reference/gen-edit-variants.template.mjs +118 -0
  404. package/project-templates/phaser/.claude/skills/playcraft-image-generation/reference/process-batch.template.mjs +137 -0
  405. package/project-templates/phaser/.claude/skills/playcraft-image-generation/reference/prompt-cookbook.md +397 -0
  406. package/project-templates/phaser/.claude/skills/playcraft-image-generation/reference/validate-sprite-sheet.template.mjs +296 -0
  407. package/project-templates/phaser/.claude/skills/playcraft-image-ops/SKILL.md +122 -0
  408. package/project-templates/phaser/.claude/skills/playcraft-image-processing/SKILL.md +219 -0
  409. package/project-templates/phaser/.claude/skills/playcraft-masking/SKILL.md +373 -0
  410. package/project-templates/phaser/.claude/skills/playcraft-playable-optimization/SKILL.md +161 -0
  411. package/project-templates/phaser/.claude/skills/playcraft-research/SKILL.md +215 -0
  412. package/project-templates/phaser/.claude/skills/playcraft-skill-recommender/SKILL.md +331 -0
  413. package/project-templates/phaser/.claude/skills/playcraft-sprite-generation/SKILL.md +423 -0
  414. package/project-templates/phaser/.claude/skills/playcraft-sprite-remix/SKILL.md +158 -0
  415. package/project-templates/phaser/.claude/skills/playcraft-sprite-sheet/SKILL.md +100 -0
  416. package/project-templates/phaser/.claude/skills/playcraft-storyboard/SKILL.md +167 -0
  417. package/project-templates/phaser/.claude/skills/playcraft-style-qa/SKILL.md +270 -0
  418. package/project-templates/phaser/.claude/skills/playcraft-text-rendering/SKILL.md +236 -0
  419. package/project-templates/phaser/.claude/skills/playcraft-vfx-animation/SKILL.md +130 -0
  420. package/project-templates/phaser/.claude/skills/playwright-cli/SKILL.md +390 -0
  421. package/project-templates/phaser/.claude/skills/playwright-cli/references/element-attributes.md +23 -0
  422. package/project-templates/phaser/.claude/skills/playwright-cli/references/playwright-tests.md +39 -0
  423. package/project-templates/phaser/.claude/skills/playwright-cli/references/request-mocking.md +87 -0
  424. package/project-templates/phaser/.claude/skills/playwright-cli/references/running-code.md +240 -0
  425. package/project-templates/phaser/.claude/skills/playwright-cli/references/session-management.md +226 -0
  426. package/project-templates/phaser/.claude/skills/playwright-cli/references/spec-driven-testing.md +308 -0
  427. package/project-templates/phaser/.claude/skills/playwright-cli/references/storage-state.md +275 -0
  428. package/project-templates/phaser/.claude/skills/playwright-cli/references/test-generation.md +134 -0
  429. package/project-templates/phaser/.claude/skills/playwright-cli/references/tracing.md +142 -0
  430. package/project-templates/phaser/.claude/skills/playwright-cli/references/video-recording.md +153 -0
  431. package/project-templates/phaser/.claude/skills/session-analyzer/SKILL.md +386 -0
  432. package/project-templates/phaser/.claude/skills/session-analyzer/scripts/execution-breakdown.mjs +182 -0
  433. package/project-templates/phaser/.claude/skills/session-analyzer/scripts/find-turns.mjs +72 -0
  434. package/project-templates/phaser/.claude/skills/session-analyzer/scripts/heavy-output.mjs +121 -0
  435. package/project-templates/phaser/.claude/skills/session-analyzer/scripts/resolve-session.mjs +103 -0
  436. package/project-templates/phaser/.claude/skills/session-analyzer/scripts/subagent-stats.mjs +127 -0
  437. package/project-templates/phaser/.claude/skills/session-analyzer/scripts/subagent-tool-timeline.mjs +106 -0
  438. package/project-templates/phaser/.claude/skills/session-analyzer/scripts/time-gaps.mjs +128 -0
  439. package/project-templates/phaser/.claude/skills/session-analyzer/scripts/turn-timeline.mjs +67 -0
  440. package/project-templates/phaser/.claude/snapshot.mjs +257 -0
  441. package/project-templates/phaser/.cursor/hooks.json +17 -0
  442. package/project-templates/phaser/.cursor/rules/playcraft-orchestrator.mdc +142 -0
  443. package/project-templates/phaser/.cursor/rules/playcraft-subagent-boundary.mdc +29 -0
  444. package/project-templates/phaser/.playcraft-engine +1 -0
  445. package/project-templates/phaser/CHANGELOG.md +105 -0
  446. package/project-templates/phaser/CLAUDE.md +84 -0
  447. package/project-templates/phaser/assets/audio/bgm/.gitkeep +0 -0
  448. package/project-templates/phaser/assets/audio/sfx/.gitkeep +0 -0
  449. package/project-templates/phaser/assets/bundles/.gitkeep +0 -0
  450. package/project-templates/phaser/assets/images/bg/.gitkeep +0 -0
  451. package/project-templates/phaser/assets/images/reference/.gitkeep +0 -0
  452. package/project-templates/phaser/assets/images/storyboard/.gitkeep +0 -0
  453. package/project-templates/phaser/assets/images/tiles/.gitkeep +0 -0
  454. package/project-templates/phaser/assets/images/ui/.gitkeep +0 -0
  455. package/project-templates/phaser/assets/images/vfx/.gitkeep +0 -0
  456. package/project-templates/phaser/assets/models/.gitkeep +0 -0
  457. package/project-templates/phaser/docs/DESIGN.md +65 -0
  458. package/project-templates/phaser/docs/intent-clarifications.md +58 -0
  459. package/project-templates/phaser/docs/pipeline-overview.md +69 -0
  460. package/project-templates/phaser/docs/project-state.md +211 -0
  461. package/project-templates/phaser/docs/scaffold-manifest.md +10 -0
  462. package/project-templates/phaser/docs/team/agent-conduct.md +67 -0
  463. package/project-templates/phaser/docs/team/agent-runtime-matrix.md +80 -0
  464. package/project-templates/phaser/docs/team/atom-tree-format.md +114 -0
  465. package/project-templates/phaser/docs/team/collaboration.md +391 -0
  466. package/project-templates/phaser/docs/team/core-model.md +85 -0
  467. package/project-templates/phaser/docs/team/game-designer-migration-todo.md +8 -0
  468. package/project-templates/phaser/docs/team/harness-legacy-deprecation.md +23 -0
  469. package/project-templates/phaser/docs/team/platform-capabilities.md +18 -0
  470. package/project-templates/phaser/docs/team/workflow-changelog.md +137 -0
  471. package/project-templates/phaser/docs/team/workflow-consistency-checklist.md +240 -0
  472. package/project-templates/phaser/focus-lab/.gitkeep +0 -0
  473. package/project-templates/phaser/game/adapters/phaser.ts +52 -0
  474. package/project-templates/phaser/game/config/.gitkeep +0 -0
  475. package/project-templates/phaser/game/config/game-config.ts +46 -0
  476. package/project-templates/phaser/game/contract/publish.ts +141 -0
  477. package/project-templates/phaser/game/contract/types.ts +34 -0
  478. package/project-templates/phaser/game/gameplay/.gitkeep +0 -0
  479. package/project-templates/phaser/game/index.ts +81 -0
  480. package/project-templates/phaser/game/runtime-contract.ts +28 -0
  481. package/project-templates/phaser/game/scenes/.gitkeep +0 -0
  482. package/project-templates/phaser/globals.d.ts +51 -0
  483. package/project-templates/phaser/index.css +34 -0
  484. package/project-templates/phaser/index.html +18 -0
  485. package/project-templates/phaser/main.ts +9 -0
  486. package/project-templates/phaser/package.json +79 -0
  487. package/project-templates/phaser/skills/README.md +52 -0
  488. package/project-templates/phaser/skills/_shared/agent-prep.md +136 -0
  489. package/project-templates/phaser/skills/_shared/requirements.txt +1 -0
  490. package/project-templates/phaser/skills/_shared/scripts/build-evidence-gate.mjs +87 -0
  491. package/project-templates/phaser/skills/_shared/scripts/context-budget.mjs +215 -0
  492. package/project-templates/phaser/skills/_shared/scripts/create-playable.mjs +140 -0
  493. package/project-templates/phaser/skills/_shared/scripts/dev-start.mjs +151 -0
  494. package/project-templates/phaser/skills/_shared/scripts/disclosure-config.mjs +480 -0
  495. package/project-templates/phaser/skills/_shared/scripts/ensure-project.mjs +532 -0
  496. package/project-templates/phaser/skills/_shared/scripts/gen-placeholder-audio.py +345 -0
  497. package/project-templates/phaser/skills/_shared/scripts/gen-placeholder.py +244 -0
  498. package/project-templates/phaser/skills/_shared/scripts/harness-smoke.mjs +257 -0
  499. package/project-templates/phaser/skills/_shared/scripts/lib/validator-artifacts.mjs +129 -0
  500. package/project-templates/phaser/skills/_shared/scripts/placeholder-pack-sheet.mjs +67 -0
  501. package/project-templates/phaser/skills/_shared/scripts/placeholder_audio_lib.py +142 -0
  502. package/project-templates/phaser/skills/_shared/scripts/placeholder_lib.py +993 -0
  503. package/project-templates/phaser/skills/_shared/scripts/render-project-state.mjs +278 -0
  504. package/project-templates/phaser/skills/_shared/scripts/ring-config.mjs +252 -0
  505. package/project-templates/phaser/skills/_shared/scripts/skills-check.mjs +176 -0
  506. package/project-templates/phaser/skills/_shared/scripts/slice-set.mjs +77 -0
  507. package/project-templates/phaser/skills/_shared/scripts/state-advance-ring.mjs +63 -0
  508. package/project-templates/phaser/skills/_shared/scripts/state-get.mjs +215 -0
  509. package/project-templates/phaser/skills/_shared/scripts/state-handoff.mjs +120 -0
  510. package/project-templates/phaser/skills/_shared/scripts/state-set.mjs +113 -0
  511. package/project-templates/phaser/skills/_shared/scripts/state-store.mjs +893 -0
  512. package/project-templates/phaser/skills/_shared/scripts/sync-template-skills.mjs +112 -0
  513. package/project-templates/phaser/skills/_shared/scripts/verify-asset-code-sync.mjs +265 -0
  514. package/project-templates/phaser/skills/_shared/scripts/verify-atom-conformance.mjs +181 -0
  515. package/project-templates/phaser/skills/_shared/scripts/verify-env.mjs +285 -0
  516. package/project-templates/phaser/skills/_shared/scripts/verify-placeholders.mjs +161 -0
  517. package/project-templates/phaser/skills/_shared/scripts/workflow-doctor.mjs +348 -0
  518. package/project-templates/phaser/skills/playable-animate/SKILL.md +52 -0
  519. package/project-templates/phaser/skills/playable-animate/agents/openai.yaml +4 -0
  520. package/project-templates/phaser/skills/playable-animate/references/board-refresh.md +30 -0
  521. package/project-templates/phaser/skills/playable-animate/references/diagnosis-checklist.md +25 -0
  522. package/project-templates/phaser/skills/playable-animate/references/entrance-sequencing.md +33 -0
  523. package/project-templates/phaser/skills/playable-animate/references/interaction-timing.md +23 -0
  524. package/project-templates/phaser/skills/playable-art/SKILL.md +203 -0
  525. package/project-templates/phaser/skills/playable-art/agents/openai.yaml +4 -0
  526. package/project-templates/phaser/skills/playable-art/references/alpha-postprocess.md +46 -0
  527. package/project-templates/phaser/skills/playable-art/references/cta-art-spec.md +59 -0
  528. package/project-templates/phaser/skills/playable-art/references/layout-proportions.md +30 -0
  529. package/project-templates/phaser/skills/playable-art/references/prompt-patterns.md +83 -0
  530. package/project-templates/phaser/skills/playable-art/references/reference-comparison.md +30 -0
  531. package/project-templates/phaser/skills/playable-art/references/sheet-import-v2.md +91 -0
  532. package/project-templates/phaser/skills/playable-art/scripts/build-atlas.py +332 -0
  533. package/project-templates/phaser/skills/playable-art/scripts/chroma-key.py +50 -0
  534. package/project-templates/phaser/skills/playable-art/scripts/extract-alpha-split.py +65 -0
  535. package/project-templates/phaser/skills/playable-art/scripts/extract-dual-bg-alpha.py +118 -0
  536. package/project-templates/phaser/skills/playable-art/scripts/import-gpt-art-sheets-v2.py +403 -0
  537. package/project-templates/phaser/skills/playable-art/scripts/import-gpt-art-sheets.py +280 -0
  538. package/project-templates/phaser/skills/playable-art/scripts/import-gpt-tile-sheet.py +114 -0
  539. package/project-templates/phaser/skills/playable-art/scripts/import_gpt_art_pipeline.py +321 -0
  540. package/project-templates/phaser/skills/playable-art/scripts/import_gpt_art_sheets_v2_test.py +126 -0
  541. package/project-templates/phaser/skills/playable-art/scripts/placeholder_common.py +98 -0
  542. package/project-templates/phaser/skills/playable-art/scripts/remove-bg.py +91 -0
  543. package/project-templates/phaser/skills/playable-art/scripts/review-processed-assets.py +468 -0
  544. package/project-templates/phaser/skills/playable-art/scripts/review_processed_assets_test.py +116 -0
  545. package/project-templates/phaser/skills/playable-art/scripts/verify-alpha-clean.py +105 -0
  546. package/project-templates/phaser/skills/playable-debug/SKILL.md +116 -0
  547. package/project-templates/phaser/skills/playable-debug/agents/openai.yaml +4 -0
  548. package/project-templates/phaser/skills/playable-debug/references/debug-config.md +40 -0
  549. package/project-templates/phaser/skills/playable-focus/SKILL.md +148 -0
  550. package/project-templates/phaser/skills/playable-focus/scripts/bootstrap-playground.mjs +84 -0
  551. package/project-templates/phaser/skills/playable-focus/scripts/extract-focus-frames.mjs +87 -0
  552. package/project-templates/phaser/skills/playable-focus/scripts/generate-focus-reports.mjs +121 -0
  553. package/project-templates/phaser/skills/playable-focus/scripts/playground.mjs +390 -0
  554. package/project-templates/phaser/skills/playable-focus/templates/focus-lab/frame.html +39 -0
  555. package/project-templates/phaser/skills/playable-focus/templates/focus-lab/harness/clean-stage.js +114 -0
  556. package/project-templates/phaser/skills/playable-focus/templates/focus-lab/harness/frame-lab-scene.js +94 -0
  557. package/project-templates/phaser/skills/playable-focus/templates/focus-lab/harness/particle-lab-scene.js +224 -0
  558. package/project-templates/phaser/skills/playable-focus/templates/focus-lab/model.html +16 -0
  559. package/project-templates/phaser/skills/playable-focus/templates/focus-lab/particle.html +38 -0
  560. package/project-templates/phaser/skills/playable-focus/templates/focus-lab/shatter.html +47 -0
  561. package/project-templates/phaser/skills/playable-fx-decompose/SKILL.md +212 -0
  562. package/project-templates/phaser/skills/playable-fx-decompose/examples.md +89 -0
  563. package/project-templates/phaser/skills/playable-fx-decompose/reference.md +201 -0
  564. package/project-templates/phaser/skills/playable-fx-decompose/requirements.txt +2 -0
  565. package/project-templates/phaser/skills/playable-fx-decompose/scripts/build-blank-tile-crack.py +192 -0
  566. package/project-templates/phaser/skills/playable-fx-decompose/scripts/fix-shard-debris-frames.py +60 -0
  567. package/project-templates/phaser/skills/playable-fx-decompose/scripts/generate-shard-debris.py +276 -0
  568. package/project-templates/phaser/skills/playable-fx-peak/SKILL.md +237 -0
  569. package/project-templates/phaser/skills/playable-fx-peak/requirements.txt +2 -0
  570. package/project-templates/phaser/skills/playable-fx-peak/scripts/fix-frame-edges.py +70 -0
  571. package/project-templates/phaser/skills/playable-fx-peak/scripts/repack-frame-sequence.py +526 -0
  572. package/project-templates/phaser/skills/playable-fx-peak/scripts/validate-frame-anchors.py +130 -0
  573. package/project-templates/phaser/skills/playable-placeholder/SKILL.md +239 -0
  574. package/project-templates/phaser/skills/playable-record/SKILL.md +166 -0
  575. package/project-templates/phaser/skills/playable-record/scripts/lib/build-artifact.mjs +92 -0
  576. package/project-templates/phaser/skills/playable-record/scripts/lib/dev-server.mjs +104 -0
  577. package/project-templates/phaser/skills/playable-record/scripts/lib/record-audio-bridge.js +141 -0
  578. package/project-templates/phaser/skills/playable-record/scripts/lib/slice-structure-verify.mjs +199 -0
  579. package/project-templates/phaser/skills/playable-record/scripts/record-playable.mjs +583 -0
  580. package/project-templates/phaser/skills/playable-record/scripts/verify-build.mjs +185 -0
  581. package/project-templates/phaser/skills/playable-record/scripts/verify-contract.mjs +261 -0
  582. package/project-templates/phaser/skills/playable-record/scripts/verify-firstwin.mjs +398 -0
  583. package/project-templates/phaser/skills/playable-record/scripts/verify-fusion.mjs +320 -0
  584. package/project-templates/phaser/skills/playable-record/scripts/verify-lifecycle.mjs +78 -0
  585. package/project-templates/phaser/skills/playable-record/scripts/verify-slice-artist.mjs +53 -0
  586. package/project-templates/phaser/skills/playable-record/scripts/verify-slice-ta.mjs +53 -0
  587. package/project-templates/phaser/skills/playable-record/scripts/verify-slice.mjs +142 -0
  588. package/project-templates/phaser/skills/playable-record/scripts/verify-visual-ring.mjs +250 -0
  589. package/project-templates/phaser/skills/playable-record/scripts/verify-visual-ring1.mjs +190 -0
  590. package/project-templates/phaser/skills/playable-record/scripts/verify-vlm-video.mjs +227 -0
  591. package/project-templates/phaser/skills/playable-validate/SKILL.md +260 -0
  592. package/project-templates/phaser/skills/playable-validate/references/checklist-detail.md +36 -0
  593. package/project-templates/phaser/skills/playable-validate/references/single-html-bundle.md +76 -0
  594. package/project-templates/phaser/skills/playable-validate/validation-rules.md +200 -0
  595. package/project-templates/phaser/skills/playable-verify-ui/SKILL.md +295 -0
  596. package/project-templates/phaser/skills/playable-verify-ui/examples/asset-manifest.example.json +36 -0
  597. package/project-templates/phaser/skills/playable-verify-ui/examples/mahjong-match.profile.yaml +334 -0
  598. package/project-templates/phaser/skills/playable-verify-ui/profiles/template.profile.yaml +84 -0
  599. package/project-templates/phaser/skills/playable-verify-ui/reference.md +165 -0
  600. package/project-templates/phaser/skills/playable-verify-ui/references/phaser-patterns.md +67 -0
  601. package/project-templates/phaser/skills/playable-verify-ui/references/scaling-decision-tree.md +51 -0
  602. package/project-templates/phaser/skills/playable-verify-ui/requirements.txt +7 -0
  603. package/project-templates/phaser/skills/playable-verify-ui/schema/asset-manifest.schema.json +68 -0
  604. package/project-templates/phaser/skills/playable-verify-ui/schema/playable-ui-profile.schema.json +171 -0
  605. package/project-templates/phaser/skills/playable-verify-ui/scripts/asset_manifest.py +231 -0
  606. package/project-templates/phaser/skills/playable-verify-ui/scripts/lib/__init__.py +1 -0
  607. package/project-templates/phaser/skills/playable-verify-ui/scripts/lib/config_expr.js +218 -0
  608. package/project-templates/phaser/skills/playable-verify-ui/scripts/lib/node_utils.js +51 -0
  609. package/project-templates/phaser/skills/playable-verify-ui/scripts/lib/profile_loader.py +59 -0
  610. package/project-templates/phaser/skills/playable-verify-ui/scripts/lib/report.py +106 -0
  611. package/project-templates/phaser/skills/playable-verify-ui/scripts/lib/runtime_contract.js +116 -0
  612. package/project-templates/phaser/skills/playable-verify-ui/scripts/measure_frame_content.py +77 -0
  613. package/project-templates/phaser/skills/playable-verify-ui/scripts/run_all.py +265 -0
  614. package/project-templates/phaser/skills/playable-verify-ui/scripts/verify_asset_sizes.py +77 -0
  615. package/project-templates/phaser/skills/playable-verify-ui/scripts/verify_assets_present.py +62 -0
  616. package/project-templates/phaser/skills/playable-verify-ui/scripts/verify_atlas_budget.py +84 -0
  617. package/project-templates/phaser/skills/playable-verify-ui/scripts/verify_atlas_frames.py +79 -0
  618. package/project-templates/phaser/skills/playable-verify-ui/scripts/verify_bg_clean.py +365 -0
  619. package/project-templates/phaser/skills/playable-verify-ui/scripts/verify_doc_asset_sync.py +90 -0
  620. package/project-templates/phaser/skills/playable-verify-ui/scripts/verify_fetch_antipatterns.py +59 -0
  621. package/project-templates/phaser/skills/playable-verify-ui/scripts/verify_game_config_load.js +54 -0
  622. package/project-templates/phaser/skills/playable-verify-ui/scripts/verify_hardcoded_layout.js +64 -0
  623. package/project-templates/phaser/skills/playable-verify-ui/scripts/verify_layout_regions.js +110 -0
  624. package/project-templates/phaser/skills/playable-verify-ui/scripts/verify_profile_schema.py +37 -0
  625. package/project-templates/phaser/skills/playable-verify-ui/scripts/verify_ring1_visual.py +211 -0
  626. package/project-templates/phaser/skills/playable-verify-ui/scripts/verify_runtime_contract.js +58 -0
  627. package/project-templates/phaser/skills/playable-verify-ui/scripts/verify_script_load_order.js +73 -0
  628. package/project-templates/phaser/skills/playable-verify-ui/scripts/verify_slice_plan.py +238 -0
  629. package/project-templates/phaser/skills/playable-verify-ui/scripts/verify_slice_plan_test.py +125 -0
  630. package/project-templates/phaser/skills/playable-verify-ui/scripts/verify_text_overlap.py +333 -0
  631. package/project-templates/phaser/skills/playable-verify-ui/scripts/verify_tile_consistency.py +323 -0
  632. package/project-templates/phaser/skills/playable-verify-ui/scripts/verify_vision_evidence.py +894 -0
  633. package/project-templates/phaser/skills/playable-verify-ui/scripts/verify_vision_evidence_test.py +122 -0
  634. package/project-templates/phaser/skills/playable-verify-ui/scripts/verify_visual.py +422 -0
  635. package/project-templates/phaser/ta-workspace/scripts/.gitkeep +0 -0
  636. package/project-templates/phaser/templates/artist-log.template.md +182 -0
  637. package/project-templates/phaser/templates/atom-tree.template.json +133 -0
  638. package/project-templates/phaser/templates/design-brief.template.md +92 -0
  639. package/project-templates/phaser/templates/design-lens-checklist.reference.md +117 -0
  640. package/project-templates/phaser/templates/design-methodology.md +99 -0
  641. package/project-templates/phaser/templates/design.template.md +50 -0
  642. package/project-templates/phaser/templates/developer-log.template.md +258 -0
  643. package/project-templates/phaser/templates/five-axis-framework.md +194 -0
  644. package/project-templates/phaser/templates/gen-placeholder-audio.template.mjs +63 -0
  645. package/project-templates/phaser/templates/gen-placeholders.template.mjs +81 -0
  646. package/project-templates/phaser/templates/intent-clarifications.template.md +58 -0
  647. package/project-templates/phaser/templates/layout-spec.template.md +198 -0
  648. package/project-templates/phaser/templates/plan.template.md +80 -0
  649. package/project-templates/phaser/templates/project-state.template.md +136 -0
  650. package/project-templates/phaser/templates/review-report.template.md +275 -0
  651. package/project-templates/phaser/templates/style-exploration.template.md +93 -0
  652. package/project-templates/phaser/templates/ta-log.template.md +394 -0
  653. package/project-templates/phaser/tsconfig.json +20 -0
  654. package/project-templates/phaser/vite.config.ts +27 -0
  655. package/project-templates/threejs/.claude/agents/artist.md +101 -0
  656. package/project-templates/threejs/.claude/agents/developer.md +123 -0
  657. package/project-templates/threejs/.claude/agents/game-designer.md +192 -0
  658. package/project-templates/threejs/.claude/agents/refs/README.md +77 -0
  659. package/project-templates/threejs/.claude/agents/refs/artist-art-style-catalog.md +533 -0
  660. package/project-templates/threejs/.claude/agents/refs/artist-color-audio-recipes.md +153 -0
  661. package/project-templates/threejs/.claude/agents/refs/artist-deliverable-spec.md +230 -0
  662. package/project-templates/threejs/.claude/agents/refs/artist-dimension-axis.md +27 -0
  663. package/project-templates/threejs/.claude/agents/refs/artist-handoff-v2-checklist.md +76 -0
  664. package/project-templates/threejs/.claude/agents/refs/artist-legacy-fallback.md +14 -0
  665. package/project-templates/threejs/.claude/agents/refs/artist-master-composite-recipes.md +208 -0
  666. package/project-templates/threejs/.claude/agents/refs/artist-style-exploration-flow.md +55 -0
  667. package/project-templates/threejs/.claude/agents/refs/developer-dev-handoff.md +152 -0
  668. package/project-templates/threejs/.claude/agents/refs/developer-impl-cookbook.md +432 -0
  669. package/project-templates/threejs/.claude/agents/refs/developer-legacy-fallback.md +15 -0
  670. package/project-templates/threejs/.claude/agents/refs/developer-phase1-flow.md +404 -0
  671. package/project-templates/threejs/.claude/agents/refs/framework-5-component-filter.md +252 -0
  672. package/project-templates/threejs/.claude/agents/refs/framework-game-feel-juice.md +266 -0
  673. package/project-templates/threejs/.claude/agents/refs/framework-mda.md +147 -0
  674. package/project-templates/threejs/.claude/agents/refs/game-designer-gameplay-sufficiency.md +123 -0
  675. package/project-templates/threejs/.claude/agents/refs/game-designer-legacy-fallback.md +15 -0
  676. package/project-templates/threejs/.claude/agents/refs/game-designer-workflow-detail.md +69 -0
  677. package/project-templates/threejs/.claude/agents/refs/implementation-plan-guide.md +309 -0
  678. package/project-templates/threejs/.claude/agents/refs/reviewer-convergence-eval.md +350 -0
  679. package/project-templates/threejs/.claude/agents/refs/reviewer-legacy-fallback.md +14 -0
  680. package/project-templates/threejs/.claude/agents/refs/reviewer-six-dimension-eval.md +6 -0
  681. package/project-templates/threejs/.claude/agents/refs/skill-selector-guide.md +465 -0
  682. package/project-templates/threejs/.claude/agents/refs/skill-value-layers.md +145 -0
  683. package/project-templates/threejs/.claude/agents/refs/storyboard-generation-guide.md +110 -0
  684. package/project-templates/threejs/.claude/agents/refs/ta-3d-flip-recipe.md +88 -0
  685. package/project-templates/threejs/.claude/agents/refs/ta-atlas-deliverable-standard.md +67 -0
  686. package/project-templates/threejs/.claude/agents/refs/ta-batch-pipeline-recipes.md +120 -0
  687. package/project-templates/threejs/.claude/agents/refs/ta-image-generation-detail.md +300 -0
  688. package/project-templates/threejs/.claude/agents/refs/ta-image-ops-reference.md +495 -0
  689. package/project-templates/threejs/.claude/agents/refs/ta-pipeline-cookbook.md +1141 -0
  690. package/project-templates/threejs/.claude/agents/refs/ta-tools-reference.md +111 -0
  691. package/project-templates/threejs/.claude/agents/refs/ta-vfx-preset-catalog.md +365 -0
  692. package/project-templates/threejs/.claude/agents/refs/technical-artist-legacy-fallback.md +14 -0
  693. package/project-templates/threejs/.claude/agents/reviewer.md +116 -0
  694. package/project-templates/threejs/.claude/agents/technical-artist.md +100 -0
  695. package/project-templates/threejs/.claude/hooks/README.md +120 -0
  696. package/project-templates/threejs/.claude/hooks/asr-coverage-validate.mjs +381 -0
  697. package/project-templates/threejs/.claude/hooks/block-developer-preflight.mjs +185 -0
  698. package/project-templates/threejs/.claude/hooks/block-ring1-tasklist.mjs +95 -0
  699. package/project-templates/threejs/.claude/hooks/gate-dispatcher.mjs +78 -0
  700. package/project-templates/threejs/.claude/hooks/gate-runner.mjs +557 -0
  701. package/project-templates/threejs/.claude/hooks/gate.mjs +171 -0
  702. package/project-templates/threejs/.claude/hooks/snapshot-milestone.mjs +247 -0
  703. package/project-templates/threejs/.claude/hooks/user-prompt.mjs +194 -0
  704. package/project-templates/threejs/.claude/hooks/validate-atom-tree-selection.mjs +281 -0
  705. package/project-templates/threejs/.claude/hooks/validate-workflow-stop.mjs +1889 -0
  706. package/project-templates/threejs/.claude/settings.json +81 -0
  707. package/project-templates/threejs/.claude/skills/brainstorming/SKILL.md +161 -0
  708. package/project-templates/threejs/.claude/skills/brainstorming/scripts/frame-template.html +270 -0
  709. package/project-templates/threejs/.claude/skills/brainstorming/scripts/helper.js +177 -0
  710. package/project-templates/threejs/.claude/skills/brainstorming/scripts/server.cjs +354 -0
  711. package/project-templates/threejs/.claude/skills/brainstorming/scripts/start-server.sh +148 -0
  712. package/project-templates/threejs/.claude/skills/brainstorming/scripts/stop-server.sh +56 -0
  713. package/project-templates/threejs/.claude/skills/brainstorming/scripts/wait-for-selection.sh +62 -0
  714. package/project-templates/threejs/.claude/skills/brainstorming/spec-document-reviewer-prompt.md +49 -0
  715. package/project-templates/threejs/.claude/skills/brainstorming/visual-companion.md +309 -0
  716. package/project-templates/threejs/.claude/skills/playcraft-ad-psychology/SKILL.md +182 -0
  717. package/project-templates/threejs/.claude/skills/playcraft-art-style-guide/SKILL.md +123 -0
  718. package/project-templates/threejs/.claude/skills/playcraft-asset-state-sheet/SKILL.md +205 -0
  719. package/project-templates/threejs/.claude/skills/playcraft-audio-generation/SKILL.md +280 -0
  720. package/project-templates/threejs/.claude/skills/playcraft-batch-pipeline/SKILL.md +184 -0
  721. package/project-templates/threejs/.claude/skills/playcraft-build-optimizer/SKILL.md +306 -0
  722. package/project-templates/threejs/.claude/skills/playcraft-image-generation/SKILL.md +298 -0
  723. package/project-templates/threejs/.claude/skills/playcraft-image-generation/reference/build-sprite-sheet.template.mjs +123 -0
  724. package/project-templates/threejs/.claude/skills/playcraft-image-generation/reference/compare-style.template.mjs +254 -0
  725. package/project-templates/threejs/.claude/skills/playcraft-image-generation/reference/gen-batch-sprite.template.mjs +324 -0
  726. package/project-templates/threejs/.claude/skills/playcraft-image-generation/reference/gen-batch.template.mjs +97 -0
  727. package/project-templates/threejs/.claude/skills/playcraft-image-generation/reference/gen-edit-variants.template.mjs +118 -0
  728. package/project-templates/threejs/.claude/skills/playcraft-image-generation/reference/process-batch.template.mjs +137 -0
  729. package/project-templates/threejs/.claude/skills/playcraft-image-generation/reference/prompt-cookbook.md +397 -0
  730. package/project-templates/threejs/.claude/skills/playcraft-image-generation/reference/validate-sprite-sheet.template.mjs +296 -0
  731. package/project-templates/threejs/.claude/skills/playcraft-image-ops/SKILL.md +122 -0
  732. package/project-templates/threejs/.claude/skills/playcraft-image-processing/SKILL.md +219 -0
  733. package/project-templates/threejs/.claude/skills/playcraft-masking/SKILL.md +373 -0
  734. package/project-templates/threejs/.claude/skills/playcraft-playable-optimization/SKILL.md +161 -0
  735. package/project-templates/threejs/.claude/skills/playcraft-research/SKILL.md +215 -0
  736. package/project-templates/threejs/.claude/skills/playcraft-skill-recommender/SKILL.md +331 -0
  737. package/project-templates/threejs/.claude/skills/playcraft-sprite-generation/SKILL.md +423 -0
  738. package/project-templates/threejs/.claude/skills/playcraft-sprite-remix/SKILL.md +158 -0
  739. package/project-templates/threejs/.claude/skills/playcraft-sprite-sheet/SKILL.md +100 -0
  740. package/project-templates/threejs/.claude/skills/playcraft-storyboard/SKILL.md +167 -0
  741. package/project-templates/threejs/.claude/skills/playcraft-style-qa/SKILL.md +270 -0
  742. package/project-templates/threejs/.claude/skills/playcraft-text-rendering/SKILL.md +236 -0
  743. package/project-templates/threejs/.claude/skills/playcraft-vfx-animation/SKILL.md +130 -0
  744. package/project-templates/threejs/.claude/skills/playwright-cli/SKILL.md +390 -0
  745. package/project-templates/threejs/.claude/skills/playwright-cli/references/element-attributes.md +23 -0
  746. package/project-templates/threejs/.claude/skills/playwright-cli/references/playwright-tests.md +39 -0
  747. package/project-templates/threejs/.claude/skills/playwright-cli/references/request-mocking.md +87 -0
  748. package/project-templates/threejs/.claude/skills/playwright-cli/references/running-code.md +240 -0
  749. package/project-templates/threejs/.claude/skills/playwright-cli/references/session-management.md +226 -0
  750. package/project-templates/threejs/.claude/skills/playwright-cli/references/spec-driven-testing.md +308 -0
  751. package/project-templates/threejs/.claude/skills/playwright-cli/references/storage-state.md +275 -0
  752. package/project-templates/threejs/.claude/skills/playwright-cli/references/test-generation.md +134 -0
  753. package/project-templates/threejs/.claude/skills/playwright-cli/references/tracing.md +142 -0
  754. package/project-templates/threejs/.claude/skills/playwright-cli/references/video-recording.md +153 -0
  755. package/project-templates/threejs/.claude/skills/session-analyzer/SKILL.md +386 -0
  756. package/project-templates/threejs/.claude/skills/session-analyzer/scripts/execution-breakdown.mjs +182 -0
  757. package/project-templates/threejs/.claude/skills/session-analyzer/scripts/find-turns.mjs +72 -0
  758. package/project-templates/threejs/.claude/skills/session-analyzer/scripts/heavy-output.mjs +121 -0
  759. package/project-templates/threejs/.claude/skills/session-analyzer/scripts/resolve-session.mjs +103 -0
  760. package/project-templates/threejs/.claude/skills/session-analyzer/scripts/subagent-stats.mjs +127 -0
  761. package/project-templates/threejs/.claude/skills/session-analyzer/scripts/subagent-tool-timeline.mjs +106 -0
  762. package/project-templates/threejs/.claude/skills/session-analyzer/scripts/time-gaps.mjs +128 -0
  763. package/project-templates/threejs/.claude/skills/session-analyzer/scripts/turn-timeline.mjs +67 -0
  764. package/project-templates/threejs/.claude/snapshot.mjs +257 -0
  765. package/project-templates/threejs/.cursor/hooks.json +17 -0
  766. package/project-templates/threejs/.cursor/rules/playcraft-orchestrator.mdc +142 -0
  767. package/project-templates/threejs/.cursor/rules/playcraft-subagent-boundary.mdc +29 -0
  768. package/project-templates/threejs/.playcraft-engine +1 -0
  769. package/project-templates/threejs/CHANGELOG.md +105 -0
  770. package/project-templates/threejs/CLAUDE.md +84 -0
  771. package/project-templates/threejs/assets/audio/bgm/.gitkeep +0 -0
  772. package/project-templates/threejs/assets/audio/sfx/.gitkeep +0 -0
  773. package/project-templates/threejs/assets/bundles/.gitkeep +0 -0
  774. package/project-templates/threejs/assets/images/bg/.gitkeep +0 -0
  775. package/project-templates/threejs/assets/images/reference/.gitkeep +0 -0
  776. package/project-templates/threejs/assets/images/storyboard/.gitkeep +0 -0
  777. package/project-templates/threejs/assets/images/tiles/.gitkeep +0 -0
  778. package/project-templates/threejs/assets/images/ui/.gitkeep +0 -0
  779. package/project-templates/threejs/assets/images/vfx/.gitkeep +0 -0
  780. package/project-templates/threejs/assets/models/.gitkeep +0 -0
  781. package/project-templates/threejs/docs/DESIGN.md +65 -0
  782. package/project-templates/threejs/docs/intent-clarifications.md +58 -0
  783. package/project-templates/threejs/docs/pipeline-overview.md +69 -0
  784. package/project-templates/threejs/docs/project-state.md +211 -0
  785. package/project-templates/threejs/docs/scaffold-manifest.md +10 -0
  786. package/project-templates/threejs/docs/team/agent-conduct.md +67 -0
  787. package/project-templates/threejs/docs/team/agent-runtime-matrix.md +80 -0
  788. package/project-templates/threejs/docs/team/atom-tree-format.md +114 -0
  789. package/project-templates/threejs/docs/team/collaboration.md +391 -0
  790. package/project-templates/threejs/docs/team/core-model.md +85 -0
  791. package/project-templates/threejs/docs/team/game-designer-migration-todo.md +8 -0
  792. package/project-templates/threejs/docs/team/harness-legacy-deprecation.md +23 -0
  793. package/project-templates/threejs/docs/team/platform-capabilities.md +18 -0
  794. package/project-templates/threejs/docs/team/workflow-changelog.md +137 -0
  795. package/project-templates/threejs/docs/team/workflow-consistency-checklist.md +240 -0
  796. package/project-templates/threejs/focus-lab/.gitkeep +0 -0
  797. package/project-templates/threejs/game/config/.gitkeep +0 -0
  798. package/project-templates/threejs/game/config/game-config.ts +46 -0
  799. package/project-templates/threejs/game/contract/publish.ts +141 -0
  800. package/project-templates/threejs/game/contract/types.ts +34 -0
  801. package/project-templates/threejs/game/gameplay/.gitkeep +0 -0
  802. package/project-templates/threejs/game/index.ts +76 -0
  803. package/project-templates/threejs/game/runtime-contract.ts +15 -0
  804. package/project-templates/threejs/game/scenes/.gitkeep +0 -0
  805. package/project-templates/threejs/globals.d.ts +51 -0
  806. package/project-templates/threejs/index.css +34 -0
  807. package/project-templates/threejs/index.html +18 -0
  808. package/project-templates/threejs/main.ts +9 -0
  809. package/project-templates/threejs/package.json +79 -0
  810. package/project-templates/threejs/skills/README.md +52 -0
  811. package/project-templates/threejs/skills/_shared/agent-prep.md +136 -0
  812. package/project-templates/threejs/skills/_shared/requirements.txt +1 -0
  813. package/project-templates/threejs/skills/_shared/scripts/build-evidence-gate.mjs +87 -0
  814. package/project-templates/threejs/skills/_shared/scripts/context-budget.mjs +215 -0
  815. package/project-templates/threejs/skills/_shared/scripts/create-playable.mjs +140 -0
  816. package/project-templates/threejs/skills/_shared/scripts/dev-start.mjs +151 -0
  817. package/project-templates/threejs/skills/_shared/scripts/disclosure-config.mjs +480 -0
  818. package/project-templates/threejs/skills/_shared/scripts/ensure-project.mjs +532 -0
  819. package/project-templates/threejs/skills/_shared/scripts/gen-placeholder-audio.py +345 -0
  820. package/project-templates/threejs/skills/_shared/scripts/gen-placeholder.py +244 -0
  821. package/project-templates/threejs/skills/_shared/scripts/harness-smoke.mjs +257 -0
  822. package/project-templates/threejs/skills/_shared/scripts/lib/validator-artifacts.mjs +129 -0
  823. package/project-templates/threejs/skills/_shared/scripts/placeholder-pack-sheet.mjs +67 -0
  824. package/project-templates/threejs/skills/_shared/scripts/placeholder_audio_lib.py +142 -0
  825. package/project-templates/threejs/skills/_shared/scripts/placeholder_lib.py +993 -0
  826. package/project-templates/threejs/skills/_shared/scripts/render-project-state.mjs +278 -0
  827. package/project-templates/threejs/skills/_shared/scripts/ring-config.mjs +252 -0
  828. package/project-templates/threejs/skills/_shared/scripts/skills-check.mjs +176 -0
  829. package/project-templates/threejs/skills/_shared/scripts/slice-set.mjs +77 -0
  830. package/project-templates/threejs/skills/_shared/scripts/state-advance-ring.mjs +63 -0
  831. package/project-templates/threejs/skills/_shared/scripts/state-get.mjs +215 -0
  832. package/project-templates/threejs/skills/_shared/scripts/state-handoff.mjs +120 -0
  833. package/project-templates/threejs/skills/_shared/scripts/state-set.mjs +113 -0
  834. package/project-templates/threejs/skills/_shared/scripts/state-store.mjs +893 -0
  835. package/project-templates/threejs/skills/_shared/scripts/sync-template-skills.mjs +112 -0
  836. package/project-templates/threejs/skills/_shared/scripts/verify-asset-code-sync.mjs +265 -0
  837. package/project-templates/threejs/skills/_shared/scripts/verify-atom-conformance.mjs +181 -0
  838. package/project-templates/threejs/skills/_shared/scripts/verify-env.mjs +285 -0
  839. package/project-templates/threejs/skills/_shared/scripts/verify-placeholders.mjs +161 -0
  840. package/project-templates/threejs/skills/_shared/scripts/workflow-doctor.mjs +348 -0
  841. package/project-templates/threejs/skills/playable-animate/SKILL.md +52 -0
  842. package/project-templates/threejs/skills/playable-animate/agents/openai.yaml +4 -0
  843. package/project-templates/threejs/skills/playable-animate/references/board-refresh.md +30 -0
  844. package/project-templates/threejs/skills/playable-animate/references/diagnosis-checklist.md +25 -0
  845. package/project-templates/threejs/skills/playable-animate/references/entrance-sequencing.md +33 -0
  846. package/project-templates/threejs/skills/playable-animate/references/interaction-timing.md +23 -0
  847. package/project-templates/threejs/skills/playable-art/SKILL.md +203 -0
  848. package/project-templates/threejs/skills/playable-art/agents/openai.yaml +4 -0
  849. package/project-templates/threejs/skills/playable-art/references/alpha-postprocess.md +46 -0
  850. package/project-templates/threejs/skills/playable-art/references/cta-art-spec.md +59 -0
  851. package/project-templates/threejs/skills/playable-art/references/layout-proportions.md +30 -0
  852. package/project-templates/threejs/skills/playable-art/references/prompt-patterns.md +83 -0
  853. package/project-templates/threejs/skills/playable-art/references/reference-comparison.md +30 -0
  854. package/project-templates/threejs/skills/playable-art/references/sheet-import-v2.md +91 -0
  855. package/project-templates/threejs/skills/playable-art/scripts/build-atlas.py +332 -0
  856. package/project-templates/threejs/skills/playable-art/scripts/chroma-key.py +50 -0
  857. package/project-templates/threejs/skills/playable-art/scripts/extract-alpha-split.py +65 -0
  858. package/project-templates/threejs/skills/playable-art/scripts/extract-dual-bg-alpha.py +118 -0
  859. package/project-templates/threejs/skills/playable-art/scripts/import-gpt-art-sheets-v2.py +403 -0
  860. package/project-templates/threejs/skills/playable-art/scripts/import-gpt-art-sheets.py +280 -0
  861. package/project-templates/threejs/skills/playable-art/scripts/import-gpt-tile-sheet.py +114 -0
  862. package/project-templates/threejs/skills/playable-art/scripts/import_gpt_art_pipeline.py +321 -0
  863. package/project-templates/threejs/skills/playable-art/scripts/import_gpt_art_sheets_v2_test.py +126 -0
  864. package/project-templates/threejs/skills/playable-art/scripts/placeholder_common.py +98 -0
  865. package/project-templates/threejs/skills/playable-art/scripts/remove-bg.py +91 -0
  866. package/project-templates/threejs/skills/playable-art/scripts/review-processed-assets.py +468 -0
  867. package/project-templates/threejs/skills/playable-art/scripts/review_processed_assets_test.py +116 -0
  868. package/project-templates/threejs/skills/playable-art/scripts/verify-alpha-clean.py +105 -0
  869. package/project-templates/threejs/skills/playable-debug/SKILL.md +116 -0
  870. package/project-templates/threejs/skills/playable-debug/agents/openai.yaml +4 -0
  871. package/project-templates/threejs/skills/playable-debug/references/debug-config.md +40 -0
  872. package/project-templates/threejs/skills/playable-focus/SKILL.md +148 -0
  873. package/project-templates/threejs/skills/playable-focus/scripts/bootstrap-playground.mjs +84 -0
  874. package/project-templates/threejs/skills/playable-focus/scripts/extract-focus-frames.mjs +87 -0
  875. package/project-templates/threejs/skills/playable-focus/scripts/generate-focus-reports.mjs +121 -0
  876. package/project-templates/threejs/skills/playable-focus/scripts/playground.mjs +390 -0
  877. package/project-templates/threejs/skills/playable-focus/templates/focus-lab/frame.html +39 -0
  878. package/project-templates/threejs/skills/playable-focus/templates/focus-lab/harness/clean-stage.js +114 -0
  879. package/project-templates/threejs/skills/playable-focus/templates/focus-lab/harness/frame-lab-scene.js +94 -0
  880. package/project-templates/threejs/skills/playable-focus/templates/focus-lab/harness/particle-lab-scene.js +224 -0
  881. package/project-templates/threejs/skills/playable-focus/templates/focus-lab/model.html +16 -0
  882. package/project-templates/threejs/skills/playable-focus/templates/focus-lab/particle.html +38 -0
  883. package/project-templates/threejs/skills/playable-focus/templates/focus-lab/shatter.html +47 -0
  884. package/project-templates/threejs/skills/playable-fx-decompose/SKILL.md +212 -0
  885. package/project-templates/threejs/skills/playable-fx-decompose/examples.md +89 -0
  886. package/project-templates/threejs/skills/playable-fx-decompose/reference.md +201 -0
  887. package/project-templates/threejs/skills/playable-fx-decompose/requirements.txt +2 -0
  888. package/project-templates/threejs/skills/playable-fx-decompose/scripts/build-blank-tile-crack.py +192 -0
  889. package/project-templates/threejs/skills/playable-fx-decompose/scripts/fix-shard-debris-frames.py +60 -0
  890. package/project-templates/threejs/skills/playable-fx-decompose/scripts/generate-shard-debris.py +276 -0
  891. package/project-templates/threejs/skills/playable-fx-peak/SKILL.md +237 -0
  892. package/project-templates/threejs/skills/playable-fx-peak/requirements.txt +2 -0
  893. package/project-templates/threejs/skills/playable-fx-peak/scripts/fix-frame-edges.py +70 -0
  894. package/project-templates/threejs/skills/playable-fx-peak/scripts/repack-frame-sequence.py +526 -0
  895. package/project-templates/threejs/skills/playable-fx-peak/scripts/validate-frame-anchors.py +130 -0
  896. package/project-templates/threejs/skills/playable-placeholder/SKILL.md +239 -0
  897. package/project-templates/threejs/skills/playable-record/SKILL.md +166 -0
  898. package/project-templates/threejs/skills/playable-record/scripts/lib/build-artifact.mjs +92 -0
  899. package/project-templates/threejs/skills/playable-record/scripts/lib/dev-server.mjs +104 -0
  900. package/project-templates/threejs/skills/playable-record/scripts/lib/record-audio-bridge.js +141 -0
  901. package/project-templates/threejs/skills/playable-record/scripts/lib/slice-structure-verify.mjs +199 -0
  902. package/project-templates/threejs/skills/playable-record/scripts/record-playable.mjs +583 -0
  903. package/project-templates/threejs/skills/playable-record/scripts/verify-build.mjs +185 -0
  904. package/project-templates/threejs/skills/playable-record/scripts/verify-contract.mjs +261 -0
  905. package/project-templates/threejs/skills/playable-record/scripts/verify-firstwin.mjs +398 -0
  906. package/project-templates/threejs/skills/playable-record/scripts/verify-fusion.mjs +320 -0
  907. package/project-templates/threejs/skills/playable-record/scripts/verify-lifecycle.mjs +78 -0
  908. package/project-templates/threejs/skills/playable-record/scripts/verify-slice-artist.mjs +53 -0
  909. package/project-templates/threejs/skills/playable-record/scripts/verify-slice-ta.mjs +53 -0
  910. package/project-templates/threejs/skills/playable-record/scripts/verify-slice.mjs +142 -0
  911. package/project-templates/threejs/skills/playable-record/scripts/verify-visual-ring.mjs +250 -0
  912. package/project-templates/threejs/skills/playable-record/scripts/verify-visual-ring1.mjs +190 -0
  913. package/project-templates/threejs/skills/playable-record/scripts/verify-vlm-video.mjs +227 -0
  914. package/project-templates/threejs/skills/playable-validate/SKILL.md +260 -0
  915. package/project-templates/threejs/skills/playable-validate/references/checklist-detail.md +36 -0
  916. package/project-templates/threejs/skills/playable-validate/references/single-html-bundle.md +76 -0
  917. package/project-templates/threejs/skills/playable-validate/validation-rules.md +200 -0
  918. package/project-templates/threejs/skills/playable-verify-ui/SKILL.md +295 -0
  919. package/project-templates/threejs/skills/playable-verify-ui/examples/asset-manifest.example.json +36 -0
  920. package/project-templates/threejs/skills/playable-verify-ui/examples/mahjong-match.profile.yaml +334 -0
  921. package/project-templates/threejs/skills/playable-verify-ui/profiles/template.profile.yaml +84 -0
  922. package/project-templates/threejs/skills/playable-verify-ui/reference.md +165 -0
  923. package/project-templates/threejs/skills/playable-verify-ui/references/phaser-patterns.md +67 -0
  924. package/project-templates/threejs/skills/playable-verify-ui/references/scaling-decision-tree.md +51 -0
  925. package/project-templates/threejs/skills/playable-verify-ui/requirements.txt +7 -0
  926. package/project-templates/threejs/skills/playable-verify-ui/schema/asset-manifest.schema.json +68 -0
  927. package/project-templates/threejs/skills/playable-verify-ui/schema/playable-ui-profile.schema.json +171 -0
  928. package/project-templates/threejs/skills/playable-verify-ui/scripts/asset_manifest.py +231 -0
  929. package/project-templates/threejs/skills/playable-verify-ui/scripts/lib/__init__.py +1 -0
  930. package/project-templates/threejs/skills/playable-verify-ui/scripts/lib/config_expr.js +218 -0
  931. package/project-templates/threejs/skills/playable-verify-ui/scripts/lib/node_utils.js +51 -0
  932. package/project-templates/threejs/skills/playable-verify-ui/scripts/lib/profile_loader.py +59 -0
  933. package/project-templates/threejs/skills/playable-verify-ui/scripts/lib/report.py +106 -0
  934. package/project-templates/threejs/skills/playable-verify-ui/scripts/lib/runtime_contract.js +116 -0
  935. package/project-templates/threejs/skills/playable-verify-ui/scripts/measure_frame_content.py +77 -0
  936. package/project-templates/threejs/skills/playable-verify-ui/scripts/run_all.py +265 -0
  937. package/project-templates/threejs/skills/playable-verify-ui/scripts/verify_asset_sizes.py +77 -0
  938. package/project-templates/threejs/skills/playable-verify-ui/scripts/verify_assets_present.py +62 -0
  939. package/project-templates/threejs/skills/playable-verify-ui/scripts/verify_atlas_budget.py +84 -0
  940. package/project-templates/threejs/skills/playable-verify-ui/scripts/verify_atlas_frames.py +79 -0
  941. package/project-templates/threejs/skills/playable-verify-ui/scripts/verify_bg_clean.py +365 -0
  942. package/project-templates/threejs/skills/playable-verify-ui/scripts/verify_doc_asset_sync.py +90 -0
  943. package/project-templates/threejs/skills/playable-verify-ui/scripts/verify_fetch_antipatterns.py +59 -0
  944. package/project-templates/threejs/skills/playable-verify-ui/scripts/verify_game_config_load.js +54 -0
  945. package/project-templates/threejs/skills/playable-verify-ui/scripts/verify_hardcoded_layout.js +64 -0
  946. package/project-templates/threejs/skills/playable-verify-ui/scripts/verify_layout_regions.js +110 -0
  947. package/project-templates/threejs/skills/playable-verify-ui/scripts/verify_profile_schema.py +37 -0
  948. package/project-templates/threejs/skills/playable-verify-ui/scripts/verify_ring1_visual.py +211 -0
  949. package/project-templates/threejs/skills/playable-verify-ui/scripts/verify_runtime_contract.js +58 -0
  950. package/project-templates/threejs/skills/playable-verify-ui/scripts/verify_script_load_order.js +73 -0
  951. package/project-templates/threejs/skills/playable-verify-ui/scripts/verify_slice_plan.py +238 -0
  952. package/project-templates/threejs/skills/playable-verify-ui/scripts/verify_slice_plan_test.py +125 -0
  953. package/project-templates/threejs/skills/playable-verify-ui/scripts/verify_text_overlap.py +333 -0
  954. package/project-templates/threejs/skills/playable-verify-ui/scripts/verify_tile_consistency.py +323 -0
  955. package/project-templates/threejs/skills/playable-verify-ui/scripts/verify_vision_evidence.py +894 -0
  956. package/project-templates/threejs/skills/playable-verify-ui/scripts/verify_vision_evidence_test.py +122 -0
  957. package/project-templates/threejs/skills/playable-verify-ui/scripts/verify_visual.py +422 -0
  958. package/project-templates/threejs/ta-workspace/scripts/.gitkeep +0 -0
  959. package/project-templates/threejs/templates/artist-log.template.md +182 -0
  960. package/project-templates/threejs/templates/atom-tree.template.json +133 -0
  961. package/project-templates/threejs/templates/design-brief.template.md +92 -0
  962. package/project-templates/threejs/templates/design-lens-checklist.reference.md +117 -0
  963. package/project-templates/threejs/templates/design-methodology.md +99 -0
  964. package/project-templates/threejs/templates/design.template.md +50 -0
  965. package/project-templates/threejs/templates/developer-log.template.md +258 -0
  966. package/project-templates/threejs/templates/five-axis-framework.md +194 -0
  967. package/project-templates/threejs/templates/gen-placeholder-audio.template.mjs +63 -0
  968. package/project-templates/threejs/templates/gen-placeholders.template.mjs +81 -0
  969. package/project-templates/threejs/templates/intent-clarifications.template.md +58 -0
  970. package/project-templates/threejs/templates/layout-spec.template.md +198 -0
  971. package/project-templates/threejs/templates/plan.template.md +80 -0
  972. package/project-templates/threejs/templates/project-state.template.md +136 -0
  973. package/project-templates/threejs/templates/review-report.template.md +275 -0
  974. package/project-templates/threejs/templates/style-exploration.template.md +93 -0
  975. package/project-templates/threejs/templates/ta-log.template.md +394 -0
  976. package/project-templates/threejs/tsconfig.json +20 -0
  977. package/project-templates/threejs/vite.config.ts +27 -0
  978. package/dist/atom-plan/validate-atom-plan.js +0 -298
  979. package/project-template/.claude/agents/designer.md +0 -124
  980. package/project-template/.claude/agents/pm.md +0 -166
  981. package/project-template/.claude/agents/refs/designer-style-exploration-flow.md +0 -67
  982. package/project-template/.claude/agents/refs/pm-workflow-detail.md +0 -567
  983. package/project-template/.claude/hooks/validate-atom-plan.mjs +0 -224
  984. package/project-template/.claude/skills/playcraft-workflow/SKILL.md +0 -485
  985. package/project-template/docs/team/atom-plan-format.md +0 -173
  986. package/project-template/templates/atom-plan.template.json +0 -44
  987. package/project-template/templates/atom-plan.template.md +0 -108
  988. /package/dist/{atom-plan → asr}/validate-asr-coverage.js +0 -0
  989. /package/{project-template → engine-packs/phaser}/game/config/.gitkeep +0 -0
  990. /package/{project-template → engine-packs/phaser}/game/gameplay/.gitkeep +0 -0
  991. /package/{project-template → engine-packs/phaser}/game/scenes/.gitkeep +0 -0
  992. /package/project-template/.claude/agents/refs/{designer-art-style-catalog.md → artist-art-style-catalog.md} +0 -0
@@ -9,18 +9,21 @@
9
9
  * list 列出所有可用 skill(支持按 engine/category/tag 过滤)
10
10
  * match 按引擎+意图推荐完整 skill 集合(含依赖图遍历)
11
11
  * scaffold 将指定 skill 的 ref 文件复制到目标项目目录
12
+ * inspect 输出 skill 的文件清单(scaffold 路径 + files 逻辑索引 + 用途描述)
13
+ * read 输出指定 skill 的 SKILL.md(供 Agent 读取详细使用说明)
12
14
  *
13
15
  * Skill 目录发现优先级:
14
16
  * 1. --skills-dir 参数
15
17
  * 2. AGENT_SKILLS_PATHS 环境变量(逗号分隔)
16
- * 3. <cwd>/node_modules/@playcraft/skills/skills
17
- * 4. <CLI 自身>/../../skills/skills(monorepo 开发时)
18
+ * 3. <cwd>/node_modules/@playcraft/skills/playcraft-skills(兼容旧路径 skills)
19
+ * 4. <CLI 自身>/../../skills/playcraft-skills(monorepo 开发时,兼容旧路径 skills)
18
20
  */
19
21
  import * as fs from 'node:fs';
20
22
  import * as path from 'node:path';
21
- import { fileURLToPath } from 'node:url';
22
- import { parseSkillRefsFromAtomPlanProject, validateAtomPlanProject } from '../atom-plan/validate-atom-plan.js';
23
- import { validateAsrCoverageProject } from '../atom-plan/validate-asr-coverage.js';
23
+ import { getFusionHintsForGameplayAtoms, } from '../atom-tree/fusion-hints.js';
24
+ import { collectPinnedDependencies, loadSkillManifestIndex, parseAtomTreeJson, parseSelectedAtomRefsFromAtomTreeProject, isUninitializedAtomTreeSkeleton, resolveSkillsDirs, validateAtomTreeSelectionProject, } from '../atom-tree/selection.js';
25
+ import { buildAtomTreeSkeleton } from '../atom-tree/build-skeleton.js';
26
+ import { validateAsrCoverageProject } from '../asr/validate-asr-coverage.js';
24
27
  import { loadConfig } from '../config.js';
25
28
  const KNOWN_ENGINES = ['phaser', 'threejs'];
26
29
  const SCORE_THRESHOLD_FULL = 8;
@@ -32,6 +35,13 @@ export const MAX_MEDIA_ALTERNATIVES_PER_CLASS = 10;
32
35
  export function capAlternatives(ranked, limit = MAX_ALTERNATIVES_PER_CLASS) {
33
36
  return { items: ranked.slice(0, limit), total: ranked.length };
34
37
  }
38
+ /** Resolve atom-graph fusesWith hints for matched gameplay skills. */
39
+ export function resolveFusionHintsFromMatchItems(items) {
40
+ const gameplayAtomIds = items
41
+ .map((item) => item.atomId)
42
+ .filter((atomId) => atomId.endsWith('.aigameplay'));
43
+ return getFusionHintsForGameplayAtoms(gameplayAtomIds);
44
+ }
35
45
  /** 泛化 intent 词:单独命中时不应拉起大量无关 gameplay / 组件 */
36
46
  const GENERIC_INTENT_TAGS = new Set([
37
47
  'grid',
@@ -207,79 +217,37 @@ const LAYER_ORDER = {
207
217
  '其他': 99,
208
218
  };
209
219
  function getCategoryLayer(category, atomId) {
210
- const id = atomId.toLowerCase();
220
+ // 新 6 值 category 直接映射
211
221
  if (category === 'engine')
212
222
  return '引擎层';
213
- if (category === 'build')
214
- return '构建层';
215
- if (category === 'layout')
216
- return '布局层';
217
- if (category === 'scene')
218
- return '场景层';
219
- if (category === 'state')
220
- return '状态层';
221
223
  if (category === 'gameplay')
222
224
  return '玩法层';
223
- if (category === 'visual' || id.includes('sprite') || id.includes('image') || id.includes('aiimage'))
224
- return '实体层';
225
- if (category === 'audio')
226
- return '音频层';
227
- if (category === 'config')
228
- return '配置层';
229
- if (category === 'util')
230
- return '工具层';
231
- // component 需要按子类型细分
232
- if (id.includes('input') || id.includes('handler'))
233
- return '输入层';
234
- if (id.includes('render') || id.includes('renderer'))
235
- return '渲染层';
236
- if (id.includes('animation') || id.includes('anim'))
237
- return '动画层';
238
- if (id.includes('camera'))
239
- return '场景层';
240
- if (id.includes('tray') || id.includes('ui') || id.includes('bar') || id.includes('panel'))
225
+ if (category === 'visual')
226
+ return '视觉层';
227
+ if (category === 'ui')
241
228
  return 'UI层';
242
- return 'UI层'; // component 默认 UI 层
243
- }
244
- // ─── Skill 目录发现 ──────────────────────────────────────────────────────────
245
- /**
246
- * 解析 skills 目录,优先级:
247
- * 1. --skills-dir CLI 参数
248
- * 2. playcraft.config.json 中的 skillsDir
249
- * 3. AGENT_SKILLS_PATHS 环境变量(逗号分隔)
250
- * 4. node_modules/@playcraft/skills/skills
251
- * 5. monorepo 开发时的相对路径
252
- */
253
- function resolveSkillsDirs(customDir, configDir) {
254
- if (customDir) {
255
- return [path.resolve(customDir)];
256
- }
257
- const dirs = [];
258
- // 来自 playcraft.config.json
259
- if (configDir) {
260
- dirs.push(path.resolve(configDir));
261
- }
262
- // 来自环境变量
263
- const envPaths = process.env.AGENT_SKILLS_PATHS?.split(',').map((p) => p.trim()).filter(Boolean) ?? [];
264
- dirs.push(...envPaths);
265
- // node_modules 中的 @playcraft/skills
266
- const cwdModulesPath = path.join(process.cwd(), 'node_modules', '@playcraft', 'skills', 'skills');
267
- if (fs.existsSync(cwdModulesPath)) {
268
- dirs.push(cwdModulesPath);
269
- }
270
- // monorepo 开发时:CLI 包相对于 skills 包的路径
271
- const __filename = fileURLToPath(import.meta.url);
272
- const __dirname = path.dirname(__filename);
273
- const monoSkillsPath = path.resolve(__dirname, '..', '..', '..', '..', 'skills', 'skills');
274
- if (fs.existsSync(monoSkillsPath)) {
275
- dirs.push(monoSkillsPath);
276
- }
277
- // 编译后的 dist 路径
278
- const distSkillsPath = path.resolve(__dirname, '..', '..', '..', 'skills', 'skills');
279
- if (fs.existsSync(distSkillsPath) && distSkillsPath !== monoSkillsPath) {
280
- dirs.push(distSkillsPath);
229
+ if (category === 'asset')
230
+ return '资产层';
231
+ if (category === 'misc')
232
+ return '工具层';
233
+ // 旧 category 值兼容(渐进迁移期间可能遇到)
234
+ const id = atomId.toLowerCase();
235
+ if (category === 'build')
236
+ return '引擎层';
237
+ if (category === 'layout' || category === 'scene' || category === 'state')
238
+ return '玩法层';
239
+ if (category === 'audio' || category === 'config')
240
+ return '资产层';
241
+ if (category === 'util' || category === 'tool' || category === 'validator')
242
+ return '工具层';
243
+ if (category === 'animation' || category === 'vfx' || category === 'rendering')
244
+ return '视觉层';
245
+ if (category === 'component') {
246
+ if (id.includes('ui') || id.includes('bar') || id.includes('panel') || id.includes('tray'))
247
+ return 'UI层';
248
+ return '玩法层';
281
249
  }
282
- return [...new Set(dirs)];
250
+ return '玩法层';
283
251
  }
284
252
  // ─── Manifest 索引器 ─────────────────────────────────────────────────────────
285
253
  function loadSkillIndex(skillsDirs) {
@@ -315,12 +283,35 @@ function getGameplayType(manifest) {
315
283
  const gen = manifest['generation'];
316
284
  return gen?.gameplayType;
317
285
  }
318
- function pinAtomIdToEngine(pinAtomId) {
319
- if (!pinAtomId.endsWith('.aicomponent'))
286
+ function isEngineManifest(manifest) {
287
+ return manifest.category === 'engine' || manifest.bundleType === 'aicomponent';
288
+ }
289
+ function isGameplayManifest(manifest) {
290
+ return manifest.category === 'gameplay' || manifest.bundleType === 'aigameplay';
291
+ }
292
+ function isMediaOrConfigManifest(manifest) {
293
+ return manifest.category === 'asset' || manifest.category === 'config';
294
+ }
295
+ /**
296
+ * 从 skill 的 manifest 中判断是否指向一个已知引擎 skill。
297
+ * 不再依赖 pinAtomId 后缀(如 .aicomponent、-runtime),而是通过传入 manifest 的 bundleType / category 来判断。
298
+ */
299
+ function pinAtomIdToEngine(pinAtomId, manifest) {
300
+ if (!isEngineManifest(manifest))
320
301
  return null;
321
- const name = pinAtomId.slice(0, -'.aicomponent'.length);
302
+ const name = pinAtomIdToBareName(pinAtomId);
322
303
  return KNOWN_ENGINES.includes(name) ? name : null;
323
304
  }
305
+ /**
306
+ * 从 pinAtomId 提取裸名。
307
+ * 这里只做兼容性归一化,不再把后缀作为类型判断依据。
308
+ */
309
+ function pinAtomIdToBareName(pinAtomId) {
310
+ if (pinAtomId.endsWith('.aicomponent')) {
311
+ return pinAtomId.slice(0, -'.aicomponent'.length);
312
+ }
313
+ return pinAtomId.replace(/-runtime$/, '');
314
+ }
324
315
  /**
325
316
  * 仅按 intent 打分(discovery 相位)。不假设目标引擎。
326
317
  */
@@ -347,7 +338,7 @@ export function scoreSkillIntentOnly(entry, intentTags) {
347
338
  /**
348
339
  * 从 discovery 相位已匹配的 skill 聚合候选引擎。
349
340
  */
350
- export function inferSuggestedEngines(scored, intentTags) {
341
+ export function inferSuggestedEngines(scored, intentTags, indexMap) {
351
342
  const totals = new Map();
352
343
  const bump = (engine, delta, reason) => {
353
344
  if (!KNOWN_ENGINES.includes(engine))
@@ -376,12 +367,13 @@ export function inferSuggestedEngines(scored, intentTags) {
376
367
  for (const imp of manifest.imports ?? []) {
377
368
  if (!imp.pinAtomId)
378
369
  continue;
379
- const eng = pinAtomIdToEngine(imp.pinAtomId);
370
+ const impManifest = indexMap.get(imp.pinAtomId)?.manifest;
371
+ const eng = pinAtomIdToEngine(imp.pinAtomId, impManifest ?? manifest);
380
372
  if (eng)
381
373
  bump(eng, score, `${entry.atomId} → ${imp.pinAtomId}`);
382
374
  }
383
375
  if (manifest.category === 'engine') {
384
- const eng = pinAtomIdToEngine(manifest.atomId);
376
+ const eng = pinAtomIdToEngine(manifest.atomId, manifest);
385
377
  if (eng)
386
378
  bump(eng, score + 5, `引擎节点 ${manifest.atomId}`);
387
379
  }
@@ -419,7 +411,11 @@ export function scoreSkill(entry, engine, intentTags) {
419
411
  score += 3;
420
412
  }
421
413
  // 4. imports 中依赖该引擎的 skill
422
- const importsEngine = manifest.imports?.some((imp) => imp.pinAtomId === `${engine}.aicomponent`);
414
+ const importsEngine = manifest.imports?.some((imp) => {
415
+ if (!imp.pinAtomId)
416
+ return false;
417
+ return pinAtomIdToEngine(imp.pinAtomId, { category: 'engine' }) === engine;
418
+ });
423
419
  if (importsEngine)
424
420
  score += 5;
425
421
  return score;
@@ -430,7 +426,11 @@ export function scoreSkill(entry, engine, intentTags) {
430
426
  * engineVariants[engine].imports 而非 manifest 顶层 imports,
431
427
  * 避免将另一引擎的依赖(如 phaser.aicomponent)错误地拉进来。
432
428
  */
433
- function collectDependencies(atomId, index, engine, visited = new Set()) {
429
+ function collectDependencies(atomId, index, engine, visited = new Set(), depth = 0) {
430
+ const MAX_DEPTH = 100;
431
+ if (depth > MAX_DEPTH) {
432
+ throw new Error(`依赖收集递归深度超过限制 (${MAX_DEPTH}),当前atom: ${atomId}`);
433
+ }
434
434
  if (visited.has(atomId))
435
435
  return [];
436
436
  visited.add(atomId);
@@ -446,7 +446,7 @@ function collectDependencies(atomId, index, engine, visited = new Set()) {
446
446
  for (const pinId of variantImports) {
447
447
  if (!visited.has(pinId)) {
448
448
  deps.push(pinId);
449
- deps.push(...collectDependencies(pinId, index, engine, visited));
449
+ deps.push(...collectDependencies(pinId, index, engine, visited, depth + 1));
450
450
  }
451
451
  }
452
452
  }
@@ -455,7 +455,7 @@ function collectDependencies(atomId, index, engine, visited = new Set()) {
455
455
  for (const imp of entry.manifest.imports ?? []) {
456
456
  if (imp.pinAtomId && !visited.has(imp.pinAtomId)) {
457
457
  deps.push(imp.pinAtomId);
458
- deps.push(...collectDependencies(imp.pinAtomId, index, engine, visited));
458
+ deps.push(...collectDependencies(imp.pinAtomId, index, engine, visited, depth + 1));
459
459
  }
460
460
  }
461
461
  }
@@ -474,10 +474,23 @@ function getScaffoldFiles(manifest, engine) {
474
474
  const sharedFiles = manifest['sharedFiles'];
475
475
  const scaffoldFiles = {};
476
476
  if (manifest.scaffold?.files) {
477
- for (const [k, v] of Object.entries(manifest.scaffold.files)) {
478
- const contribution = v;
479
- if (contribution.source) {
480
- scaffoldFiles[k] = { source: contribution.source, mode: contribution.mode ?? 'full', weight: contribution.weight ?? 10 };
477
+ const files = manifest.scaffold.files;
478
+ if (Array.isArray(files)) {
479
+ // Array format: [{ source, dest, mode?, weight? }]
480
+ for (const item of files) {
481
+ const entry = item;
482
+ if (entry.source && entry.dest) {
483
+ scaffoldFiles[entry.dest] = { source: entry.source, mode: entry.mode ?? 'full', weight: entry.weight ?? 10 };
484
+ }
485
+ }
486
+ }
487
+ else {
488
+ // Record format: { 'dest/path': { source, mode?, weight? } }
489
+ for (const [k, v] of Object.entries(files)) {
490
+ const contribution = v;
491
+ if (contribution.source) {
492
+ scaffoldFiles[k] = { source: contribution.source, mode: contribution.mode ?? 'full', weight: contribution.weight ?? 10 };
493
+ }
481
494
  }
482
495
  }
483
496
  }
@@ -489,7 +502,7 @@ function getScaffoldFiles(manifest, engine) {
489
502
  return scaffoldFiles;
490
503
  }
491
504
  /** 无法推断引擎时的兜底:仅 intent 命中项,无依赖展开与 scaffold */
492
- function buildIntentOnlyMatchResult(entries, intentTags, suggestedEngines) {
505
+ function buildIntentOnlyMatchResult(entries, intentTags, suggestedEngines, indexMap) {
493
506
  const scored = entries
494
507
  .map((entry) => ({ entry, score: scoreSkillIntentOnly(entry, intentTags) }))
495
508
  .filter(({ score }) => score >= SCORE_THRESHOLD_DISCOVERY)
@@ -511,7 +524,11 @@ function buildIntentOnlyMatchResult(entries, intentTags, suggestedEngines) {
511
524
  if (engineVariants)
512
525
  supportedEngines.push(...Object.keys(engineVariants));
513
526
  for (const imp of manifest.imports ?? []) {
514
- const eng = imp.pinAtomId ? pinAtomIdToEngine(imp.pinAtomId) : null;
527
+ const pinAtomId = imp.pinAtomId;
528
+ if (!pinAtomId)
529
+ continue;
530
+ const impManifest = indexMap?.get(pinAtomId)?.manifest ?? manifest;
531
+ const eng = pinAtomIdToEngine(pinAtomId, impManifest);
515
532
  if (eng)
516
533
  supportedEngines.push(eng);
517
534
  }
@@ -524,6 +541,7 @@ function buildIntentOnlyMatchResult(entries, intentTags, suggestedEngines) {
524
541
  label: manifest.label,
525
542
  layer,
526
543
  reason,
544
+ roles: [],
527
545
  scaffoldFiles: {},
528
546
  hasEngineVariant: false,
529
547
  skillDir: entry.dir,
@@ -539,6 +557,7 @@ function buildIntentOnlyMatchResult(entries, intentTags, suggestedEngines) {
539
557
  items,
540
558
  mediaGroups,
541
559
  missingSlots: [],
560
+ fusionHints: resolveFusionHintsFromMatchItems(items),
542
561
  };
543
562
  }
544
563
  /**
@@ -548,10 +567,11 @@ export function buildAutoMatchResult(entries, intentTags) {
548
567
  const scored = entries
549
568
  .map((entry) => ({ entry, score: scoreSkillIntentOnly(entry, intentTags) }))
550
569
  .filter(({ score }) => score >= SCORE_THRESHOLD_DISCOVERY);
551
- const suggestedEngines = inferSuggestedEngines(scored, intentTags);
570
+ const indexMap = new Map(entries.map((e) => [e.atomId, e]));
571
+ const suggestedEngines = inferSuggestedEngines(scored, intentTags, indexMap);
552
572
  const engine = suggestedEngines[0]?.engine;
553
573
  if (!engine) {
554
- return buildIntentOnlyMatchResult(entries, intentTags, suggestedEngines);
574
+ return buildIntentOnlyMatchResult(entries, intentTags, suggestedEngines, indexMap);
555
575
  }
556
576
  const full = buildMatchResult(entries, engine, intentTags);
557
577
  const gap = suggestedEngines.length > 1 ? suggestedEngines[0].score - suggestedEngines[1].score : 99;
@@ -563,17 +583,21 @@ export function buildAutoMatchResult(entries, intentTags) {
563
583
  };
564
584
  }
565
585
  function selectMatchSeedAtomIds(scored, engine, intentTags) {
566
- const seeds = new Set([`${engine}.aicomponent`]);
586
+ const seeds = new Set();
567
587
  for (const { entry, score } of scored) {
568
588
  const { manifest } = entry;
569
- if (manifest.category === 'gameplay') {
589
+ if (isEngineManifest(manifest) && pinAtomIdToEngine(entry.atomId, manifest) === engine) {
590
+ seeds.add(entry.atomId);
591
+ continue;
592
+ }
593
+ if (isGameplayManifest(manifest)) {
570
594
  if (passesGameplayIntentFilter(entry, intentTags))
571
595
  seeds.add(entry.atomId);
572
596
  continue;
573
597
  }
574
- if (manifest.category === 'engine')
598
+ if (isEngineManifest(manifest))
575
599
  continue;
576
- if (['aiimage', 'aiaudio', 'aiconfig'].includes(manifest.bundleType))
600
+ if (isMediaOrConfigManifest(manifest))
577
601
  continue;
578
602
  const specificHits = intentTags.filter((t) => isSpecificIntentTag(t) && entryMatchesIntentTag(entry, t));
579
603
  if (specificHits.length > 0 && score >= SCORE_THRESHOLD_FULL)
@@ -591,10 +615,52 @@ function expandSeedClosure(seedIds, indexMap, engine) {
591
615
  }
592
616
  return closed;
593
617
  }
594
- function buildItemReason(entry, engine, intentTags, score, via) {
618
+ function formatDependencyReason(dep) {
619
+ const dependedByText = dep.dependedBy.length > 0 ? dep.dependedBy.join('、') : '上游 skill';
620
+ return `传递依赖:被 ${dependedByText} 依赖`;
621
+ }
622
+ function buildDependencyReasonMap(requiredIds, indexMap, engine) {
623
+ const reasonMap = new Map();
624
+ const getOrCreate = (atomId) => {
625
+ const current = reasonMap.get(atomId) ?? { dependedBy: new Set(), roles: new Set() };
626
+ reasonMap.set(atomId, current);
627
+ return current;
628
+ };
629
+ for (const parentId of requiredIds) {
630
+ const entry = indexMap.get(parentId);
631
+ if (!entry)
632
+ continue;
633
+ const engineVariants = entry.manifest['engineVariants'];
634
+ const variantImports = engineVariants?.[engine]?.imports;
635
+ if (variantImports && variantImports.length > 0) {
636
+ for (const depId of variantImports) {
637
+ if (!requiredIds.has(depId))
638
+ continue;
639
+ const ctx = getOrCreate(depId);
640
+ ctx.dependedBy.add(parentId);
641
+ }
642
+ continue;
643
+ }
644
+ for (const imp of entry.manifest.imports ?? []) {
645
+ if (imp.pinAtomId && requiredIds.has(imp.pinAtomId)) {
646
+ const ctx = getOrCreate(imp.pinAtomId);
647
+ ctx.dependedBy.add(parentId);
648
+ for (const role of imp.matchBindingRoles ?? []) {
649
+ ctx.roles.add(role);
650
+ }
651
+ }
652
+ }
653
+ }
654
+ return new Map([...reasonMap.entries()].map(([atomId, ctx]) => [atomId, {
655
+ dependedBy: [...ctx.dependedBy].sort(),
656
+ roles: [...ctx.roles].sort(),
657
+ }]));
658
+ }
659
+ function buildItemReason(entry, engine, intentTags, score, via, dependencyReason) {
595
660
  const { manifest } = entry;
596
- if (via === 'dependency')
597
- return '传递依赖';
661
+ if (via === 'dependency') {
662
+ return formatDependencyReason(dependencyReason ?? { dependedBy: [], roles: [] });
663
+ }
598
664
  if (manifest.category === 'engine')
599
665
  return '引擎根节点';
600
666
  const gt = getGameplayType(manifest);
@@ -623,12 +689,12 @@ export function buildMatchResult(entries, engine, intentTags) {
623
689
  const seedIds = selectMatchSeedAtomIds(scored, engine, expandedIntent);
624
690
  const requiredIds = expandSeedClosure(seedIds, indexMap, engine);
625
691
  const seedIdSet = seedIds;
692
+ const dependencyReasonMap = buildDependencyReasonMap(requiredIds, indexMap, engine);
626
693
  const anchorEntries = [];
627
694
  for (const id of seedIds) {
628
- if (id.endsWith('.aigameplay')) {
629
- const e = indexMap.get(id);
630
- if (e)
631
- anchorEntries.push(e);
695
+ const e = indexMap.get(id);
696
+ if (e && isGameplayManifest(e.manifest)) {
697
+ anchorEntries.push(e);
632
698
  }
633
699
  }
634
700
  const allItems = [];
@@ -641,12 +707,14 @@ export function buildMatchResult(entries, engine, intentTags) {
641
707
  const scaffoldFiles = getScaffoldFiles(manifest, engine);
642
708
  const hasEngineVariant = !!manifest['engineVariants']?.[engine];
643
709
  const via = seedIdSet.has(atomId) ? 'seed' : 'dependency';
644
- const reason = buildItemReason(entry, engine, expandedIntent, scoreById.get(atomId) ?? null, via);
710
+ const dependencyReason = dependencyReasonMap.get(atomId);
711
+ const reason = buildItemReason(entry, engine, expandedIntent, scoreById.get(atomId) ?? null, via, dependencyReason);
645
712
  allItems.push({
646
713
  atomId: manifest.atomId,
647
714
  label: manifest.label,
648
715
  layer,
649
716
  reason,
717
+ roles: dependencyReason?.roles ?? [],
650
718
  scaffoldFiles,
651
719
  hasEngineVariant,
652
720
  skillDir: entry.dir,
@@ -654,7 +722,6 @@ export function buildMatchResult(entries, engine, intentTags) {
654
722
  }
655
723
  allItems.sort((a, b) => (LAYER_ORDER[a.layer] ?? 99) - (LAYER_ORDER[b.layer] ?? 99));
656
724
  const mediaGroups = buildMediaGroupsForAnchors(entries, expandedIntent, anchorEntries);
657
- const mediaRoles = new Set(mediaGroups.map((g) => g.role));
658
725
  const missingSlots = [];
659
726
  for (const entry of anchorEntries) {
660
727
  for (const imp of entry.manifest.imports ?? []) {
@@ -675,6 +742,7 @@ export function buildMatchResult(entries, engine, intentTags) {
675
742
  items: allItems,
676
743
  mediaGroups,
677
744
  missingSlots,
745
+ fusionHints: resolveFusionHintsFromMatchItems(allItems),
678
746
  };
679
747
  }
680
748
  // ─── 媒体/配置资产匹配(第二遍:引擎无关,按 bindingRole 分组)────────────
@@ -698,7 +766,7 @@ function collectAnchorBindingRoles(anchorEntries) {
698
766
  return roles;
699
767
  }
700
768
  function collectMediaRoleMap(entries) {
701
- const mediaEntries = entries.filter((e) => ['aiimage', 'aiaudio', 'aiconfig'].includes(e.manifest.bundleType));
769
+ const mediaEntries = entries.filter((e) => isMediaOrConfigManifest(e.manifest));
702
770
  const roleMap = new Map();
703
771
  for (const entry of mediaEntries) {
704
772
  for (const role of entry.manifest.bindingRoles ?? []) {
@@ -810,7 +878,7 @@ function printMatchResult(result, verbose, entries) {
810
878
  const aliasOnly = findIntentAliasOnlyTags(entries, intent);
811
879
  const intentStr = intent.length > 0 ? `意图[${intent.join(', ')}]` : '';
812
880
  const totalMedia = mediaGroups.reduce((s, g) => s + g.candidates.length, 0);
813
- const codeItems = items.filter((i) => /\.(aigameplay|aicomponent|aiconfig)$/.test(i.atomId));
881
+ const codeItems = items.filter((i) => i.layer !== '资产层');
814
882
  const engineLine = engineSource === 'inferred'
815
883
  ? `engine=${engine}(自动)`
816
884
  : engineSource === 'none'
@@ -865,7 +933,7 @@ function printMatchResult(result, verbose, entries) {
865
933
  for (const s of result.missingSlots)
866
934
  console.log(` - ${s}`);
867
935
  }
868
- console.log('\n加 --json 供 atom-plan;加 --verbose 查看 scaffold 明细与命令。\n');
936
+ console.log('\n加 --json 供 atom-tree;加 --verbose 查看 scaffold 明细与命令。\n');
869
937
  return;
870
938
  }
871
939
  // ── verbose:完整分层列表 ──
@@ -906,18 +974,58 @@ function printMatchResult(result, verbose, entries) {
906
974
  function printMatchFooters(result, verbose) {
907
975
  if (!result.engine)
908
976
  return;
909
- const codeItems = result.items.filter((i) => /\.(aigameplay|aicomponent|aiconfig)$/.test(i.atomId));
977
+ const codeItems = result.items.filter((i) => i.layer !== '资产层');
910
978
  if (!verbose) {
911
979
  const ids = codeItems.map((i) => i.atomId).join(',');
912
980
  console.log(`scaffold: playcraft skills scaffold --engine ${result.engine} --atoms ${ids} --out ./game`);
913
- console.log(`link: playcraft skills link --from-atom-plan --prune # 或 --atoms <子集>`);
981
+ console.log(`link: playcraft skills link --from-atom-tree --prune # 或 --atoms <子集>`);
914
982
  return;
915
983
  }
916
984
  const atomIdList = codeItems.map((i) => i.atomId).join(',');
917
985
  console.log(`\n💡 Scaffold:playcraft skills scaffold --engine ${result.engine} --atoms ${atomIdList} --out ./game`);
918
986
  console.log(`🔗 Link:playcraft skills link --atoms ${atomIdList}`);
919
987
  }
920
- // ─── Scaffold 辅助:merge-json 模式 ──────────────────────────────────────────
988
+ // ─── Scaffold 辅助 ───────────────────────────────────────────────────────────
989
+ /**
990
+ * 修复 Windows 路径中因 shell/Agent 转义丢失的路径分隔符。
991
+ *
992
+ * 典型场景:Agent 传入 D:\project\playcraft-tencent\test19\game,
993
+ * 反斜杠被 shell 消费后变成 D:projectplaycraft-tencenttest19game。
994
+ *
995
+ * 策略:如果路径不以 \ 或 / 开头且不含路径分隔符,尝试在已知目录边界插入分隔符。
996
+ */
997
+ function sanitizeWindowsPath(raw) {
998
+ // 已经包含路径分隔符(/ 或 \\),说明未被破坏
999
+ if (/[\\/]/.test(raw))
1000
+ return raw;
1001
+ // 只处理 Windows 绝对路径格式 D:xxx(盘符后无分隔符)
1002
+ const m = raw.match(/^([A-Za-z]:)(.+)$/);
1003
+ if (!m)
1004
+ return raw;
1005
+ const drive = m[1];
1006
+ const rest = m[2];
1007
+ // 尝试在已知的 monorepo 目录边界插入 /
1008
+ // projectplaycraft-tencent → project/playcraft-tencent
1009
+ // playcraft-tencenttest19 → playcraft-tencent/test19
1010
+ const segments = [
1011
+ 'project', 'playcraft-tencent', 'playcraft-skills', 'packages',
1012
+ 'test16', 'test19', 'src', 'game', 'docs', 'skills',
1013
+ ];
1014
+ let result = rest;
1015
+ for (const seg of segments) {
1016
+ const idx = result.indexOf(seg);
1017
+ if (idx > 0) {
1018
+ // 在已知段之前插入 /
1019
+ result = result.slice(0, idx) + '/' + result.slice(idx);
1020
+ }
1021
+ }
1022
+ // 如果仍然没有分隔符,尝试按大写字母边界插入(兜底)
1023
+ if (!/[\\/]/.test(result)) {
1024
+ // 不做暴力拆分,保持原样
1025
+ }
1026
+ return drive + '/' + result;
1027
+ }
1028
+ // ─── Scaffold merge-json 模式 ─────────────────────────────────────────────
921
1029
  /**
922
1030
  * 深度合并两个 JSON 对象:incoming 中的新 key 追加到 existing,已有 key 保留 existing 的值。
923
1031
  * 对象类型字段(dependencies / devDependencies / compilerOptions 等)递归合并。
@@ -940,23 +1048,57 @@ function mergeJson(existing, incoming) {
940
1048
  }
941
1049
  return result;
942
1050
  }
1051
+ function dedupeAtomRefs(atomRefs) {
1052
+ const seen = new Set();
1053
+ const deduped = [];
1054
+ for (const atomRef of atomRefs) {
1055
+ const normalized = atomRef.trim();
1056
+ if (!normalized || seen.has(normalized))
1057
+ continue;
1058
+ seen.add(normalized);
1059
+ deduped.push(normalized);
1060
+ }
1061
+ return deduped;
1062
+ }
1063
+ function getImportedAtomIdsForEngine(entry, engine) {
1064
+ const engineVariants = entry.manifest['engineVariants'];
1065
+ const variantImports = engineVariants?.[engine]?.imports;
1066
+ if (variantImports && variantImports.length > 0) {
1067
+ return dedupeAtomRefs(variantImports);
1068
+ }
1069
+ const topLevelImports = (entry.manifest.imports ?? [])
1070
+ .map((imp) => imp.pinAtomId?.trim())
1071
+ .filter((pinAtomId) => !!pinAtomId);
1072
+ return dedupeAtomRefs(topLevelImports);
1073
+ }
943
1074
  // ─── Scaffold 执行器 ─────────────────────────────────────────────────────────
944
1075
  function runScaffold(opts) {
945
1076
  const { entries, atomIds, engine, outDir, dryRun, force } = opts;
946
1077
  const indexMap = new Map(entries.map((e) => [e.atomId, e]));
947
1078
  let copied = 0;
948
1079
  let skipped = 0;
1080
+ // manifest 数据:记录每个 atomId 复制了哪些文件,用于落盘 scaffold-manifest.md
1081
+ const manifestData = [];
949
1082
  for (const atomId of atomIds) {
950
1083
  const entry = indexMap.get(atomId);
951
1084
  if (!entry) {
952
1085
  console.warn(`[warn] 未找到 skill: ${atomId}`);
953
1086
  continue;
954
1087
  }
1088
+ const importedAtomIds = getImportedAtomIdsForEngine(entry, engine);
1089
+ if (importedAtomIds.length > 0) {
1090
+ for (const importedAtomId of importedAtomIds) {
1091
+ const importedEntry = indexMap.get(importedAtomId);
1092
+ const importedDir = importedEntry?.dir ?? '(missing skill dir)';
1093
+ console.log(`[import-link] [${atomId}] ${importedAtomId} -> ${importedDir}`);
1094
+ }
1095
+ }
955
1096
  const scaffoldFiles = getScaffoldFiles(entry.manifest, engine);
956
1097
  if (Object.keys(scaffoldFiles).length === 0) {
957
1098
  console.log(`[skip] ${atomId}:无 scaffold 文件`);
958
1099
  continue;
959
1100
  }
1101
+ const atomManifestFiles = [];
960
1102
  for (const [destRelPath, { source, mode }] of Object.entries(scaffoldFiles)) {
961
1103
  const srcPath = path.join(entry.dir, source);
962
1104
  const destPath = path.join(outDir, destRelPath);
@@ -968,7 +1110,8 @@ function runScaffold(opts) {
968
1110
  // merge-json 模式:目标文件已存在时合并而非覆盖(不受 --force 影响)
969
1111
  if (mode === 'merge-json' && destExists) {
970
1112
  if (dryRun) {
971
- console.log(`[dry-run] ${source} → ${destRelPath} (merge-json)`);
1113
+ console.log(`[dry-run] [${atomId}] ${source} → ${destRelPath} (merge-json)`);
1114
+ atomManifestFiles.push({ dest: destRelPath, status: 'dry-run' });
972
1115
  copied++;
973
1116
  continue;
974
1117
  }
@@ -977,7 +1120,8 @@ function runScaffold(opts) {
977
1120
  const incoming = JSON.parse(fs.readFileSync(srcPath, 'utf-8'));
978
1121
  const merged = mergeJson(existing, incoming);
979
1122
  fs.writeFileSync(destPath, JSON.stringify(merged, null, 2) + '\n', 'utf-8');
980
- console.log(`[merged] ${source} → ${destRelPath}`);
1123
+ console.log(`[merged] [${atomId}] ${source} → ${destRelPath}`);
1124
+ atomManifestFiles.push({ dest: destRelPath, status: 'merged' });
981
1125
  copied++;
982
1126
  }
983
1127
  catch (e) {
@@ -987,82 +1131,143 @@ function runScaffold(opts) {
987
1131
  continue;
988
1132
  }
989
1133
  if (destExists && !force) {
990
- console.log(`[skip] ${destRelPath} 已存在(使用 --force 覆盖)`);
1134
+ console.log(`[skip] [${atomId}] ${destRelPath} 已存在(使用 --force 覆盖)`);
1135
+ atomManifestFiles.push({ dest: destRelPath, status: 'skipped' });
991
1136
  skipped++;
992
1137
  continue;
993
1138
  }
994
1139
  if (dryRun) {
995
- console.log(`[dry-run] ${source} → ${destRelPath}`);
1140
+ console.log(`[dry-run] [${atomId}] ${source} → ${destRelPath}`);
1141
+ atomManifestFiles.push({ dest: destRelPath, status: 'dry-run' });
996
1142
  copied++;
997
1143
  continue;
998
1144
  }
999
- fs.mkdirSync(path.dirname(destPath), { recursive: true });
1000
- fs.copyFileSync(srcPath, destPath);
1001
- console.log(`[copied] ${source} → ${destRelPath}`);
1145
+ if (fs.statSync(srcPath).isDirectory()) {
1146
+ // 检查目录深度,防止递归过深
1147
+ const pathDepth = srcPath.split(path.sep).length;
1148
+ const MAX_PATH_DEPTH = 100;
1149
+ if (pathDepth > MAX_PATH_DEPTH) {
1150
+ throw new Error(`目录路径深度超过限制 (${MAX_PATH_DEPTH}),路径: ${srcPath}`);
1151
+ }
1152
+ fs.mkdirSync(destPath, { recursive: true });
1153
+ fs.cpSync(srcPath, destPath, { recursive: true });
1154
+ }
1155
+ else {
1156
+ fs.mkdirSync(path.dirname(destPath), { recursive: true });
1157
+ fs.copyFileSync(srcPath, destPath);
1158
+ }
1159
+ console.log(`[copied] [${atomId}] ${source} → ${destRelPath}`);
1160
+ atomManifestFiles.push({ dest: destRelPath, status: 'copied' });
1002
1161
  copied++;
1003
1162
  }
1163
+ if (atomManifestFiles.length > 0) {
1164
+ manifestData.push({ atomId, files: atomManifestFiles });
1165
+ }
1004
1166
  }
1005
1167
  console.log(`\n完成:${copied} 个文件${dryRun ? '(dry-run)' : ''},${skipped} 个跳过。`);
1168
+ // ── 生成 docs/scaffold-manifest.md 落盘(增量 upsert)──────────────────────
1169
+ if (!dryRun && manifestData.length > 0) {
1170
+ const docsDir = path.join(process.cwd(), 'docs');
1171
+ const manifestPath = path.join(docsDir, 'scaffold-manifest.md');
1172
+ const now = new Date().toISOString().replace('T', ' ').slice(0, 19);
1173
+ /** 将一条 manifestData 条目渲染为 markdown 段落(不含末尾空行) */
1174
+ const renderAtomSection = (atomId, files) => {
1175
+ const sectionLines = [];
1176
+ sectionLines.push(`## ${atomId}`);
1177
+ sectionLines.push(``);
1178
+ sectionLines.push(`> 查看用法:\`playcraft skills read ${atomId}\``);
1179
+ sectionLines.push(``);
1180
+ const copiedFiles = files.filter((f) => f.status === 'copied' || f.status === 'merged');
1181
+ const skippedFiles = files.filter((f) => f.status === 'skipped');
1182
+ if (copiedFiles.length > 0) {
1183
+ for (const f of copiedFiles) {
1184
+ sectionLines.push(`- \`${f.dest}\`${f.status === 'merged' ? ' *(merged)*' : ''}`);
1185
+ }
1186
+ }
1187
+ if (skippedFiles.length > 0) {
1188
+ sectionLines.push(``);
1189
+ sectionLines.push(`已跳过(文件已存在):`);
1190
+ for (const f of skippedFiles) {
1191
+ sectionLines.push(`- \`${f.dest}\` *(skipped)*`);
1192
+ }
1193
+ }
1194
+ return sectionLines.join('\n');
1195
+ };
1196
+ try {
1197
+ fs.mkdirSync(docsDir, { recursive: true });
1198
+ // ── 增量 upsert:文件已存在时保留旧段落,只替换/追加本次涉及的 atomId ──
1199
+ if (fs.existsSync(manifestPath)) {
1200
+ const existing = fs.readFileSync(manifestPath, 'utf-8');
1201
+ // 按 "## <atomId>" 分割成段落 Map(key = atomId,value = 段落文本,不含前导 ##)
1202
+ // 段落边界:下一个 "^## " 或文件末尾
1203
+ const sectionRegex = /^## (.+)$/m;
1204
+ // 将文件拆分为:header 部分(第一个 ## 之前)+ 各 atomId 段落
1205
+ const firstSectionIdx = existing.search(/^## /m);
1206
+ let header = firstSectionIdx === -1 ? existing : existing.slice(0, firstSectionIdx);
1207
+ // 更新 header 里的"最后更新时间"行(> 生成时间:...)
1208
+ header = header.replace(/^> 生成时间:.+$/m, `> 最后更新:${now}`);
1209
+ // 如果 header 里没有"最后更新"行(旧格式只有"生成时间"),则追加
1210
+ if (!header.includes('> 最后更新:')) {
1211
+ header = header.replace(/^> 生成时间:.+$/m, `> 生成时间:$&\n> 最后更新:${now}`);
1212
+ }
1213
+ // 解析现有段落(排除 footer:段落内尾部的 "---\n\n*Developer…*" 不属于 atom 内容)
1214
+ const existingSections = new Map();
1215
+ const rawSections = existing.slice(firstSectionIdx === -1 ? existing.length : firstSectionIdx);
1216
+ // 用正则逐段切割
1217
+ const splitPattern = /(?=^## )/m;
1218
+ for (const chunk of rawSections.split(splitPattern)) {
1219
+ const match = chunk.match(sectionRegex);
1220
+ if (match) {
1221
+ // 去掉段落内混入的 footer(旧格式可能把 "---\n\n*Developer…*" 写在每段末尾)
1222
+ const atomContent = chunk.replace(/\n---\n[\s\S]*$/, '').trimEnd();
1223
+ existingSections.set(match[1].trim(), atomContent);
1224
+ }
1225
+ }
1226
+ // upsert:本次 manifestData 覆盖同名段落,新 atomId 追加
1227
+ for (const { atomId, files } of manifestData) {
1228
+ existingSections.set(atomId, renderAtomSection(atomId, files));
1229
+ }
1230
+ // 重新拼装:header + 各段落(按原有顺序,新增的追加到末尾)
1231
+ const footer = `\n---\n\n*Developer:在 Step 0 摄入阶段请先阅读本文件,了解各资源来自哪个 skill atom,再通过 \`playcraft skills read <atomId>\` 读取对应 SKILL.md 理解绑定方式。*\n`;
1232
+ const body = [...existingSections.values()].join('\n\n') + '\n';
1233
+ fs.writeFileSync(manifestPath, header + body + footer, 'utf-8');
1234
+ console.log(`\n[manifest] 已更新 docs/scaffold-manifest.md(增量)`);
1235
+ }
1236
+ else {
1237
+ // 文件不存在:全量写入
1238
+ const lines = [
1239
+ `# Scaffold Manifest`,
1240
+ ``,
1241
+ `> 由 \`playcraft skills scaffold\` 自动生成,记录本次 scaffold 的文件来源。`,
1242
+ `> 生成时间:${now}`,
1243
+ `> 引擎:${engine}`,
1244
+ ``,
1245
+ `---`,
1246
+ ``,
1247
+ ];
1248
+ for (const { atomId, files } of manifestData) {
1249
+ lines.push(renderAtomSection(atomId, files));
1250
+ lines.push(``);
1251
+ }
1252
+ lines.push(`---`);
1253
+ lines.push(``);
1254
+ lines.push(`*Developer:在 Step 0 摄入阶段请先阅读本文件,了解各资源来自哪个 skill atom,再通过 \`playcraft skills read <atomId>\` 读取对应 SKILL.md 理解绑定方式。*`);
1255
+ lines.push(``);
1256
+ fs.writeFileSync(manifestPath, lines.join('\n'), 'utf-8');
1257
+ console.log(`\n[manifest] 已生成 docs/scaffold-manifest.md`);
1258
+ }
1259
+ }
1260
+ catch (e) {
1261
+ console.warn(`[warn] scaffold-manifest.md 写入失败: ${e.message}`);
1262
+ }
1263
+ }
1006
1264
  }
1007
1265
  // ─── 项目内 DAG Skill 软连接(IDE 自动发现)────────────────────────────────────
1008
- const ATOM_SKILL_LINK_SUFFIXES = [
1009
- '.aigameplay',
1010
- '.aicomponent',
1011
- '.aiconfig',
1012
- '.aiimage',
1013
- '.aiaudio',
1014
- '.aivalidator',
1015
- ];
1016
1266
  /** 模板内静态 Skill 目录名前缀,禁止被 link/prune 覆盖 */
1017
1267
  const PROTECTED_SKILL_PREFIXES = ['playcraft-'];
1018
- function isAtomSkillLinkName(name) {
1019
- return ATOM_SKILL_LINK_SUFFIXES.some((suffix) => name.endsWith(suffix));
1020
- }
1021
1268
  function isProtectedSkillDirName(name) {
1022
1269
  return PROTECTED_SKILL_PREFIXES.some((prefix) => name.startsWith(prefix));
1023
1270
  }
1024
- /**
1025
- * 从 atom-plan.md 的 Atom List 表解析非空的 skillRef 列。
1026
- * 仅解析 `## Atom List` 与下一个 `##` 之间的 Markdown 表格。
1027
- */
1028
- export function parseSkillRefsFromAtomPlan(content) {
1029
- const lines = content.split('\n');
1030
- let inAtomList = false;
1031
- let skillRefIdx = -1;
1032
- const refs = new Set();
1033
- for (const line of lines) {
1034
- if (/^##\s+Atom List\b/i.test(line)) {
1035
- inAtomList = true;
1036
- skillRefIdx = -1;
1037
- continue;
1038
- }
1039
- if (inAtomList && /^##\s+/.test(line))
1040
- break;
1041
- if (!inAtomList || !line.trim().startsWith('|'))
1042
- continue;
1043
- const cols = line
1044
- .split('|')
1045
- .slice(1, -1)
1046
- .map((c) => c.trim());
1047
- if (cols.length === 0)
1048
- continue;
1049
- if (skillRefIdx < 0) {
1050
- if (cols.includes('skillRef')) {
1051
- skillRefIdx = cols.indexOf('skillRef');
1052
- }
1053
- continue;
1054
- }
1055
- if (cols.every((c) => /^:?-+:?$/.test(c) || c === ''))
1056
- continue;
1057
- if (cols.includes('atomId') && cols.includes('assignTo'))
1058
- continue;
1059
- const ref = cols[skillRefIdx];
1060
- if (!ref || ref === '—' || ref === '-' || ref.startsWith('{{'))
1061
- continue;
1062
- refs.add(ref);
1063
- }
1064
- return [...refs];
1065
- }
1066
1271
  export async function linkAtomSkillsToProject(options) {
1067
1272
  const projectDir = path.resolve(options.projectDir);
1068
1273
  const skillsLinkDir = path.join(projectDir, '.claude', 'skills');
@@ -1122,13 +1327,28 @@ export async function linkAtomSkillsToProject(options) {
1122
1327
  await fs.promises.symlink(relTarget, linkPath, 'dir');
1123
1328
  linked.push(atomId);
1124
1329
  }
1330
+ const manifestPath = path.join(skillsLinkDir, '.playcraft-dag-links.json');
1125
1331
  const pruned = [];
1126
1332
  if (options.prune && fs.existsSync(skillsLinkDir)) {
1127
1333
  const children = await fs.promises.readdir(skillsLinkDir, { withFileTypes: true });
1334
+ let knownLinkedIds = new Set();
1335
+ if (fs.existsSync(manifestPath)) {
1336
+ try {
1337
+ const prev = JSON.parse(await fs.promises.readFile(manifestPath, 'utf-8'));
1338
+ knownLinkedIds = new Set((prev.atomIds ?? []).filter(Boolean));
1339
+ }
1340
+ catch {
1341
+ knownLinkedIds = new Set();
1342
+ }
1343
+ }
1128
1344
  for (const child of children) {
1129
- if (!child.isSymbolicLink() || !isAtomSkillLinkName(child.name))
1345
+ if (!child.isSymbolicLink())
1346
+ continue;
1347
+ if (isProtectedSkillDirName(child.name))
1348
+ continue;
1349
+ if (targetIds.has(child.name))
1130
1350
  continue;
1131
- if (targetIds.has(child.name) || isProtectedSkillDirName(child.name))
1351
+ if (knownLinkedIds.size > 0 && !knownLinkedIds.has(child.name))
1132
1352
  continue;
1133
1353
  const linkPath = path.join(skillsLinkDir, child.name);
1134
1354
  if (options.dryRun) {
@@ -1151,6 +1371,31 @@ export async function linkAtomSkillsToProject(options) {
1151
1371
  }
1152
1372
  return { linked, skipped, pruned, missing };
1153
1373
  }
1374
+ /**
1375
+ * Convenience wrapper: read meta.selectedAtoms from docs/atom-tree.json and link
1376
+ * them into the project's .claude/skills/ (prune stale by default). Used as a
1377
+ * fallback inside init-template so a project is never left with selected-but-unlinked
1378
+ * atoms. Returns the link result plus the atom ids it acted on.
1379
+ */
1380
+ export async function autoLinkSkillsFromAtomTree(projectDir, opts = {}) {
1381
+ const atomIds = parseSelectedAtomRefsFromAtomTreeProject(projectDir);
1382
+ if (atomIds.length === 0) {
1383
+ return { linked: [], skipped: [], pruned: [], missing: [], atomIds: [] };
1384
+ }
1385
+ const config = await loadConfig({}).catch(() => ({ skillsDir: undefined }));
1386
+ const dirs = resolveSkillsDirs(opts.skillsDir, config.skillsDir);
1387
+ if (dirs.length === 0) {
1388
+ return { linked: [], skipped: [], pruned: [], missing: atomIds, atomIds };
1389
+ }
1390
+ const entries = loadSkillIndex(dirs);
1391
+ const result = await linkAtomSkillsToProject({
1392
+ projectDir,
1393
+ atomIds,
1394
+ entries,
1395
+ prune: opts.prune ?? true,
1396
+ });
1397
+ return { ...result, atomIds };
1398
+ }
1154
1399
  // ─── CLI 注册 ────────────────────────────────────────────────────────────────
1155
1400
  export function registerSkillsCommands(program) {
1156
1401
  const skills = program
@@ -1259,11 +1504,15 @@ export function registerSkillsCommands(program) {
1259
1504
  }
1260
1505
  const entries = loadSkillIndex(dirs);
1261
1506
  const atomIds = opts.atoms.split(',').map((s) => s.trim()).filter(Boolean);
1507
+ // 修复 Windows 路径中因 shell/Agent 转义丢失的路径分隔符:
1508
+ // D:\project\playcraft-tencent\test19\game → 反斜杠被 shell 消费后变成 D:projectplaycraft-tencenttest19game
1509
+ // 这里尝试恢复为 D:/project/playcraft-tencent/test19/game
1510
+ const sanitizedOut = sanitizeWindowsPath(opts.out);
1262
1511
  runScaffold({
1263
1512
  entries,
1264
1513
  atomIds,
1265
1514
  engine: opts.engine,
1266
- outDir: path.resolve(opts.out),
1515
+ outDir: path.resolve(sanitizedOut),
1267
1516
  dryRun: !!opts.dryRun,
1268
1517
  force: !!opts.force,
1269
1518
  });
@@ -1273,7 +1522,7 @@ export function registerSkillsCommands(program) {
1273
1522
  .command('link')
1274
1523
  .description('将 DAG Atom Skill 软连接到 .claude/skills/<atomId>/;下游优先读项目内路径,playcraft skills read 仅作断链兜底(不替代 scaffold)')
1275
1524
  .option('--atoms <ids>', 'atomId 列表,逗号分隔')
1276
- .option('--from-atom-plan', '从 docs/atom-plan.md 的 Atom List 解析 skillRef 列')
1525
+ .option('--from-atom-tree', '从 docs/atom-tree.json 的 meta.selectedAtoms 读取当次参考 atom 列表')
1277
1526
  .option('--project-dir <path>', '项目根目录', process.cwd())
1278
1527
  .option('--prune', '移除 .claude/skills/ 下不在本次列表中的 DAG 软连接')
1279
1528
  .option('--dry-run', '只打印将创建/删除的软连接')
@@ -1286,10 +1535,10 @@ export function registerSkillsCommands(program) {
1286
1535
  }
1287
1536
  const entries = loadSkillIndex(dirs);
1288
1537
  let atomIds = [];
1289
- if (opts.fromAtomPlan) {
1290
- atomIds = parseSkillRefsFromAtomPlanProject(opts.projectDir);
1538
+ if (opts.fromAtomTree) {
1539
+ atomIds = parseSelectedAtomRefsFromAtomTreeProject(opts.projectDir);
1291
1540
  if (atomIds.length === 0) {
1292
- console.error('未找到 docs/atom-plan.json(或 atom-plan.md)中的非空 skillRef');
1541
+ console.error('未找到 docs/atom-tree.json 的非空 meta.selectedAtoms');
1293
1542
  process.exit(1);
1294
1543
  }
1295
1544
  }
@@ -1298,7 +1547,7 @@ export function registerSkillsCommands(program) {
1298
1547
  }
1299
1548
  atomIds = [...new Set(atomIds)];
1300
1549
  if (atomIds.length === 0) {
1301
- console.error('请指定 --atoms <id1,id2> 或 --from-atom-plan(且 atom-plan 中含非空 skillRef)');
1550
+ console.error('请指定 --atoms <id1,id2> 或 --from-atom-tree(且 atom-tree 中含非空 meta.selectedAtoms)');
1302
1551
  process.exit(1);
1303
1552
  }
1304
1553
  const result = await linkAtomSkillsToProject({
@@ -1321,29 +1570,27 @@ export function registerSkillsCommands(program) {
1321
1570
  if (result.missing.length > 0)
1322
1571
  process.exit(1);
1323
1572
  });
1324
- // ── validate-atom-plan ────────────────────────────────────────────────────
1573
+ // ── validate-atom-tree-selection ──────────────────────────────────────────
1325
1574
  addSkillsDirOption(skills
1326
- .command('validate-atom-plan')
1327
- .description('校验 docs/atom-plan.json 中 skillRef 均存在于 skills 库,且落在 skillsMatch.items 内')
1575
+ .command('validate-atom-tree-selection')
1576
+ .description('校验 docs/atom-tree.json 的 meta.selectedAtoms 是否存在、匹配并满足软槽位约束')
1328
1577
  .option('--project-dir <path>', '项目根目录', process.cwd())
1329
- .option('--allow-markdown', '若无 JSON 则回退校验 atom-plan.md(不推荐)')
1330
1578
  .option('--json', '输出 JSON 格式')).action(async (opts) => {
1331
- const result = await validateAtomPlanProject({
1579
+ const result = await validateAtomTreeSelectionProject({
1332
1580
  projectDir: opts.projectDir,
1333
1581
  skillsDir: opts.skillsDir,
1334
- preferJson: !opts.allowMarkdown,
1335
1582
  });
1336
1583
  if (opts.json) {
1337
1584
  process.stdout.write(JSON.stringify(result, null, 2) + '\n');
1338
1585
  }
1339
1586
  else if (result.ok) {
1340
- console.log(`\n✓ atom-plan 校验通过(${result.source},${result.skillRefsChecked.length} 个 skillRef)`);
1587
+ console.log(`\n✓ atom-tree selection 校验通过(${result.selectedAtomRefsChecked.length} 个 selected atom refs)`);
1341
1588
  for (const w of result.warnings)
1342
1589
  console.log(` ⚠ ${w}`);
1343
1590
  console.log();
1344
1591
  }
1345
1592
  else {
1346
- console.error(`\n✗ atom-plan 校验失败(${result.source})\n`);
1593
+ console.error('\n✗ atom-tree selection 校验失败\n');
1347
1594
  for (const e of result.errors)
1348
1595
  console.error(` - ${e}`);
1349
1596
  for (const w of result.warnings)
@@ -1400,4 +1647,533 @@ export function registerSkillsCommands(program) {
1400
1647
  }
1401
1648
  process.stdout.write(fs.readFileSync(skillMdPath, 'utf-8'));
1402
1649
  });
1650
+ // ── inspect ───────────────────────────────────────────────────────────────
1651
+ addSkillsDirOption(skills
1652
+ .command('inspect <atomId>')
1653
+ .description('输出 skill 的文件清单:scaffold 目标路径 + files 逻辑索引 + 各文件用途描述')
1654
+ .option('--json', '输出 JSON 格式')).action(async (atomId, opts) => {
1655
+ const config = await loadConfig({}).catch(() => ({ skillsDir: undefined }));
1656
+ const dirs = resolveSkillsDirs(opts.skillsDir, config.skillsDir);
1657
+ if (dirs.length === 0) {
1658
+ console.error('未找到 skills 目录。请通过 --skills-dir 指定、在 playcraft.config.json 中配置 skillsDir,或设置 AGENT_SKILLS_PATHS 环境变量。');
1659
+ process.exit(1);
1660
+ }
1661
+ const entries = loadSkillIndex(dirs);
1662
+ const entry = entries.find((e) => e.atomId === atomId);
1663
+ if (!entry) {
1664
+ console.error(`未找到 skill: ${atomId}`);
1665
+ console.error(`可用 skill 列表:playcraft skills list`);
1666
+ process.exit(1);
1667
+ }
1668
+ const { manifest, dir } = entry;
1669
+ const scaffoldRows = [];
1670
+ if (manifest.scaffold?.files) {
1671
+ for (const [dest, v] of Object.entries(manifest.scaffold.files)) {
1672
+ const contribution = v;
1673
+ if (!contribution.source)
1674
+ continue;
1675
+ const srcAbs = path.join(dir, contribution.source);
1676
+ // 目录项:检查目录是否存在
1677
+ const exists = contribution.source.endsWith('/')
1678
+ ? fs.existsSync(srcAbs)
1679
+ : fs.existsSync(srcAbs);
1680
+ scaffoldRows.push({
1681
+ dest,
1682
+ source: contribution.source,
1683
+ mode: contribution.mode ?? 'full',
1684
+ exists,
1685
+ });
1686
+ }
1687
+ }
1688
+ const fileRows = [];
1689
+ if (Array.isArray(manifest.files)) {
1690
+ for (const item of manifest.files) {
1691
+ const absPath = path.join(dir, item.path);
1692
+ fileRows.push({
1693
+ path: item.path,
1694
+ description: item.description ?? '',
1695
+ exists: fs.existsSync(absPath),
1696
+ });
1697
+ }
1698
+ }
1699
+ // ── JSON 输出 ──────────────────────────────────────────────────────────
1700
+ if (opts.json) {
1701
+ process.stdout.write(JSON.stringify({
1702
+ atomId,
1703
+ label: manifest.label,
1704
+ bundleType: manifest.bundleType,
1705
+ scaffoldFiles: scaffoldRows,
1706
+ files: fileRows,
1707
+ }, null, 2) + '\n');
1708
+ return;
1709
+ }
1710
+ // ── 人读输出 ──────────────────────────────────────────────────────────
1711
+ console.log(`\n${atomId} ${manifest.label.zh} / ${manifest.label.en}`);
1712
+ console.log(`bundleType: ${manifest.bundleType} category: ${manifest.category}\n`);
1713
+ if (scaffoldRows.length > 0) {
1714
+ console.log('── Scaffold 文件(playcraft skills scaffold 会复制这些)────────────────');
1715
+ const maxDest = Math.max(...scaffoldRows.map((r) => r.dest.length), 4);
1716
+ const maxSrc = Math.max(...scaffoldRows.map((r) => r.source.length), 6);
1717
+ console.log(` ${'dest'.padEnd(maxDest)} ${'source'.padEnd(maxSrc)} mode`);
1718
+ console.log(` ${'─'.repeat(maxDest)} ${'─'.repeat(maxSrc)} ────`);
1719
+ for (const r of scaffoldRows) {
1720
+ const missing = r.exists ? '' : ' ⚠ 源文件缺失';
1721
+ console.log(` ${r.dest.padEnd(maxDest)} ${r.source.padEnd(maxSrc)} ${r.mode}${missing}`);
1722
+ }
1723
+ console.log();
1724
+ }
1725
+ else {
1726
+ console.log('── Scaffold 文件:(无)────────────────\n');
1727
+ }
1728
+ if (fileRows.length > 0) {
1729
+ console.log('── Files(ref/ 内容清单 + 用途说明)────────────────────────────────────');
1730
+ for (const r of fileRows) {
1731
+ const missing = r.exists ? '' : ' ⚠ 文件缺失';
1732
+ console.log(` ${r.path}${missing}`);
1733
+ if (r.description) {
1734
+ const lines = r.description.match(/.{1,80}/g) ?? [r.description];
1735
+ for (const line of lines)
1736
+ console.log(` ${line}`);
1737
+ }
1738
+ }
1739
+ console.log();
1740
+ }
1741
+ else {
1742
+ console.log('── Files:(无)────────────────\n');
1743
+ }
1744
+ });
1745
+ // ── gettags ───────────────────────────────────────────────────────────────
1746
+ addSkillsDirOption(skills
1747
+ .command('gettags')
1748
+ .description('列出所有 skill 的 tags(去重)')
1749
+ .option('--category <category>', '按 category 筛选(逗号分隔)')
1750
+ .option('--json', '输出 JSON 格式')).action(async (opts) => {
1751
+ const config = await loadConfig({}).catch(() => ({ skillsDir: undefined }));
1752
+ const dirs = resolveSkillsDirs(opts.skillsDir, config.skillsDir);
1753
+ if (dirs.length === 0) {
1754
+ console.error('未找到 skills 目录。请通过 --skills-dir 指定、在 playcraft.config.json 中配置 skillsDir,或设置 AGENT_SKILLS_PATHS 环境变量。');
1755
+ process.exit(1);
1756
+ }
1757
+ const filterCategories = opts.category?.split(',').map((s) => s.trim().toLowerCase()).filter(Boolean) ?? [];
1758
+ let entries = loadSkillIndex(dirs);
1759
+ if (filterCategories.length > 0) {
1760
+ entries = entries.filter((e) => filterCategories.includes(e.manifest.category.toLowerCase()));
1761
+ }
1762
+ const tags = [...new Set(entries.flatMap((e) => e.manifest.tags ?? []))].sort();
1763
+ if (opts.json) {
1764
+ process.stdout.write(JSON.stringify(tags) + '\n');
1765
+ return;
1766
+ }
1767
+ process.stdout.write(tags.join(' ') + '\n');
1768
+ });
1769
+ // ── getkeywords ───────────────────────────────────────────────────────────
1770
+ addSkillsDirOption(skills
1771
+ .command('getkeywords')
1772
+ .description('列出所有 skill 的 keywords(去重)')
1773
+ .option('--category <category>', '按 category 筛选(逗号分隔)')
1774
+ .option('--json', '输出 JSON 格式')).action(async (opts) => {
1775
+ const config = await loadConfig({}).catch(() => ({ skillsDir: undefined }));
1776
+ const dirs = resolveSkillsDirs(opts.skillsDir, config.skillsDir);
1777
+ if (dirs.length === 0) {
1778
+ console.error('未找到 skills 目录。请通过 --skills-dir 指定、在 playcraft.config.json 中配置 skillsDir,或设置 AGENT_SKILLS_PATHS 环境变量。');
1779
+ process.exit(1);
1780
+ }
1781
+ const filterCategories = opts.category?.split(',').map((s) => s.trim().toLowerCase()).filter(Boolean) ?? [];
1782
+ let entries = loadSkillIndex(dirs);
1783
+ if (filterCategories.length > 0) {
1784
+ entries = entries.filter((e) => filterCategories.includes(e.manifest.category.toLowerCase()));
1785
+ }
1786
+ const keywords = [...new Set(entries.flatMap((e) => e.manifest.keywords ?? []))].sort();
1787
+ if (opts.json) {
1788
+ process.stdout.write(JSON.stringify(keywords) + ',');
1789
+ return;
1790
+ }
1791
+ process.stdout.write(keywords.join(' ') + ',');
1792
+ });
1793
+ // ── filter ────────────────────────────────────────────────────────────────
1794
+ addSkillsDirOption(skills
1795
+ .command('filter')
1796
+ .description('按 tags / keywords / category / role 筛选 skill')
1797
+ .option('--tags <tags>', '按 tags 筛选(逗号分隔)')
1798
+ .option('--keywords <keywords>', '按 keywords 筛选(逗号分隔)')
1799
+ .option('--category <category>', '按 category 筛选(逗号分隔)')
1800
+ .option('--role <roles>', '按 bindingRoles 筛选(逗号分隔)')
1801
+ .option('--and', '维度内使用 AND(默认 OR,如 --tags a,b 命中含 a 或 b 的 skill)')
1802
+ .option('--json', '输出 JSON 格式')).action(async (opts) => {
1803
+ const config = await loadConfig({}).catch(() => ({ skillsDir: undefined }));
1804
+ const dirs = resolveSkillsDirs(opts.skillsDir, config.skillsDir);
1805
+ if (dirs.length === 0) {
1806
+ console.error('未找到 skills 目录。请通过 --skills-dir 指定、在 playcraft.config.json 中配置 skillsDir,或设置 AGENT_SKILLS_PATHS 环境变量。');
1807
+ process.exit(1);
1808
+ }
1809
+ const filterTags = opts.tags?.split(',').map((s) => s.trim()).filter(Boolean) ?? [];
1810
+ const filterKeywords = opts.keywords?.split(',').map((s) => s.trim()).filter(Boolean) ?? [];
1811
+ const filterCategories = opts.category?.split(',').map((s) => s.trim().toLowerCase()).filter(Boolean) ?? [];
1812
+ const filterRoles = opts.role?.split(',').map((s) => s.trim()).filter(Boolean) ?? [];
1813
+ if (filterTags.length === 0 && filterKeywords.length === 0 && filterCategories.length === 0 && filterRoles.length === 0) {
1814
+ console.error('请至少指定一个筛选条件:--tags、--keywords、--category 或 --role');
1815
+ process.exit(1);
1816
+ }
1817
+ const entries = loadSkillIndex(dirs);
1818
+ const useAnd = !!opts.and;
1819
+ const matched = entries.filter((entry) => {
1820
+ const manifest = entry.manifest;
1821
+ const entryTags = manifest.tags ?? [];
1822
+ const entryKeywords = manifest.keywords ?? [];
1823
+ const entryCategory = manifest.category.toLowerCase();
1824
+ const entryRoles = manifest.bindingRoles ?? [];
1825
+ const conditions = [];
1826
+ if (filterTags.length > 0) {
1827
+ conditions.push(useAnd
1828
+ ? filterTags.every((t) => entryTags.includes(t))
1829
+ : filterTags.some((t) => entryTags.includes(t)));
1830
+ }
1831
+ if (filterKeywords.length > 0) {
1832
+ conditions.push(useAnd
1833
+ ? filterKeywords.every((kw) => entryKeywords.includes(kw))
1834
+ : filterKeywords.some((kw) => entryKeywords.includes(kw)));
1835
+ }
1836
+ if (filterCategories.length > 0) {
1837
+ conditions.push(useAnd
1838
+ ? filterCategories.every((c) => entryCategory === c)
1839
+ : filterCategories.includes(entryCategory));
1840
+ }
1841
+ if (filterRoles.length > 0) {
1842
+ conditions.push(useAnd
1843
+ ? filterRoles.every((r) => entryRoles.includes(r))
1844
+ : filterRoles.some((r) => entryRoles.includes(r)));
1845
+ }
1846
+ return conditions.every(Boolean);
1847
+ });
1848
+ if (opts.json) {
1849
+ const output = matched.map((e) => {
1850
+ const m = e.manifest;
1851
+ const gen = m['generation'];
1852
+ return {
1853
+ atomId: e.atomId,
1854
+ category: m.category,
1855
+ bundleType: m.bundleType ?? null,
1856
+ mode: m.mode ?? null,
1857
+ label: m.label,
1858
+ tags: m.tags ?? [],
1859
+ bindingRoles: m.bindingRoles ?? [],
1860
+ gameplayType: gen?.gameplayType ?? null,
1861
+ };
1862
+ });
1863
+ process.stdout.write(JSON.stringify(output) + '\n');
1864
+ return;
1865
+ }
1866
+ for (const entry of matched) {
1867
+ const label = entry.manifest.label.zh || entry.manifest.label.en;
1868
+ console.log(`[${entry.manifest.category}] ${entry.atomId} — ${label}`);
1869
+ }
1870
+ });
1871
+ // ── init-atom-tree ─────────────────────────────────────────────────────────
1872
+ addSkillsDirOption(skills
1873
+ .command('init-atom-tree')
1874
+ .description('初始化 docs/atom-tree.json(运行 match 填充 meta.skillsMatch,meta.selectedAtoms 待追加)')
1875
+ .requiredOption('--intent <tags>', '意图关键词,逗号或空格分隔(如 mahjong,tile,match 或 "match3 swap gem")')
1876
+ .option('--engine <name>', '目标引擎(phaser / threejs);省略则从 intent 推断')
1877
+ .option('--project-dir <path>', '项目根目录', process.cwd())
1878
+ .option('--force', '覆盖已存在的 atom-tree.json')).action(async (opts) => {
1879
+ const config = await loadConfig({}).catch(() => ({ skillsDir: undefined }));
1880
+ const dirs = resolveSkillsDirs(opts.skillsDir, config.skillsDir);
1881
+ if (dirs.length === 0) {
1882
+ console.error('未找到 skills 目录。请通过 --skills-dir 指定、在 playcraft.config.json 中配置 skillsDir,或设置 AGENT_SKILLS_PATHS 环境变量。');
1883
+ process.exit(1);
1884
+ }
1885
+ const entries = loadSkillIndex(dirs);
1886
+ const intentTags = opts.intent
1887
+ .split(/[,\s]+/)
1888
+ .map((t) => t.trim())
1889
+ .filter(Boolean);
1890
+ if (intentTags.length === 0) {
1891
+ console.log('--intent 不能为空。示例:--intent "match3,swap,gem" 或 --intent "match3 swap gem"');
1892
+ console.log('若 atom-tree.json 已存在:playcraft skills init-atom-tree --intent "..." --engine phaser --force');
1893
+ process.exit(1);
1894
+ }
1895
+ const projectDir = path.resolve(opts.projectDir);
1896
+ const docsDir = path.join(projectDir, 'docs');
1897
+ const outputPath = path.join(docsDir, 'atom-tree.json');
1898
+ if (fs.existsSync(outputPath) && !opts.force) {
1899
+ try {
1900
+ const existing = parseAtomTreeJson(fs.readFileSync(outputPath, 'utf-8'));
1901
+ if (!isUninitializedAtomTreeSkeleton(existing)) {
1902
+ console.log(`${outputPath} 已存在且含用户选择。`);
1903
+ console.log('下一步:playcraft skills select-atom --id "<atom-ref>" 或 init-atom-tree --force 覆盖');
1904
+ process.exit(1);
1905
+ }
1906
+ }
1907
+ catch {
1908
+ console.log(`${outputPath} 已存在但无法解析 JSON。使用 --force 覆盖。`);
1909
+ process.exit(1);
1910
+ }
1911
+ }
1912
+ const result = opts.engine
1913
+ ? buildMatchResult(entries, opts.engine, intentTags)
1914
+ : buildAutoMatchResult(entries, intentTags);
1915
+ if (!result.engine) {
1916
+ console.log('无法从 intent 推断运行引擎。请指定:--engine phaser 或 --engine threejs');
1917
+ console.log(`示例:playcraft skills init-atom-tree --intent "${intentTags.join(',')}" --engine phaser --force`);
1918
+ process.exit(1);
1919
+ }
1920
+ const skillsMatch = {
1921
+ engine: result.engine,
1922
+ engineSource: result.engineSource,
1923
+ intent: result.intent,
1924
+ items: result.items.map((i) => ({
1925
+ atomId: i.atomId,
1926
+ label: i.label,
1927
+ layer: i.layer,
1928
+ reason: i.reason,
1929
+ roles: i.roles,
1930
+ hasEngineVariant: i.hasEngineVariant,
1931
+ })),
1932
+ mediaGroups: result.mediaGroups.map((g) => ({
1933
+ role: g.role,
1934
+ label: g.label,
1935
+ layer: g.layer,
1936
+ candidates: g.candidates.map((c) => ({ atomId: c.atomId })),
1937
+ })),
1938
+ ...(result.fusionHints?.length ? { fusionHints: result.fusionHints } : {}),
1939
+ };
1940
+ const tree = buildAtomTreeSkeleton({
1941
+ engine: result.engine,
1942
+ engineSource: result.engineSource,
1943
+ intent: intentTags,
1944
+ skillsMatch,
1945
+ ringsRing1Only: true,
1946
+ selectionRevision: {
1947
+ changes: 'Initial skeleton from playcraft skills init-atom-tree',
1948
+ author: 'cli',
1949
+ },
1950
+ });
1951
+ fs.mkdirSync(docsDir, { recursive: true });
1952
+ fs.writeFileSync(outputPath, JSON.stringify(tree, null, 2) + '\n');
1953
+ console.log(`\n✓ ${outputPath} 已生成`);
1954
+ console.log(` meta.skillsMatch: ${result.items.length} items + ${result.mediaGroups.length} mediaGroups`);
1955
+ if (result.fusionHints?.length) {
1956
+ console.log(` meta.skillsMatch.fusionHints: ${result.fusionHints.length} pair(s)`);
1957
+ for (const hint of result.fusionHints) {
1958
+ console.log(` - ${hint.from} × ${hint.to} → ${hint.recommendedType} (${hint.risk} risk${hint.preferPseudoFusion ? ', prefer pseudo-fusion' : ''})`);
1959
+ }
1960
+ }
1961
+ console.log(' meta.selectedAtoms: [] (使用 playcraft skills select-atom 逐条追加 selected atom ref)\n');
1962
+ console.log('下一步:');
1963
+ console.log(' playcraft remix init-template --from-atom-tree');
1964
+ console.log(' npm install && npm run verify:contract');
1965
+ console.log(' playcraft skills select-atom --id "<selected-atom-ref>" # 逗号分隔可批量:a,b,c');
1966
+ console.log(' playcraft skills validate-atom-tree-selection');
1967
+ console.log(' playcraft skills link --from-atom-tree --prune\n');
1968
+ });
1969
+ // ── select-atom ────────────────────────────────────────────────────────────
1970
+ addSkillsDirOption(skills
1971
+ .command('select-atom')
1972
+ .description('向 docs/atom-tree.json 的 meta.selectedAtoms 追加 selected atom ref(支持逗号批量),并自动补齐硬依赖')
1973
+ .requiredOption('--id <atomId>', '要加入 meta.selectedAtoms 的 atom ref(逗号分隔可批量,如 phaser.aicomponent,match3_core.aigameplay)')
1974
+ .option('--project-dir <path>', '项目根目录', process.cwd())).action(async (opts) => {
1975
+ const config = await loadConfig({}).catch(() => ({ skillsDir: undefined }));
1976
+ const dirs = resolveSkillsDirs(opts.skillsDir, config.skillsDir);
1977
+ if (dirs.length === 0) {
1978
+ console.error('未找到 skills 目录。请通过 --skills-dir 指定、在 playcraft.config.json 中配置 skillsDir,或设置 AGENT_SKILLS_PATHS 环境变量。');
1979
+ process.exit(1);
1980
+ }
1981
+ const manifestIndex = loadSkillManifestIndex(dirs);
1982
+ const projectDir = path.resolve(opts.projectDir);
1983
+ const jsonPath = path.join(projectDir, 'docs', 'atom-tree.json');
1984
+ if (!fs.existsSync(jsonPath)) {
1985
+ console.error(`${jsonPath} 不存在。先运行 playcraft skills init-atom-tree。`);
1986
+ process.exit(1);
1987
+ }
1988
+ const tree = parseAtomTreeJson(fs.readFileSync(jsonPath, 'utf-8'));
1989
+ const atomRefs = dedupeAtomRefs(opts.id
1990
+ .split(',')
1991
+ .map((s) => s.trim())
1992
+ .filter(Boolean));
1993
+ if (atomRefs.length === 0) {
1994
+ console.error('atom ref 不能为空。示例:--id "phaser.aicomponent,match3_core.aigameplay"');
1995
+ process.exit(1);
1996
+ }
1997
+ tree.meta.selectedAtoms = dedupeAtomRefs(tree.meta?.selectedAtoms ?? []);
1998
+ const itemIds = dedupeAtomRefs((tree.meta?.skillsMatch?.items ?? []).map((i) => i.atomId));
1999
+ const notInMatch = atomRefs.filter((ref) => itemIds.length > 0 && !itemIds.includes(ref));
2000
+ const toSelect = atomRefs.filter((ref) => !notInMatch.includes(ref));
2001
+ if (notInMatch.length > 0) {
2002
+ console.log(`跳过不在 meta.skillsMatch.items 中的 atom refs: ${notInMatch.join(', ')}`);
2003
+ if (itemIds.length > 0) {
2004
+ console.log(`可用的 atom refs: ${itemIds.join(', ')}`);
2005
+ }
2006
+ }
2007
+ if (toSelect.length === 0) {
2008
+ process.exit(1);
2009
+ }
2010
+ const engine = tree.meta?.skillsMatch?.engine?.trim();
2011
+ if (!engine) {
2012
+ console.log('docs/atom-tree.json 缺少 meta.skillsMatch.engine。');
2013
+ console.log('下一步:playcraft skills init-atom-tree --intent "..." --engine phaser --force');
2014
+ process.exit(1);
2015
+ }
2016
+ let totalAppended = 0;
2017
+ for (const atomRef of toSelect) {
2018
+ if (tree.meta.selectedAtoms.includes(atomRef)) {
2019
+ console.log(`已存在,跳过: ${atomRef}`);
2020
+ continue;
2021
+ }
2022
+ if (!manifestIndex.has(atomRef)) {
2023
+ console.log(`未找到 skill manifest,跳过: ${atomRef}`);
2024
+ continue;
2025
+ }
2026
+ const importedAtomIds = dedupeAtomRefs(collectPinnedDependencies(atomRef, manifestIndex, engine));
2027
+ const missingImportedAtomIds = importedAtomIds.filter((importedAtomId) => !manifestIndex.has(importedAtomId));
2028
+ if (missingImportedAtomIds.length > 0) {
2029
+ console.log(`跳过 ${atomRef}:import 依赖未找到 ${missingImportedAtomIds.join(', ')}`);
2030
+ continue;
2031
+ }
2032
+ const currentSelected = tree.meta.selectedAtoms;
2033
+ const nextAtoms = dedupeAtomRefs([...currentSelected, atomRef, ...importedAtomIds]);
2034
+ const appendedAtomIds = nextAtoms.filter((id) => !currentSelected.includes(id));
2035
+ tree.meta.selectedAtoms = nextAtoms;
2036
+ totalAppended += appendedAtomIds.length;
2037
+ console.log(`✓ 已添加 selected atom ref "${atomRef}"`);
2038
+ if (importedAtomIds.length > 0) {
2039
+ console.log(` 自动补齐硬依赖 imports: ${importedAtomIds.join(', ')}`);
2040
+ }
2041
+ console.log(` 本次新增 ${appendedAtomIds.length} 条:${appendedAtomIds.join(', ')}`);
2042
+ }
2043
+ if (totalAppended === 0) {
2044
+ process.exit(1);
2045
+ }
2046
+ fs.writeFileSync(jsonPath, JSON.stringify(tree, null, 2) + '\n');
2047
+ console.log(` meta.selectedAtoms 当前共 ${tree.meta.selectedAtoms.length} 条`);
2048
+ });
2049
+ // ── replace-selected-atom ─────────────────────────────────────────────────
2050
+ skills
2051
+ .command('replace-selected-atom')
2052
+ .description('替换 docs/atom-tree.json meta.selectedAtoms 中已有的 selected atom ref')
2053
+ .requiredOption('--id <atomId>', '当前的 atom ref')
2054
+ .requiredOption('--to <atomId>', '替换后的 atom ref')
2055
+ .option('--project-dir <path>', '项目根目录', process.cwd())
2056
+ .action((opts) => {
2057
+ const projectDir = path.resolve(opts.projectDir);
2058
+ const jsonPath = path.join(projectDir, 'docs', 'atom-tree.json');
2059
+ if (!fs.existsSync(jsonPath)) {
2060
+ console.error(`${jsonPath} 不存在。先运行 playcraft skills init-atom-tree。`);
2061
+ process.exit(1);
2062
+ }
2063
+ const tree = parseAtomTreeJson(fs.readFileSync(jsonPath, 'utf-8'));
2064
+ const fromRef = opts.id.trim();
2065
+ const toRef = opts.to.trim();
2066
+ if (!fromRef || !toRef) {
2067
+ console.error('当前 atom ref 与替换后的 atom ref 都不能为空。');
2068
+ process.exit(1);
2069
+ }
2070
+ tree.meta.selectedAtoms ??= [];
2071
+ const idx = tree.meta.selectedAtoms.findIndex((atom) => atom === fromRef);
2072
+ if (idx === -1) {
2073
+ console.error(`atom ref "${fromRef}" 不存在于 meta.selectedAtoms 中。`);
2074
+ console.error(`现有 selectedAtoms: ${tree.meta.selectedAtoms.join(', ')}`);
2075
+ process.exit(1);
2076
+ }
2077
+ if (fromRef !== toRef && tree.meta.selectedAtoms.includes(toRef)) {
2078
+ console.error(`atom ref "${toRef}" 已存在于 meta.selectedAtoms 中。`);
2079
+ process.exit(1);
2080
+ }
2081
+ const itemIds = (tree.meta?.skillsMatch?.items ?? []).map((i) => i.atomId);
2082
+ if (itemIds.length > 0 && !itemIds.includes(toRef)) {
2083
+ console.error(`atom ref "${toRef}" 不在 meta.skillsMatch.items 中。`);
2084
+ console.error(`可用的 atom refs: ${itemIds.join(', ')}`);
2085
+ process.exit(1);
2086
+ }
2087
+ tree.meta.selectedAtoms[idx] = toRef;
2088
+ fs.writeFileSync(jsonPath, JSON.stringify(tree, null, 2) + '\n');
2089
+ console.log(`✓ 已更新 selected atom ref "${fromRef}" → "${toRef}"`);
2090
+ });
2091
+ // ── unselect-atom ──────────────────────────────────────────────────────────
2092
+ skills
2093
+ .command('unselect-atom')
2094
+ .description('从 docs/atom-tree.json 的 meta.selectedAtoms 中删除指定 selected atom ref')
2095
+ .requiredOption('--id <atomId>', '要删除的 atom ref')
2096
+ .option('--project-dir <path>', '项目根目录', process.cwd())
2097
+ .action((opts) => {
2098
+ const projectDir = path.resolve(opts.projectDir);
2099
+ const jsonPath = path.join(projectDir, 'docs', 'atom-tree.json');
2100
+ if (!fs.existsSync(jsonPath)) {
2101
+ console.error(`${jsonPath} 不存在。`);
2102
+ process.exit(1);
2103
+ }
2104
+ const tree = parseAtomTreeJson(fs.readFileSync(jsonPath, 'utf-8'));
2105
+ tree.meta.selectedAtoms ??= [];
2106
+ const idx = tree.meta.selectedAtoms.findIndex((atom) => atom === opts.id);
2107
+ if (idx === -1) {
2108
+ console.error(`atom ref "${opts.id}" 不存在于 meta.selectedAtoms 中。`);
2109
+ console.error(`现有 selectedAtoms: ${tree.meta.selectedAtoms.join(', ')}`);
2110
+ process.exit(1);
2111
+ }
2112
+ tree.meta.selectedAtoms.splice(idx, 1);
2113
+ fs.writeFileSync(jsonPath, JSON.stringify(tree, null, 2) + '\n');
2114
+ console.log(`✓ 已删除 selected atom ref "${opts.id}"`);
2115
+ console.log(` meta.selectedAtoms 当前共 ${tree.meta.selectedAtoms.length} 条`);
2116
+ });
2117
+ // ── gen-atom-tree-md ───────────────────────────────────────────────────────
2118
+ skills
2119
+ .command('gen-atom-tree-md')
2120
+ .description('从 docs/atom-tree.json 生成/更新 docs/atom-tree.md 阅读指南')
2121
+ .option('--project-dir <path>', '项目根目录', process.cwd())
2122
+ .option('--force', '覆盖已存在的 atom-tree.md')
2123
+ .action((opts) => {
2124
+ const projectDir = path.resolve(opts.projectDir);
2125
+ const jsonPath = path.join(projectDir, 'docs', 'atom-tree.json');
2126
+ const mdPath = path.join(projectDir, 'docs', 'atom-tree.md');
2127
+ if (!fs.existsSync(jsonPath)) {
2128
+ console.error(`${jsonPath} 不存在。先运行 playcraft skills init-atom-tree。`);
2129
+ process.exit(1);
2130
+ }
2131
+ if (fs.existsSync(mdPath) && !opts.force) {
2132
+ console.error(`${mdPath} 已存在。使用 --force 覆盖。`);
2133
+ process.exit(1);
2134
+ }
2135
+ const tree = parseAtomTreeJson(fs.readFileSync(jsonPath, 'utf-8'));
2136
+ const sm = tree.meta?.skillsMatch;
2137
+ const skillRefs = [...new Set(tree.meta?.selectedAtoms ?? [])];
2138
+ let md = '# Atom Tree Reading Guide\n\n';
2139
+ md += '> Canonical machine-readable source: `docs/atom-tree.json`\n\n';
2140
+ md += `> Engine: \`${sm?.engine ?? 'unknown'}\` | Intent: \`${(sm?.intent ?? []).join(', ')}\`\n\n`;
2141
+ md += '## Acceptance Criteria\n\n';
2142
+ md += '| itemId | Criteria |\n| ------ | -------- |\n';
2143
+ for (const ref of skillRefs) {
2144
+ md += `| ${ref} | _TODO_ |\n`;
2145
+ }
2146
+ md += '\n';
2147
+ md += '## Matched Skills Summary\n\n';
2148
+ md += '| skillRef | reuseLevel | purpose | matchReason | keyConstraint | readFirst |\n';
2149
+ md += '| -------- | ---------- | ------- | ----------- | ------------- | --------- |\n';
2150
+ for (const ref of skillRefs) {
2151
+ const item = sm?.items?.find((i) => i.atomId === ref);
2152
+ const label = item?.label?.zh || item?.label?.en || '';
2153
+ md += `| ${ref} | _TODO_ | ${label} | _TODO_ | _TODO_ | _TODO_ |\n`;
2154
+ }
2155
+ md += '\n';
2156
+ md += '## Per-Skill Reading Notes\n\n';
2157
+ for (const ref of skillRefs) {
2158
+ md += `### ${ref}\n\n`;
2159
+ md += '- Purpose: _TODO_\n';
2160
+ md += '- Read First: _TODO_\n';
2161
+ md += '- Watch Out: _TODO_\n\n';
2162
+ }
2163
+ md += '## mediaGroups Disposition\n\n';
2164
+ md += '| candidate | decision | reason | notes |\n';
2165
+ md += '| --------- | -------- | ------ | ----- |\n';
2166
+ for (const group of sm?.mediaGroups ?? []) {
2167
+ for (const c of group.candidates) {
2168
+ md += `| ${c.atomId} | _TODO_ | ${group.label} | |\n`;
2169
+ }
2170
+ }
2171
+ md += '\n';
2172
+ md += '## Gaps\n\n';
2173
+ md += '| need | whyNoMatch |\n| ---- | ---------- |\n';
2174
+ md += '| _none identified_ | |\n\n';
2175
+ fs.writeFileSync(mdPath, md);
2176
+ console.log(`✓ 已生成 ${mdPath}`);
2177
+ console.log(` selectedAtoms: ${skillRefs.length} | mediaGroups: ${(sm?.mediaGroups ?? []).length}`);
2178
+ });
1403
2179
  }