@playcraft/cli 0.0.53 → 0.0.56

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 (242) hide show
  1. package/README.md +21 -0
  2. package/REMIX.md +103 -19
  3. package/dist/atom-tree/fusion-hints.js +8 -3
  4. package/dist/atom-tree/fusion-intent.js +62 -0
  5. package/dist/atom-tree/selection.js +3 -1
  6. package/dist/commands/create.js +439 -159
  7. package/dist/commands/image.js +79 -69
  8. package/dist/commands/levels.js +29 -0
  9. package/dist/commands/patterns.js +75 -0
  10. package/dist/commands/remix.js +47 -0
  11. package/dist/commands/skills.js +48 -17
  12. package/dist/index.js +13 -6
  13. package/dist/remix/clone-source.js +208 -0
  14. package/dist/remix/clone.js +255 -0
  15. package/dist/scaffold/templates.js +35 -0
  16. package/dist/utils/agent-api-client.js +10 -3
  17. package/dist/utils/remix-image-attachments.js +4 -32
  18. package/dist/utils/sandbox-path.js +20 -0
  19. package/package.json +3 -3
  20. package/project-template/.claude/agents/creative-director.md +114 -0
  21. package/project-template/.claude/agents/developer.md +5 -2
  22. package/project-template/.claude/agents/game-designer.md +41 -20
  23. package/project-template/.claude/agents/refs/README.md +8 -0
  24. package/project-template/.claude/agents/refs/creative-director-guide.md +35 -0
  25. package/project-template/.claude/agents/refs/creative-director-kickoff-guide.md +52 -0
  26. package/project-template/.claude/agents/refs/creative-discovery-guide.md +76 -0
  27. package/project-template/.claude/agents/refs/creative-north-star-examples.md +82 -0
  28. package/project-template/.claude/agents/refs/developer-dev-handoff.md +1 -1
  29. package/project-template/.claude/agents/refs/game-designer-fusion-depth-playbook.md +28 -0
  30. package/project-template/.claude/agents/refs/game-designer-gameplay-sufficiency.md +4 -4
  31. package/project-template/.claude/agents/reviewer.md +11 -0
  32. package/project-template/.claude/hooks/README.md +12 -12
  33. package/project-template/.claude/hooks/block-developer-preflight.mjs +49 -13
  34. package/project-template/.claude/hooks/block-ring1-tasklist.mjs +1 -1
  35. package/project-template/.claude/hooks/user-prompt.mjs +3 -13
  36. package/project-template/.claude/hooks/validate-workflow-stop.mjs +209 -12
  37. package/project-template/.claude/settings.json +2 -2
  38. package/project-template/.claude/skills/playcraft-playable-optimization/SKILL.md +20 -20
  39. package/project-template/.claude/skills/session-analyzer/scripts/resolve-session.mjs +0 -1
  40. package/project-template/.claude/snapshot.mjs +0 -1
  41. package/project-template/.codebuddy/settings.json +1 -1
  42. package/project-template/.cursor/rules/playcraft-orchestrator.mdc +1 -1
  43. package/project-template/CLAUDE.md +9 -9
  44. package/project-template/docs/project-state.md +1 -1
  45. package/project-template/docs/team/agent-conduct.md +18 -18
  46. package/project-template/docs/team/collaboration.md +19 -7
  47. package/project-template/docs/team/workflow-changelog.md +15 -0
  48. package/project-template/fusion-patterns/catalog.json +77 -0
  49. package/project-template/package.json +2 -0
  50. package/project-template/skills/_shared/scripts/create-playable.mjs +31 -11
  51. package/project-template/skills/_shared/scripts/dev-start.mjs +3 -3
  52. package/project-template/skills/_shared/scripts/disclosure-config.mjs +65 -39
  53. package/project-template/skills/_shared/scripts/harness-smoke.mjs +3 -1
  54. package/project-template/skills/_shared/scripts/lib/fusion-intent.mjs +88 -0
  55. package/project-template/skills/_shared/scripts/lib/skill-fidelity.mjs +185 -0
  56. package/project-template/skills/_shared/scripts/slice-set.mjs +29 -2
  57. package/project-template/skills/_shared/scripts/state-get.mjs +124 -7
  58. package/project-template/skills/_shared/scripts/state-store.mjs +25 -2
  59. package/project-template/skills/_shared/scripts/validate-level-sketch.mjs +444 -0
  60. package/project-template/skills/_shared/scripts/verify-design-consistency.mjs +728 -0
  61. package/project-template/skills/playable-record/scripts/lib/dev-server.mjs +46 -11
  62. package/project-template/skills/playable-record/scripts/record-playable.mjs +2 -1
  63. package/project-template/skills/playable-record/scripts/verify-contract.mjs +4 -4
  64. package/project-template/skills/playable-record/scripts/verify-firstwin.mjs +3 -3
  65. package/project-template/skills/playable-record/scripts/verify-fusion.mjs +37 -2
  66. package/project-template/skills/playable-record/scripts/verify-slice.mjs +2 -2
  67. package/project-template/templates/design-brief.template.md +34 -0
  68. package/project-template/templates/design-methodology.md +1 -2
  69. package/project-template/templates/design.template.md +65 -0
  70. package/project-template/templates/developer-log.template.md +12 -1
  71. package/project-template/templates/five-axis-framework.md +12 -0
  72. package/project-template/templates/game-designer-creative-process.template.md +99 -0
  73. package/project-template/templates/level-sketch.template.json +56 -0
  74. package/project-template/templates/north-star.template.md +43 -0
  75. package/project-template/templates/project-state.template.md +1 -1
  76. package/project-template/vite.config.ts +4 -5
  77. package/project-templates/phaser/.claude/agents/creative-director.md +114 -0
  78. package/project-templates/phaser/.claude/agents/game-designer.md +41 -20
  79. package/project-templates/phaser/.claude/agents/refs/creative-director-guide.md +35 -0
  80. package/project-templates/phaser/.claude/agents/refs/creative-director-kickoff-guide.md +52 -0
  81. package/project-templates/phaser/.claude/agents/refs/creative-discovery-guide.md +76 -0
  82. package/project-templates/phaser/.claude/agents/refs/creative-north-star-examples.md +82 -0
  83. package/project-templates/phaser/.claude/agents/refs/developer-dev-handoff.md +1 -1
  84. package/project-templates/phaser/.claude/agents/refs/game-designer-fusion-depth-playbook.md +28 -0
  85. package/project-templates/phaser/.claude/agents/reviewer.md +11 -0
  86. package/project-templates/phaser/.claude/hooks/block-ring1-tasklist.mjs +2 -2
  87. package/project-templates/phaser/.claude/hooks/user-prompt.mjs +17 -18
  88. package/project-templates/phaser/.claude/hooks/validate-workflow-stop.mjs +209 -12
  89. package/project-templates/phaser/.claude/skills/playcraft-playable-optimization/SKILL.md +20 -20
  90. package/project-templates/phaser/.cursor/rules/playcraft-orchestrator.mdc +1 -1
  91. package/project-templates/phaser/CLAUDE.md +88 -54
  92. package/project-templates/phaser/docs/DESIGN.md +25 -0
  93. package/project-templates/phaser/docs/project-state.md +1 -1
  94. package/project-templates/phaser/docs/team/collaboration.md +19 -7
  95. package/project-templates/phaser/docs/team/workflow-changelog.md +14 -0
  96. package/project-templates/phaser/fusion-patterns/catalog.json +77 -0
  97. package/project-templates/phaser/package.json +3 -1
  98. package/project-templates/phaser/skills/_shared/scripts/dev-start.mjs +3 -3
  99. package/project-templates/phaser/skills/_shared/scripts/disclosure-config.mjs +65 -39
  100. package/project-templates/phaser/skills/_shared/scripts/harness-smoke.mjs +3 -1
  101. package/project-templates/phaser/skills/_shared/scripts/lib/fusion-intent.mjs +88 -0
  102. package/project-templates/phaser/skills/_shared/scripts/lib/skill-fidelity.mjs +185 -0
  103. package/project-templates/phaser/skills/_shared/scripts/state-get.mjs +124 -7
  104. package/project-templates/phaser/skills/_shared/scripts/state-store.mjs +25 -2
  105. package/project-templates/phaser/skills/_shared/scripts/validate-level-sketch.mjs +444 -0
  106. package/project-templates/phaser/skills/_shared/scripts/verify-design-consistency.mjs +728 -0
  107. package/project-templates/phaser/skills/playable-record/scripts/lib/dev-server.mjs +46 -11
  108. package/project-templates/phaser/skills/playable-record/scripts/record-playable.mjs +2 -1
  109. package/project-templates/phaser/skills/playable-record/scripts/verify-contract.mjs +4 -4
  110. package/project-templates/phaser/skills/playable-record/scripts/verify-firstwin.mjs +3 -3
  111. package/project-templates/phaser/skills/playable-record/scripts/verify-fusion.mjs +37 -2
  112. package/project-templates/phaser/skills/playable-record/scripts/verify-slice.mjs +2 -2
  113. package/project-templates/phaser/templates/design-brief.template.md +42 -11
  114. package/project-templates/phaser/templates/design-methodology.md +1 -2
  115. package/project-templates/phaser/templates/design.template.md +77 -0
  116. package/project-templates/phaser/templates/five-axis-framework.md +50 -1
  117. package/project-templates/phaser/templates/game-designer-creative-process.template.md +99 -0
  118. package/project-templates/phaser/templates/level-sketch.template.json +56 -0
  119. package/project-templates/phaser/templates/north-star.template.md +43 -0
  120. package/project-templates/phaser/templates/project-state.template.md +1 -1
  121. package/project-templates/phaser/vite.config.ts +4 -5
  122. package/project-templates/playable-template-phaser/.playcraftignore +10 -0
  123. package/project-templates/playable-template-phaser/CLAUDE.md +178 -0
  124. package/project-templates/playable-template-phaser/PLAYCRAFT-MIGRATION.md +328 -0
  125. package/project-templates/playable-template-phaser/README.md +381 -0
  126. package/project-templates/playable-template-phaser/assets/brain.png +0 -0
  127. package/project-templates/playable-template-phaser/assets/button.png +0 -0
  128. package/project-templates/playable-template-phaser/assets/interact.png +0 -0
  129. package/project-templates/playable-template-phaser/assets/logo.webp +0 -0
  130. package/project-templates/playable-template-phaser/assets/warn_brain.png +0 -0
  131. package/project-templates/playable-template-phaser/builds.config.js +12 -0
  132. package/project-templates/playable-template-phaser/globals.d.ts +2 -0
  133. package/project-templates/playable-template-phaser/index.html +12 -0
  134. package/project-templates/playable-template-phaser/package.json +45 -0
  135. package/project-templates/playable-template-phaser/scripts/base64-to-mp3.js +340 -0
  136. package/project-templates/playable-template-phaser/scripts/convert-to-webp.js +370 -0
  137. package/project-templates/playable-template-phaser/src/Game.ts +154 -0
  138. package/project-templates/playable-template-phaser/src/config/LoadingConfig.ts +40 -0
  139. package/project-templates/playable-template-phaser/src/config/ThemeConfig.ts +81 -0
  140. package/project-templates/playable-template-phaser/src/index.css +18 -0
  141. package/project-templates/playable-template-phaser/src/index.html +9 -0
  142. package/project-templates/playable-template-phaser/src/index.ts +30 -0
  143. package/project-templates/playable-template-phaser/src/json5.d.ts +4 -0
  144. package/project-templates/playable-template-phaser/src/managers/AudioManager.ts +559 -0
  145. package/project-templates/playable-template-phaser/src/scenes/MainScene.ts +301 -0
  146. package/project-templates/playable-template-phaser/src/scenes/PreloadScene.ts +256 -0
  147. package/project-templates/playable-template-phaser/src/theme/README.md +194 -0
  148. package/project-templates/playable-template-phaser/src/theme/blue-theme/assets/.gitkeep +24 -0
  149. package/project-templates/playable-template-phaser/src/theme/blue-theme/index.ts +27 -0
  150. package/project-templates/playable-template-phaser/src/theme/blue-theme/theme.data.json5 +19 -0
  151. package/project-templates/playable-template-phaser/src/theme/green-theme/assets/.gitkeep +3 -0
  152. package/project-templates/playable-template-phaser/src/theme/green-theme/index.ts +20 -0
  153. package/project-templates/playable-template-phaser/src/theme/green-theme/theme.data.json5 +19 -0
  154. package/project-templates/playable-template-phaser/src/theme/index.ts +1 -0
  155. package/project-templates/playable-template-phaser/src/theme/red-theme/assets/.gitkeep +3 -0
  156. package/project-templates/playable-template-phaser/src/theme/red-theme/index.ts +20 -0
  157. package/project-templates/playable-template-phaser/src/theme/red-theme/theme.data.json5 +19 -0
  158. package/project-templates/playable-template-phaser/src/theme/theme.metadata.md +60 -0
  159. package/project-templates/playable-template-phaser/src/theme/theme.schema.json5 +149 -0
  160. package/project-templates/playable-template-phaser/src/ui/BrainAgeUI.ts +681 -0
  161. package/project-templates/playable-template-phaser/src/ui/CountdownUI.ts +565 -0
  162. package/project-templates/playable-template-phaser/src/ui/PlayButton.ts +51 -0
  163. package/project-templates/playable-template-phaser/src/ui/ScoreUI.ts +207 -0
  164. package/project-templates/playable-template-phaser/src/ui/SuccessUI.ts +43 -0
  165. package/project-templates/playable-template-phaser/src/ui/index.ts +15 -0
  166. package/project-templates/playable-template-phaser/tsconfig.json +26 -0
  167. package/project-templates/playable-template-phaser/vite.config.ts +64 -0
  168. package/project-templates/playable-template-phaser//346/226/260/346/211/213/345/274/200/345/217/221/346/214/207/345/215/227.md +209 -0
  169. package/project-templates/playable-template-three/README.md +795 -0
  170. package/project-templates/playable-template-three/assets/Stork.glb +0 -0
  171. package/project-templates/playable-template-three/assets/button.png +0 -0
  172. package/project-templates/playable-template-three/assets/logo.webp +0 -0
  173. package/project-templates/playable-template-three/build.json +7 -0
  174. package/project-templates/playable-template-three/builds.config.example.js +151 -0
  175. package/project-templates/playable-template-three/builds.config.js +158 -0
  176. package/project-templates/playable-template-three/package.json +36 -0
  177. package/project-templates/playable-template-three/scripts/convert-to-webp.js +370 -0
  178. package/project-templates/playable-template-three/scripts/theme-builds.js +514 -0
  179. package/project-templates/playable-template-three/src/Game.ts +215 -0
  180. package/project-templates/playable-template-three/src/config/LoadingConfig.ts +27 -0
  181. package/project-templates/playable-template-three/src/config/ThemeConfig.ts +36 -0
  182. package/project-templates/playable-template-three/src/global.d.ts +108 -0
  183. package/project-templates/playable-template-three/src/index.css +18 -0
  184. package/project-templates/playable-template-three/src/index.html +9 -0
  185. package/project-templates/playable-template-three/src/index.ts +20 -0
  186. package/project-templates/playable-template-three/src/managers/AudioManager.ts +538 -0
  187. package/project-templates/playable-template-three/src/scenes/PreloadScene.ts +305 -0
  188. package/project-templates/playable-template-three/src/theme/README.md +225 -0
  189. package/project-templates/playable-template-three/src/theme/blue-theme/assets/.gitkeep +28 -0
  190. package/project-templates/playable-template-three/src/theme/blue-theme/index.ts +45 -0
  191. package/project-templates/playable-template-three/src/theme/current.ts +12 -0
  192. package/project-templates/playable-template-three/src/theme/green-theme/assets/.gitkeep +3 -0
  193. package/project-templates/playable-template-three/src/theme/green-theme/index.ts +34 -0
  194. package/project-templates/playable-template-three/src/theme/red-theme/assets/.gitkeep +3 -0
  195. package/project-templates/playable-template-three/src/theme/red-theme/index.ts +34 -0
  196. package/project-templates/playable-template-three/tsconfig.json +24 -0
  197. package/project-templates/threejs/.claude/agents/creative-director.md +114 -0
  198. package/project-templates/threejs/.claude/agents/game-designer.md +41 -20
  199. package/project-templates/threejs/.claude/agents/refs/creative-director-guide.md +35 -0
  200. package/project-templates/threejs/.claude/agents/refs/creative-director-kickoff-guide.md +52 -0
  201. package/project-templates/threejs/.claude/agents/refs/creative-discovery-guide.md +76 -0
  202. package/project-templates/threejs/.claude/agents/refs/creative-north-star-examples.md +82 -0
  203. package/project-templates/threejs/.claude/agents/refs/developer-dev-handoff.md +1 -1
  204. package/project-templates/threejs/.claude/agents/refs/game-designer-fusion-depth-playbook.md +28 -0
  205. package/project-templates/threejs/.claude/agents/reviewer.md +11 -0
  206. package/project-templates/threejs/.claude/hooks/block-ring1-tasklist.mjs +2 -2
  207. package/project-templates/threejs/.claude/hooks/user-prompt.mjs +17 -18
  208. package/project-templates/threejs/.claude/hooks/validate-workflow-stop.mjs +209 -12
  209. package/project-templates/threejs/.claude/skills/playcraft-playable-optimization/SKILL.md +20 -20
  210. package/project-templates/threejs/.cursor/rules/playcraft-orchestrator.mdc +1 -1
  211. package/project-templates/threejs/CLAUDE.md +88 -54
  212. package/project-templates/threejs/docs/DESIGN.md +25 -0
  213. package/project-templates/threejs/docs/project-state.md +1 -1
  214. package/project-templates/threejs/docs/team/collaboration.md +19 -7
  215. package/project-templates/threejs/docs/team/workflow-changelog.md +14 -0
  216. package/project-templates/threejs/fusion-patterns/catalog.json +77 -0
  217. package/project-templates/threejs/package.json +3 -1
  218. package/project-templates/threejs/skills/_shared/scripts/dev-start.mjs +3 -3
  219. package/project-templates/threejs/skills/_shared/scripts/disclosure-config.mjs +65 -39
  220. package/project-templates/threejs/skills/_shared/scripts/harness-smoke.mjs +3 -1
  221. package/project-templates/threejs/skills/_shared/scripts/lib/fusion-intent.mjs +88 -0
  222. package/project-templates/threejs/skills/_shared/scripts/lib/skill-fidelity.mjs +185 -0
  223. package/project-templates/threejs/skills/_shared/scripts/state-get.mjs +124 -7
  224. package/project-templates/threejs/skills/_shared/scripts/state-store.mjs +25 -2
  225. package/project-templates/threejs/skills/_shared/scripts/validate-level-sketch.mjs +444 -0
  226. package/project-templates/threejs/skills/_shared/scripts/verify-design-consistency.mjs +728 -0
  227. package/project-templates/threejs/skills/playable-record/scripts/lib/dev-server.mjs +46 -11
  228. package/project-templates/threejs/skills/playable-record/scripts/record-playable.mjs +2 -1
  229. package/project-templates/threejs/skills/playable-record/scripts/verify-contract.mjs +4 -4
  230. package/project-templates/threejs/skills/playable-record/scripts/verify-firstwin.mjs +3 -3
  231. package/project-templates/threejs/skills/playable-record/scripts/verify-fusion.mjs +37 -2
  232. package/project-templates/threejs/skills/playable-record/scripts/verify-slice.mjs +2 -2
  233. package/project-templates/threejs/templates/design-brief.template.md +42 -11
  234. package/project-templates/threejs/templates/design-methodology.md +1 -2
  235. package/project-templates/threejs/templates/design.template.md +77 -0
  236. package/project-templates/threejs/templates/five-axis-framework.md +50 -1
  237. package/project-templates/threejs/templates/game-designer-creative-process.template.md +99 -0
  238. package/project-templates/threejs/templates/level-sketch.template.json +56 -0
  239. package/project-templates/threejs/templates/north-star.template.md +43 -0
  240. package/project-templates/threejs/templates/project-state.template.md +1 -1
  241. package/project-templates/threejs/vite.config.ts +4 -5
  242. package/project-template/docs/DESIGN.md +0 -65
