@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
@@ -5,6 +5,7 @@ import path from 'path';
5
5
  import { fileURLToPath } from 'url';
6
6
  import pc from 'picocolors';
7
7
  import ora from 'ora';
8
+ import { resolveCreateTemplate, resolveCreateTemplateDir, } from '../scaffold/templates.js';
8
9
  const __filename = fileURLToPath(import.meta.url);
9
10
  const __dirname = path.dirname(__filename);
10
11
  /** 生成一个本地占位 Project ID(99xxxxxx),后续可通过 playcraft config set 更新为真实 ID */
@@ -20,6 +21,43 @@ function toPackageName(name) {
20
21
  .replace(/[^a-z0-9\-]/g, '')
21
22
  .replace(/^-+|-+$/g, '');
22
23
  }
24
+ /** 检测目标目录的父目录是否在 Git 仓库内 */
25
+ function isInsideGitWorktree(dir) {
26
+ try {
27
+ execSync('git rev-parse --is-inside-work-tree', {
28
+ cwd: dir,
29
+ stdio: 'pipe',
30
+ });
31
+ return true;
32
+ }
33
+ catch {
34
+ return false;
35
+ }
36
+ }
37
+ function gitBranchExists(cwd, branchName) {
38
+ try {
39
+ execSync(`git show-ref --verify --quiet "refs/heads/${branchName}"`, {
40
+ cwd,
41
+ stdio: 'pipe',
42
+ });
43
+ return true;
44
+ }
45
+ catch {
46
+ return false;
47
+ }
48
+ }
49
+ function getUniqueProjectBranchName(cwd, projectName) {
50
+ const baseName = `project/${toPackageName(path.basename(projectName))}`;
51
+ if (!gitBranchExists(cwd, baseName))
52
+ return baseName;
53
+ const suffixBase = Date.now().toString(36);
54
+ for (let i = 0; i < 100; i += 1) {
55
+ const candidate = `${baseName}-${suffixBase}${i === 0 ? '' : `-${i}`}`;
56
+ if (!gitBranchExists(cwd, candidate))
57
+ return candidate;
58
+ }
59
+ throw new Error(`无法生成唯一 Git 分支名: ${baseName}`);
60
+ }
23
61
  /** 初始化 git 仓库并创建初始提交(供 Claude Code 子 agent worktree 隔离使用) */
24
62
  async function initGitRepository(targetDir) {
25
63
  const gitDir = path.join(targetDir, '.git');
@@ -45,6 +83,48 @@ async function initGitRepository(targetDir) {
45
83
  env: gitEnv,
46
84
  });
47
85
  }