@@ -21,6 +21,7 @@
21
21
  import * as fs from 'node:fs';
22
22
  import * as path from 'node:path';
23
23
  import { getFusionHintsForGameplayAtoms, } from '../atom-tree/fusion-hints.js';
24
+ import { detectFusionIntent, fusionCompositeScoreBoost, fusionSingleMechanicPenalty, isFusionCompositeManifest, } from '../atom-tree/fusion-intent.js';
24
25
  import { collectPinnedDependencies, loadSkillManifestIndex, parseAtomTreeJson, parseSelectedAtomRefsFromAtomTreeProject, isUninitializedAtomTreeSkeleton, resolveSkillsDirs, validateAtomTreeSelectionProject, } from '../atom-tree/selection.js';
25
26
  import { buildAtomTreeSkeleton } from '../atom-tree/build-skeleton.js';
26
27
  import { validateAsrCoverageProject } from '../asr/validate-asr-coverage.js';
@@ -83,6 +84,10 @@ const INTENT_TAG_ALIASES = {
83
84
  mahjong: ['tile', 'tiles', '牌面'],
84
85
  麻将: ['tile', 'tiles', 'mahjong', '牌面'],
85
86
  solitaire: ['solitaire', 'klondike', 'card'],
87
+ 消除: ['elimination', 'match-3', 'match3', 'match'],
88
+ 箭头: ['arrow', 'path', 'slide-out'],
89
+ fusion: ['融合', 'hybrid', 'combine'],
90
+ 融合: ['fusion', 'hybrid', 'combine', 'match-3', 'arrow'],
86
91
  };
87
92
  const ANCHOR_MEDIA_ROLE_LABELS = {
88
93
  tileTextureSet: { label: '牌面/瓦片贴图组', layer: '视觉资产层' },
@@ -250,7 +255,7 @@ function getCategoryLayer(category, atomId) {
250
255
  return '玩法层';
251
256
  }
252
257
  // ─── Manifest 索引器 ─────────────────────────────────────────────────────────
253
- function loadSkillIndex(skillsDirs) {
258
+ export function loadSkillIndex(skillsDirs) {
254
259
  const entries = [];
255
260
  const seen = new Set();
256
261
  for (const dir of skillsDirs) {
@@ -281,7 +286,10 @@ function loadSkillIndex(skillsDirs) {
281
286
  // ─── 推荐算法 ────────────────────────────────────────────────────────────────
282
287
  function getGameplayType(manifest) {
283
288
  const gen = manifest['generation'];
284
- return gen?.gameplayType;
289
+ if (gen?.gameplayType)
290
+ return gen.gameplayType;
291
+ const pgs = manifest['pgs'];
292
+ return pgs?.gameplayType;
285
293
  }
286
294
  function isEngineManifest(manifest) {
287
295
  return manifest.category === 'engine' || manifest.bundleType === 'aicomponent';
@@ -315,7 +323,7 @@ function pinAtomIdToBareName(pinAtomId) {
315
323
  /**
316
324
  * 仅按 intent 打分(discovery 相位)。不假设目标引擎。
317
325
  */
318
- export function scoreSkillIntentOnly(entry, intentTags) {
326
+ export function scoreSkillIntentOnly(entry, intentTags, rawIntent = '') {
319
327
  if (intentTags.length === 0)
320
328
  return 0;
321
329
  const { manifest } = entry;
@@ -333,6 +341,8 @@ export function scoreSkillIntentOnly(entry, intentTags) {
333
341
  }
334
342
  if (manifest.category === 'engine')
335
343
  score += 1;
344
+ score += fusionCompositeScoreBoost(manifest, intentTags, rawIntent);
345
+ score += fusionSingleMechanicPenalty(manifest, intentTags, rawIntent);
336
346
  return score;
337
347
  }
338
348
  /**
@@ -392,7 +402,7 @@ export function inferSuggestedEngines(scored, intentTags, indexMap) {
392
402
  * 计算一个 skill 对给定 engine + intent 的匹配分数。
393
403
  * 得分越高,越应该被推荐。
394
404
  */
395
- export function scoreSkill(entry, engine, intentTags) {
405
+ export function scoreSkill(entry, engine, intentTags, rawIntent = '') {
396
406
  const { manifest } = entry;
397
407
  let score = 0;
398
408
  // 1. 引擎精确匹配:renderBackend 或 engineVariants 中有指定引擎
@@ -418,6 +428,8 @@ export function scoreSkill(entry, engine, intentTags) {
418
428
  });
419
429
  if (importsEngine)
420
430
  score += 5;
431
+ score += fusionCompositeScoreBoost(manifest, intentTags, rawIntent);
432
+ score += fusionSingleMechanicPenalty(manifest, intentTags, rawIntent);
421
433
  return score;
422
434
  }
423
435
  /**
@@ -452,9 +464,12 @@ function collectDependencies(atomId, index, engine, visited = new Set(), depth =
452
464
  }
453
465
  else {
454
466
  // 无 variant,使用顶层 imports
467
+ // 只对 requires 做递归传递依赖展开;compatibleWith 只记录直接引用
455
468
  for (const imp of entry.manifest.imports ?? []) {
456
- if (imp.pinAtomId && !visited.has(imp.pinAtomId)) {
457
- deps.push(imp.pinAtomId);
469
+ if (!imp.pinAtomId || visited.has(imp.pinAtomId))
470
+ continue;
471
+ deps.push(imp.pinAtomId);
472
+ if (imp.edgeKind === 'requires') {
458
473
  deps.push(...collectDependencies(imp.pinAtomId, index, engine, visited, depth + 1));
459
474
  }
460
475
  }
@@ -563,9 +578,10 @@ function buildIntentOnlyMatchResult(entries, intentTags, suggestedEngines, index
563
578
  /**
564
579
  * 仅 --intent:先推断引擎,再对首选引擎做完整 match(一轮输出全部内容)。
565
580
  */
566
- export function buildAutoMatchResult(entries, intentTags) {
581
+ export function buildAutoMatchResult(entries, intentTags, rawIntent = '') {
582
+ const intentBlob = rawIntent || intentTags.join(' ');
567
583
  const scored = entries
568
- .map((entry) => ({ entry, score: scoreSkillIntentOnly(entry, intentTags) }))
584
+ .map((entry) => ({ entry, score: scoreSkillIntentOnly(entry, intentTags, intentBlob) }))
569
585
  .filter(({ score }) => score >= SCORE_THRESHOLD_DISCOVERY);
570
586
  const indexMap = new Map(entries.map((e) => [e.atomId, e]));
571
587
  const suggestedEngines = inferSuggestedEngines(scored, intentTags, indexMap);
@@ -573,7 +589,7 @@ export function buildAutoMatchResult(entries, intentTags) {
573
589
  if (!engine) {
574
590
  return buildIntentOnlyMatchResult(entries, intentTags, suggestedEngines, indexMap);
575
591
  }
576
- const full = buildMatchResult(entries, engine, intentTags);
592
+ const full = buildMatchResult(entries, engine, intentTags, intentBlob);
577
593
  const gap = suggestedEngines.length > 1 ? suggestedEngines[0].score - suggestedEngines[1].score : 99;
578
594
  return {
579
595
  ...full,
@@ -582,8 +598,20 @@ export function buildAutoMatchResult(entries, intentTags) {
582
598
  engineAmbiguous: suggestedEngines.length > 1 && gap < 5,
583
599
  };
584
600
  }
585
- function selectMatchSeedAtomIds(scored, engine, intentTags) {
601
+ function selectMatchSeedAtomIds(scored, engine, intentTags, rawIntent = '') {
586
602
  const seeds = new Set();
603
+ const fusionIntent = detectFusionIntent(intentTags, rawIntent);
604
+ if (fusionIntent) {
605
+ const composites = scored.filter(({ entry, score }) => isGameplayManifest(entry.manifest) &&
606
+ isFusionCompositeManifest(entry.manifest) &&
607
+ score >= SCORE_THRESHOLD_FULL &&
608
+ passesGameplayIntentFilter(entry, intentTags));
609
+ if (composites.length > 0) {
610
+ for (const { entry } of composites)
611
+ seeds.add(entry.atomId);
612
+ return seeds;
613
+ }
614
+ }
587
615
  for (const { entry, score } of scored) {
588
616
  const { manifest } = entry;
589
617
  if (isEngineManifest(manifest) && pinAtomIdToEngine(entry.atomId, manifest) === engine) {
@@ -674,19 +702,20 @@ function buildItemReason(entry, engine, intentTags, score, via, dependencyReason
674
702
  return `标签:${matchedTags.join(', ')}`;
675
703
  return score !== null ? `score=${score}` : '推荐';
676
704
  }
677
- export function buildMatchResult(entries, engine, intentTags) {
705
+ export function buildMatchResult(entries, engine, intentTags, rawIntent = '') {
706
+ const intentBlob = rawIntent || intentTags.join(' ');
678
707
  const indexMap = new Map(entries.map((e) => [e.atomId, e]));
679
708
  const scoreById = new Map();
680
709
  const expandedIntent = expandIntentTags(intentTags);
681
710
  const scored = entries
682
711
  .map((entry) => {
683
- const score = scoreSkill(entry, engine, expandedIntent);
712
+ const score = scoreSkill(entry, engine, expandedIntent, intentBlob);
684
713
  scoreById.set(entry.atomId, score);
685
714
  return { entry, score };
686
715
  })
687
716
  .filter(({ score }) => score >= SCORE_THRESHOLD_FULL)
688
717
  .sort((a, b) => b.score - a.score);
689
- const seedIds = selectMatchSeedAtomIds(scored, engine, expandedIntent);
718
+ const seedIds = selectMatchSeedAtomIds(scored, engine, expandedIntent, intentBlob);
690
719
  const requiredIds = expandSeedClosure(seedIds, indexMap, engine);
691
720
  const seedIdSet = seedIds;
692
721
  const dependencyReasonMap = buildDependencyReasonMap(requiredIds, indexMap, engine);
@@ -1475,9 +1504,10 @@ export function registerSkillsCommands(program) {
1475
1504
  ' 示例:playcraft skills match --engine phaser --intent "match3,grid" --json');
1476
1505
  process.exit(1);
1477
1506
  }
1507
+ const intentBlob = opts.intent;
1478
1508
  const result = opts.engine
1479
- ? buildMatchResult(entries, opts.engine, intentTags)
1480
- : buildAutoMatchResult(entries, intentTags);
1509
+ ? buildMatchResult(entries, opts.engine, intentTags, intentBlob)
1510
+ : buildAutoMatchResult(entries, intentTags, intentBlob);
1481
1511
  if (opts.json) {
1482
1512
  process.stdout.write(JSON.stringify(result, null, 2) + '\n');
1483
1513
  return;
@@ -1909,9 +1939,10 @@ export function registerSkillsCommands(program) {
1909
1939
  process.exit(1);
1910
1940
  }
1911
1941
  }
1942
+ const intentBlob = opts.intent;
1912
1943
  const result = opts.engine
1913
- ? buildMatchResult(entries, opts.engine, intentTags)
1914
- : buildAutoMatchResult(entries, intentTags);
1944
+ ? buildMatchResult(entries, opts.engine, intentTags, intentBlob)
1945
+ : buildAutoMatchResult(entries, intentTags, intentBlob);
1915
1946
  if (!result.engine) {
1916
1947
  console.log('无法从 intent 推断运行引擎。请指定:--engine phaser 或 --engine threejs');
1917
1948
  console.log(`示例:playcraft skills init-atom-tree --intent "${intentTags.join(',')}" --engine phaser --force`);
package/dist/index.js CHANGED
@@ -21,8 +21,10 @@ import { registerImageCommands } from './commands/image.js';
21
21
  import { registerAudioCommands } from './commands/audio.js';
22
22
  import { registerPrefabCommands } from './commands/prefab.js';
23
23
  import { registerRecommendCommands } from './commands/recommend.js';
24
- import { registerRemixCommands } from './commands/remix.js';
24
+ import { registerRemixCommands, registerGitCredentialCommand } from './commands/remix.js';
25
25
  import { registerSkillsCommands } from './commands/skills.js';
26
+ import { registerLevelsCommands } from './commands/levels.js';
27
+ import { registerPatternsCommands } from './commands/patterns.js';
26
28
  import { registerPradCommands } from './commands/prad.js';
27
29
  import { register3DCommands } from './commands/3d.js';
28
30
  import { CLI_ROOT_DESCRIPTION, getCliTopicsHelpText, registerRootProgramHelp, } from './cli-root-help.js';
@@ -30,10 +32,7 @@ const __filename = fileURLToPath(import.meta.url);
30
32
  const __dirname = dirname(__filename);
31
33
  const packageJson = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf-8'));
32
34
  const program = new Command();
33
- program
34
- .name('playcraft')
35
- .description(CLI_ROOT_DESCRIPTION)
36
- .version(packageJson.version);
35
+ program.name('playcraft').description(CLI_ROOT_DESCRIPTION).version(packageJson.version);
37
36
  registerRootProgramHelp(program);
38
37
  // 版本检查(非阻塞,后台执行)
39
38
  // 只在非 upgrade 命令时检查,避免重复提示
@@ -43,8 +42,13 @@ if (!process.argv.includes('upgrade')) {
43
42
  // create 命令
44
43
  program
45
44
  .command('create [project-name]')
46
- .description('从模板创建新的可玩广告项目(包含 Agent Team 工作目录结构)')
45
+ .description('从模板创建新的可玩广告项目')
47
46
  .option('-d, --dir <path>', '在指定目录下创建项目', process.cwd())
47
+ .option('--port <number>', '本地 Agent 端口(非交互模式默认 2468)', '2468')
48
+ .option('--non-interactive', '跳过交互式问答,使用命令行参数或默认值')
49
+ .option('-t, --template <id>', '模板: agent | phaser-playable | three-playable', 'agent')
50
+ .option('--here', '在当前目录(或 --dir 指定目录)直接初始化,不创建子目录')
51
+ .option('-f, --force', '非交互模式:目标目录非空时仍覆盖同名文件')
48
52
  .action(async (projectName, options) => {
49
53
  await createCommand(projectName, options);
50
54
  });
@@ -356,8 +360,11 @@ registerPrefabCommands(program);
356
360
  registerRecommendCommands(program);
357
361
  // Remix workflow (playcraft remix <command>)
358
362
  registerRemixCommands(program);
363
+ registerGitCredentialCommand(program);
359
364
  // 开发时 Skill 发现与脚手架 (playcraft skills <command>)
360
365
  registerSkillsCommands(program);
366
+ registerLevelsCommands(program);
367
+ registerPatternsCommands(program);
361
368
  registerPradCommands(program);
362
369
  // 3D model utilities (playcraft 3d <command>)
363
370
  register3DCommands(program);
@@ -0,0 +1,208 @@
1
+ /**
2
+ * Parse sources accepted by `playcraft remix clone`:
3
+ * - PlayCraft remix detail: /projects/:id/remixes/:branch
4
+ * - PlayCraft remix editor: /editor/remix/:id?branch=
5
+ * - Gongfeng / GitLab / GitHub branch tree or bare repo URL
6
+ */
7
+ /** 工蜂内网 / 外网域名互为别名(详情页可能展示任一)。 */
8
+ export const GONGFENG_HOST_ALIASES = ['git.woa.com', 'git.tencent.com'];
9
+ const GIT_HOST_HINTS = [
10
+ ...GONGFENG_HOST_ALIASES,
11
+ 'github.com',
12
+ 'gitlab.com',
13
+ 'gitee.com',
14
+ ];
15
+ function looksLikeHttpUrl(value) {
16
+ return /^https?:\/\//i.test(value.trim());
17
+ }
18
+ /** Canonical host for Gongfeng alias matching (woa ↔ tencent). */
19
+ export function canonicalizeGitHost(hostname) {
20
+ const host = hostname.trim().toLowerCase();
21
+ if (GONGFENG_HOST_ALIASES.includes(host)) {
22
+ return 'git.woa.com';
23
+ }
24
+ return host;
25
+ }
26
+ function isGongfengHost(hostname) {
27
+ const host = hostname.trim().toLowerCase();
28
+ return GONGFENG_HOST_ALIASES.includes(host);
29
+ }
30
+ function decodePathSegment(segment) {
31
+ try {
32
+ return decodeURIComponent(segment);
33
+ }
34
+ catch {
35
+ return segment;
36
+ }
37
+ }
38
+ /** Strip credentials and trailing .git / slash for comparison. Gongfeng hosts are aliased. */
39
+ export function normalizeGitRepositoryUrl(raw) {
40
+ let url = raw.trim();
41
+ // git@host:group/repo.git → https://host/group/repo
42
+ const scp = /^git@([^:]+):(.+)$/.exec(url);
43
+ if (scp) {
44
+ url = `https://${scp[1]}/${scp[2]}`;
45
+ }
46
+ try {
47
+ const u = new URL(url);
48
+ u.hash = '';
49
+ u.search = '';
50
+ u.username = '';
51
+ u.password = '';
52
+ u.hostname = canonicalizeGitHost(u.hostname);
53
+ let pathname = u.pathname.replace(/\/+$/, '');
54
+ pathname = pathname.replace(/\.git$/i, '');
55
+ return `${u.protocol}//${u.host}${pathname}`.toLowerCase();
56
+ }
57
+ catch {
58
+ return url
59
+ .replace(/\.git$/i, '')
60
+ .replace(/\/+$/, '')
61
+ .toLowerCase();
62
+ }
63
+ }
64
+ /**
65
+ * From a host tree/blob URL, extract repo base + optional branch.
66
+ * Supports:
67
+ * .../tree/<branch>
68
+ * .../-/tree/<branch> (GitLab)
69
+ * .../blob/<branch>/...
70
+ */
71
+ export function parseGitHostTreeUrl(raw) {
72
+ let input = raw.trim();
73
+ if (!looksLikeHttpUrl(input) && !input.startsWith('git@'))
74
+ return null;
75
+ // scp-style
76
+ if (input.startsWith('git@')) {
77
+ const scp = /^git@([^:]+):(.+)$/.exec(input);
78
+ if (!scp)
79
+ return null;
80
+ input = `https://${scp[1]}/${scp[2]}`;
81
+ }
82
+ let u;
83
+ try {
84
+ u = new URL(input);
85
+ }
86
+ catch {
87
+ return null;
88
+ }
89
+ const host = u.hostname.toLowerCase();
90
+ const isKnownHost = isGongfengHost(host) ||
91
+ GIT_HOST_HINTS.some((h) => host === h || host.endsWith(`.${h}`)) ||
92
+ host.includes('git.');
93
+ // PlayCraft portal hosts are handled elsewhere
94
+ if (!isKnownHost && !u.pathname.includes('/tree/') && !u.pathname.includes('/-/tree/')) {
95
+ // Still allow any https URL that looks like *.git
96
+ if (!u.pathname.endsWith('.git') && !input.endsWith('.git')) {
97
+ return null;
98
+ }
99
+ }
100
+ const path = u.pathname;
101
+ const gitlabTree = /\/-\/tree\/([^/]+)(?:\/|$)/.exec(path);
102
+ const plainTree = /\/tree\/([^/]+)(?:\/|$)/.exec(path);
103
+ const blob = /\/blob\/([^/]+)(?:\/|$)/.exec(path);
104
+ const match = gitlabTree || plainTree || blob;
105
+ let repositoryPath = path;
106
+ let branch;
107
+ if (match) {
108
+ branch = decodePathSegment(match[1]);
109
+ repositoryPath = path.slice(0, match.index);
110
+ }
111
+ repositoryPath = repositoryPath.replace(/\.git$/i, '').replace(/\/+$/, '');
112
+ if (!repositoryPath || repositoryPath === '/')
113
+ return null;
114
+ const repositoryUrl = `${u.protocol}//${u.host}${repositoryPath}`;
115
+ return { repositoryUrl, branch };
116
+ }
117
+ export function parsePlaycraftRemixUrl(raw) {
118
+ if (!looksLikeHttpUrl(raw))
119
+ return null;
120
+ let u;
121
+ try {
122
+ u = new URL(raw.trim());
123
+ }
124
+ catch {
125
+ return null;
126
+ }
127
+ // /projects/:projectId/remixes/:branch
128
+ const detail = /^\/projects\/([^/]+)\/remixes\/([^/]+)\/?$/.exec(u.pathname);
129
+ if (detail) {
130
+ const projectId = decodePathSegment(detail[1]);
131
+ const branch = decodePathSegment(detail[2]);
132
+ if (!projectId || !branch)
133
+ return null;
134
+ return {
135
+ kind: 'playcraft-detail',
136
+ projectId,
137
+ branch,
138
+ origin: u.origin,
139
+ };
140
+ }
141
+ // /editor/remix/:projectId?branch=
142
+ const editor = /^\/editor\/remix\/([^/]+)\/?$/.exec(u.pathname);
143
+ if (editor) {
144
+ const projectId = decodePathSegment(editor[1]);
145
+ const branchRaw = u.searchParams.get('branch') || u.searchParams.get('branchId');
146
+ if (!projectId || !branchRaw?.trim())
147
+ return null;
148
+ return {
149
+ kind: 'playcraft-editor',
150
+ projectId,
151
+ branch: decodePathSegment(branchRaw.trim()),
152
+ origin: u.origin,
153
+ };
154
+ }
155
+ return null;
156
+ }
157
+ /**
158
+ * Resolve clone source from a pasted URL or explicit flags.
159
+ * Throws with a user-facing Chinese message on conflict / missing fields.
160
+ */
161
+ export function resolveRemixCloneTarget(input) {
162
+ const source = input.source?.trim();
163
+ const flagProjectId = input.projectId?.trim();
164
+ const flagBranch = input.branch?.trim();
165
+ const sourceLooksRemote = Boolean(source) && (looksLikeHttpUrl(source) || source.startsWith('git@'));
166
+ if (source && sourceLooksRemote) {
167
+ const playcraft = parsePlaycraftRemixUrl(source);
168
+ if (playcraft && (playcraft.kind === 'playcraft-detail' || playcraft.kind === 'playcraft-editor')) {
169
+ if (flagProjectId && flagProjectId !== playcraft.projectId) {
170
+ throw new Error('URL 中的项目 ID 与 --project-id 不一致。');
171
+ }
172
+ if (flagBranch && flagBranch !== playcraft.branch) {
173
+ throw new Error('URL 中的分支与 --branch 不一致。');
174
+ }
175
+ return {
176
+ kind: playcraft.kind,
177
+ projectId: playcraft.projectId,
178
+ branch: playcraft.branch,
179
+ origin: playcraft.origin,
180
+ };
181
+ }
182
+ const gitHost = parseGitHostTreeUrl(source);
183
+ if (gitHost) {
184
+ const branch = flagBranch || gitHost.branch;
185
+ if (!branch) {
186
+ throw new Error('工蜂/Git 地址未包含分支,请在 URL 使用 /tree/<branch>,或额外传入 --branch。');
187
+ }
188
+ return {
189
+ kind: 'git-host',
190
+ repositoryUrl: gitHost.repositoryUrl,
191
+ branch,
192
+ };
193
+ }
194
+ throw new Error('无法识别该 URL。请粘贴 Remix 详情页、Remix Editor 地址,或工蜂分支页(…/tree/<branch>)。');
195
+ }
196
+ if (flagProjectId && flagBranch) {
197
+ return { kind: 'flags', projectId: flagProjectId, branch: flagBranch };
198
+ }
199
+ throw new Error('请传入 Remix 详情/编辑器 URL,或工蜂 Git 地址;也可使用 --project-id 与 --branch。');
200
+ }
201
+ export function isRemoteCloneSource(value) {
202
+ const v = value?.trim();
203
+ return Boolean(v && (looksLikeHttpUrl(v) || v.startsWith('git@')));
204
+ }
205
+ /** @deprecated use isRemoteCloneSource */
206
+ export function isHttpCloneSource(value) {
207
+ return isRemoteCloneSource(value);
208
+ }