86
+ /**
87
+ * 在父 Git 仓库中为新项目创建独立 worktree 并初始化 orphan 分支。
88
+ *
89
+ * - 使用 --no-checkout 避免在 Windows / 大工程场景下复制父仓库文件
90
+ * - checkout --orphan 保证分支历史干净,不含父仓库的任何文件
91
+ * - 后续 add -A + commit 在 worktree 内天然安全
92
+ */
93
+ async function initParentGitWorktree(targetDir, _projectName) {
94
+ const gitEnv = {
95
+ ...process.env,
96
+ GIT_AUTHOR_NAME: process.env.GIT_AUTHOR_NAME ?? 'PlayCraft',
97
+ GIT_AUTHOR_EMAIL: process.env.GIT_AUTHOR_EMAIL ?? 'playcraft@local',
98
+ GIT_COMMITTER_NAME: process.env.GIT_COMMITTER_NAME ?? 'PlayCraft',
99
+ GIT_COMMITTER_EMAIL: process.env.GIT_COMMITTER_EMAIL ?? 'playcraft@local',
100
+ };
101
+ const parentDir = path.dirname(targetDir);
102
+ // 零膨胀创建 worktree:不 checkout 任何文件
103
+ execSync(`git worktree add --no-checkout --detach "${targetDir}" HEAD`, {
104
+ cwd: parentDir,
105
+ stdio: 'pipe',
106
+ env: gitEnv,
107
+ });
108
+ // 创建无历史 orphan 分支
109
+ execSync('git checkout --orphan worktree-init', {
110
+ cwd: targetDir,
111
+ stdio: 'pipe',
112
+ env: gitEnv,
113
+ });
114
+ // checkout --orphan 不清空 index(保留 HEAD tree),需要手动清理
115
+ execSync('git rm --cached -r --ignore-unmatch .', {
116
+ cwd: targetDir,
117
+ stdio: 'pipe',
118
+ env: gitEnv,
119
+ });
120
+ // 清理 orphan checkout 遗留文件;-x 确保父仓库 ignored 产物不会混入新模板。
121
+ execSync('git clean -fdx', {
122
+ cwd: targetDir,
123
+ stdio: 'pipe',
124
+ env: gitEnv,
125
+ });
126
+ }
127
+ /** 任何层级都要排除的通用产物(文件名 / 目录名) */
48
128
  const TEMPLATE_COPY_EXCLUDES = new Set([
49
129
  '.DS_Store',
50
130
  '.git',
@@ -52,35 +132,42 @@ const TEMPLATE_COPY_EXCLUDES = new Set([
52
132
  'dist',
53
133
  'package-lock.json',
54
134
  'playable.ui.profile.yaml',
55
- 'playground',
56
- 'recordings',
57
- 'validate',
58
135
  ]);
59
- function shouldCopyTemplateEntry(srcPath, entryName) {
60
- if (srcPath.includes(`${path.sep}skills${path.sep}playable-focus${path.sep}templates${path.sep}`)) {
61
- return true;
62
- }
136
+ /**
137
+ * 仅作为「模板根直接子目录」时才排除的运行时产物目录。
138
+ * 这些名字也会出现在 skills 内部(如 skills/playable-focus/templates/playground),
139
+ * 故只在顶层排除,避免误伤 skills 的模板素材(否则 bootstrap-playground.mjs 会因找不到
140
+ * registry.template.json 而 ENOENT 退出)。
141
+ */
142
+ const TEMPLATE_ROOTLEVEL_EXCLUDES = new Set(['playground', 'recordings', 'validate']);
143
+ function shouldCopyTemplateEntry(srcPath, entryName, templateRoot) {
63
144
  if (TEMPLATE_COPY_EXCLUDES.has(entryName))
64
145
  return false;
146
+ if (TEMPLATE_ROOTLEVEL_EXCLUDES.has(entryName) && path.dirname(srcPath) === templateRoot) {
147
+ return false;
148
+ }
65
149
  if (srcPath.includes(`${path.sep}ta-workspace${path.sep}logs${path.sep}`))
66
150
  return entryName === '.gitkeep';
67
151
  if (srcPath.includes(`${path.sep}ta-workspace${path.sep}tmp${path.sep}`))
68
152
  return entryName === '.gitkeep';
69
- if (srcPath.includes(`${path.sep}focus-lab${path.sep}`))
153
+ // 顶层 focus-lab/ 是运行时 lab 目录,只保留 .gitkeep 占位;但 skills 内部的同名目录
154
+ // (如 skills/playable-focus/templates/focus-lab)是模板素材,需正常复制,故限定在模板根之下。
155
+ const toplevelFocusLabPrefix = path.join(templateRoot, 'focus-lab') + path.sep;
156
+ if (srcPath.startsWith(toplevelFocusLabPrefix))
70
157
  return entryName === '.gitkeep';
71
158
  return true;
72
159
  }
73
160
  /** 递归复制目录,跳过模板运行时产物 */
74
- async function copyDir(src, dest) {
161
+ async function copyDir(src, dest, templateRoot) {
75
162
  await fs.mkdir(dest, { recursive: true });
76
163
  const entries = await fs.readdir(src, { withFileTypes: true });
77
164
  for (const entry of entries) {
78
165
  const srcPath = path.join(src, entry.name);
79
- if (!shouldCopyTemplateEntry(srcPath, entry.name))
166
+ if (!shouldCopyTemplateEntry(srcPath, entry.name, templateRoot))
80
167
  continue;
81
168
  const destPath = path.join(dest, entry.name);
82
169
  if (entry.isDirectory()) {
83
- await copyDir(srcPath, destPath);
170
+ await copyDir(srcPath, destPath, templateRoot);
84
171
  }
85
172
  else {
86
173
  await fs.copyFile(srcPath, destPath);
@@ -860,7 +947,12 @@ async function setupPlatformSkillLinks(targetDir, cliCommandsDir) {
860
947
  }
861
948
  async function runEnsureProject(targetDir) {
862
949
  const ensureScript = path.join(targetDir, 'skills', '_shared', 'scripts', 'ensure-project.mjs');
863
- await fs.access(ensureScript);
950
+ try {
951
+ await fs.access(ensureScript);
952
+ }
953
+ catch {
954
+ return { warnings: ['ensure-project script not found; skipped workflow bootstrap'] };
955
+ }
864
956
  const output = execFileSync(process.execPath, [ensureScript, `--root=${targetDir}`, '--no-install', '--json'], {
865
957
  cwd: targetDir,
866
958
  encoding: 'utf8',
@@ -873,97 +965,294 @@ async function runEnsureProject(targetDir) {
873
965
  return { warnings: ['ensure-project completed but returned non-JSON (JavaScript Object Notation, JavaScript 对象表示法) output'] };
874
966
  }
875
967
  }
968
+ async function runNpmInstall(targetDir) {
969
+ execSync('npm install', {
970
+ cwd: targetDir,
971
+ stdio: 'pipe',
972
+ encoding: 'utf8',
973
+ });
974
+ }
975
+ async function runHarnessSmoke(targetDir) {
976
+ const smokeScript = path.join(targetDir, 'skills', '_shared', 'scripts', 'harness-smoke.mjs');
977
+ try {
978
+ await fs.access(smokeScript);
979
+ }
980
+ catch {
981
+ return {
982
+ ok: true,
983
+ checks: [{ name: 'harness:smoke', status: 'skip', message: 'harness-smoke script not found' }],
984
+ };
985
+ }
986
+ const output = execFileSync(process.execPath, [smokeScript, `--root=${targetDir}`, '--json'], {
987
+ cwd: targetDir,
988
+ encoding: 'utf8',
989
+ stdio: ['ignore', 'pipe', 'pipe'],
990
+ });
991
+ return JSON.parse(output);
992
+ }
993
+ async function patchPlayablePackageJson(targetDir, name) {
994
+ const pkgPath = path.join(targetDir, 'package.json');
995
+ const pkg = JSON.parse(await fs.readFile(pkgPath, 'utf8'));
996
+ pkg.name = toPackageName(name);
997
+ pkg.description = `${name} — playable ad project`;
998
+ await fs.writeFile(pkgPath, `${JSON.stringify(pkg, null, 2)}\n`);
999
+ }
1000
+ async function initializeAgentProject(targetDir) {
1001
+ await setupPlatformSkillLinks(targetDir, __dirname);
1002
+ const codebuddySpinner = ora('生成 CodeBuddy Code 配置...').start();
1003
+ try {
1004
+ await setupCodeBuddyEnv(targetDir, __dirname);
1005
+ codebuddySpinner.succeed('CodeBuddy Code 配置生成完成');
1006
+ }
1007
+ catch (error) {
1008
+ const message = error instanceof Error ? error.message : String(error);
1009
+ codebuddySpinner.warn(`CodeBuddy Code 配置生成跳过: ${message}`);
1010
+ }
1011
+ const cursorSpinner = ora('生成 Cursor 配置...').start();
1012
+ try {
1013
+ await setupCursorEnv(targetDir);
1014
+ cursorSpinner.succeed('Cursor 配置生成完成');
1015
+ }
1016
+ catch (error) {
1017
+ const message = error instanceof Error ? error.message : String(error);
1018
+ cursorSpinner.warn(`Cursor 配置生成跳过: ${message}`);
1019
+ }
1020
+ try {
1021
+ const localPath = path.join(targetDir, '.claude', 'settings.local.json');
1022
+ const localRaw = await fs.readFile(localPath, 'utf-8');
1023
+ const localSettings = JSON.parse(localRaw);
1024
+ fixSettingsLocalPermissions(localSettings, targetDir);
1025
+ await fs.writeFile(localPath, JSON.stringify(localSettings, null, 2) + '\n');
1026
+ }
1027
+ catch {
1028
+ // settings.local.json 不存在或解析失败,跳过
1029
+ }
1030
+ const installSpinner = ora('安装项目依赖...').start();
1031
+ try {
1032
+ await runNpmInstall(targetDir);
1033
+ installSpinner.succeed('项目依赖安装完成');
1034
+ }
1035
+ catch (error) {
1036
+ const message = error instanceof Error ? error.message : String(error);
1037
+ installSpinner.fail(`项目依赖安装失败: ${message}`);
1038
+ console.error(pc.dim(' 提示: 请检查网络连接和 Node.js 版本(需 >= 20)'));
1039
+ process.exit(1);
1040
+ }
1041
+ const ensureSpinner = ora('初始化项目工作流...').start();
1042
+ try {
1043
+ const ensureResult = await runEnsureProject(targetDir);
1044
+ ensureSpinner.succeed('项目工作流初始化完成');
1045
+ for (const warning of ensureResult.warnings ?? []) {
1046
+ console.log(pc.dim(` ensure-project: ${warning}`));
1047
+ }
1048
+ }
1049
+ catch (error) {
1050
+ const message = error instanceof Error ? error.message : String(error);
1051
+ const stderr = error.stderr?.trimEnd();
1052
+ const stdout = error.stdout?.trimEnd();
1053
+ ensureSpinner.fail(`项目工作流初始化失败: ${message}`);
1054
+ if (stderr)
1055
+ console.error(pc.red(` ${stderr}`));
1056
+ if (stdout)
1057
+ console.error(pc.dim(` ${stdout}`));
1058
+ process.exit(1);
1059
+ }
1060
+ }
1061
+ function printCreateNextSteps(template, _targetDir, name, here, worktree) {
1062
+ console.log();
1063
+ if (worktree) {
1064
+ console.log(pc.dim('提示:本项目为 Git worktree,分支已独立提交。'));
1065
+ console.log(pc.dim(` 完成后合并到主仓库(合并后文件在 experiments/${name}/ 下,保留完整提交历史):`));
1066
+ console.log(` ${pc.cyan(`git rm --cached -r experiments/${name}/ 2>/dev/null; git read-tree --prefix=experiments/${name}/ ${worktree.projectBranchName}; T=$(git write-tree); C=$(echo "merge: ${name}" | git commit-tree $T -p HEAD -p ${worktree.projectBranchName}); git merge --ff-only $C; git checkout HEAD -- experiments/${name}/`)}`);
1067
+ console.log();
1068
+ }
1069
+ console.log(pc.dim('下一步:'));
1070
+ if (!here) {
1071
+ console.log(` ${pc.cyan(`cd ${name}`)}`);
1072
+ }
1073
+ if (template.kind === 'agent') {
1074
+ console.log(` ${pc.cyan('playcraft skills init-atom-tree --intent "<your game keywords>"')}`);
1075
+ console.log(` ${pc.cyan('playcraft remix init-template --from-atom-tree')}`);
1076
+ console.log(` ${pc.cyan('npm run verify:contract && npm run dev')}`);
1077
+ console.log();
1078
+ console.log(pc.dim('如需使用 PlayCraft CLI 打渠道包:'));
1079
+ console.log(` ${pc.cyan('playcraft build . -p facebook')}`);
1080
+ console.log();
1081
+ console.log(pc.dim('正式接入 PlayCraft 后,替换项目 ID:'));
1082
+ console.log(` ${pc.cyan('playcraft config set --key projectId --value <真实项目ID>')}`);
1083
+ }
1084
+ else {
1085
+ console.log(` ${pc.cyan('npm install')}`);
1086
+ console.log(` ${pc.cyan('npm run dev')}`);
1087
+ console.log();
1088
+ console.log(pc.dim('构建渠道包:'));
1089
+ console.log(` ${pc.cyan('npm run build -- google')}`);
1090
+ console.log(` ${pc.cyan('npm run builds')}`);
1091
+ console.log();
1092
+ console.log(pc.dim('或使用 PlayCraft CLI:'));
1093
+ console.log(` ${pc.cyan(`playcraft build ${here ? '.' : name} -p facebook`)}`);
1094
+ }
1095
+ console.log();
1096
+ }
1097
+ /** Resolve target directory and display name for `playcraft create`. */
1098
+ export function resolveCreatePaths(baseDir, projectName, here) {
1099
+ const resolvedBase = path.resolve(baseDir);
1100
+ if (here) {
1101
+ const name = projectName?.trim() || path.basename(resolvedBase) || 'project';
1102
+ return { targetDir: resolvedBase, name };
1103
+ }
1104
+ const name = projectName?.trim();
1105
+ if (!name) {
1106
+ throw new Error('project-name is required unless --here is set');
1107
+ }
1108
+ return { targetDir: path.join(resolvedBase, name), name };
1109
+ }
1110
+ /** Returns whether the target directory should be removed before worktree creation. */
1111
+ export async function confirmTargetDirOverwrite(targetDir, displayLabel, force) {
1112
+ try {
1113
+ const stat = await fs.stat(targetDir);
1114
+ if (!stat.isDirectory())
1115
+ return false;
1116
+ const entries = await fs.readdir(targetDir);
1117
+ if (entries.length === 0)
1118
+ return false;
1119
+ if (force)
1120
+ return true;
1121
+ const { overwrite } = await inquirer.prompt([
1122
+ {
1123
+ type: 'confirm',
1124
+ name: 'overwrite',
1125
+ message: pc.yellow(`目录 ${displayLabel} 已存在且非空,继续会覆盖同名文件,是否继续?`),
1126
+ default: false,
1127
+ },
1128
+ ]);
1129
+ if (!overwrite) {
1130
+ console.log(pc.yellow('已取消'));
1131
+ process.exit(0);
1132
+ }
1133
+ return true;
1134
+ }
1135
+ catch {
1136
+ return false;
1137
+ }
1138
+ }
876
1139
  export async function createCommand(projectName, options) {
877
1140
  console.log(pc.cyan('\n🎮 PlayCraft 可玩广告项目创建\n'));
1141
+ let template;
1142
+ try {
1143
+ template = resolveCreateTemplate(options.template ?? 'agent');
1144
+ }
1145
+ catch (error) {
1146
+ const message = error instanceof Error ? error.message : String(error);
1147
+ console.error(pc.red(message));
1148
+ process.exit(1);
1149
+ }
878
1150
  const baseDir = options.dir ? path.resolve(options.dir) : process.cwd();
879
- const answers = await inquirer.prompt([
880
- {
881
- type: 'input',
882
- name: 'projectName',
883
- message: '项目目录名:',
884
- default: projectName,
885
- when: !projectName,
886
- validate: (input) => {
887
- if (!input.trim())
888
- return '项目名称不能为空';
889
- if (/[/\\:*?"<>|]/.test(input))
890
- return '项目名称不能包含特殊字符';
891
- return true;
892
- },
893
- },
894
- {
895
- type: 'input',
896
- name: 'port',
897
- message: '本地 Agent 端口:',
898
- default: '2468',
899
- validate: (input) => {
900
- const port = parseInt(input, 10);
901
- if (isNaN(port) || port < 1 || port > 65535) {
902
- return '端口号必须是 1-65535 之间的数字';
903
- }
904
- return true;
905
- },
906
- },
907
- {
908
- type: 'input',
909
- name: 'skillsDir',
910
- message: 'Skills 目录路径 (可选,供 playcraft skills 命令使用,直接回车跳过):',
911
- default: '',
912
- validate: async (input) => {
913
- if (!input.trim())
1151
+ const here = options.here ?? false;
1152
+ const force = options.force ?? false;
1153
+ const nonInteractive = options.nonInteractive === true;
1154
+ const isAgentTemplate = template.kind === 'agent';
1155
+ if (nonInteractive && !projectName && !here) {
1156
+ console.error(pc.red('非交互模式需要指定项目名称: playcraft create <project-name> --non-interactive'));
1157
+ process.exit(1);
1158
+ }
1159
+ let name;
1160
+ let port;
1161
+ let skillsDir;
1162
+ if (nonInteractive) {
1163
+ ({ name } = resolveCreatePaths(baseDir, projectName, here));
1164
+ port = isAgentTemplate ? parseInt(options.port || '2468', 10) : 0;
1165
+ skillsDir = '';
1166
+ }
1167
+ else {
1168
+ const answers = await inquirer.prompt([
1169
+ {
1170
+ type: 'input',
1171
+ name: 'projectName',
1172
+ message: '项目目录名:',
1173
+ default: projectName ?? (here ? path.basename(baseDir) : undefined),
1174
+ when: !projectName && !here,
1175
+ validate: (input) => {
1176
+ if (!input.trim())
1177
+ return '项目名称不能为空';
1178
+ if (/[/\\:*?"<>|]/.test(input))
1179
+ return '项目名称不能包含特殊字符';
914
1180
  return true;
915
- try {
916
- const stat = await fs.stat(path.resolve(input));
917
- if (!stat.isDirectory())
918
- return '路径不是一个目录';
1181
+ },
1182
+ },
1183
+ {
1184
+ type: 'input',
1185
+ name: 'port',
1186
+ message: '本地 Agent 端口:',
1187
+ default: '2468',
1188
+ when: isAgentTemplate,
1189
+ validate: (input) => {
1190
+ const parsedPort = parseInt(input, 10);
1191
+ if (isNaN(parsedPort) || parsedPort < 1 || parsedPort > 65535) {
1192
+ return '端口号必须是 1-65535 之间的数字';
1193
+ }
919
1194
  return true;
920
- }
921
- catch {
922
- return '目录不存在,请检查路径';
923
- }
1195
+ },
924
1196
  },
925
- },
926
- {
927
- type: 'input',
928
- name: 'token',
929
- message: '认证令牌 (可选,可稍后通过 playcraft config set 更新):',
930
- default: '',
931
- },
932
- ]);
933
- const name = projectName || answers.projectName;
934
- const port = parseInt(answers.port, 10);
935
- const skillsDir = answers.skillsDir?.trim() || '';
936
- const token = answers.token;
1197
+ {
1198
+ type: 'input',
1199
+ name: 'skillsDir',
1200
+ message: 'Skills 目录路径 (可选,供 playcraft skills 命令使用,直接回车跳过):',
1201
+ default: '',
1202
+ when: isAgentTemplate,
1203
+ validate: async (input) => {
1204
+ if (!input.trim())
1205
+ return true;
1206
+ try {
1207
+ const stat = await fs.stat(path.resolve(input));
1208
+ if (!stat.isDirectory())
1209
+ return '路径不是一个目录';
1210
+ return true;
1211
+ }
1212
+ catch {
1213
+ return '目录不存在,请检查路径';
1214
+ }
1215
+ },
1216
+ },
1217
+ {
1218
+ type: 'input',
1219
+ name: 'token',
1220
+ message: '认证令牌 (可选,可稍后通过 playcraft config set 更新):',
1221
+ default: '',
1222
+ when: isAgentTemplate,
1223
+ },
1224
+ ]);
1225
+ name = here
1226
+ ? (projectName?.trim() || path.basename(baseDir) || 'project')
1227
+ : (projectName || answers.projectName);
1228
+ port = isAgentTemplate ? parseInt(answers.port ?? '2468', 10) : 0;
1229
+ skillsDir = isAgentTemplate ? (answers.skillsDir?.trim() || '') : '';
1230
+ }
937
1231
  const projectId = generateMockProjectId();
938
- const targetDir = path.join(baseDir, name);
939
- // 检查目标目录是否已存在
940
- try {
941
- const stat = await fs.stat(targetDir);
942
- if (stat.isDirectory()) {
943
- const entries = await fs.readdir(targetDir);
944
- if (entries.length > 0) {
945
- const { overwrite } = await inquirer.prompt([
946
- {
947
- type: 'confirm',
948
- name: 'overwrite',
949
- message: pc.yellow(`目录 ${name} 已存在且非空,继续会覆盖同名文件,是否继续?`),
950
- default: false,
951
- },
952
- ]);
953
- if (!overwrite) {
954
- console.log(pc.yellow('已取消'));
955
- process.exit(0);
956
- }
1232
+ const { targetDir } = resolveCreatePaths(baseDir, name, here);
1233
+ const overwriteLabel = here ? targetDir : name;
1234
+ let projectBranchName = `project/${toPackageName(path.basename(name))}`;
1235
+ const shouldOverwrite = await confirmTargetDirOverwrite(targetDir, overwriteLabel, force);
1236
+ let isWorktreeMode = isInsideGitWorktree(path.dirname(targetDir));
1237
+ if (isWorktreeMode) {
1238
+ const wtSpinner = ora('创建 Git worktree...').start();
1239
+ try {
1240
+ if (shouldOverwrite) {
1241
+ await fs.rm(targetDir, { recursive: true, force: true });
957
1242
  }
1243
+ await initParentGitWorktree(targetDir, name);
1244
+ projectBranchName = getUniqueProjectBranchName(targetDir, name);
1245
+ wtSpinner.succeed('Git worktree 创建完成');
1246
+ }
1247
+ catch (error) {
1248
+ const message = error instanceof Error ? error.message : String(error);
1249
+ wtSpinner.warn(`Git worktree 创建跳过: ${message}`);
1250
+ isWorktreeMode = false;
958
1251
  }
959
1252
  }
960
- catch {
961
- // 目录不存在,正常继续
962
- }
963
- const spinner = ora('复制项目模板...').start();
1253
+ const spinner = ora(`复制项目模板 (${template.id})...`).start();
964
1254
  try {
965
- // 定位 project-template 目录(打包后路径:dist/commands/ → ../../project-template)
966
- const templateDir = path.resolve(__dirname, '..', '..', 'project-template');
1255
+ const templateDir = resolveCreateTemplateDir(template.id);
967
1256
  try {
968
1257
  await fs.access(templateDir);
969
1258
  }
@@ -971,87 +1260,78 @@ export async function createCommand(projectName, options) {
971
1260
  spinner.fail(`找不到项目模板目录: ${templateDir}`);
972
1261
  process.exit(1);
973
1262
  }
974
- // 复制模板
975
- await copyDir(templateDir, targetDir);
976
- spinner.succeed('项目模板复制完成');
977
- // 为 Platform Tool skills 创建 .claude/skills/ 软连接
978
- await setupPlatformSkillLinks(targetDir, __dirname);
979
- // 为 CodeBuddy Code 生成独立的 .codebuddy/ 配置目录
980
- const codebuddySpinner = ora('生成 CodeBuddy Code 配置...').start();
981
- try {
982
- await setupCodeBuddyEnv(targetDir, __dirname);
983
- codebuddySpinner.succeed('CodeBuddy Code 配置生成完成');
984
- }
985
- catch (error) {
986
- const message = error instanceof Error ? error.message : String(error);
987
- codebuddySpinner.warn(`CodeBuddy Code 配置生成跳过: ${message}`);
1263
+ await copyDir(templateDir, targetDir, templateDir);
1264
+ spinner.succeed(`项目模板复制完成 (${template.label})`);
1265
+ if (template.kind === 'playable') {
1266
+ await patchPlayablePackageJson(targetDir, name);
988
1267
  }
989
- // 为 Cursor 生成 .cursor/rules/ 规则文件
990
- const cursorSpinner = ora('生成 Cursor 配置...').start();
991
- try {
992
- await setupCursorEnv(targetDir);
993
- cursorSpinner.succeed('Cursor 配置生成完成');
994
- }
995
- catch (error) {
996
- const message = error instanceof Error ? error.message : String(error);
997
- cursorSpinner.warn(`Cursor 配置生成跳过: ${message}`);
998
- }
999
- // 修复 .claude/settings.local.json 中硬编码的开发者路径
1000
- try {
1001
- const localPath = path.join(targetDir, '.claude', 'settings.local.json');
1002
- const localRaw = await fs.readFile(localPath, 'utf-8');
1003
- const localSettings = JSON.parse(localRaw);
1004
- fixSettingsLocalPermissions(localSettings, targetDir);
1005
- await fs.writeFile(localPath, JSON.stringify(localSettings, null, 2) + '\n');
1006
- }
1007
- catch {
1008
- // settings.local.json 不存在或解析失败,跳过
1268
+ else {
1269
+ await initializeAgentProject(targetDir);
1009
1270
  }
1010
- const ensureSpinner = ora('初始化项目工作流...').start();
1011
- try {
1012
- const ensureResult = await runEnsureProject(targetDir);
1013
- ensureSpinner.succeed('项目工作流初始化完成');
1014
- for (const warning of ensureResult.warnings ?? []) {
1015
- console.log(pc.dim(` ensure-project: ${warning}`));
1271
+ if (isAgentTemplate) {
1272
+ const smokeSpinner = ora('Harness 自检...').start();
1273
+ try {
1274
+ const smokeResult = await runHarnessSmoke(targetDir);
1275
+ if (smokeResult.ok) {
1276
+ smokeSpinner.succeed('Harness 自检通过');
1277
+ }
1278
+ else {
1279
+ smokeSpinner.warn('Harness 自检未通过(项目仍可用)');
1280
+ for (const check of smokeResult.checks) {
1281
+ if (check.status === 'fail') {
1282
+ console.log(pc.yellow(` ✗ ${check.name}: ${check.message}`));
1283
+ }
1284
+ }
1285
+ }
1286
+ }
1287
+ catch (error) {
1288
+ const message = error instanceof Error ? error.message : String(error);
1289
+ smokeSpinner.warn(`Harness 自检跳过: ${message}`);
1016
1290
  }
1017
1291
  }
1018
- catch (error) {
1019
- const message = error instanceof Error ? error.message : String(error);
1020
- ensureSpinner.fail(`项目工作流初始化失败: ${message}`);
1021
- process.exit(1);
1022
- }
1023
- const gitSpinner = ora('初始化 Git 仓库...').start();
1292
+ const gitSpinner = ora(isWorktreeMode ? '提交项目分支...' : '初始化 Git 仓库...').start();
1024
1293
  try {
1025
- await initGitRepository(targetDir);
1026
- gitSpinner.succeed('Git 仓库初始化完成');
1294
+ if (isWorktreeMode) {
1295
+ execSync(`git branch -m "${projectBranchName}"`, {
1296
+ cwd: targetDir,
1297
+ stdio: 'pipe',
1298
+ });
1299
+ execSync('git add -A', {
1300
+ cwd: targetDir,
1301
+ stdio: 'pipe',
1302
+ });
1303
+ execSync('git commit -m "chore: initial commit from playcraft create"', {
1304
+ cwd: targetDir,
1305
+ stdio: 'pipe',
1306
+ });
1307
+ gitSpinner.succeed('项目分支提交完成');
1308
+ }
1309
+ else {
1310
+ await initGitRepository(targetDir);
1311
+ gitSpinner.succeed('Git 仓库初始化完成');
1312
+ }
1027
1313
  }
1028
1314
  catch (error) {
1029
1315
  const message = error instanceof Error ? error.message : String(error);
1030
1316
  gitSpinner.warn(`Git 初始化跳过: ${message}`);
1031
1317
  console.log(pc.dim(' 提示: 安装 git 后可在项目目录执行 git init && git add -A && git commit -m "initial"'));
1032
1318
  }
1033
- // 完成提示
1034
1319
  console.log(pc.green(`\n✅ 项目创建完成!\n`));
1035
1320
  console.log(pc.bold(' 目录: ') + pc.cyan(targetDir));
1036
- console.log(pc.bold(' 项目ID: ') + pc.cyan(projectId) + pc.dim(' (临时占位 ID,正式接入 PlayCraft 后请更新)'));
1037
- console.log(pc.bold(' 端口: ') + pc.cyan(String(port)));
1038
- if (skillsDir) {
1039
- console.log(pc.bold(' Skills: ') + pc.cyan(skillsDir));
1321
+ console.log(pc.bold(' 模板: ') + pc.cyan(template.id) + pc.dim(` (${template.label})`));
1322
+ if (isAgentTemplate) {
1323
+ console.log(pc.bold(' 项目ID: ') +
1324
+ pc.cyan(projectId) +
1325
+ pc.dim(' (临时占位 ID,正式接入 PlayCraft 后请更新)'));
1326
+ console.log(pc.bold(' 端口: ') + pc.cyan(String(port)));
1327
+ if (isWorktreeMode) {
1328
+ console.log(pc.bold(' 分支: ') + pc.cyan(projectBranchName));
1329
+ }
1330
+ if (skillsDir) {
1331
+ console.log(pc.bold(' Skills: ') + pc.cyan(skillsDir));
1332
+ }
1040
1333
  }
1041
- console.log();
1042
- console.log(pc.dim('下一步:'));
1043
- console.log(` ${pc.cyan(`cd ${name}`)}`);
1044
- console.log(` ${pc.cyan('npm install')}`);
1045
- console.log(` ${pc.cyan('playcraft skills init-atom-tree --intent "<your game keywords>"')}`);
1046
- console.log(` ${pc.cyan('playcraft remix init-template --from-atom-tree')}`);
1047
- console.log(` ${pc.cyan('npm run verify:contract && npm run dev')}`);
1048
- console.log();
1049
- console.log(pc.dim('如需使用 PlayCraft CLI 打渠道包:'));
1050
- console.log(` ${pc.cyan('playcraft build . -p facebook')}`);
1051
- console.log();
1052
- console.log(pc.dim('正式接入 PlayCraft 后,替换项目 ID:'));
1053
- console.log(` ${pc.cyan(`playcraft config set --key projectId --value <真实项目ID>`)}`);
1054
- console.log();
1334
+ printCreateNextSteps(template, targetDir, name, here, isWorktreeMode ? { projectBranchName } : undefined);
1055
1335
  }
1056
1336
  catch (error) {
1057
1337
  spinner.fail('项目创建失败');