@gong-ym/ai-spec-auto 0.2.3

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 (640) hide show
  1. package/.agents/commands/README.md +33 -0
  2. package/.agents/commands/claude/spec-start-review.md +88 -0
  3. package/.agents/commands/codex/spec-continue.md +74 -0
  4. package/.agents/commands/codex/spec-orchestrate.md +35 -0
  5. package/.agents/commands/codex/spec-start-review.md +88 -0
  6. package/.agents/commands/codex/spec-start.md +67 -0
  7. package/.agents/commands/codex/spec-status.md +22 -0
  8. package/.agents/commands/codex/spec-stop.md +29 -0
  9. package/.agents/commands/codex/spec-update.md +40 -0
  10. package/.agents/commands/common/branch-review.md +117 -0
  11. package/.agents/commands/common/project-init.md +25 -0
  12. package/.agents/commands/common/spec-continue.md +74 -0
  13. package/.agents/commands/common/spec-orchestrate.md +35 -0
  14. package/.agents/commands/common/spec-start-review.md +82 -0
  15. package/.agents/commands/common/spec-start.md +67 -0
  16. package/.agents/commands/common/spec-status.md +22 -0
  17. package/.agents/commands/common/spec-stop.md +29 -0
  18. package/.agents/commands/common/spec-update.md +40 -0
  19. package/.agents/commands/cursor/opsx-apply.md +55 -0
  20. package/.agents/commands/cursor/opsx-archive.md +48 -0
  21. package/.agents/commands/cursor/opsx-explore.md +45 -0
  22. package/.agents/commands/cursor/opsx-propose.md +59 -0
  23. package/.agents/commands/cursor/spec-continue.md +63 -0
  24. package/.agents/commands/cursor/spec-orchestrate.md +53 -0
  25. package/.agents/commands/cursor/spec-start-review.md +78 -0
  26. package/.agents/commands/cursor/spec-start.md +59 -0
  27. package/.agents/commands/cursor/spec-status.md +30 -0
  28. package/.agents/commands/cursor/spec-stop.md +29 -0
  29. package/.agents/commands/cursor/spec-update.md +41 -0
  30. package/.agents/flows/FRONTMATTER.md +263 -0
  31. package/.agents/flows/RUN_OUTPUT.md +263 -0
  32. package/.agents/flows/common/README.md +29 -0
  33. package/.agents/flows/common/bugfix-to-verification.md +95 -0
  34. package/.agents/flows/common/change-to-architecture-review.md +89 -0
  35. package/.agents/flows/common/change-to-release.md +94 -0
  36. package/.agents/flows/common/prd-to-delivery.md +184 -0
  37. package/.agents/flows/common/requirement-to-observability.md +97 -0
  38. package/.agents/orchestration/README.md +22 -0
  39. package/.agents/orchestration/expert-dispatch-spec.md +155 -0
  40. package/.agents/orchestration/expert-executor-spec.md +84 -0
  41. package/.agents/orchestration/expert-runtime-action-spec.md +73 -0
  42. package/.agents/orchestration/runtime-state-handoff-spec.md +264 -0
  43. package/.agents/orchestration/task-anchor-spec.md +212 -0
  44. package/.agents/orchestration/task-orchestrator-adapter-payload.md +153 -0
  45. package/.agents/orchestration/task-orchestrator-bootstrap-payload.md +145 -0
  46. package/.agents/orchestration/task-orchestrator-output-extractor-spec.md +93 -0
  47. package/.agents/orchestration/task-orchestrator-run-plan-template.md +312 -0
  48. package/.agents/orchestration/task-orchestrator-runtime-hooks.md +214 -0
  49. package/.agents/registry/README.md +63 -0
  50. package/.agents/registry/flows.json +125 -0
  51. package/.agents/registry/profiles.json +101 -0
  52. package/.agents/registry/roles.json +1266 -0
  53. package/.agents/registry/rules.json +148 -0
  54. package/.agents/registry/scenario-packages.json +123 -0
  55. package/.agents/registry/skills.json +130 -0
  56. package/.agents/roles/INDEX.md +346 -0
  57. package/.agents/roles/common/README.md +76 -0
  58. package/.agents/roles/common/archive-change.md +80 -0
  59. package/.agents/roles/common/backend-implementer.md +92 -0
  60. package/.agents/roles/common/code-guardian.md +151 -0
  61. package/.agents/roles/common/frontend-implementer.md +146 -0
  62. package/.agents/roles/common/requirement-analyst.md +138 -0
  63. package/.agents/roles/common/task-orchestrator-routing.md +301 -0
  64. package/.agents/roles/common/task-orchestrator.md +224 -0
  65. package/.agents/roles/common/tooling-implementer.md +92 -0
  66. package/.agents/roles/domains/README.md +35 -0
  67. package/.agents/roles/domains/delivery/README.md +11 -0
  68. package/.agents/roles/domains/delivery/container-specialist.md +50 -0
  69. package/.agents/roles/domains/delivery/deployment-specialist.md +50 -0
  70. package/.agents/roles/domains/delivery/pipeline-specialist.md +50 -0
  71. package/.agents/roles/domains/demand-design/README.md +16 -0
  72. package/.agents/roles/domains/demand-design/api-contract-specialist.md +52 -0
  73. package/.agents/roles/domains/demand-design/design-collaborator.md +58 -0
  74. package/.agents/roles/domains/documentation/README.md +11 -0
  75. package/.agents/roles/domains/documentation/api-doc-specialist.md +50 -0
  76. package/.agents/roles/domains/documentation/component-doc-specialist.md +49 -0
  77. package/.agents/roles/domains/documentation/technical-writing-specialist.md +48 -0
  78. package/.agents/roles/domains/engineering/README.md +17 -0
  79. package/.agents/roles/domains/engineering/architecture-advisor.md +53 -0
  80. package/.agents/roles/domains/engineering/build-specialist.md +51 -0
  81. package/.agents/roles/domains/engineering/dependency-governor.md +52 -0
  82. package/.agents/roles/domains/governance/README.md +17 -0
  83. package/.agents/roles/domains/governance/api-governance-specialist.md +51 -0
  84. package/.agents/roles/domains/governance/lint-policy-specialist.md +49 -0
  85. package/.agents/roles/domains/governance/route-governance-specialist.md +52 -0
  86. package/.agents/roles/domains/observability/README.md +11 -0
  87. package/.agents/roles/domains/observability/error-tracker.md +50 -0
  88. package/.agents/roles/domains/observability/event-instrumentation-specialist.md +51 -0
  89. package/.agents/roles/domains/observability/rum-analyst.md +50 -0
  90. package/.agents/roles/domains/performance/README.md +11 -0
  91. package/.agents/roles/domains/performance/asset-optimizer.md +50 -0
  92. package/.agents/roles/domains/performance/performance-auditor.md +56 -0
  93. package/.agents/roles/domains/performance/vitals-analyst.md +50 -0
  94. package/.agents/roles/domains/security-a11y/README.md +11 -0
  95. package/.agents/roles/domains/security-a11y/a11y-auditor.md +50 -0
  96. package/.agents/roles/domains/security-a11y/aria-specialist.md +51 -0
  97. package/.agents/roles/domains/security-a11y/security-reviewer.md +49 -0
  98. package/.agents/roles/domains/testing/README.md +12 -0
  99. package/.agents/roles/domains/testing/coverage-analyst.md +50 -0
  100. package/.agents/roles/domains/testing/e2e-test-specialist.md +51 -0
  101. package/.agents/roles/domains/testing/unit-test-specialist.md +56 -0
  102. package/.agents/roles/domains/testing/verification-reviewer.md +67 -0
  103. package/.agents/rules/README.md +87 -0
  104. package/.agents/rules/common/02-/347/274/226/347/240/201/350/247/204/350/214/203.md +45 -0
  105. package/.agents/rules/common/08-/351/200/232/347/224/250/347/272/246/346/235/237.md +63 -0
  106. package/.agents/rules/common/10-/346/226/207/346/241/243/350/247/204/350/214/203.md +101 -0
  107. package/.agents/rules/common/12-Superpowers/346/211/247/350/241/214/350/247/204/350/214/203.md +46 -0
  108. package/.agents/rules/common/14-/345/256/241/350/256/241/346/261/207/346/212/245/350/247/204/350/214/203.md +107 -0
  109. package/.agents/rules/common/15-visual-gate-wait.md +90 -0
  110. package/.agents/rules/profiles/nestjs/01-/351/241/271/347/233/256/346/246/202/350/277/260.md +27 -0
  111. package/.agents/rules/profiles/nestjs/03-/351/241/271/347/233/256/347/273/223/346/236/204.md +20 -0
  112. package/.agents/rules/profiles/nestjs/04-/346/250/241/345/235/227/347/273/223/346/236/204/350/247/204/350/214/203.md +24 -0
  113. package/.agents/rules/profiles/nestjs/05-/346/216/245/345/217/243/344/270/216/345/245/221/347/272/246/350/247/204/350/214/203.md +24 -0
  114. package/.agents/rules/profiles/nestjs/06-/346/225/260/346/215/256/350/256/277/351/227/256/350/247/204/350/214/203.md +24 -0
  115. package/.agents/rules/profiles/nestjs/07-/351/205/215/347/275/256/344/270/216/350/277/220/350/241/214/346/227/266/350/247/204/350/214/203.md +20 -0
  116. package/.agents/rules/profiles/nestjs/09-/345/274/202/345/270/270/344/270/216/346/227/245/345/277/227/350/247/204/350/214/203.md +20 -0
  117. package/.agents/rules/profiles/nestjs/11-/346/265/213/350/257/225/350/247/204/350/214/203.md +24 -0
  118. package/.agents/rules/profiles/nestjs/13-/344/273/243/347/240/201/346/240/274/345/274/217/345/214/226/344/270/216/346/243/200/346/237/245.md +20 -0
  119. package/.agents/rules/profiles/node-tooling/01-/351/241/271/347/233/256/346/246/202/350/277/260.md +30 -0
  120. package/.agents/rules/profiles/node-tooling/03-/351/241/271/347/233/256/347/273/223/346/236/204.md +37 -0
  121. package/.agents/rules/profiles/node-tooling/04-CLI/344/270/216/346/250/241/345/235/227/350/247/204/350/214/203.md +42 -0
  122. package/.agents/rules/profiles/node-tooling/05-Contract/344/270/216Schema/350/247/204/350/214/203.md +42 -0
  123. package/.agents/rules/profiles/node-tooling/06-/350/277/220/350/241/214/346/227/266/346/226/207/344/273/266/350/247/204/350/214/203.md +30 -0
  124. package/.agents/rules/profiles/node-tooling/07-/346/227/245/345/277/227/344/270/216/351/224/231/350/257/257/345/244/204/347/220/206/350/247/204/350/214/203.md +60 -0
  125. package/.agents/rules/profiles/node-tooling/09-/350/204/232/346/234/254/344/270/216/345/205/245/345/217/243/350/247/204/350/214/203.md +45 -0
  126. package/.agents/rules/profiles/node-tooling/11-/346/265/213/350/257/225/350/247/204/350/214/203.md +41 -0
  127. package/.agents/rules/profiles/node-tooling/13-/344/273/243/347/240/201/346/240/274/345/274/217/345/214/226/344/270/216/346/243/200/346/237/245.md +55 -0
  128. package/.agents/rules/profiles/react/01-/351/241/271/347/233/256/346/246/202/350/277/260.md +29 -0
  129. package/.agents/rules/profiles/react/03-/351/241/271/347/233/256/347/273/223/346/236/204.md +104 -0
  130. package/.agents/rules/profiles/react/04-/347/273/204/344/273/266/350/247/204/350/214/203.md +46 -0
  131. package/.agents/rules/profiles/react/05-API/350/247/204/350/214/203.md +67 -0
  132. package/.agents/rules/profiles/react/06-/350/267/257/347/224/261/350/247/204/350/214/203.md +54 -0
  133. package/.agents/rules/profiles/react/07-/347/212/266/346/200/201/347/256/241/347/220/206.md +226 -0
  134. package/.agents/rules/profiles/react/09-/346/240/267/345/274/217/350/247/204/350/214/203.md +71 -0
  135. package/.agents/rules/profiles/react/11-/346/265/213/350/257/225/350/247/204/350/214/203.md +80 -0
  136. package/.agents/rules/profiles/react/13-/344/273/243/347/240/201/346/240/274/345/274/217/345/214/226/344/270/216/346/243/200/346/237/245.md +159 -0
  137. package/.agents/rules/profiles/springboot/01-/351/241/271/347/233/256/346/246/202/350/277/260.md +31 -0
  138. package/.agents/rules/profiles/springboot/03-/351/241/271/347/233/256/347/273/223/346/236/204.md +37 -0
  139. package/.agents/rules/profiles/springboot/04-/345/210/206/345/261/202/350/247/204/350/214/203.md +33 -0
  140. package/.agents/rules/profiles/springboot/05-/346/216/245/345/217/243/344/270/216/345/245/221/347/272/246/350/247/204/350/214/203.md +51 -0
  141. package/.agents/rules/profiles/springboot/06-/346/225/260/346/215/256/350/256/277/351/227/256/350/247/204/350/214/203.md +34 -0
  142. package/.agents/rules/profiles/springboot/07-/351/205/215/347/275/256/344/270/216/350/277/220/350/241/214/346/227/266/350/247/204/350/214/203.md +38 -0
  143. package/.agents/rules/profiles/springboot/09-/345/274/202/345/270/270/344/270/216/346/227/245/345/277/227/350/247/204/350/214/203.md +48 -0
  144. package/.agents/rules/profiles/springboot/11-/346/265/213/350/257/225/350/247/204/350/214/203.md +43 -0
  145. package/.agents/rules/profiles/springboot/13-/344/273/243/347/240/201/346/240/274/345/274/217/345/214/226/344/270/216/346/243/200/346/237/245.md +48 -0
  146. package/.agents/rules/profiles/vue/01-/351/241/271/347/233/256/346/246/202/350/277/260.md +47 -0
  147. package/.agents/rules/profiles/vue/03-/351/241/271/347/233/256/347/273/223/346/236/204.md +106 -0
  148. package/.agents/rules/profiles/vue/04-/347/273/204/344/273/266/350/247/204/350/214/203.md +61 -0
  149. package/.agents/rules/profiles/vue/05-API/350/247/204/350/214/203.md +67 -0
  150. package/.agents/rules/profiles/vue/06-/350/267/257/347/224/261/350/247/204/350/214/203.md +69 -0
  151. package/.agents/rules/profiles/vue/07-/347/212/266/346/200/201/347/256/241/347/220/206.md +93 -0
  152. package/.agents/rules/profiles/vue/09-/346/240/267/345/274/217/350/247/204/350/214/203.md +67 -0
  153. package/.agents/rules/profiles/vue/11-/346/265/213/350/257/225/350/247/204/350/214/203.md +80 -0
  154. package/.agents/rules/profiles/vue/13-/344/273/243/347/240/201/346/240/274/345/274/217/345/214/226/344/270/216/346/243/200/346/237/245.md +159 -0
  155. package/.agents/skills/README.md +171 -0
  156. package/.agents/skills/common/archive-change/SKILL.md +180 -0
  157. package/.agents/skills/common/branch-code-reviewer/SKILL.md +459 -0
  158. package/.agents/skills/common/branch-code-reviewer/references/business-risk-guide.md +293 -0
  159. package/.agents/skills/common/branch-code-reviewer/references/html-template-guide.md +121 -0
  160. package/.agents/skills/common/config-and-secret-scan/SKILL.md +99 -0
  161. package/.agents/skills/common/create-proposal/SKILL.md +192 -0
  162. package/.agents/skills/common/create-proposal/evals/evals.json +16 -0
  163. package/.agents/skills/common/create-proposal/evals/train_queries.json +18 -0
  164. package/.agents/skills/common/create-proposal/evals/validation_queries.json +18 -0
  165. package/.agents/skills/common/create-proposal/references/interaction-spec-template.md +42 -0
  166. package/.agents/skills/common/create-test/SKILL.md +292 -0
  167. package/.agents/skills/common/dependency-impact-graph/SKILL.md +80 -0
  168. package/.agents/skills/common/execute-task/SKILL.md +206 -0
  169. package/.agents/skills/common/execute-task/evals/evals.json +16 -0
  170. package/.agents/skills/common/execute-task/evals/train_queries.json +18 -0
  171. package/.agents/skills/common/execute-task/evals/validation_queries.json +18 -0
  172. package/.agents/skills/common/find-skills/SKILL.md +144 -0
  173. package/.agents/skills/common/install-ai-spec-auto/SKILL.md +260 -0
  174. package/.agents/skills/common/install-ai-spec-auto/evals/evals.json +17 -0
  175. package/.agents/skills/common/install-ai-spec-auto/evals/train_queries.json +18 -0
  176. package/.agents/skills/common/install-ai-spec-auto/evals/validation_queries.json +18 -0
  177. package/.agents/skills/common/project-init/SKILL.md +178 -0
  178. package/.agents/skills/common/project-init/evals/evals.json +16 -0
  179. package/.agents/skills/common/project-init/evals/train_queries.json +18 -0
  180. package/.agents/skills/common/project-init/evals/validation_queries.json +18 -0
  181. package/.agents/skills/common/project-init/references/custom-rule-generation.md +89 -0
  182. package/.agents/skills/common/project-init/references/deep-scan-rules.md +67 -0
  183. package/.agents/skills/common/project-init/references/output-contracts.md +71 -0
  184. package/.agents/skills/common/project-init/references/repo-fact-gathering.md +83 -0
  185. package/.agents/skills/common/project-init/references/scope-resolution.md +76 -0
  186. package/.agents/skills/common/project-init/scripts/inspect-project.js +112 -0
  187. package/.agents/skills/common/skill-creator/LICENSE.txt +202 -0
  188. package/.agents/skills/common/skill-creator/SKILL.md +370 -0
  189. package/.agents/skills/common/skill-creator/evals/evals.json +16 -0
  190. package/.agents/skills/common/skill-creator/evals/train_queries.json +18 -0
  191. package/.agents/skills/common/skill-creator/evals/validation_queries.json +18 -0
  192. package/.agents/skills/common/skill-creator/references/output-patterns.md +82 -0
  193. package/.agents/skills/common/skill-creator/references/workflows.md +28 -0
  194. package/.agents/skills/common/skill-creator/scripts/init_skill.py +209 -0
  195. package/.agents/skills/common/skill-creator/scripts/package_skill.py +110 -0
  196. package/.agents/skills/common/skill-creator/scripts/quick_validate.py +51 -0
  197. package/.agents/skills/common/skill-optimizer/SKILL.md +102 -0
  198. package/.agents/skills/common/skill-optimizer/evals/evals.json +16 -0
  199. package/.agents/skills/common/skill-optimizer/evals/train_queries.json +18 -0
  200. package/.agents/skills/common/skill-optimizer/evals/validation_queries.json +18 -0
  201. package/.agents/skills/common/skill-optimizer/references/design-patterns.md +26 -0
  202. package/.agents/skills/common/skill-optimizer/references/review-checklist.md +22 -0
  203. package/.agents/skills/common/using-superpowers/SKILL.md +151 -0
  204. package/.agents/skills/common/wait-for-gate-signal/SKILL.md +85 -0
  205. package/.agents/skills/domains/README.md +19 -0
  206. package/.agents/skills/domains/ui-ux-pro-max/SKILL.md +58 -0
  207. package/.agents/skills/domains/web/design-analysis/SKILL.md +89 -0
  208. package/.agents/skills/domains/web/design-analysis/rules/analysis-order.md +61 -0
  209. package/.agents/skills/domains/web/design-analysis/rules/analysis-priorities.md +136 -0
  210. package/.agents/skills/domains/web/design-analysis/rules/checklist-common-misses.md +107 -0
  211. package/.agents/skills/domains/web/design-analysis/rules/implementation-common-errors.md +204 -0
  212. package/.agents/skills/domains/web/design-analysis/rules/implementation-guidelines.md +211 -0
  213. package/.agents/skills/domains/web/design-analysis/rules/output-analysis-checklist.md +247 -0
  214. package/.agents/skills/domains/web/design-analysis/rules/tools-design-guidelines.md +108 -0
  215. package/.agents/skills/domains/web/design-analysis/rules/workflow-element-extraction.md +162 -0
  216. package/.agents/skills/domains/web/design-analysis/rules/workflow-layout-map.md +131 -0
  217. package/.agents/skills/domains/web/design-analysis/rules/workflow-output-checklist.md +70 -0
  218. package/.agents/skills/domains/web/design-analysis/rules/workflow-style-summary.md +91 -0
  219. package/.agents/skills/domains/web/route-permission-map/SKILL.md +103 -0
  220. package/.agents/skills/domains/web/ui-verification/SKILL.md +114 -0
  221. package/.agents/skills/domains/web/ui-verification/evals/evals.json +16 -0
  222. package/.agents/skills/domains/web/ui-verification/evals/train_queries.json +18 -0
  223. package/.agents/skills/domains/web/ui-verification/evals/validation_queries.json +18 -0
  224. package/.agents/skills/domains/web/ui-verification/rules/comparison-content-image.md +34 -0
  225. package/.agents/skills/domains/web/ui-verification/rules/comparison-content-text.md +30 -0
  226. package/.agents/skills/domains/web/ui-verification/rules/comparison-hierarchy.md +33 -0
  227. package/.agents/skills/domains/web/ui-verification/rules/comparison-layout.md +35 -0
  228. package/.agents/skills/domains/web/ui-verification/rules/errors-alignment.md +42 -0
  229. package/.agents/skills/domains/web/ui-verification/rules/errors-button-dimensions.md +28 -0
  230. package/.agents/skills/domains/web/ui-verification/rules/errors-button-position.md +25 -0
  231. package/.agents/skills/domains/web/ui-verification/rules/errors-css-priority.md +50 -0
  232. package/.agents/skills/domains/web/ui-verification/rules/errors-flex-column-width.md +46 -0
  233. package/.agents/skills/domains/web/ui-verification/rules/errors-flex-layout.md +46 -0
  234. package/.agents/skills/domains/web/ui-verification/rules/errors-grid-container-width.md +44 -0
  235. package/.agents/skills/domains/web/ui-verification/rules/errors-page-container-width.md +39 -0
  236. package/.agents/skills/domains/web/ui-verification/rules/tools-browser-navigation.md +53 -0
  237. package/.agents/skills/domains/web/ui-verification/rules/tools-design-guidelines.md +53 -0
  238. package/.agents/skills/domains/web/ui-verification/rules/workflow-checklist.md +27 -0
  239. package/.agents/skills/domains/web/ui-verification/rules/workflow-problem-list.md +56 -0
  240. package/.agents/skills/domains/web/ui-verification/rules/workflow-reflection.md +44 -0
  241. package/.agents/skills/domains/web/ui-verification/rules/writing-alignment.md +44 -0
  242. package/.agents/skills/domains/web/ui-verification/rules/writing-element-completeness.md +63 -0
  243. package/.agents/skills/domains/web/ui-verification/rules/writing-list-layout.md +75 -0
  244. package/.agents/skills/domains/web/ui-verification/rules/writing-page-container-width.md +37 -0
  245. package/.agents/skills/domains/web/web-design-guidelines/SKILL.md +40 -0
  246. package/.agents/skills/profiles/nestjs/README.md +4 -0
  247. package/.agents/skills/profiles/node-tooling/README.md +9 -0
  248. package/.agents/skills/profiles/react/create-api/SKILL.md +145 -0
  249. package/.agents/skills/profiles/react/create-component/SKILL.md +160 -0
  250. package/.agents/skills/profiles/react/create-route/SKILL.md +168 -0
  251. package/.agents/skills/profiles/react/create-store/SKILL.md +262 -0
  252. package/.agents/skills/profiles/react/theme-variables/SKILL.md +82 -0
  253. package/.agents/skills/profiles/react/vercel-composition-patterns/AGENTS.md +899 -0
  254. package/.agents/skills/profiles/react/vercel-composition-patterns/SKILL.md +81 -0
  255. package/.agents/skills/profiles/react/vercel-composition-patterns/rules/architecture-avoid-boolean-props.md +100 -0
  256. package/.agents/skills/profiles/react/vercel-composition-patterns/rules/architecture-compound-components.md +112 -0
  257. package/.agents/skills/profiles/react/vercel-composition-patterns/rules/patterns-children-over-render-props.md +87 -0
  258. package/.agents/skills/profiles/react/vercel-composition-patterns/rules/patterns-explicit-variants.md +100 -0
  259. package/.agents/skills/profiles/react/vercel-composition-patterns/rules/state-context-interface.md +191 -0
  260. package/.agents/skills/profiles/react/vercel-composition-patterns/rules/state-decouple-implementation.md +113 -0
  261. package/.agents/skills/profiles/react/vercel-composition-patterns/rules/state-lift-state.md +125 -0
  262. package/.agents/skills/profiles/react/vercel-react-best-practices/AGENTS.md +2934 -0
  263. package/.agents/skills/profiles/react/vercel-react-best-practices/SKILL.md +136 -0
  264. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/advanced-event-handler-refs.md +55 -0
  265. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/advanced-init-once.md +42 -0
  266. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/advanced-use-latest.md +39 -0
  267. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/async-api-routes.md +38 -0
  268. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/async-defer-await.md +80 -0
  269. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/async-dependencies.md +51 -0
  270. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/async-parallel.md +28 -0
  271. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/async-suspense-boundaries.md +99 -0
  272. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/bundle-barrel-imports.md +59 -0
  273. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/bundle-conditional.md +31 -0
  274. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/bundle-defer-third-party.md +49 -0
  275. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/bundle-dynamic-imports.md +35 -0
  276. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/bundle-preload.md +50 -0
  277. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/client-event-listeners.md +74 -0
  278. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/client-localstorage-schema.md +71 -0
  279. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/client-passive-event-listeners.md +48 -0
  280. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/client-swr-dedup.md +56 -0
  281. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/js-batch-dom-css.md +107 -0
  282. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/js-cache-function-results.md +80 -0
  283. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/js-cache-property-access.md +28 -0
  284. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/js-cache-storage.md +70 -0
  285. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/js-combine-iterations.md +32 -0
  286. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/js-early-exit.md +50 -0
  287. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/js-hoist-regexp.md +45 -0
  288. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/js-index-maps.md +37 -0
  289. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/js-length-check-first.md +49 -0
  290. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/js-min-max-loop.md +82 -0
  291. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/js-set-map-lookups.md +24 -0
  292. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/js-tosorted-immutable.md +57 -0
  293. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/rendering-activity.md +26 -0
  294. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/rendering-animate-svg-wrapper.md +47 -0
  295. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/rendering-conditional-render.md +40 -0
  296. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/rendering-content-visibility.md +38 -0
  297. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/rendering-hoist-jsx.md +46 -0
  298. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/rendering-hydration-no-flicker.md +82 -0
  299. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/rendering-hydration-suppress-warning.md +30 -0
  300. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/rendering-svg-precision.md +28 -0
  301. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/rendering-usetransition-loading.md +75 -0
  302. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/rerender-defer-reads.md +39 -0
  303. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/rerender-dependencies.md +45 -0
  304. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/rerender-derived-state-no-effect.md +40 -0
  305. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/rerender-derived-state.md +29 -0
  306. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/rerender-functional-setstate.md +74 -0
  307. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/rerender-lazy-state-init.md +58 -0
  308. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/rerender-memo-with-default-value.md +38 -0
  309. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/rerender-memo.md +44 -0
  310. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/rerender-move-effect-to-event.md +45 -0
  311. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/rerender-simple-expression-in-memo.md +35 -0
  312. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/rerender-transitions.md +40 -0
  313. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/rerender-use-ref-transient-values.md +73 -0
  314. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/server-after-nonblocking.md +73 -0
  315. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/server-auth-actions.md +96 -0
  316. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/server-cache-lru.md +41 -0
  317. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/server-cache-react.md +76 -0
  318. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/server-dedup-props.md +65 -0
  319. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/server-parallel-fetching.md +83 -0
  320. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/server-serialization.md +38 -0
  321. package/.agents/skills/profiles/springboot/README.md +10 -0
  322. package/.agents/skills/profiles/vue/create-api/SKILL.md +105 -0
  323. package/.agents/skills/profiles/vue/create-component/SKILL.md +76 -0
  324. package/.agents/skills/profiles/vue/create-route/SKILL.md +141 -0
  325. package/.agents/skills/profiles/vue/create-store/SKILL.md +97 -0
  326. package/.agents/skills/profiles/vue/create-view/SKILL.md +81 -0
  327. package/.agents/skills/profiles/vue/theme-variables/SKILL.md +73 -0
  328. package/.agents/skills/profiles/vue/vue-best-practices/SKILL.md +166 -0
  329. package/.agents/skills/profiles/vue/vue-best-practices/references/animation-class-based-technique.md +254 -0
  330. package/.agents/skills/profiles/vue/vue-best-practices/references/animation-state-driven-technique.md +291 -0
  331. package/.agents/skills/profiles/vue/vue-best-practices/references/component-async.md +97 -0
  332. package/.agents/skills/profiles/vue/vue-best-practices/references/component-data-flow.md +307 -0
  333. package/.agents/skills/profiles/vue/vue-best-practices/references/component-fallthrough-attrs.md +174 -0
  334. package/.agents/skills/profiles/vue/vue-best-practices/references/component-keep-alive.md +137 -0
  335. package/.agents/skills/profiles/vue/vue-best-practices/references/component-slots.md +216 -0
  336. package/.agents/skills/profiles/vue/vue-best-practices/references/component-suspense.md +228 -0
  337. package/.agents/skills/profiles/vue/vue-best-practices/references/component-teleport.md +108 -0
  338. package/.agents/skills/profiles/vue/vue-best-practices/references/component-transition-group.md +128 -0
  339. package/.agents/skills/profiles/vue/vue-best-practices/references/component-transition.md +125 -0
  340. package/.agents/skills/profiles/vue/vue-best-practices/references/composables.md +290 -0
  341. package/.agents/skills/profiles/vue/vue-best-practices/references/directives.md +162 -0
  342. package/.agents/skills/profiles/vue/vue-best-practices/references/perf-avoid-component-abstraction-in-lists.md +159 -0
  343. package/.agents/skills/profiles/vue/vue-best-practices/references/perf-v-once-v-memo-directives.md +182 -0
  344. package/.agents/skills/profiles/vue/vue-best-practices/references/perf-virtualize-large-lists.md +187 -0
  345. package/.agents/skills/profiles/vue/vue-best-practices/references/plugins.md +166 -0
  346. package/.agents/skills/profiles/vue/vue-best-practices/references/reactivity.md +344 -0
  347. package/.agents/skills/profiles/vue/vue-best-practices/references/render-functions.md +201 -0
  348. package/.agents/skills/profiles/vue/vue-best-practices/references/sfc.md +310 -0
  349. package/.agents/skills/profiles/vue/vue-best-practices/references/state-management.md +135 -0
  350. package/.agents/skills/profiles/vue/vue-best-practices/references/updated-hook-performance.md +187 -0
  351. package/.agents/templates/common/README.md +23 -0
  352. package/.agents/templates/common/bugfix.md +22 -0
  353. package/.agents/templates/common/create-expert-package.md +458 -0
  354. package/.agents/templates/common/mock-page.md +28 -0
  355. package/.agents/templates/common/new-component.md +25 -0
  356. package/.agents/templates/common/new-page.md +31 -0
  357. package/.cursor/mcp.json +36 -0
  358. package/.qoder/README.md +114 -0
  359. package/.qoder/commands +1 -0
  360. package/.qoder/mcp.json +26 -0
  361. package/.qoder/rules +1 -0
  362. package/.qoder/skills +1 -0
  363. package/LICENSE +21 -0
  364. package/README.md +433 -0
  365. package/bin/archive-change.js +474 -0
  366. package/bin/check-command.js +62 -0
  367. package/bin/cli.js +295 -0
  368. package/bin/command-template-renderer.js +40 -0
  369. package/bin/context-command.js +102 -0
  370. package/bin/demo-runtime-smoke.js +760 -0
  371. package/bin/execution-semantics.js +821 -0
  372. package/bin/executor-command.js +93 -0
  373. package/bin/expert-dispatch.js +334 -0
  374. package/bin/expert-executor.js +1148 -0
  375. package/bin/guard-command.js +52 -0
  376. package/bin/hub-command.js +876 -0
  377. package/bin/ide-command.js +242 -0
  378. package/bin/init-command.js +193 -0
  379. package/bin/install-workflow.js +2983 -0
  380. package/bin/manifest-export.js +34 -0
  381. package/bin/profile-registry.js +90 -0
  382. package/bin/protocol-workflow.js +446 -0
  383. package/bin/repair-command.js +161 -0
  384. package/bin/repo-map.js +177 -0
  385. package/bin/report-command.js +236 -0
  386. package/bin/runtime-bootstrap.js +428 -0
  387. package/bin/runtime-embedded.js +101 -0
  388. package/bin/runtime-fallback.js +106 -0
  389. package/bin/runtime-launcher.js +116 -0
  390. package/bin/runtime-paths.js +177 -0
  391. package/bin/runtime-registry.js +289 -0
  392. package/bin/runtime-state.js +2541 -0
  393. package/bin/scan.js +96 -0
  394. package/bin/self-upgrade.js +206 -0
  395. package/bin/skill-spec-validator.js +457 -0
  396. package/bin/spec-command.js +366 -0
  397. package/bin/superpowers.js +384 -0
  398. package/bin/sync-command.js +59 -0
  399. package/bin/sync.js +1904 -0
  400. package/bin/task-orchestrator-adapter.js +341 -0
  401. package/bin/task-orchestrator-extractor.js +274 -0
  402. package/bin/task-orchestrator-runner.js +1208 -0
  403. package/bin/telemetry/README.md +66 -0
  404. package/bin/telemetry/aspect.js +153 -0
  405. package/bin/telemetry/collect.js +67 -0
  406. package/bin/telemetry/config.js +114 -0
  407. package/bin/telemetry/defaults.json +5 -0
  408. package/bin/telemetry/healthcheck.js +195 -0
  409. package/bin/telemetry/identity.js +53 -0
  410. package/bin/telemetry/index.js +25 -0
  411. package/bin/telemetry/reporter.js +83 -0
  412. package/bin/telemetry/safe.js +39 -0
  413. package/bin/validate-registry.js +740 -0
  414. package/bin/visual-bridge-config.js +117 -0
  415. package/bin/visual-bridge.js +287 -0
  416. package/bin/visual-command.js +432 -0
  417. package/bin/worktree-command.js +194 -0
  418. package/configs/common/.editorconfig +15 -0
  419. package/configs/common/.husky/commit-msg +4 -0
  420. package/configs/common/.husky/pre-commit +4 -0
  421. package/configs/common/.lintstagedrc +11 -0
  422. package/configs/common/.prettierignore +11 -0
  423. package/configs/common/.prettierrc.json +11 -0
  424. package/configs/common/.stylelintignore +14 -0
  425. package/configs/common/.stylelintrc.json +21 -0
  426. package/configs/common/commitlint.config.js +3 -0
  427. package/configs/profiles/nestjs/.gitkeep +1 -0
  428. package/configs/profiles/node-tooling/.gitkeep +1 -0
  429. package/configs/profiles/react/.eslintignore +6 -0
  430. package/configs/profiles/react/.eslintrc.js +16 -0
  431. package/configs/profiles/react/.stylelintrc.json +18 -0
  432. package/configs/profiles/springboot/.gitkeep +1 -0
  433. package/configs/profiles/vue/.eslintignore +7 -0
  434. package/configs/profiles/vue/.eslintrc.cjs +17 -0
  435. package/contracts/README.md +28 -0
  436. package/contracts/fixtures/asset-package.fixture.json +26 -0
  437. package/contracts/fixtures/asset-usage-feedback.fixture.json +14 -0
  438. package/contracts/fixtures/evidence-report.fixture.json +28 -0
  439. package/contracts/fixtures/manifest.fixture.json +20 -0
  440. package/contracts/fixtures/run-event.fixture.json +15 -0
  441. package/contracts/schemas/asset-package.schema.json +76 -0
  442. package/contracts/schemas/asset-usage-feedback.schema.json +57 -0
  443. package/contracts/schemas/evidence-report.schema.json +60 -0
  444. package/contracts/schemas/manifest.schema.json +63 -0
  445. package/contracts/schemas/run-event.schema.json +72 -0
  446. package/install.ps1 +35 -0
  447. package/install.sh +17 -0
  448. package/internal/ai-protocol-workflow.js +5600 -0
  449. package/internal/hub-client.js +98 -0
  450. package/internal/hub-sync-selection.js +69 -0
  451. package/internal/visual-hooks/README.md +481 -0
  452. package/internal/visual-hooks/config-loader.js +218 -0
  453. package/internal/visual-hooks/control-puller.js +206 -0
  454. package/internal/visual-hooks/gate-signal.js +150 -0
  455. package/internal/visual-hooks/inbox-consumer.js +469 -0
  456. package/internal/visual-hooks/index.js +197 -0
  457. package/internal/visual-hooks/push-client.js +189 -0
  458. package/internal/visual-hooks/receipt-pusher.js +176 -0
  459. package/internal/visual-hooks/runtime-state-pusher.js +128 -0
  460. package/openspec/changes/.gitkeep +0 -0
  461. package/openspec/changes/archive/.gitkeep +0 -0
  462. package/openspec/config.yaml.template +52 -0
  463. package/openspec/schemas/expert-delivery/schema.yaml +68 -0
  464. package/openspec/schemas/expert-delivery/templates/checklist.md +39 -0
  465. package/openspec/schemas/expert-delivery/templates/design.md +61 -0
  466. package/openspec/schemas/expert-delivery/templates/iterations.md +25 -0
  467. package/openspec/schemas/expert-delivery/templates/proposal.md +45 -0
  468. package/openspec/schemas/expert-delivery/templates/spec.md +29 -0
  469. package/openspec/schemas/expert-delivery/templates/tasks.md +24 -0
  470. package/openspec/specs/.gitkeep +0 -0
  471. package/package.json +73 -0
  472. package/scripts/acceptance-zero-intrusion.sh +168 -0
  473. package/scripts/hub-sync-assets.config.example.json +296 -0
  474. package/scripts/hub-sync-assets.js +2038 -0
  475. package/scripts/local-verify.sh +280 -0
  476. package/scripts/post-publish-auto-fix-check.js +404 -0
  477. package/scripts/post-publish-verify.sh +175 -0
  478. package/scripts/setup-cursor-manual-test.sh +107 -0
  479. package/scripts/setup-cursor-spec-archive-test.sh +111 -0
  480. package/scripts/setup-visual-integration.sh +225 -0
  481. package/scripts/test-integration.sh +176 -0
  482. package/scripts/update-test-project.sh +93 -0
  483. package/scripts/upload-four-web.sh +57 -0
  484. package/scripts/verify-install-ps1-bom.js +26 -0
  485. package/src/agent/agent-context.js +259 -0
  486. package/src/agent/agent-profile.js +185 -0
  487. package/src/agent/agent-templates.js +161 -0
  488. package/src/agent/agent-types.js +108 -0
  489. package/src/agent/collaboration-protocol.js +333 -0
  490. package/src/agent/conflict-handler.js +364 -0
  491. package/src/agent/file-permission.js +121 -0
  492. package/src/agent/index.js +38 -0
  493. package/src/agent/permission-audit.js +151 -0
  494. package/src/agent/review-repair-loop.js +270 -0
  495. package/src/agent/tool-permission.js +101 -0
  496. package/src/asset/asset-dependency.js +322 -0
  497. package/src/asset/asset-feedback.js +350 -0
  498. package/src/asset/asset-fork.js +300 -0
  499. package/src/asset/asset-install.js +278 -0
  500. package/src/asset/asset-installer.js +497 -0
  501. package/src/asset/asset-lifecycle.js +324 -0
  502. package/src/asset/asset-manager.js +245 -0
  503. package/src/asset/asset-package-manager.js +349 -0
  504. package/src/asset/asset-package.js +186 -0
  505. package/src/asset/asset-quality.js +262 -0
  506. package/src/asset/asset-registry.js +387 -0
  507. package/src/asset/asset-version.js +293 -0
  508. package/src/asset/index.js +86 -0
  509. package/src/cache/agent-profile-cache.js +59 -0
  510. package/src/cache/asset-cache.js +63 -0
  511. package/src/cache/global-cache.js +61 -0
  512. package/src/cache/manifest-cache.js +30 -0
  513. package/src/check/check-service.js +32 -0
  514. package/src/config/config-layer.js +343 -0
  515. package/src/config/config-loader.js +60 -0
  516. package/src/config/defaults.js +49 -0
  517. package/src/connectors/hub/asset-package.js +72 -0
  518. package/src/connectors/hub/asset-usage-feedback.js +46 -0
  519. package/src/connectors/hub/hub-connector.js +44 -0
  520. package/src/connectors/hub/index.js +21 -0
  521. package/src/connectors/visual/evidence-report.js +49 -0
  522. package/src/connectors/visual/index.js +15 -0
  523. package/src/connectors/visual/queue.js +41 -0
  524. package/src/connectors/visual/run-event.js +81 -0
  525. package/src/connectors/visual/visual-connector.js +77 -0
  526. package/src/context/context-budget.js +59 -0
  527. package/src/context/context-builder.js +285 -0
  528. package/src/context/context-loader.js +116 -0
  529. package/src/context/context-planner.js +158 -0
  530. package/src/context/types.js +96 -0
  531. package/src/contracts/index.js +63 -0
  532. package/src/executor/executor-registry.js +78 -0
  533. package/src/executor/executor-result-parser.js +44 -0
  534. package/src/executor/executor-runner.js +141 -0
  535. package/src/executor/executor-selector.js +139 -0
  536. package/src/executor/executor-timeout.js +36 -0
  537. package/src/executor/providers/base-provider-utils.js +189 -0
  538. package/src/executor/providers/claude-code-executor-provider.js +128 -0
  539. package/src/executor/providers/codex-executor-provider.js +126 -0
  540. package/src/executor/providers/cursor-executor-provider.js +99 -0
  541. package/src/executor/types.js +137 -0
  542. package/src/git/branch-manager.js +71 -0
  543. package/src/git/dirty-checker.js +43 -0
  544. package/src/git/dirty-strategy-handler.js +29 -0
  545. package/src/git/git-command.js +37 -0
  546. package/src/git/git-repository-detector.js +45 -0
  547. package/src/git/multi-repo-worktree-planner.js +88 -0
  548. package/src/git/policy.js +19 -0
  549. package/src/git/strategies/block-dirty-strategy.js +34 -0
  550. package/src/git/strategies/ignore-dirty-strategy.js +33 -0
  551. package/src/git/strategies/patch-snapshot-strategy.js +53 -0
  552. package/src/git/strategies/wip-commit-strategy.js +38 -0
  553. package/src/git/types.js +71 -0
  554. package/src/git/worktree-manager.js +85 -0
  555. package/src/governance/asset-review.js +351 -0
  556. package/src/governance/audit-log.js +368 -0
  557. package/src/governance/gray-release.js +312 -0
  558. package/src/governance/index.js +31 -0
  559. package/src/governance/policy-types.js +56 -0
  560. package/src/governance/rbac-types.js +171 -0
  561. package/src/governance/rbac.js +382 -0
  562. package/src/governance/rollback.js +360 -0
  563. package/src/governance/security-policy.js +354 -0
  564. package/src/hook/hook-config-writer.js +125 -0
  565. package/src/hub/hub-client.js +186 -0
  566. package/src/hub/hub-config.js +39 -0
  567. package/src/hub/project-facts.js +31 -0
  568. package/src/hub/runtime-feedback-reporter.js +55 -0
  569. package/src/ide/adapters/adapter-protocol.js +385 -0
  570. package/src/ide/adapters/claude-adapter.js +419 -0
  571. package/src/ide/adapters/codex-adapter.js +60 -0
  572. package/src/ide/adapters/cursor-adapter.js +484 -0
  573. package/src/ide/adapters/index.js +24 -0
  574. package/src/ide/anchors/markdown-anchor-writer.js +152 -0
  575. package/src/ide/ide-service.js +270 -0
  576. package/src/ide/ide-types.js +94 -0
  577. package/src/ide/links/link-mode-resolver.js +160 -0
  578. package/src/ide/registry/ide-registry-builder.js +165 -0
  579. package/src/incident/incident-writer.js +47 -0
  580. package/src/incident/types.js +22 -0
  581. package/src/init/ide-linker.js +126 -0
  582. package/src/init/ide-pointer-injector.js +75 -0
  583. package/src/init/init-applier.js +197 -0
  584. package/src/init/init-plan.js +294 -0
  585. package/src/init/init-service.js +65 -0
  586. package/src/init/manifest-installer.js +302 -0
  587. package/src/init/types.js +26 -0
  588. package/src/project/config-writer.js +83 -0
  589. package/src/project/context-index-writer.js +82 -0
  590. package/src/project/json-utils.js +72 -0
  591. package/src/project/local-state-writer.js +50 -0
  592. package/src/project/lock-file-writer.js +98 -0
  593. package/src/project/manifest-writer.js +126 -0
  594. package/src/project/policy-config-writer.js +91 -0
  595. package/src/project/project-config-writer.js +74 -0
  596. package/src/project/project-files.js +39 -0
  597. package/src/project/registry-index-writer.js +43 -0
  598. package/src/project/workspace-config-writer.js +63 -0
  599. package/src/run/index.js +11 -0
  600. package/src/run/run-id.js +32 -0
  601. package/src/run/run-service.js +269 -0
  602. package/src/run/run-store.js +80 -0
  603. package/src/scanner/aggregator/detection-aggregator.js +23 -0
  604. package/src/scanner/boundary/boundary-resolver.js +229 -0
  605. package/src/scanner/detectors/detector-registry.js +44 -0
  606. package/src/scanner/detectors/fastapi-detector.js +46 -0
  607. package/src/scanner/detectors/go-detector.js +46 -0
  608. package/src/scanner/detectors/nestjs-detector.js +57 -0
  609. package/src/scanner/detectors/nextjs-detector.js +52 -0
  610. package/src/scanner/detectors/react-vite-detector.js +52 -0
  611. package/src/scanner/detectors/react-webpack-detector.js +57 -0
  612. package/src/scanner/detectors/springboot-detector.js +46 -0
  613. package/src/scanner/detectors/springcloud-detector.js +46 -0
  614. package/src/scanner/detectors/springmvc-detector.js +46 -0
  615. package/src/scanner/detectors/vue-vite-detector.js +52 -0
  616. package/src/scanner/engine.js +72 -0
  617. package/src/scanner/facts/fact-extractor.js +211 -0
  618. package/src/scanner/types.js +30 -0
  619. package/src/security/asset-tamper-checker.js +188 -0
  620. package/src/security/checksum.js +40 -0
  621. package/src/spec/spec-writer.js +302 -0
  622. package/src/state-machine/circuit-breaker.js +112 -0
  623. package/src/state-machine/escape-hatch.js +49 -0
  624. package/src/state-machine/stage-runner.js +281 -0
  625. package/src/state-machine/state-machine.js +24 -0
  626. package/src/state-machine/transition-guard.js +36 -0
  627. package/src/state-machine/types.js +37 -0
  628. package/src/sync/sync-service.js +192 -0
  629. package/src/visual/agent-visual.js +142 -0
  630. package/src/visual/event-gateway.js +357 -0
  631. package/src/visual/event-mapper.js +128 -0
  632. package/src/visual/hook-dashboard.js +216 -0
  633. package/src/visual/index.js +27 -0
  634. package/src/visual/metrics.js +287 -0
  635. package/src/visual/privacy-filter.js +100 -0
  636. package/src/visual/risk-board.js +252 -0
  637. package/src/visual/timeline.js +245 -0
  638. package/src/visual/visual-client.js +94 -0
  639. package/src/visual/visual-config.js +40 -0
  640. package/src/visual/visual-reporter.js +88 -0
@@ -0,0 +1,2983 @@
1
+ #!/usr/bin/env node
2
+ const fs = require('fs');
3
+ const os = require('os');
4
+ const path = require('path');
5
+ const { spawnSync } = require('child_process');
6
+ const readline = require('readline');
7
+ const readlinePromises = require('readline/promises');
8
+ const {
9
+ readProfilesRegistry,
10
+ resolveProfileId,
11
+ getProfileEntries,
12
+ formatSupportedProfiles,
13
+ } = require('./profile-registry');
14
+ const {
15
+ normalizeSuperpowersManifest,
16
+ buildSuperpowersState,
17
+ writeSuperpowersState,
18
+ readSuperpowersState,
19
+ shouldExposeSkillToIde,
20
+ upsertManagedAgentsBlock,
21
+ } = require('./superpowers');
22
+ const {
23
+ VISUAL_BRIDGE_STATE_REL_PATH,
24
+ normalizeVisualBridgeManifest,
25
+ buildVisualBridgeState,
26
+ writeVisualBridgeState,
27
+ readVisualBridgeState,
28
+ } = require('./visual-bridge-config');
29
+ const { readRenderedCommandTemplate } = require('./command-template-renderer');
30
+
31
+ const PKG_ROOT = path.join(__dirname, '..');
32
+ const VERSION = '2.0.0';
33
+ const DEFAULT_PROFILE = 'vue';
34
+ const DEFAULT_LEVEL = 'L3';
35
+ const DEFAULT_IDE_FILTER = 'default';
36
+ const DEFAULT_IDES = ['cursor', 'claude'];
37
+ const ALL_IDES = ['claude', 'cursor', 'codex', 'opencode', 'trae', 'qoder'];
38
+ const CURSOR_PROTOCOL_COMMAND_EXPECTATIONS = [
39
+ ['spec-start.md', ['protocol-step --target . --user-input']],
40
+ ['spec-continue.md', ['protocol-update --target . --user-input', 'protocol-advance --target . --json']],
41
+ ['spec-update.md', ['protocol-update --target . --user-input']],
42
+ ['spec-status.md', ['protocol-status --target . --json']],
43
+ ['spec-stop.md', ['protocol-stop --target . --json']],
44
+ ['spec-orchestrate.md', [
45
+ 'protocol-step --target . --user-input',
46
+ 'protocol-update --target . --user-input',
47
+ 'protocol-advance --target . --json',
48
+ ]],
49
+ ];
50
+ const PROJECT_SPECIFIC_RULES = new Set(['01-项目概述.md', '03-项目结构.md']);
51
+ const UPDATE_RULE_PROTECTED_FILES = new Set(['README.md', '12-Superpowers执行规范.md']);
52
+ const UPDATE_RULE_MODES = {
53
+ LEGACY: 'legacy',
54
+ STANDARD: 'standard',
55
+ SELECTED: 'selected',
56
+ ALL: 'all',
57
+ };
58
+ const CUSTOMIZABLE_RULES = [
59
+ ['01-项目概述.md', '项目定位、技术栈、业务边界、关键约束'],
60
+ ['03-项目结构.md', '目录树、分层设计、模块职责、组织约定'],
61
+ ['04-组件规范.md', 'SFC 结构、Props/Emits、组件目录、拆分策略'],
62
+ ['05-API规范.md', '接口目录、请求封装、命名约定、错误处理'],
63
+ ['06-路由规范.md', '路由配置、懒加载、导航守卫、目录结构'],
64
+ ['07-状态管理.md', 'Store 目录、模块划分、命名约定'],
65
+ ['09-样式规范.md', 'CSS Modules/Scoped、主题变量、全局样式'],
66
+ ];
67
+ const PROFILE_SUMMARIES = {
68
+ vue: 'Frontend / Vue',
69
+ react: 'Frontend / React',
70
+ nestjs: 'Backend / NestJS',
71
+ springboot: 'Backend / Spring Boot',
72
+ 'node-tooling': 'Tooling / Node.js',
73
+ };
74
+ const DEFAULT_CUSTOM_RULE_SELECTION = CUSTOMIZABLE_RULES.map(([name]) => name);
75
+ const UPDATE_MODULE_ITEMS = [
76
+ ['updateSkills', 'Skills(技能)'],
77
+ ['updateRules', 'Rules(规范规则)'],
78
+ ['updateConfigs', 'Configs(lint/format)'],
79
+ ['updateCommands', 'Commands(命令模板)'],
80
+ ['updateIdeLinks', 'IDE Links(IDE 链接)'],
81
+ ['updateOpenSpec', 'OpenSpec'],
82
+ ['updateUipro', 'UI UX Pro Max'],
83
+ ];
84
+ const INSTALL_STATE_FILE = '.ai-spec/install-state.json';
85
+ const SHARED_CONFIG_FILES = [
86
+ '.prettierrc.json',
87
+ '.prettierignore',
88
+ '.stylelintrc.json',
89
+ '.stylelintignore',
90
+ '.eslintrc.js',
91
+ '.eslintrc.cjs',
92
+ '.eslintignore',
93
+ '.lintstagedrc',
94
+ 'commitlint.config.js',
95
+ '.editorconfig',
96
+ ];
97
+ const LINT_DEP_SPECS = ['eslint', 'prettier', 'stylelint', 'stylelint-config-standard'];
98
+ const VUE_LINT_DEP_SPECS = ['stylelint-config-html', 'stylelint-config-recommended-vue', 'postcss-html'];
99
+ const HUSKY_DEP_SPECS = ['husky@8', 'lint-staged@15', '@commitlint/cli@19', '@commitlint/config-conventional@19'];
100
+
101
+ const C = {
102
+ red: '\x1b[31m',
103
+ green: '\x1b[32m',
104
+ yellow: '\x1b[33m',
105
+ blue: '\x1b[34m',
106
+ cyan: '\x1b[36m',
107
+ bold: '\x1b[1m',
108
+ reset: '\x1b[0m',
109
+ };
110
+
111
+ function color(text, token) {
112
+ if (!process.stdout.isTTY) {
113
+ return text;
114
+ }
115
+ return `${C[token] || ''}${text}${C.reset}`;
116
+ }
117
+
118
+ function info(msg) {
119
+ console.log(`${color('ℹ', 'blue')} ${msg}`);
120
+ }
121
+
122
+ function ok(msg) {
123
+ console.log(`${color('✔', 'green')} ${msg}`);
124
+ }
125
+
126
+ function warn(msg) {
127
+ console.log(`${color('⚠', 'yellow')} ${msg}`);
128
+ }
129
+
130
+ function err(msg) {
131
+ console.error(`${color('✖', 'red')} ${msg}`);
132
+ }
133
+
134
+ function isInteractive() {
135
+ return Boolean(process.stdin.isTTY && process.stdout.isTTY);
136
+ }
137
+
138
+ function isWindows() {
139
+ return process.platform === 'win32';
140
+ }
141
+
142
+ function getSourceDir() {
143
+ if (process.env.ENGINEERED_SPEC_LOCAL && fs.existsSync(path.join(process.env.ENGINEERED_SPEC_LOCAL, '.agents'))) {
144
+ return process.env.ENGINEERED_SPEC_LOCAL;
145
+ }
146
+ if (fs.existsSync(path.join(PKG_ROOT, '.agents'))) {
147
+ return PKG_ROOT;
148
+ }
149
+ const cacheDir = process.env.ENGINEERED_SPEC_CACHE || path.join(os.homedir(), '.ai-spec-auto');
150
+ const repo = process.env.ENGINEERED_SPEC_REPO || 'https://github.com/Colouful/engineered-spec.git';
151
+ const branch = process.env.ENGINEERED_SPEC_BRANCH || 'main';
152
+ if (fs.existsSync(path.join(cacheDir, '.git'))) {
153
+ spawnSync('git', ['-C', cacheDir, 'pull', '--quiet'], { stdio: 'ignore' });
154
+ } else {
155
+ const cloned = spawnSync('git', ['clone', '--quiet', '-b', branch, repo, cacheDir], { stdio: 'inherit' });
156
+ if (cloned.status !== 0) {
157
+ throw new Error(`克隆规范库失败: ${repo}`);
158
+ }
159
+ }
160
+ return cacheDir;
161
+ }
162
+
163
+ function normalizeList(value) {
164
+ if (!value) return [];
165
+ if (Array.isArray(value)) return [...new Set(value.map((item) => String(item || '').trim()).filter(Boolean))];
166
+ return [...new Set(String(value).split(',').map((item) => item.trim()).filter(Boolean))];
167
+ }
168
+
169
+ function normalizeCustomRulesSelection(value) {
170
+ const allowed = new Set(DEFAULT_CUSTOM_RULE_SELECTION);
171
+ return normalizeList(value).filter((item) => allowed.has(item));
172
+ }
173
+
174
+ function parseArgs(argv) {
175
+ const args = [...argv];
176
+ const options = {
177
+ command: '',
178
+ target: '.',
179
+ manifest: '',
180
+ profile: DEFAULT_PROFILE,
181
+ profiles: [],
182
+ level: DEFAULT_LEVEL,
183
+ ideFilter: DEFAULT_IDE_FILTER,
184
+ rulesStrategy: 'ask',
185
+ customRules: [],
186
+ installLint: 'ask',
187
+ installHusky: 'ask',
188
+ uipro: 'ask',
189
+ superpowers: 'ask',
190
+ visualBridge: 'yes', // 默认启用,不再提示
191
+ updateSkills: 'yes',
192
+ updateRules: 'yes',
193
+ forceUpdateRules: 'ask',
194
+ updateConfigs: 'yes',
195
+ updateCommands: 'yes',
196
+ updateIdeLinks: 'yes',
197
+ updateOpenSpec: 'yes',
198
+ updateUipro: 'no',
199
+ updateRuleMode: UPDATE_RULE_MODES.LEGACY,
200
+ selectedUpdateRuleFiles: [],
201
+ force: false,
202
+ workspacePackageSubpath: '',
203
+ workspaceRoot: false,
204
+ profileExplicit: false,
205
+ ideExplicit: false,
206
+ levelExplicit: false,
207
+ hubOrigin: '',
208
+ hubFetch: true,
209
+ refreshSuperpowers: false,
210
+ superpowersExplicit: false,
211
+ visualBridgeExplicit: false,
212
+ };
213
+
214
+ while (args.length > 0) {
215
+ const arg = args.shift();
216
+ switch (arg) {
217
+ case 'init':
218
+ case 'update':
219
+ case 'check':
220
+ case 'uninstall':
221
+ case 'help':
222
+ if (!options.command) {
223
+ options.command = arg;
224
+ } else if (options.target === '.') {
225
+ options.target = arg;
226
+ } else {
227
+ throw new Error(`Unknown argument: ${arg}`);
228
+ }
229
+ break;
230
+ case '--profile':
231
+ options.profile = requireArg(arg, args);
232
+ options.profileExplicit = true;
233
+ break;
234
+ case '--profiles':
235
+ options.profiles = requireArg(arg, args)
236
+ .split(',')
237
+ .map((s) => s.trim())
238
+ .filter(Boolean);
239
+ options.profileExplicit = true;
240
+ break;
241
+ case '--level':
242
+ options.level = requireArg(arg, args).toUpperCase();
243
+ options.levelExplicit = true;
244
+ break;
245
+ case '--ide':
246
+ options.ideFilter = requireArg(arg, args);
247
+ options.ideExplicit = true;
248
+ break;
249
+ case '--manifest':
250
+ options.manifest = requireArg(arg, args);
251
+ break;
252
+ case '--hub-origin':
253
+ options.hubOrigin = requireArg(arg, args);
254
+ break;
255
+ case '--no-hub-fetch':
256
+ options.hubFetch = false;
257
+ break;
258
+ case '--standard-rules':
259
+ options.rulesStrategy = 'standard';
260
+ options.customRules = [];
261
+ break;
262
+ case '--custom-rules':
263
+ options.rulesStrategy = 'custom';
264
+ options.customRules = CUSTOMIZABLE_RULES.map(([name]) => name);
265
+ break;
266
+ case '--lint':
267
+ options.installLint = 'yes';
268
+ break;
269
+ case '--no-lint':
270
+ options.installLint = 'no';
271
+ break;
272
+ case '--husky':
273
+ options.installHusky = 'yes';
274
+ break;
275
+ case '--no-husky':
276
+ options.installHusky = 'no';
277
+ break;
278
+ case '--uipro':
279
+ options.uipro = 'yes';
280
+ break;
281
+ case '--no-uipro':
282
+ options.uipro = 'no';
283
+ break;
284
+ case '--superpowers':
285
+ options.superpowers = 'yes';
286
+ options.superpowersExplicit = true;
287
+ break;
288
+ case '--no-superpowers':
289
+ options.superpowers = 'no';
290
+ options.superpowersExplicit = true;
291
+ break;
292
+ case '--refresh-superpowers':
293
+ options.refreshSuperpowers = true;
294
+ break;
295
+ case '--visual-bridge':
296
+ options.visualBridge = 'yes';
297
+ options.visualBridgeExplicit = true;
298
+ break;
299
+ case '--no-visual-bridge':
300
+ options.visualBridge = 'no';
301
+ options.visualBridgeExplicit = true;
302
+ break;
303
+ case '--update-rules':
304
+ options.updateRules = 'yes';
305
+ break;
306
+ case '--no-update-rules':
307
+ options.updateRules = 'no';
308
+ break;
309
+ case '--force-update-rules':
310
+ options.forceUpdateRules = 'yes';
311
+ break;
312
+ case '--no-force-update-rules':
313
+ options.forceUpdateRules = 'no';
314
+ break;
315
+ case '--skip-skills':
316
+ options.updateSkills = 'no';
317
+ break;
318
+ case '--skip-configs':
319
+ options.updateConfigs = 'no';
320
+ break;
321
+ case '--skip-commands':
322
+ options.updateCommands = 'no';
323
+ break;
324
+ case '--update-commands':
325
+ options.updateCommands = 'yes';
326
+ break;
327
+ case '--skip-ide-links':
328
+ options.updateIdeLinks = 'no';
329
+ break;
330
+ case '--skip-openspec':
331
+ options.updateOpenSpec = 'no';
332
+ break;
333
+ case '--skip-uipro':
334
+ options.updateUipro = 'no';
335
+ break;
336
+ case '--update-uipro':
337
+ options.updateUipro = 'yes';
338
+ break;
339
+ case '--package':
340
+ options.workspacePackageSubpath = requireArg(arg, args);
341
+ break;
342
+ case '--workspace-root':
343
+ options.workspaceRoot = true;
344
+ break;
345
+ case '-y':
346
+ case '--force':
347
+ options.force = true;
348
+ break;
349
+ case '-h':
350
+ case '--help':
351
+ options.command = 'help';
352
+ break;
353
+ default:
354
+ if (!arg.startsWith('-') && options.target === '.') {
355
+ options.target = arg;
356
+ break;
357
+ }
358
+ throw new Error(`Unknown argument: ${arg}`);
359
+ }
360
+ }
361
+
362
+ if (!options.command) {
363
+ options.command = 'help';
364
+ }
365
+ return options;
366
+ }
367
+
368
+ function requireArg(flag, args) {
369
+ const next = args.shift();
370
+ if (!next || next.startsWith('--')) {
371
+ throw new Error(`选项 ${flag} 需要一个参数值`);
372
+ }
373
+ return next;
374
+ }
375
+
376
+ function commandExists(name) {
377
+ const probe = spawnSync(name, ['--version'], { stdio: 'ignore' });
378
+ return probe.status === 0;
379
+ }
380
+
381
+ function runCommand(command, args, options = {}) {
382
+ const result = spawnSync(command, args, {
383
+ cwd: options.cwd || process.cwd(),
384
+ env: { ...process.env, ...(options.env || {}) },
385
+ stdio: options.stdio || 'inherit',
386
+ shell: false,
387
+ encoding: options.encoding || 'utf8',
388
+ });
389
+ return result;
390
+ }
391
+
392
+ function readJson(filePath, label = filePath) {
393
+ try {
394
+ return JSON.parse(fs.readFileSync(filePath, 'utf8'));
395
+ } catch (error) {
396
+ throw new Error(`${label} 不是合法 JSON: ${filePath}`);
397
+ }
398
+ }
399
+
400
+ function writeJson(filePath, value) {
401
+ ensureDir(path.dirname(filePath));
402
+ fs.writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`, 'utf8');
403
+ }
404
+
405
+ function ensureDir(dirPath) {
406
+ fs.mkdirSync(dirPath, { recursive: true });
407
+ }
408
+
409
+ function copyFile(sourcePath, destPath, options = {}) {
410
+ if (!fs.existsSync(sourcePath)) return false;
411
+ ensureDir(path.dirname(destPath));
412
+ if (options.skipExisting && fs.existsSync(destPath)) {
413
+ return false;
414
+ }
415
+ fs.copyFileSync(sourcePath, destPath);
416
+ return true;
417
+ }
418
+
419
+ function removePath(targetPath) {
420
+ if (!fs.existsSync(targetPath)) {
421
+ return;
422
+ }
423
+ try {
424
+ fs.rmSync(targetPath, { recursive: true, force: true });
425
+ } catch (error) {
426
+ try {
427
+ fs.unlinkSync(targetPath);
428
+ } catch (_) {
429
+ // ignore
430
+ }
431
+ }
432
+ }
433
+
434
+ function copyDirReplace(sourceDir, destDir) {
435
+ if (!fs.existsSync(sourceDir)) {
436
+ return false;
437
+ }
438
+ removePath(destDir);
439
+ ensureDir(path.dirname(destDir));
440
+ fs.cpSync(sourceDir, destDir, { recursive: true });
441
+ return true;
442
+ }
443
+
444
+ function walkFiles(rootDir) {
445
+ if (!fs.existsSync(rootDir)) return [];
446
+ const results = [];
447
+ const stack = [rootDir];
448
+ while (stack.length > 0) {
449
+ const current = stack.pop();
450
+ for (const entry of fs.readdirSync(current, { withFileTypes: true })) {
451
+ if (entry.name === '.DS_Store') continue;
452
+ const fullPath = path.join(current, entry.name);
453
+ if (entry.isDirectory()) {
454
+ stack.push(fullPath);
455
+ } else {
456
+ results.push(fullPath);
457
+ }
458
+ }
459
+ }
460
+ return results.sort();
461
+ }
462
+
463
+ function copyDirIncremental(sourceDir, destDir, options = {}) {
464
+ if (!fs.existsSync(sourceDir)) {
465
+ return { copiedAny: false, createdPaths: [] };
466
+ }
467
+ let copiedAny = false;
468
+ const createdPaths = [];
469
+ for (const filePath of walkFiles(sourceDir)) {
470
+ const rel = path.relative(sourceDir, filePath);
471
+ const firstSegment = rel.split(path.sep)[0];
472
+ const baseName = path.basename(filePath);
473
+ if (options.skipHuskyArtifacts && (firstSegment === '.husky' || baseName === '.lintstagedrc' || baseName === 'commitlint.config.js')) {
474
+ continue;
475
+ }
476
+ const destPath = path.join(destDir, rel);
477
+ ensureDir(path.dirname(destPath));
478
+ const existedBefore = fs.existsSync(destPath);
479
+ if (options.skipExisting && fs.existsSync(destPath)) {
480
+ info(` 跳过已存在: ${rel.split(path.sep).join('/')}`);
481
+ continue;
482
+ }
483
+ fs.copyFileSync(filePath, destPath);
484
+ copiedAny = true;
485
+ if (!existedBefore) {
486
+ createdPaths.push(rel.split(path.sep).join('/'));
487
+ }
488
+ }
489
+ return { copiedAny, createdPaths };
490
+ }
491
+
492
+ function readInstalledManifestSuperpowers(targetDir) {
493
+ const manifestPath = path.join(targetDir, '.ai-spec', 'manifest.json');
494
+ if (!fs.existsSync(manifestPath)) {
495
+ return null;
496
+ }
497
+ const manifest = readJson(manifestPath, 'existing manifest');
498
+ return normalizeSuperpowersManifest(manifest?.superpowers, null);
499
+ }
500
+
501
+ function readInstalledManifestVisualBridge(targetDir) {
502
+ const manifestPath = path.join(targetDir, '.ai-spec', 'manifest.json');
503
+ if (!fs.existsSync(manifestPath)) {
504
+ return null;
505
+ }
506
+ const manifest = readJson(manifestPath, 'existing manifest');
507
+ return normalizeVisualBridgeManifest(manifest?.visual_bridge, null);
508
+ }
509
+
510
+ function createDirLink(targetAbsolute, linkPath) {
511
+ removePath(linkPath);
512
+ ensureDir(path.dirname(linkPath));
513
+ if (isWindows()) {
514
+ fs.symlinkSync(targetAbsolute, linkPath, 'junction');
515
+ } else {
516
+ const rel = path.relative(path.dirname(linkPath), targetAbsolute) || '.';
517
+ fs.symlinkSync(rel, linkPath);
518
+ }
519
+ }
520
+
521
+ function normalizeIdeFilter(value) {
522
+ const raw = String(value || DEFAULT_IDE_FILTER).trim();
523
+ if (raw === 'default') return [...DEFAULT_IDES];
524
+ if (raw === 'all') return [...ALL_IDES];
525
+ const list = normalizeList(raw);
526
+ const unknown = list.filter((item) => !ALL_IDES.includes(item));
527
+ if (unknown.length > 0) {
528
+ throw new Error(`Unsupported ides: ${unknown.join(', ')}`);
529
+ }
530
+ return list;
531
+ }
532
+
533
+ function sameStringList(left, right) {
534
+ if (left.length !== right.length) return false;
535
+ return left.every((item, index) => item === right[index]);
536
+ }
537
+
538
+ function readPackageJson(filePath) {
539
+ if (!fs.existsSync(filePath)) return null;
540
+ return readJson(filePath, 'package.json');
541
+ }
542
+
543
+ function normalizeInstallState(state) {
544
+ const base = state && typeof state === 'object' ? state : {};
545
+ return {
546
+ schema_version: 1,
547
+ managed_paths: normalizeList(base.managed_paths),
548
+ created_config_files: normalizeList(base.created_config_files),
549
+ added_dev_dependencies: normalizeList(base.added_dev_dependencies),
550
+ package_json: base.package_json && typeof base.package_json === 'object'
551
+ ? {
552
+ prepare_script: typeof base.package_json.prepare_script === 'string' ? base.package_json.prepare_script : '',
553
+ }
554
+ : {
555
+ prepare_script: '',
556
+ },
557
+ };
558
+ }
559
+
560
+ function getInstallStatePath(targetDir) {
561
+ return path.join(targetDir, INSTALL_STATE_FILE);
562
+ }
563
+
564
+ function readInstallState(targetDir) {
565
+ const filePath = getInstallStatePath(targetDir);
566
+ if (!fs.existsSync(filePath)) {
567
+ return normalizeInstallState(null);
568
+ }
569
+ return normalizeInstallState(readJson(filePath, 'install-state'));
570
+ }
571
+
572
+ function readPackageSnapshot(targetDir) {
573
+ const pkgPath = path.join(targetDir, 'package.json');
574
+ const pkg = readPackageJson(pkgPath);
575
+ return {
576
+ exists: Boolean(pkg),
577
+ dependencies: new Set(Object.keys(pkg?.dependencies || {})),
578
+ devDependencies: new Set(Object.keys(pkg?.devDependencies || {})),
579
+ prepareScript: typeof pkg?.scripts?.prepare === 'string' ? pkg.scripts.prepare : '',
580
+ };
581
+ }
582
+
583
+ function extractPackageName(spec) {
584
+ const value = String(spec || '').trim();
585
+ if (!value) return '';
586
+ if (value.startsWith('@')) {
587
+ const secondAt = value.indexOf('@', 1);
588
+ return secondAt === -1 ? value : value.slice(0, secondAt);
589
+ }
590
+ const firstAt = value.indexOf('@');
591
+ return firstAt === -1 ? value : value.slice(0, firstAt);
592
+ }
593
+
594
+ function collectNewPackageNames(beforeSnapshot, afterSnapshot, packageSpecs) {
595
+ const afterNames = new Set([
596
+ ...afterSnapshot.dependencies,
597
+ ...afterSnapshot.devDependencies,
598
+ ]);
599
+ return normalizeList(packageSpecs.map((spec) => extractPackageName(spec))).filter((name) => (
600
+ name &&
601
+ afterNames.has(name) &&
602
+ !beforeSnapshot.dependencies.has(name) &&
603
+ !beforeSnapshot.devDependencies.has(name)
604
+ ));
605
+ }
606
+
607
+ function detectExistingIdeDirs(targetDir) {
608
+ return ALL_IDES.filter((ide) => fs.existsSync(path.join(targetDir, `.${ide}`)));
609
+ }
610
+
611
+ function detectInstalledManifestIdes(targetDir) {
612
+ const manifestPath = path.join(targetDir, '.ai-spec', 'manifest.json');
613
+ if (!fs.existsSync(manifestPath)) {
614
+ return [];
615
+ }
616
+ const manifest = readJson(manifestPath, 'existing manifest');
617
+ if (!manifest.ides) {
618
+ return [];
619
+ }
620
+ return normalizeIdeFilter(manifest.ides);
621
+ }
622
+
623
+ function resolveTargetIdes(targetDir, options) {
624
+ if (options.ideExplicit) {
625
+ return normalizeIdeFilter(options.ideFilter);
626
+ }
627
+ const manifestIdes = detectInstalledManifestIdes(targetDir);
628
+ if (manifestIdes.length > 0) {
629
+ return manifestIdes;
630
+ }
631
+ const existingIdes = detectExistingIdeDirs(targetDir);
632
+ if (existingIdes.length > 0) {
633
+ return existingIdes;
634
+ }
635
+ return normalizeIdeFilter(options.ideFilter || DEFAULT_IDE_FILTER);
636
+ }
637
+
638
+ function listTemplateCommandFiles(sourceDir, ideName) {
639
+ const commandFiles = new Set();
640
+ for (const relDir of [
641
+ path.join(sourceDir, '.agents', 'commands', 'common'),
642
+ path.join(sourceDir, '.agents', 'commands', ideName),
643
+ ]) {
644
+ if (!fs.existsSync(relDir)) continue;
645
+ for (const entry of fs.readdirSync(relDir)) {
646
+ if (entry.endsWith('.md')) {
647
+ commandFiles.add(`.${ideName}/commands/${entry}`);
648
+ }
649
+ }
650
+ }
651
+ return [...commandFiles].sort();
652
+ }
653
+
654
+ function isLinkToTarget(linkPath, expectedTargetPath) {
655
+ try {
656
+ if (!fs.lstatSync(linkPath).isSymbolicLink()) {
657
+ return false;
658
+ }
659
+ const actualTarget = fs.readlinkSync(linkPath);
660
+ const resolvedTarget = path.resolve(path.dirname(linkPath), actualTarget);
661
+ return resolvedTarget === expectedTargetPath;
662
+ } catch (error) {
663
+ return false;
664
+ }
665
+ }
666
+
667
+ function collectManagedIdePaths(targetDir, sourceDir) {
668
+ const managed = [];
669
+ for (const ide of ALL_IDES) {
670
+ const ideDir = path.join(targetDir, `.${ide}`);
671
+ if (!fs.existsSync(ideDir)) {
672
+ continue;
673
+ }
674
+ const rulesPath = path.join(ideDir, 'rules');
675
+ if (isLinkToTarget(rulesPath, path.join(targetDir, '.agents', 'rules'))) {
676
+ managed.push(`.${ide}/rules`);
677
+ }
678
+ const skillsDir = path.join(ideDir, 'skills');
679
+ if (fs.existsSync(skillsDir)) {
680
+ for (const entry of fs.readdirSync(skillsDir, { withFileTypes: true })) {
681
+ const fullPath = path.join(skillsDir, entry.name);
682
+ if (!isLinkToTarget(fullPath, path.join(targetDir, '.agents', 'skills', entry.name))) continue;
683
+ managed.push(`.${ide}/skills/${entry.name}`);
684
+ }
685
+ }
686
+ for (const relPath of listTemplateCommandFiles(sourceDir, ide)) {
687
+ if (fs.existsSync(path.join(targetDir, relPath))) {
688
+ managed.push(relPath);
689
+ }
690
+ }
691
+ }
692
+ return managed;
693
+ }
694
+
695
+ function writeInstallState(targetDir, sourceDir, previousState, additions = {}) {
696
+ const nextState = normalizeInstallState(previousState);
697
+ nextState.generated_at = new Date().toISOString();
698
+ nextState.managed_paths = normalizeList([
699
+ ...(fs.existsSync(path.join(targetDir, '.agents')) ? ['.agents'] : []),
700
+ ...collectManagedIdePaths(targetDir, sourceDir),
701
+ ]);
702
+ nextState.created_config_files = normalizeList([
703
+ ...nextState.created_config_files.filter((filePath) => fs.existsSync(path.join(targetDir, filePath))),
704
+ ...(additions.createdConfigFiles || []),
705
+ ]);
706
+ nextState.added_dev_dependencies = normalizeList([
707
+ ...nextState.added_dev_dependencies,
708
+ ...(additions.addedDevDependencies || []),
709
+ ]);
710
+ nextState.package_json = {
711
+ prepare_script: additions.prepareScript || nextState.package_json.prepare_script || '',
712
+ };
713
+ writeJson(getInstallStatePath(targetDir), nextState);
714
+ }
715
+
716
+ function writeProfileManifest(targetDir, options) {
717
+ const manifestPath = path.join(targetDir, '.ai-spec', 'manifest.json');
718
+ const existing = fs.existsSync(manifestPath) ? readJson(manifestPath, 'manifest') : {};
719
+ const profiles = options.profiles && options.profiles.length > 0 ? options.profiles : [options.profile];
720
+ const next = {
721
+ ...existing,
722
+ profiles,
723
+ profile: profiles[0],
724
+ generated_at: new Date().toISOString(),
725
+ };
726
+ if (options.packages && options.packages.length > 0) {
727
+ next.packages = options.packages;
728
+ }
729
+ ensureDir(path.dirname(manifestPath));
730
+ writeJson(manifestPath, next);
731
+ }
732
+
733
+ function sortPathsForRemoval(paths) {
734
+ return [...new Set(paths)].sort((left, right) => {
735
+ const leftDepth = left.split('/').length;
736
+ const rightDepth = right.split('/').length;
737
+ if (leftDepth !== rightDepth) {
738
+ return rightDepth - leftDepth;
739
+ }
740
+ return right.localeCompare(left);
741
+ });
742
+ }
743
+
744
+ function removeManagedPaths(targetDir, relPaths) {
745
+ for (const relPath of sortPathsForRemoval(relPaths)) {
746
+ removePath(path.join(targetDir, relPath));
747
+ }
748
+ }
749
+
750
+ const AI_SPEC_MANAGED_RUNTIME_PATHS = [
751
+ '.ai-spec/current-run.json',
752
+ '.ai-spec/repo-map.json',
753
+ '.ai-spec/visual-bridge.json',
754
+ '.ai-spec/checkpoints',
755
+ '.ai-spec/internal',
756
+ '.ai-spec/tmp',
757
+ '.ai-spec/current-dispatch.json',
758
+ '.ai-spec/current-execution.json',
759
+ '.ai-spec/current-execution.md',
760
+ '.ai-spec/current-runtime-action.json',
761
+ '.ai-spec/current-runtime-action.md',
762
+ '.ai-spec/runs',
763
+ '.ai-spec/dispatches',
764
+ '.ai-spec/executions',
765
+ '.ai-spec/runtime-actions',
766
+ '.ai-spec/runner',
767
+ ];
768
+
769
+ function removeManagedAiSpecRuntime(targetDir) {
770
+ removeManagedPaths(targetDir, AI_SPEC_MANAGED_RUNTIME_PATHS);
771
+ }
772
+
773
+ function listLegacyManagedPaths(targetDir, sourceDir) {
774
+ const managed = [];
775
+ if (fs.existsSync(path.join(targetDir, '.agents'))) {
776
+ managed.push('.agents');
777
+ }
778
+ for (const ide of ALL_IDES) {
779
+ const ideDir = path.join(targetDir, `.${ide}`);
780
+ if (!fs.existsSync(ideDir)) {
781
+ continue;
782
+ }
783
+ if (isLinkToTarget(path.join(ideDir, 'rules'), path.join(targetDir, '.agents', 'rules'))) {
784
+ managed.push(`.${ide}/rules`);
785
+ }
786
+ const skillsDir = path.join(ideDir, 'skills');
787
+ if (fs.existsSync(skillsDir)) {
788
+ for (const entry of fs.readdirSync(skillsDir, { withFileTypes: true })) {
789
+ const fullPath = path.join(skillsDir, entry.name);
790
+ if (!isLinkToTarget(fullPath, path.join(targetDir, '.agents', 'skills', entry.name))) continue;
791
+ managed.push(`.${ide}/skills/${entry.name}`);
792
+ }
793
+ }
794
+ managed.push(...listTemplateCommandFiles(sourceDir, ide).filter((relPath) => fs.existsSync(path.join(targetDir, relPath))));
795
+ }
796
+ return managed;
797
+ }
798
+
799
+ function cleanupEmptyIdeDirs(targetDir) {
800
+ for (const ide of ALL_IDES) {
801
+ const ideDir = path.join(targetDir, `.${ide}`);
802
+ if (!fs.existsSync(ideDir)) {
803
+ continue;
804
+ }
805
+ for (const child of ['commands', 'skills']) {
806
+ const childDir = path.join(ideDir, child);
807
+ if (fs.existsSync(childDir) && fs.readdirSync(childDir).filter((entry) => entry !== '.DS_Store').length === 0) {
808
+ removePath(childDir);
809
+ }
810
+ }
811
+ const remaining = fs.readdirSync(ideDir).filter((entry) => entry !== '.DS_Store');
812
+ if (remaining.length === 0) {
813
+ removePath(ideDir);
814
+ }
815
+ }
816
+ }
817
+
818
+ function pkgJsonHasWorkspaces(dir) {
819
+ const pkg = readPackageJson(path.join(dir, 'package.json'));
820
+ return Boolean(pkg && pkg.workspaces);
821
+ }
822
+
823
+ function findMonorepoWorkspaceRoot(startDir) {
824
+ let current = path.resolve(startDir);
825
+ while (true) {
826
+ if (fs.existsSync(path.join(current, 'pnpm-workspace.yaml')) || pkgJsonHasWorkspaces(current)) {
827
+ return current;
828
+ }
829
+ const parent = path.dirname(current);
830
+ if (parent === current) {
831
+ return null;
832
+ }
833
+ current = parent;
834
+ }
835
+ }
836
+
837
+ function detectInstalledProfile(targetDir, profilesRegistry) {
838
+ const manifestPath = path.join(targetDir, '.ai-spec', 'manifest.json');
839
+ if (fs.existsSync(manifestPath)) {
840
+ const manifest = readJson(manifestPath, 'existing manifest');
841
+ // profiles 数组优先
842
+ if (Array.isArray(manifest.profiles) && manifest.profiles.length > 0) {
843
+ return manifest.profiles[0];
844
+ }
845
+ const resolved = resolveProfileId(profilesRegistry, manifest.profile);
846
+ if (resolved) {
847
+ return resolved;
848
+ }
849
+ }
850
+ return DEFAULT_PROFILE;
851
+ }
852
+
853
+ function detectInstalledLevel(targetDir) {
854
+ if (fs.existsSync(path.join(targetDir, 'openspec'))) {
855
+ return 'L3';
856
+ }
857
+ if (ALL_IDES.some((ide) => fs.existsSync(path.join(targetDir, `.${ide}`)))) {
858
+ return 'L2';
859
+ }
860
+ return 'L1';
861
+ }
862
+
863
+ function isSyncManagedProject(targetDir) {
864
+ return (
865
+ fs.existsSync(path.join(targetDir, '.ai-spec', 'manifest.json')) ||
866
+ fs.existsSync(path.join(targetDir, '.ai-spec', 'lock.json'))
867
+ );
868
+ }
869
+
870
+ async function ask(question, defaultValue = '') {
871
+ const rl = readlinePromises.createInterface({ input: process.stdin, output: process.stdout });
872
+ try {
873
+ const prompt = defaultValue ? `${question} [默认 ${defaultValue}]: ` : `${question}: `;
874
+ const answer = await rl.question(prompt);
875
+ return answer.trim() || defaultValue;
876
+ } finally {
877
+ rl.close();
878
+ }
879
+ }
880
+
881
+ async function confirm(question, defaultYes = false) {
882
+ const hint = defaultYes ? '(Y/n)' : '(y/N)';
883
+ const answer = (await ask(`${question} ${hint}`, defaultYes ? 'Y' : 'N')).toLowerCase();
884
+ if (!answer) {
885
+ return defaultYes;
886
+ }
887
+ return answer === 'y' || answer === 'yes';
888
+ }
889
+
890
+ async function selectFromList(title, items, defaultIndex = 0) {
891
+ if (isInteractive()) {
892
+ return selectSingleFromList(title, items, defaultIndex);
893
+ }
894
+
895
+ console.log('');
896
+ info(title);
897
+ items.forEach((item, index) => {
898
+ console.log(` ${index + 1}) ${item.label}${item.desc ? ` — ${item.desc}` : ''}`);
899
+ });
900
+ console.log('');
901
+ const answer = await ask(`请选择 (1-${items.length})`, String(defaultIndex + 1));
902
+ const index = Number(answer) - 1;
903
+ if (Number.isInteger(index) && index >= 0 && index < items.length) {
904
+ return items[index].value;
905
+ }
906
+ return items[defaultIndex].value;
907
+ }
908
+
909
+ function formatSingleSelectLine(item, selectedIndex, cursorIndex, index) {
910
+ const marker = index === selectedIndex ? '[✓]' : '[ ]';
911
+ const prefix = index === cursorIndex ? color('❯', 'cyan') : ' ';
912
+ return ` ${prefix} ${marker} ${item.label}${item.desc ? ` — ${item.desc}` : ''}`;
913
+ }
914
+
915
+ async function selectSingleFromList(title, items, defaultIndex = 0) {
916
+ console.log('');
917
+ info(title);
918
+ console.log(' ↑/↓ 移动,空格选择,Enter 确认');
919
+ console.log('');
920
+
921
+ const stdin = process.stdin;
922
+ if (typeof stdin.setRawMode !== 'function') {
923
+ return items[defaultIndex]?.value;
924
+ }
925
+
926
+ let cursorIndex = Math.max(0, Math.min(defaultIndex, items.length - 1));
927
+ let selectedIndex = cursorIndex;
928
+ let renderedLines = 0;
929
+
930
+ return await new Promise((resolve, reject) => {
931
+ let finished = false;
932
+
933
+ const cleanup = () => {
934
+ stdin.removeListener('keypress', onKeypress);
935
+ if (typeof stdin.setRawMode === 'function') {
936
+ stdin.setRawMode(false);
937
+ }
938
+ stdin.pause();
939
+ process.stdout.write('\x1b[?25h');
940
+ };
941
+
942
+ const finish = (result) => {
943
+ if (finished) return;
944
+ finished = true;
945
+ cleanup();
946
+ resolve(result);
947
+ };
948
+
949
+ const fail = (error) => {
950
+ if (finished) return;
951
+ finished = true;
952
+ cleanup();
953
+ reject(error);
954
+ };
955
+
956
+ const render = () => {
957
+ if (renderedLines > 0) {
958
+ process.stdout.write(`\x1b[${renderedLines}A`);
959
+ }
960
+ items.forEach((item, index) => {
961
+ process.stdout.write(`\x1b[2K\r${formatSingleSelectLine(item, selectedIndex, cursorIndex, index)}\n`);
962
+ });
963
+ renderedLines = items.length;
964
+ };
965
+
966
+ const onKeypress = (_str, key = {}) => {
967
+ if (key.ctrl && key.name === 'c') {
968
+ process.stdout.write('\n');
969
+ fail(new Error('已取消选择'));
970
+ return;
971
+ }
972
+
973
+ if (key.name === 'up') {
974
+ cursorIndex = cursorIndex > 0 ? cursorIndex - 1 : cursorIndex;
975
+ render();
976
+ return;
977
+ }
978
+
979
+ if (key.name === 'down') {
980
+ cursorIndex = cursorIndex < items.length - 1 ? cursorIndex + 1 : cursorIndex;
981
+ render();
982
+ return;
983
+ }
984
+
985
+ if (key.name === 'space') {
986
+ selectedIndex = cursorIndex;
987
+ render();
988
+ return;
989
+ }
990
+
991
+ if (key.name === 'return' || key.name === 'enter') {
992
+ finish(items[selectedIndex]?.value);
993
+ }
994
+ };
995
+
996
+ readline.emitKeypressEvents(stdin);
997
+ stdin.setRawMode(true);
998
+ stdin.resume();
999
+ process.stdout.write('\x1b[?25l');
1000
+ stdin.on('keypress', onKeypress);
1001
+ render();
1002
+ });
1003
+ }
1004
+
1005
+ function formatMultiSelectLine(item, selectedValues, cursorIndex, index) {
1006
+ const marker = selectedValues.has(item.value) ? '[✓]' : '[ ]';
1007
+ const prefix = index === cursorIndex ? color('❯', 'cyan') : ' ';
1008
+ return ` ${prefix} ${marker} ${item.label}${item.desc ? ` — ${item.desc}` : ''}`;
1009
+ }
1010
+
1011
+ async function selectMultipleFromList(title, items, config = {}) {
1012
+ if (!isInteractive()) {
1013
+ const defaultValues = new Set(normalizeList(config.defaultValues));
1014
+ return items.filter((item) => defaultValues.has(item.value)).map((item) => item.value);
1015
+ }
1016
+
1017
+ console.log('');
1018
+ info(title);
1019
+ if (config.description) {
1020
+ console.log(` ${config.description}`);
1021
+ }
1022
+ if (config.hint) {
1023
+ console.log(` ${config.hint}`);
1024
+ }
1025
+ console.log(` ${config.instructions || '↑/↓ 移动,空格选中/取消,Enter 确认'}`);
1026
+ console.log('');
1027
+
1028
+ const stdin = process.stdin;
1029
+ const selectedValues = new Set(normalizeList(config.defaultValues));
1030
+
1031
+ if (typeof stdin.setRawMode !== 'function') {
1032
+ return items.filter((item) => selectedValues.has(item.value)).map((item) => item.value);
1033
+ }
1034
+
1035
+ let cursorIndex = 0;
1036
+ let renderedLines = 0;
1037
+ let statusMessage = '';
1038
+
1039
+ return await new Promise((resolve, reject) => {
1040
+ let finished = false;
1041
+
1042
+ const cleanup = () => {
1043
+ stdin.removeListener('keypress', onKeypress);
1044
+ if (typeof stdin.setRawMode === 'function') {
1045
+ stdin.setRawMode(false);
1046
+ }
1047
+ stdin.pause();
1048
+ process.stdout.write('\x1b[?25h');
1049
+ };
1050
+
1051
+ const finish = (result) => {
1052
+ if (finished) return;
1053
+ finished = true;
1054
+ cleanup();
1055
+ resolve(result);
1056
+ };
1057
+
1058
+ const fail = (error) => {
1059
+ if (finished) return;
1060
+ finished = true;
1061
+ cleanup();
1062
+ reject(error);
1063
+ };
1064
+
1065
+ const render = () => {
1066
+ if (renderedLines > 0) {
1067
+ process.stdout.write(`\x1b[${renderedLines}A`);
1068
+ }
1069
+ items.forEach((item, index) => {
1070
+ process.stdout.write(`\x1b[2K\r${formatMultiSelectLine(item, selectedValues, cursorIndex, index)}\n`);
1071
+ });
1072
+ process.stdout.write(`\x1b[2K\r${statusMessage ? color(`⚠ ${statusMessage}`, 'yellow') : ''}\n`);
1073
+ renderedLines = items.length + 1;
1074
+ };
1075
+
1076
+ const onKeypress = (_str, key = {}) => {
1077
+ if (key.ctrl && key.name === 'c') {
1078
+ process.stdout.write('\n');
1079
+ fail(new Error('已取消选择'));
1080
+ return;
1081
+ }
1082
+
1083
+ if (key.name === 'up') {
1084
+ statusMessage = '';
1085
+ cursorIndex = cursorIndex > 0 ? cursorIndex - 1 : cursorIndex;
1086
+ render();
1087
+ return;
1088
+ }
1089
+
1090
+ if (key.name === 'down') {
1091
+ statusMessage = '';
1092
+ cursorIndex = cursorIndex < items.length - 1 ? cursorIndex + 1 : cursorIndex;
1093
+ render();
1094
+ return;
1095
+ }
1096
+
1097
+ if (key.name === 'space') {
1098
+ statusMessage = '';
1099
+ const current = items[cursorIndex];
1100
+ if (current) {
1101
+ if (selectedValues.has(current.value)) {
1102
+ selectedValues.delete(current.value);
1103
+ } else {
1104
+ selectedValues.add(current.value);
1105
+ }
1106
+ render();
1107
+ }
1108
+ return;
1109
+ }
1110
+
1111
+ if (key.name === 'return' || key.name === 'enter') {
1112
+ const result = items.filter((item) => selectedValues.has(item.value)).map((item) => item.value);
1113
+ if (config.minSelection && result.length < config.minSelection) {
1114
+ statusMessage = config.minSelectionMessage || `至少选择 ${config.minSelection} 项`;
1115
+ render();
1116
+ return;
1117
+ }
1118
+ finish(result);
1119
+ }
1120
+ };
1121
+
1122
+ readline.emitKeypressEvents(stdin);
1123
+ stdin.setRawMode(true);
1124
+ stdin.resume();
1125
+ process.stdout.write('\x1b[?25l');
1126
+ stdin.on('keypress', onKeypress);
1127
+ render();
1128
+ });
1129
+ }
1130
+
1131
+ async function selectCustomRuleList(options, config = {}) {
1132
+ const defaultRules = normalizeCustomRulesSelection(config.defaultRules || DEFAULT_CUSTOM_RULE_SELECTION);
1133
+ if (!isInteractive()) {
1134
+ options.customRules = options.rulesStrategy === 'custom'
1135
+ ? normalizeCustomRulesSelection(options.customRules.length > 0 ? options.customRules : defaultRules)
1136
+ : [];
1137
+ return;
1138
+ }
1139
+
1140
+ options.customRules = await selectMultipleFromList(
1141
+ config.title || '选择需要根据项目自定义的规则(空格选中/取消,Enter 确认):',
1142
+ CUSTOMIZABLE_RULES.map(([fileName, desc]) => ({
1143
+ value: fileName,
1144
+ label: fileName.replace('.md', ''),
1145
+ desc,
1146
+ })),
1147
+ {
1148
+ defaultValues: defaultRules,
1149
+ description: config.description,
1150
+ hint: config.hint || '默认已勾选全部规则,可按空格取消',
1151
+ },
1152
+ );
1153
+
1154
+ if (options.customRules.length === 0) {
1155
+ options.rulesStrategy = 'standard';
1156
+ warn(config.emptySelectionLabel || '未选择任何自定义规则,将使用标准规范。');
1157
+ return;
1158
+ }
1159
+
1160
+ ok(`${config.resultLabel || '以下规则将根据项目自定义:'}${options.customRules.map((name) => `\n • ${name}`).join('')}`);
1161
+ }
1162
+
1163
+ async function selectRulesStrategy(options, config = {}) {
1164
+ const mode = config.mode || 'default';
1165
+ if (!isInteractive() || options.rulesStrategy !== 'ask') {
1166
+ options.rulesStrategy = options.rulesStrategy === 'ask' ? 'standard' : options.rulesStrategy;
1167
+ if (options.rulesStrategy === 'custom') {
1168
+ options.customRules = normalizeCustomRulesSelection(options.customRules.length > 0 ? options.customRules : DEFAULT_CUSTOM_RULE_SELECTION);
1169
+ } else {
1170
+ options.customRules = [];
1171
+ }
1172
+ return;
1173
+ }
1174
+
1175
+ const strategy = await selectFromList(mode === 'manifest' ? '规则内容偏好:' : '规则安装策略:', [
1176
+ {
1177
+ value: 'standard',
1178
+ label: mode === 'manifest' ? '沿用安装模板' : '使用标准规范',
1179
+ desc: mode === 'manifest' ? '保持 manifest 安装下来的规则模板内容,后续 /project-init 只刷新 01/03' : '直接使用规范库中的规则,适合快速接入',
1180
+ },
1181
+ {
1182
+ value: 'custom',
1183
+ label: '根据项目自定义',
1184
+ desc: mode === 'manifest'
1185
+ ? '规则模板照常安装,后续由 /project-init 按项目实际情况生成或刷新所选规则'
1186
+ : '跳过部分规则,后续由 /project-init 按项目生成',
1187
+ },
1188
+ ], 0);
1189
+ options.rulesStrategy = strategy;
1190
+ if (strategy !== 'custom') {
1191
+ options.customRules = [];
1192
+ return;
1193
+ }
1194
+
1195
+ await selectCustomRuleList(options, {
1196
+ defaultRules: DEFAULT_CUSTOM_RULE_SELECTION,
1197
+ title: mode === 'manifest'
1198
+ ? '选择需要在 /project-init 中根据项目自定义的规则(空格选中/取消,Enter 确认):'
1199
+ : '选择需要根据项目自定义的规则(空格选中/取消,Enter 确认):',
1200
+ hint: mode === 'manifest'
1201
+ ? '规则模板照常安装,后续由 /project-init 按项目实际情况生成或刷新所选规则'
1202
+ : '选中的规则将不从规范库复制,而是由 AI 根据项目实际情况生成',
1203
+ resultLabel: mode === 'manifest'
1204
+ ? '以下规则将在 /project-init 时根据项目自定义生成或刷新:'
1205
+ : '以下规则将根据项目自定义:',
1206
+ emptySelectionLabel: mode === 'manifest'
1207
+ ? '未选择任何需要在 /project-init 中自定义的规则,将沿用安装模板。'
1208
+ : '未选择任何自定义规则,将使用标准规范。',
1209
+ });
1210
+ }
1211
+
1212
+ function getActiveProfiles(options) {
1213
+ return options.profiles && options.profiles.length > 0 ? options.profiles : [options.profile];
1214
+ }
1215
+
1216
+ function listSelectableUpdateRules(sourceDir, profilesRegistry, options) {
1217
+ const items = [];
1218
+ const seen = new Set();
1219
+ const ruleSources = [
1220
+ {
1221
+ tag: 'common',
1222
+ label: '公共规则',
1223
+ dir: path.join(sourceDir, '.agents', 'rules', 'common'),
1224
+ },
1225
+ ...getActiveProfiles(options).map((profileId) => ({
1226
+ tag: profileId,
1227
+ label: `${profileId} 规则`,
1228
+ dir: getProfileDirs(sourceDir, profileId, profilesRegistry).rulesDir,
1229
+ })),
1230
+ ];
1231
+
1232
+ for (const source of ruleSources) {
1233
+ if (!fs.existsSync(source.dir)) continue;
1234
+ for (const fileName of fs.readdirSync(source.dir).filter((name) => name.endsWith('.md')).sort()) {
1235
+ if (UPDATE_RULE_PROTECTED_FILES.has(fileName) || seen.has(fileName)) {
1236
+ continue;
1237
+ }
1238
+ seen.add(fileName);
1239
+ items.push({
1240
+ value: fileName,
1241
+ label: fileName.replace('.md', ''),
1242
+ desc: source.label,
1243
+ });
1244
+ }
1245
+ }
1246
+
1247
+ return items;
1248
+ }
1249
+
1250
+ async function selectUpdateModules(options) {
1251
+ const selected = await selectMultipleFromList(
1252
+ '请选择要更新的模块(空格选中/取消,Enter 确认):',
1253
+ UPDATE_MODULE_ITEMS.map(([key, label]) => ({
1254
+ value: key,
1255
+ label,
1256
+ })),
1257
+ {
1258
+ defaultValues: UPDATE_MODULE_ITEMS
1259
+ .map(([key]) => (options[key] === 'yes' ? key : ''))
1260
+ .filter(Boolean),
1261
+ hint: '默认已勾选当前会更新的模块,可按空格取消或补选',
1262
+ },
1263
+ );
1264
+
1265
+ const selectedSet = new Set(selected);
1266
+ UPDATE_MODULE_ITEMS.forEach(([key]) => {
1267
+ options[key] = selectedSet.has(key) ? 'yes' : 'no';
1268
+ });
1269
+ }
1270
+
1271
+ async function selectUpdateRuleFiles(options, sourceDir, profilesRegistry) {
1272
+ const items = listSelectableUpdateRules(sourceDir, profilesRegistry, options);
1273
+ options.selectedUpdateRuleFiles = await selectMultipleFromList(
1274
+ '选择要更新的规则文件(空格选中/取消,Enter 确认):',
1275
+ items,
1276
+ {
1277
+ defaultValues: options.selectedUpdateRuleFiles,
1278
+ hint: '默认不选,请按空格勾选要更新的规则文件',
1279
+ minSelection: 1,
1280
+ minSelectionMessage: '至少选择 1 个规则文件',
1281
+ },
1282
+ );
1283
+ ok(`以下规则将在 update 时按选择覆盖更新:${options.selectedUpdateRuleFiles.map((name) => `\n • ${name}`).join('')}`);
1284
+ }
1285
+
1286
+ async function selectUpdateRuleMode(options, sourceDir, profilesRegistry) {
1287
+ if (!isInteractive() || options.updateRules !== 'yes') {
1288
+ return;
1289
+ }
1290
+
1291
+ const mode = await selectFromList('选择 Rules(规范规则)的更新方式:', [
1292
+ {
1293
+ value: UPDATE_RULE_MODES.STANDARD,
1294
+ label: '标准更新',
1295
+ desc: '仅补缺失规则,保留已有规则文件',
1296
+ },
1297
+ {
1298
+ value: UPDATE_RULE_MODES.SELECTED,
1299
+ label: '自定义选择',
1300
+ desc: '手动勾选要更新的规则文件,仅覆盖选中项',
1301
+ },
1302
+ {
1303
+ value: UPDATE_RULE_MODES.ALL,
1304
+ label: '全部更新',
1305
+ desc: '覆盖全部规则文件,但排除 README.md 和 12-Superpowers执行规范.md',
1306
+ },
1307
+ ], 0);
1308
+
1309
+ options.updateRuleMode = mode;
1310
+ options.rulesStrategy = 'standard';
1311
+ options.forceUpdateRules = 'no';
1312
+ options.selectedUpdateRuleFiles = [];
1313
+
1314
+ if (mode === UPDATE_RULE_MODES.SELECTED) {
1315
+ await selectUpdateRuleFiles(options, sourceDir, profilesRegistry);
1316
+ }
1317
+ }
1318
+
1319
+ async function selectBootstrapChoices(options) {
1320
+ if (!isInteractive()) {
1321
+ if (options.installLint === 'ask') options.installLint = 'yes';
1322
+ if (options.installHusky === 'ask') options.installHusky = 'no';
1323
+ if (options.uipro === 'ask') options.uipro = 'no';
1324
+ return;
1325
+ }
1326
+
1327
+ if (options.uipro === 'ask') {
1328
+ console.log('');
1329
+ info('是否安装 UI UX Pro Max 设计智能技能?');
1330
+ console.log(' 提供 67 种 UI 风格、161 套配色方案、57 组字体搭配、99 条 UX 准则');
1331
+ options.uipro = (await confirm('安装 UI UX Pro Max?', true)) ? 'yes' : 'no';
1332
+ ok(options.uipro === 'yes' ? '将安装 UI UX Pro Max' : '跳过 UI UX Pro Max');
1333
+ }
1334
+
1335
+ if (options.visualBridge === 'ask') {
1336
+ // 默认启用 visual bridge,不再提示用户
1337
+ options.visualBridge = 'yes';
1338
+ }
1339
+
1340
+ if (options.superpowers === 'ask') {
1341
+ console.log('');
1342
+ info('是否启用 Superpowers 平台增强?');
1343
+ console.log(' 启用后会生成项目级 superpowers bridge(超能力桥接)配置,并按 IDE 入口注入增强资产。');
1344
+ options.superpowers = (await confirm('启用 superpowers?', true)) ? 'yes' : 'no';
1345
+ ok(options.superpowers === 'yes' ? '将启用 superpowers 平台增强' : '跳过 superpowers 平台增强');
1346
+ }
1347
+
1348
+ if (options.installLint === 'ask') {
1349
+ console.log('');
1350
+ info('是否安装 ESLint + Prettier + Stylelint 配置?');
1351
+ options.installLint = (await confirm('安装 lint/format 工具?', true)) ? 'yes' : 'no';
1352
+ ok(options.installLint === 'yes' ? '将安装 lint/format 工具' : '跳过 lint/format 工具');
1353
+ }
1354
+
1355
+ if (options.installHusky === 'ask') {
1356
+ console.log('');
1357
+ info('是否安装 Husky 提交校验(husky + lint-staged + commitlint)?');
1358
+ options.installHusky = (await confirm('安装提交校验?', false)) ? 'yes' : 'no';
1359
+ ok(options.installHusky === 'yes' ? '将安装提交校验' : '跳过提交校验');
1360
+ }
1361
+ }
1362
+
1363
+ async function selectInitChoices(options, profilesRegistry) {
1364
+ if (!isInteractive()) {
1365
+ if (options.rulesStrategy === 'ask') options.rulesStrategy = 'standard';
1366
+ await selectBootstrapChoices(options);
1367
+ return;
1368
+ }
1369
+
1370
+ if (!options.profileExplicit) {
1371
+ const profileItems = Object.entries(getProfileEntries(profilesRegistry)).map(([id, entry]) => ({
1372
+ value: id,
1373
+ label: id,
1374
+ desc: PROFILE_SUMMARIES[id] || entry.label || id,
1375
+ }));
1376
+ options.profiles = await selectMultipleFromList('选择技术栈 Profile(可多选,空格选中,回车确认):', profileItems, {
1377
+ minSelection: 1,
1378
+ defaultValues: options.profiles.length ? options.profiles : [],
1379
+ });
1380
+ options.profile = options.profiles[0] || DEFAULT_PROFILE;
1381
+ ok(`已选择 Profile: ${options.profiles.join(', ')}`);
1382
+ }
1383
+
1384
+ await selectRulesStrategy(options);
1385
+ await selectBootstrapChoices(options);
1386
+ }
1387
+
1388
+ async function resolveMonorepoTarget(targetDir, options) {
1389
+ const resolvedTarget = path.resolve(targetDir);
1390
+ const workspaceRoot = findMonorepoWorkspaceRoot(resolvedTarget);
1391
+ if (!workspaceRoot) {
1392
+ return resolvedTarget;
1393
+ }
1394
+
1395
+ if (resolvedTarget !== workspaceRoot) {
1396
+ if (fs.existsSync(path.join(resolvedTarget, 'package.json'))) {
1397
+ info(`检测到 Monorepo,当前安装目标为子包: ${resolvedTarget}(工作区根: ${workspaceRoot})`);
1398
+ }
1399
+ return resolvedTarget;
1400
+ }
1401
+
1402
+ const requestedSubPath = options.workspacePackageSubpath || process.env.EX_AI_SPEC_WORKSPACE_PACKAGE || '';
1403
+ if (requestedSubPath) {
1404
+ const candidate = path.resolve(workspaceRoot, requestedSubPath);
1405
+ if (!fs.existsSync(candidate) || !fs.existsSync(path.join(candidate, 'package.json'))) {
1406
+ throw new Error(`Monorepo 子包路径无效: ${candidate}`);
1407
+ }
1408
+ ok(`已根据 --package / EX_AI_SPEC_WORKSPACE_PACKAGE 将安装目标设为: ${candidate}`);
1409
+ return candidate;
1410
+ }
1411
+
1412
+ if (options.workspaceRoot) {
1413
+ return workspaceRoot;
1414
+ }
1415
+
1416
+ if (!isInteractive()) {
1417
+ warn(`检测到 Monorepo(工作区根: ${workspaceRoot}),非交互模式将继续在根目录安装。`);
1418
+ warn(`如需安装到子包,请使用: npx @engineered/ai-spec-auto@latest init . --package packages/your-app`);
1419
+ return workspaceRoot;
1420
+ }
1421
+
1422
+ console.log('');
1423
+ info(`检测到 Monorepo(pnpm / npm workspaces),工作区根目录: ${workspaceRoot}`);
1424
+ info('规范与 lint/husky 等依赖将写入「安装目标」目录及其 package.json。');
1425
+ console.log(' 1) 在工作区根目录继续安装');
1426
+ console.log(' 2) 改为在具体子包中安装(推荐)');
1427
+ console.log(' 若仅在根 package.json 添加依赖,pnpm 可使用: pnpm add -w <包名>');
1428
+ const choice = await ask('请选择 [1/2]', '2');
1429
+ if (choice === '1') {
1430
+ return workspaceRoot;
1431
+ }
1432
+ for (let i = 0; i < 3; i += 1) {
1433
+ const rel = (await ask('请输入子包相对路径(相对工作区根,如 packages/web)', '')).replace(/^\/+|\/+$/g, '');
1434
+ if (!rel) {
1435
+ warn('路径不能为空');
1436
+ continue;
1437
+ }
1438
+ const candidate = path.resolve(workspaceRoot, rel);
1439
+ if (!fs.existsSync(candidate)) {
1440
+ warn(`目录不存在: ${candidate}`);
1441
+ continue;
1442
+ }
1443
+ if (!fs.existsSync(path.join(candidate, 'package.json'))) {
1444
+ warn(`该目录下缺少 package.json: ${candidate}`);
1445
+ continue;
1446
+ }
1447
+ ok(`安装目标已切换为: ${candidate}`);
1448
+ return candidate;
1449
+ }
1450
+ throw new Error('多次输入无效的子包路径,请使用 --package 显式指定');
1451
+ }
1452
+
1453
+ function testNodeEnv() {
1454
+ const result = spawnSync('node', ['--version'], { encoding: 'utf8' });
1455
+ if (result.status !== 0) {
1456
+ throw new Error('未检测到 Node.js 环境');
1457
+ }
1458
+ const version = result.stdout.trim();
1459
+ const major = Number(version.replace(/^v/, '').split('.')[0]);
1460
+ if (!Number.isFinite(major) || major < 18) {
1461
+ throw new Error(`Node.js 版本过低: ${version} (最低要求: v18)`);
1462
+ }
1463
+ ok(`Node.js ${version} 环境就绪`);
1464
+ }
1465
+
1466
+ function detectPkgManager(targetDir) {
1467
+ if (fs.existsSync(path.join(targetDir, 'pnpm-lock.yaml')) && commandExists('pnpm')) {
1468
+ return 'pnpm';
1469
+ }
1470
+ if (commandExists('pnpm')) {
1471
+ return 'pnpm';
1472
+ }
1473
+ if (commandExists('npm')) {
1474
+ return 'npm';
1475
+ }
1476
+ return '';
1477
+ }
1478
+
1479
+ function readSourcePackageField(sourceDir, field) {
1480
+ const sourcePkgPath = path.join(sourceDir, 'package.json');
1481
+ if (!fs.existsSync(sourcePkgPath)) return null;
1482
+ const pkg = readJson(sourcePkgPath, 'source package.json');
1483
+ if (field === 'ident') {
1484
+ return pkg.name && pkg.version ? `${pkg.name}@${pkg.version}` : null;
1485
+ }
1486
+ if (field === 'name') return pkg.name || null;
1487
+ if (field === 'registry') return pkg.publishConfig?.registry || null;
1488
+ return null;
1489
+ }
1490
+
1491
+ function isWorkspaceRootInstallTarget(targetDir) {
1492
+ const resolvedTarget = path.resolve(targetDir);
1493
+ return findMonorepoWorkspaceRoot(resolvedTarget) === resolvedTarget;
1494
+ }
1495
+
1496
+ function buildDevDependencyInstallArgs(targetDir, pkgManager, packages) {
1497
+ if (pkgManager === 'pnpm') {
1498
+ const args = ['add'];
1499
+ if (isWorkspaceRootInstallTarget(targetDir)) {
1500
+ args.push('-w');
1501
+ }
1502
+ args.push('-D', ...packages);
1503
+ return args;
1504
+ }
1505
+ return ['install', '-D', ...packages];
1506
+ }
1507
+
1508
+ function installDevDependencies(targetDir, pkgManager, packages) {
1509
+ if (!pkgManager) return { status: 1 };
1510
+ return runCommand(pkgManager, buildDevDependencyInstallArgs(targetDir, pkgManager, packages), { cwd: targetDir, stdio: 'inherit' });
1511
+ }
1512
+
1513
+ function syncCommands(targetDir, sourceDir, ideName, overwrite) {
1514
+ const commonDir = path.join(sourceDir, '.agents', 'commands', 'common');
1515
+ const ideDir = path.join(sourceDir, '.agents', 'commands', ideName);
1516
+ const destDir = path.join(targetDir, `.${ideName}`, 'commands');
1517
+ const copiedInThisRun = new Set();
1518
+ ensureDir(destDir);
1519
+ for (const dir of [commonDir, ideDir]) {
1520
+ if (!fs.existsSync(dir)) continue;
1521
+ for (const entry of fs.readdirSync(dir)) {
1522
+ if (!entry.endsWith('.md')) continue;
1523
+ const sourcePath = path.join(dir, entry);
1524
+ const destPath = path.join(destDir, entry);
1525
+ const canReplaceCommonCopy = dir === ideDir && copiedInThisRun.has(destPath);
1526
+ if (fs.existsSync(destPath) && !overwrite && !canReplaceCommonCopy) {
1527
+ info(` 跳过已存在命令: ${entry}`);
1528
+ continue;
1529
+ }
1530
+ const rendered = readRenderedCommandTemplate(sourcePath, {
1531
+ forceLocalProtocol: process.env.ENGINEERED_SPEC_FORCE_LOCAL_CLI === '1',
1532
+ });
1533
+ ensureDir(path.dirname(destPath));
1534
+ fs.writeFileSync(destPath, rendered, 'utf8');
1535
+ copiedInThisRun.add(destPath);
1536
+ }
1537
+ }
1538
+ if (fs.existsSync(destDir)) {
1539
+ ok(`.${ideName}/commands/ 已同步`);
1540
+ }
1541
+ }
1542
+
1543
+ function hasYamlFrontmatter(content) {
1544
+ return /^---\r?\n[\s\S]*?\r?\n---(?:\r?\n|$)/.test(content);
1545
+ }
1546
+
1547
+ function collectStaleCursorProtocolCommands(targetDir) {
1548
+ const commandsDir = path.join(targetDir, '.cursor', 'commands');
1549
+ if (!fs.existsSync(commandsDir)) {
1550
+ return [];
1551
+ }
1552
+
1553
+ const staleFiles = [];
1554
+ for (const [fileName, requiredSnippets] of CURSOR_PROTOCOL_COMMAND_EXPECTATIONS) {
1555
+ const filePath = path.join(commandsDir, fileName);
1556
+ if (!fs.existsSync(filePath)) {
1557
+ continue;
1558
+ }
1559
+
1560
+ let content = '';
1561
+ try {
1562
+ content = fs.readFileSync(filePath, 'utf8');
1563
+ } catch (error) {
1564
+ staleFiles.push(fileName);
1565
+ continue;
1566
+ }
1567
+
1568
+ if (!hasYamlFrontmatter(content) || requiredSnippets.some((snippet) => !content.includes(snippet))) {
1569
+ staleFiles.push(fileName);
1570
+ }
1571
+ }
1572
+ return staleFiles;
1573
+ }
1574
+
1575
+ function getProfileDirs(sourceDir, profileId, profilesRegistry) {
1576
+ const entry = getProfileEntries(profilesRegistry)[profileId];
1577
+ if (!entry) {
1578
+ throw new Error(`Unsupported profile: ${profileId}. Supported profiles: ${formatSupportedProfiles(profilesRegistry)}`);
1579
+ }
1580
+ return {
1581
+ rulesDir: path.join(sourceDir, entry.rules_dir),
1582
+ skillsDir: path.join(sourceDir, entry.skills_dir),
1583
+ configsDir: path.join(sourceDir, entry.configs_dir),
1584
+ };
1585
+ }
1586
+
1587
+ function isCustomRule(ruleName, options) {
1588
+ return options.rulesStrategy === 'custom' && options.customRules.includes(ruleName);
1589
+ }
1590
+
1591
+ function syncRulesAssets(targetDir, sourceDir, profilesRegistry, options, copyMode = {}) {
1592
+ const rulesOut = path.join(targetDir, '.agents', 'rules');
1593
+ const profileList = getActiveProfiles(options);
1594
+ const commonRulesDir = path.join(sourceDir, '.agents', 'rules', 'common');
1595
+ const profileRulesDirs = profileList.map((profileId) => getProfileDirs(sourceDir, profileId, profilesRegistry).rulesDir);
1596
+ const sourceRuleDirs = [commonRulesDir, ...profileRulesDirs];
1597
+ const ruleMode = copyMode.updateRuleMode || UPDATE_RULE_MODES.LEGACY;
1598
+ const selectedRuleFiles = new Set(normalizeList(copyMode.selectedRuleFiles));
1599
+
1600
+ info(`同步 rules (common + ${profileList.join(', ')}) ...`);
1601
+
1602
+ if (ruleMode === UPDATE_RULE_MODES.SELECTED) {
1603
+ for (const sourceRuleDir of sourceRuleDirs) {
1604
+ if (!fs.existsSync(sourceRuleDir)) continue;
1605
+ for (const fileName of fs.readdirSync(sourceRuleDir).filter((name) => name.endsWith('.md'))) {
1606
+ if (UPDATE_RULE_PROTECTED_FILES.has(fileName) || !selectedRuleFiles.has(fileName)) {
1607
+ continue;
1608
+ }
1609
+ copyFile(path.join(sourceRuleDir, fileName), path.join(rulesOut, fileName));
1610
+ }
1611
+ }
1612
+ return;
1613
+ }
1614
+
1615
+ if (ruleMode === UPDATE_RULE_MODES.ALL) {
1616
+ for (const sourceRuleDir of sourceRuleDirs) {
1617
+ if (!fs.existsSync(sourceRuleDir)) continue;
1618
+ for (const fileName of fs.readdirSync(sourceRuleDir).filter((name) => name.endsWith('.md'))) {
1619
+ if (UPDATE_RULE_PROTECTED_FILES.has(fileName)) {
1620
+ info(`跳过受保护规则: ${fileName}`);
1621
+ continue;
1622
+ }
1623
+ copyFile(path.join(sourceRuleDir, fileName), path.join(rulesOut, fileName));
1624
+ }
1625
+ }
1626
+ return;
1627
+ }
1628
+
1629
+ for (const sourceRuleDir of sourceRuleDirs) {
1630
+ if (!fs.existsSync(sourceRuleDir)) continue;
1631
+ for (const fileName of fs.readdirSync(sourceRuleDir).filter((name) => name.endsWith('.md'))) {
1632
+ const sourcePath = path.join(sourceRuleDir, fileName);
1633
+ const destPath = path.join(rulesOut, fileName);
1634
+ if (isCustomRule(fileName, options)) {
1635
+ info(`跳过自定义规则: ${fileName}(保留项目自定义)`);
1636
+ continue;
1637
+ }
1638
+ if (PROJECT_SPECIFIC_RULES.has(fileName) && fs.existsSync(destPath)) {
1639
+ warn(`跳过项目特有规则: ${fileName}(已存在)`);
1640
+ continue;
1641
+ }
1642
+ if (copyMode.skipExistingRules && fs.existsSync(destPath)) {
1643
+ info(` 跳过已存在规则: ${fileName}(如需强制覆盖请使用 --force-update-rules)`);
1644
+ continue;
1645
+ }
1646
+ copyFile(sourcePath, destPath);
1647
+ if (PROJECT_SPECIFIC_RULES.has(fileName)) {
1648
+ info(`已生成模板: ${fileName} → 请根据项目实际情况修改`);
1649
+ }
1650
+ }
1651
+ }
1652
+ copyFile(path.join(sourceDir, '.agents', 'rules', 'README.md'), path.join(rulesOut, 'README.md'));
1653
+ }
1654
+
1655
+ function copyAgents(targetDir, sourceDir, profilesRegistry, options, copyMode = {}) {
1656
+ const agentsDir = path.join(targetDir, '.agents');
1657
+ const rulesOut = path.join(agentsDir, 'rules');
1658
+ const skillsOut = path.join(agentsDir, 'skills');
1659
+ ensureDir(rulesOut);
1660
+ ensureDir(skillsOut);
1661
+
1662
+ const profileList = getActiveProfiles(options);
1663
+ const commonSkillsDir = path.join(sourceDir, '.agents', 'skills', 'common');
1664
+
1665
+ if (!copyMode.skipRules) {
1666
+ syncRulesAssets(targetDir, sourceDir, profilesRegistry, options, copyMode);
1667
+ } else {
1668
+ info('跳过 rules 同步(用户选择不更新规则)');
1669
+ }
1670
+
1671
+ if (!copyMode.skipSkills) {
1672
+ const profileSkillsDirs = profileList.map((profileId) => getProfileDirs(sourceDir, profileId, profilesRegistry).skillsDir);
1673
+ info(`同步 skills (common + ${profileList.join(', ')}) ...`);
1674
+ for (const sourceSkillsDir of [commonSkillsDir, ...profileSkillsDirs]) {
1675
+ if (!fs.existsSync(sourceSkillsDir)) continue;
1676
+ for (const entry of fs.readdirSync(sourceSkillsDir, { withFileTypes: true })) {
1677
+ if (!entry.isDirectory()) continue;
1678
+ copyDirReplace(path.join(sourceSkillsDir, entry.name), path.join(skillsOut, entry.name));
1679
+ }
1680
+ }
1681
+ copyFile(path.join(sourceDir, '.agents', 'skills', 'README.md'), path.join(skillsOut, 'README.md'));
1682
+ } else {
1683
+ info('跳过 skills 同步(用户选择不更新技能)');
1684
+ }
1685
+
1686
+ ok(`.agents/ 同步完成 (profiles: ${profileList.join(', ')})`);
1687
+ }
1688
+
1689
+ function readInstallRegistry(sourceDir, fileName, objectKey) {
1690
+ const filePath = path.join(sourceDir, '.agents', 'registry', fileName);
1691
+ if (!fs.existsSync(filePath)) {
1692
+ throw new Error(`Registry file not found: ${filePath}`);
1693
+ }
1694
+ const data = readJson(filePath, fileName);
1695
+ if (!data || typeof data !== 'object' || !data[objectKey] || typeof data[objectKey] !== 'object') {
1696
+ throw new Error(`Registry ${fileName} is missing root key "${objectKey}"`);
1697
+ }
1698
+ return data;
1699
+ }
1700
+
1701
+ function copyRequiredProtocolFile(sourceDir, targetDir, relPath, copiedPaths) {
1702
+ if (!relPath || copiedPaths.has(relPath)) {
1703
+ return;
1704
+ }
1705
+
1706
+ const sourcePath = path.join(sourceDir, relPath);
1707
+ const targetPath = path.join(targetDir, relPath);
1708
+ if (!copyFile(sourcePath, targetPath)) {
1709
+ throw new Error(`Protocol asset missing: ${relPath}`);
1710
+ }
1711
+ copiedPaths.add(relPath);
1712
+ }
1713
+
1714
+ function syncProtocolAssets(targetDir, sourceDir) {
1715
+ info('同步协议资产(roles / flows / orchestration)...');
1716
+ const rolesRegistry = readInstallRegistry(sourceDir, 'roles.json', 'roles');
1717
+ const flowsRegistry = readInstallRegistry(sourceDir, 'flows.json', 'flows');
1718
+ const copiedPaths = new Set();
1719
+ const activeRoles = Object.entries(rolesRegistry.roles || {})
1720
+ .filter(([, entry]) => entry?.status === 'active')
1721
+ .map(([id, entry]) => ({ id, ...entry }));
1722
+ const activeFlows = Object.entries(flowsRegistry.flows || {})
1723
+ .filter(([, entry]) => entry?.status === 'active')
1724
+ .map(([id, entry]) => ({ id, ...entry }));
1725
+
1726
+ for (const relPath of normalizeList(rolesRegistry.support_files)) {
1727
+ copyRequiredProtocolFile(sourceDir, targetDir, relPath, copiedPaths);
1728
+ }
1729
+ for (const relPath of normalizeList(flowsRegistry.support_files)) {
1730
+ copyRequiredProtocolFile(sourceDir, targetDir, relPath, copiedPaths);
1731
+ }
1732
+
1733
+ for (const role of activeRoles) {
1734
+ if (typeof role.source !== 'string' || !role.source.trim()) {
1735
+ throw new Error(`Active role is missing source: ${role.id}`);
1736
+ }
1737
+ copyRequiredProtocolFile(sourceDir, targetDir, role.source, copiedPaths);
1738
+ const domainMatch = role.source.match(/^\.agents\/roles\/domains\/([^/]+)\//);
1739
+ if (domainMatch) {
1740
+ copyRequiredProtocolFile(sourceDir, targetDir, `.agents/roles/domains/${domainMatch[1]}/README.md`, copiedPaths);
1741
+ }
1742
+ }
1743
+
1744
+ for (const flow of activeFlows) {
1745
+ if (typeof flow.source !== 'string' || !flow.source.trim()) {
1746
+ throw new Error(`Active flow is missing source: ${flow.id}`);
1747
+ }
1748
+ copyRequiredProtocolFile(sourceDir, targetDir, flow.source, copiedPaths);
1749
+ }
1750
+
1751
+ ok(`协议资产同步完成 (roles: ${activeRoles.length}, flows: ${activeFlows.length})`);
1752
+ return {
1753
+ roles: activeRoles.map((item) => item.id),
1754
+ flows: activeFlows.map((item) => item.id),
1755
+ };
1756
+ }
1757
+
1758
+ function copyConfigs(targetDir, sourceDir, profilesRegistry, options, skipExisting = true) {
1759
+ const commonDir = path.join(sourceDir, 'configs', 'common');
1760
+ const { configsDir } = getProfileDirs(sourceDir, options.profile, profilesRegistry);
1761
+ let copied = false;
1762
+ const createdPaths = [];
1763
+ const skipHuskyArtifacts = options.installHusky !== 'yes' && !fs.existsSync(path.join(targetDir, '.husky'));
1764
+
1765
+ if (skipHuskyArtifacts) {
1766
+ info('提交校验相关配置(.husky / .lintstagedrc / commitlint)将跳过同步');
1767
+ }
1768
+
1769
+ if (fs.existsSync(commonDir)) {
1770
+ info('同步 lint/format 配置 (common) ...');
1771
+ const result = copyDirIncremental(commonDir, targetDir, { skipExisting, skipHuskyArtifacts });
1772
+ copied = result.copiedAny || copied;
1773
+ createdPaths.push(...result.createdPaths);
1774
+ }
1775
+ if (fs.existsSync(configsDir)) {
1776
+ info(`同步 lint/format 配置 (${path.relative(sourceDir, configsDir).split(path.sep).join('/')}) ...`);
1777
+ const result = copyDirIncremental(configsDir, targetDir, { skipExisting, skipHuskyArtifacts });
1778
+ copied = result.copiedAny || copied;
1779
+ createdPaths.push(...result.createdPaths);
1780
+ }
1781
+
1782
+ if (copied) ok('lint/format 配置部署完成');
1783
+ else info('未找到 lint/format 配置模板,跳过');
1784
+ return normalizeList(createdPaths);
1785
+ }
1786
+
1787
+ function installLocalCli(targetDir, sourceDir, pkgManager, pending, options = {}) {
1788
+ const targetPkg = path.join(targetDir, 'package.json');
1789
+ if (!fs.existsSync(targetPkg)) {
1790
+ warn('未找到 package.json,跳过本地 ai-spec-auto CLI 安装');
1791
+ return [];
1792
+ }
1793
+ if (!pkgManager) {
1794
+ warn('无可用的包管理器,跳过本地 ai-spec-auto CLI 安装');
1795
+ return [];
1796
+ }
1797
+
1798
+ const mode = options.mode || 'init';
1799
+ const beforeSnapshot = readPackageSnapshot(targetDir);
1800
+ const forcedLocal = Boolean(process.env.ENGINEERED_SPEC_FORCE_LOCAL_CLI);
1801
+ const packageName = readSourcePackageField(sourceDir, 'name');
1802
+ const sourceIdent = readSourcePackageField(sourceDir, 'ident');
1803
+ // 解析 install spec:
1804
+ // 1. ENGINEERED_SPEC_FORCE_LOCAL_CLI=1 -> 用本地 sourceDir 路径(开发场景)
1805
+ // 2. ENGINEERED_SPEC_LOCAL_CLI_VERSION 显式指定版本/dist-tag -> name@<value>
1806
+ // 3. update 模式:默认 name@latest,确保从 registry 解析到最新版本
1807
+ // 4. init/default-init: use sourceDir current name@version (preserve original behavior, avoid breaking locked version scenarios)
1808
+ const explicitVersion = process.env.ENGINEERED_SPEC_LOCAL_CLI_VERSION;
1809
+ let installSpec;
1810
+ if (forcedLocal) {
1811
+ installSpec = sourceDir;
1812
+ } else if (explicitVersion && packageName) {
1813
+ installSpec = `${packageName}@${explicitVersion}`;
1814
+ } else if (mode === 'update' && packageName) {
1815
+ installSpec = `${packageName}@latest`;
1816
+ } else {
1817
+ installSpec = sourceIdent || sourceDir;
1818
+ }
1819
+ const registry = readSourcePackageField(sourceDir, 'registry');
1820
+ const scopeName = packageName && packageName.startsWith('@') ? packageName.split('/')[0] : '';
1821
+ const args = buildDevDependencyInstallArgs(targetDir, pkgManager, [installSpec]);
1822
+ if (registry) {
1823
+ args.push('--registry', registry);
1824
+ if (scopeName) {
1825
+ args.push(`--${scopeName}:registry=${registry}`);
1826
+ }
1827
+ }
1828
+ info(`正在使用 ${pkgManager} 安装项目内 ai-spec-auto CLI ...`);
1829
+ info(` source: ${forcedLocal ? `${installSpec} (forced local path)` : `${installSpec}${registry ? ` via ${registry}` : ''}`}`);
1830
+ const result = runCommand(pkgManager, args, { cwd: targetDir, stdio: 'inherit' });
1831
+ if (result.status !== 0) {
1832
+ pending.failures.push(`本地 ai-spec-auto CLI 安装失败:请在 ${targetDir} 手动执行 ${pkgManager} ${args.join(' ')}`);
1833
+ return [];
1834
+ }
1835
+ ok('项目内 ai-spec-auto CLI 已就绪 (./node_modules/.bin/ai-spec-auto)');
1836
+ const afterSnapshot = readPackageSnapshot(targetDir);
1837
+ return collectNewPackageNames(beforeSnapshot, afterSnapshot, [packageName || installSpec]);
1838
+ }
1839
+
1840
+ function installLintDeps(targetDir, pkgManager, options, pending) {
1841
+ if (!fs.existsSync(path.join(targetDir, 'package.json'))) {
1842
+ pending.failures.push('lint/format:未找到 package.json,已跳过依赖安装。');
1843
+ return { addedPackages: [], prepareScript: '' };
1844
+ }
1845
+ if (!pkgManager) {
1846
+ pending.failures.push('lint/format:无可用的包管理器,无法安装 ESLint 等依赖。');
1847
+ return { addedPackages: [], prepareScript: '' };
1848
+ }
1849
+ const deps = [...LINT_DEP_SPECS];
1850
+ const profileList = options.profiles && options.profiles.length > 0 ? options.profiles : [options.profile];
1851
+ if (profileList.includes('vue')) {
1852
+ deps.push(...VUE_LINT_DEP_SPECS);
1853
+ }
1854
+ const beforeSnapshot = readPackageSnapshot(targetDir);
1855
+ info(`正在使用 ${pkgManager} 安装 lint/format 依赖,请稍候 ...`);
1856
+ info(` ${deps.join(' ')}`);
1857
+ const result = installDevDependencies(targetDir, pkgManager, deps);
1858
+ if (result.status !== 0) {
1859
+ pending.failures.push(`lint/format 依赖安装失败:请在 ${targetDir} 手动安装 ${deps.join(' ')}`);
1860
+ return { addedPackages: [], prepareScript: '' };
1861
+ }
1862
+ ok('lint/format 依赖安装完成');
1863
+ const afterSnapshot = readPackageSnapshot(targetDir);
1864
+ return {
1865
+ addedPackages: collectNewPackageNames(beforeSnapshot, afterSnapshot, deps),
1866
+ prepareScript: !beforeSnapshot.prepareScript && afterSnapshot.prepareScript ? afterSnapshot.prepareScript : '',
1867
+ };
1868
+ }
1869
+
1870
+ function installCommitHooks(targetDir, pkgManager, pending) {
1871
+ if (!fs.existsSync(path.join(targetDir, 'package.json'))) {
1872
+ pending.failures.push('提交校验:未找到 package.json,已跳过依赖安装。');
1873
+ return { addedPackages: [], prepareScript: '' };
1874
+ }
1875
+ if (!pkgManager) {
1876
+ pending.failures.push('提交校验:无可用的包管理器,无法安装 husky 等依赖。');
1877
+ return { addedPackages: [], prepareScript: '' };
1878
+ }
1879
+ const deps = [...HUSKY_DEP_SPECS];
1880
+ const beforeSnapshot = readPackageSnapshot(targetDir);
1881
+ info(`正在使用 ${pkgManager} 安装提交校验依赖,请稍候 ...`);
1882
+ info(` ${deps.join(' ')}`);
1883
+ const result = installDevDependencies(targetDir, pkgManager, deps);
1884
+ if (result.status !== 0) {
1885
+ pending.failures.push(`提交校验依赖安装失败:请在 ${targetDir} 手动安装 ${deps.join(' ')}`);
1886
+ return { addedPackages: [], prepareScript: '' };
1887
+ }
1888
+ info('初始化 husky ...');
1889
+ const huskyResult = runCommand('npx', ['husky', 'install'], { cwd: targetDir, stdio: 'inherit' });
1890
+ if (huskyResult.status !== 0) {
1891
+ pending.failures.push(`husky install 失败:请在 ${targetDir} 手动执行 npx husky install`);
1892
+ return { addedPackages: [], prepareScript: '' };
1893
+ }
1894
+ ok('提交校验工具链安装完成 (husky@8 + lint-staged + commitlint)');
1895
+ const afterSnapshot = readPackageSnapshot(targetDir);
1896
+ return {
1897
+ addedPackages: collectNewPackageNames(beforeSnapshot, afterSnapshot, deps),
1898
+ prepareScript: !beforeSnapshot.prepareScript && afterSnapshot.prepareScript ? afterSnapshot.prepareScript : '',
1899
+ };
1900
+ }
1901
+
1902
+ function createIdeLinks(targetDir, sourceDir, options, superpowersEnabled = false) {
1903
+ for (const ide of normalizeIdeFilter(options.ideFilter)) {
1904
+ const ideDir = path.join(targetDir, `.${ide}`);
1905
+ ensureDir(ideDir);
1906
+ createDirLink(path.join(targetDir, '.agents', 'rules'), path.join(ideDir, 'rules'));
1907
+ const ideSkillsDir = path.join(ideDir, 'skills');
1908
+ ensureDir(ideSkillsDir);
1909
+
1910
+ const agentsSkillsDir = path.join(targetDir, '.agents', 'skills');
1911
+ if (fs.existsSync(agentsSkillsDir)) {
1912
+ for (const entry of fs.readdirSync(agentsSkillsDir, { withFileTypes: true })) {
1913
+ if (!entry.isDirectory()) continue;
1914
+ if (entry.name === 'common' || entry.name === 'profiles') continue;
1915
+ const linkPath = path.join(ideSkillsDir, entry.name);
1916
+ if (!shouldExposeSkillToIde(entry.name, superpowersEnabled)) {
1917
+ removePath(linkPath);
1918
+ continue;
1919
+ }
1920
+ createDirLink(path.join(agentsSkillsDir, entry.name), linkPath);
1921
+ }
1922
+ }
1923
+ ok(`.${ide}/ 链接就绪`);
1924
+ }
1925
+
1926
+ if (normalizeIdeFilter(options.ideFilter).includes('cursor')) {
1927
+ const mcpSrc = path.join(sourceDir, '.cursor', 'mcp.json');
1928
+ const mcpDest = path.join(targetDir, '.cursor', 'mcp.json');
1929
+ if (fs.existsSync(mcpSrc) && !fs.existsSync(mcpDest)) {
1930
+ copyFile(mcpSrc, mcpDest);
1931
+ info('.cursor/mcp.json 已生成(请在 Cursor「设置 → MCP」中按需启用并完成凭证配置)');
1932
+ }
1933
+ }
1934
+
1935
+ for (const ide of normalizeIdeFilter(options.ideFilter)) {
1936
+ syncCommands(targetDir, sourceDir, ide, options.updateCommands === 'yes');
1937
+ }
1938
+ }
1939
+
1940
+ function resolveSuperpowersEnabled(targetDir, options, manifestConfig = null) {
1941
+ if (options.superpowers === 'yes') {
1942
+ return true;
1943
+ }
1944
+ if (options.superpowers === 'no') {
1945
+ return false;
1946
+ }
1947
+ const normalizedManifest = normalizeSuperpowersManifest(manifestConfig, readInstalledManifestSuperpowers(targetDir));
1948
+ if (normalizedManifest) {
1949
+ return Boolean(normalizedManifest.enabled);
1950
+ }
1951
+ const existingState = readSuperpowersState(targetDir);
1952
+ return Boolean(existingState?.enabled);
1953
+ }
1954
+
1955
+ function resolveVisualBridgeEnabled(targetDir, options, manifestConfig = null) {
1956
+ if (options.visualBridge === 'yes') {
1957
+ return true;
1958
+ }
1959
+ if (options.visualBridge === 'no') {
1960
+ return false;
1961
+ }
1962
+ const normalizedManifest = normalizeVisualBridgeManifest(manifestConfig, readInstalledManifestVisualBridge(targetDir));
1963
+ if (normalizedManifest) {
1964
+ return Boolean(normalizedManifest.enabled);
1965
+ }
1966
+ const existingState = readVisualBridgeState(targetDir);
1967
+ return Boolean(existingState?.enabled);
1968
+ }
1969
+
1970
+ function applySuperpowersBridge(targetDir, options, source = 'init', manifestConfig = null) {
1971
+ const enabled = resolveSuperpowersEnabled(targetDir, options, manifestConfig);
1972
+ const state = buildSuperpowersState({
1973
+ targetDir,
1974
+ enabled,
1975
+ manifestConfig: normalizeSuperpowersManifest(manifestConfig, null),
1976
+ ides: normalizeIdeFilter(options.ideFilter),
1977
+ env: process.env,
1978
+ cliVersion: VERSION,
1979
+ source,
1980
+ previousState: readSuperpowersState(targetDir),
1981
+ });
1982
+ writeSuperpowersState(targetDir, state);
1983
+ upsertManagedAgentsBlock(targetDir, enabled && normalizeIdeFilter(options.ideFilter).includes('codex'));
1984
+ return state;
1985
+ }
1986
+
1987
+ function applyVisualBridge(targetDir, options, source = 'init', manifestConfig = null) {
1988
+ const normalizedManifest = normalizeVisualBridgeManifest(manifestConfig, null) || {};
1989
+ const enabled = resolveVisualBridgeEnabled(targetDir, options, normalizedManifest);
1990
+ const state = buildVisualBridgeState({
1991
+ targetDir,
1992
+ manifestConfig: {
1993
+ ...normalizedManifest,
1994
+ enabled,
1995
+ },
1996
+ cliVersion: VERSION,
1997
+ source,
1998
+ previousState: readVisualBridgeState(targetDir),
1999
+ });
2000
+ if (state) {
2001
+ writeVisualBridgeState(targetDir, state);
2002
+ }
2003
+ return state;
2004
+ }
2005
+
2006
+ function ensureOpenSpecDirs(targetDir) {
2007
+ ensureDir(path.join(targetDir, 'openspec', 'specs'));
2008
+ ensureDir(path.join(targetDir, 'openspec', 'changes', 'archive'));
2009
+ ensureDir(path.join(targetDir, 'openspec', 'schemas'));
2010
+ }
2011
+
2012
+ function setupOpenSpec(targetDir, sourceDir, options, pkgManager, pending) {
2013
+ info('配置 OpenSpec ...');
2014
+ const openspecAvailable = spawnSync('npx', ['openspec', '--version'], { stdio: 'ignore' }).status === 0;
2015
+ if (!openspecAvailable) {
2016
+ if (!pkgManager) {
2017
+ pending.failures.push('OpenSpec CLI 不可用,且未检测到包管理器。请手动安装 @fission-ai/openspec。');
2018
+ } else {
2019
+ info('正在全局安装 @fission-ai/openspec ...');
2020
+ const install = pkgManager === 'pnpm'
2021
+ ? runCommand('pnpm', ['add', '-g', '@fission-ai/openspec@latest'], { stdio: 'inherit' })
2022
+ : runCommand('npm', ['install', '-g', '@fission-ai/openspec@latest'], { stdio: 'inherit' });
2023
+ if (install.status !== 0 || spawnSync('npx', ['openspec', '--version'], { stdio: 'ignore' }).status !== 0) {
2024
+ pending.failures.push('OpenSpec CLI 自动安装失败,请手动执行 npm install -g @fission-ai/openspec@latest');
2025
+ } else {
2026
+ ok('openspec CLI 已安装并可用');
2027
+ }
2028
+ }
2029
+ } else {
2030
+ ok('openspec CLI 可用');
2031
+ }
2032
+
2033
+ ensureOpenSpecDirs(targetDir);
2034
+ const toolsArg = normalizeIdeFilter(options.ideFilter).join(',');
2035
+ const configYaml = path.join(targetDir, 'openspec', 'config.yaml');
2036
+ const configYml = path.join(targetDir, 'openspec', 'config.yml');
2037
+ if (spawnSync('npx', ['openspec', '--version'], { stdio: 'ignore' }).status === 0) {
2038
+ if (!fs.existsSync(configYaml) && !fs.existsSync(configYml)) {
2039
+ info('运行 openspec init ...');
2040
+ const init = runCommand('npx', ['openspec', 'init', '--tools', toolsArg, '--force'], { cwd: targetDir, stdio: 'inherit' });
2041
+ if (init.status !== 0) {
2042
+ pending.failures.push(`openspec init 失败:请在 ${targetDir} 手动执行 npx openspec init --tools "${toolsArg}"`);
2043
+ }
2044
+ } else {
2045
+ info('openspec/ 已存在,运行 openspec update ...');
2046
+ const update = runCommand('npx', ['openspec', 'update', '--force'], { cwd: targetDir, stdio: 'inherit' });
2047
+ if (update.status !== 0) {
2048
+ pending.failures.push(`openspec update 失败:请在 ${targetDir} 手动执行 npx openspec update --force`);
2049
+ }
2050
+ }
2051
+ }
2052
+
2053
+ const schemaSrc = path.join(sourceDir, 'openspec', 'schemas');
2054
+ const schemaDst = path.join(targetDir, 'openspec', 'schemas');
2055
+ if (fs.existsSync(schemaSrc)) {
2056
+ for (const entry of fs.readdirSync(schemaSrc, { withFileTypes: true })) {
2057
+ if (!entry.isDirectory()) continue;
2058
+ copyDirReplace(path.join(schemaSrc, entry.name), path.join(schemaDst, entry.name));
2059
+ }
2060
+ }
2061
+
2062
+ const template = path.join(sourceDir, 'openspec', 'config.yaml.template');
2063
+ if (fs.existsSync(template)) {
2064
+ const templateRaw = fs.readFileSync(template, 'utf8');
2065
+ const templateSchemaLine = templateRaw.split(/\r?\n/).find((line) => /^schema:\s*/.test(line)) || '';
2066
+ if (!fs.existsSync(configYaml)) {
2067
+ copyFile(template, configYaml);
2068
+ ok('openspec/config.yaml 已创建');
2069
+ } else {
2070
+ let current = fs.readFileSync(configYaml, 'utf8');
2071
+ if (!/^context:/m.test(current)) {
2072
+ const contextIdx = templateRaw.indexOf('context:');
2073
+ if (contextIdx >= 0) {
2074
+ current = `${current.replace(/\s*$/, '\n\n')}${templateRaw.slice(contextIdx)}`;
2075
+ }
2076
+ ok('config.yaml 已增量补充 rules 子键');
2077
+ }
2078
+ if (templateSchemaLine) {
2079
+ if (/^schema:\s*/m.test(current)) {
2080
+ current = current.replace(/^schema:\s*.*$/m, templateSchemaLine);
2081
+ } else {
2082
+ current = `${templateSchemaLine}\n\n${current}`;
2083
+ }
2084
+ }
2085
+ fs.writeFileSync(configYaml, current, 'utf8');
2086
+ }
2087
+ }
2088
+ ok('OpenSpec 配置完成');
2089
+ }
2090
+
2091
+ function getUiproSkillPaths(targetDir) {
2092
+ const legacySkillDir = path.join(targetDir, '.agents', 'skills', 'ui-ux-pro-max');
2093
+ const skillDir = path.join(targetDir, '.agents', 'skills', 'domains', 'ui-ux-pro-max');
2094
+ return {
2095
+ legacySkillDir,
2096
+ skillDir,
2097
+ legacyDataDir: path.join(legacySkillDir, 'data'),
2098
+ skillDataDir: path.join(skillDir, 'data'),
2099
+ };
2100
+ }
2101
+
2102
+ function hasInstalledUiproData(targetDir) {
2103
+ const { legacyDataDir, skillDataDir } = getUiproSkillPaths(targetDir);
2104
+ return fs.existsSync(skillDataDir) || fs.existsSync(legacyDataDir);
2105
+ }
2106
+
2107
+ function hasAnyUiproAssets(targetDir) {
2108
+ const { legacySkillDir, skillDir } = getUiproSkillPaths(targetDir);
2109
+ return fs.existsSync(skillDir) || fs.existsSync(legacySkillDir);
2110
+ }
2111
+
2112
+ function resolveUiproInstallOutput(tmpDir) {
2113
+ const sharedDataDir = path.join(tmpDir, '.shared', 'ui-ux-pro-max');
2114
+ const commandPromptFile = path.join(tmpDir, '.cursor', 'commands', 'ui-ux-pro-max.md');
2115
+ if (fs.existsSync(sharedDataDir)) {
2116
+ return {
2117
+ dataDir: sharedDataDir,
2118
+ promptFile: commandPromptFile,
2119
+ };
2120
+ }
2121
+
2122
+ const cursorSkillDir = path.join(tmpDir, '.cursor', 'skills', 'ui-ux-pro-max');
2123
+ const cursorSkillDataDir = path.join(cursorSkillDir, 'data');
2124
+ const cursorSkillFile = path.join(cursorSkillDir, 'SKILL.md');
2125
+ if (fs.existsSync(cursorSkillDataDir)) {
2126
+ return {
2127
+ dataDir: cursorSkillDataDir,
2128
+ promptFile: cursorSkillFile,
2129
+ };
2130
+ }
2131
+ if (fs.existsSync(cursorSkillDir)) {
2132
+ return {
2133
+ dataDir: cursorSkillDir,
2134
+ promptFile: cursorSkillFile,
2135
+ };
2136
+ }
2137
+
2138
+ return {
2139
+ dataDir: '',
2140
+ promptFile: commandPromptFile,
2141
+ };
2142
+ }
2143
+
2144
+ function writeUiproSkillFile(skillDir, promptFile) {
2145
+ const defaultContent = `---\nname: ui-ux-pro-max\ndescription: AI 设计智能技能,提供 67 种 UI 风格、161 套配色方案、57 组字体搭配、99 条 UX 准则。当需要 AI 自主做出 UI/UX 设计决策时使用本技能。\n---\n\n# UI UX Pro Max\n\n本技能为 AI 注入专业 UI/UX 设计决策能力。\n`;
2146
+ if (!fs.existsSync(promptFile)) {
2147
+ fs.writeFileSync(path.join(skillDir, 'SKILL.md'), defaultContent, 'utf8');
2148
+ return;
2149
+ }
2150
+
2151
+ const prompt = fs.readFileSync(promptFile, 'utf8')
2152
+ .replace(/\.shared\/ui-ux-pro-max\//g, 'data/')
2153
+ .replace(/\.cursor\/skills\/ui-ux-pro-max\/data\//g, 'data/')
2154
+ .replace(/\.cursor\/skills\/ui-ux-pro-max\//g, 'data/');
2155
+ const content = hasYamlFrontmatter(prompt)
2156
+ ? prompt
2157
+ : `---\nname: ui-ux-pro-max\ndescription: AI 设计智能技能,提供 67 种 UI 风格、161 套配色方案、57 组字体搭配、99 条 UX 准则。当需要 AI 自主做出 UI/UX 设计决策时使用本技能。\n---\n\n${prompt}`;
2158
+ fs.writeFileSync(path.join(skillDir, 'SKILL.md'), content, 'utf8');
2159
+ }
2160
+
2161
+ function setupUipro(targetDir, pkgManager, pending) {
2162
+ const { legacySkillDir, skillDir, skillDataDir } = getUiproSkillPaths(targetDir);
2163
+ if (fs.existsSync(skillDataDir)) {
2164
+ ok('UI UX Pro Max 已安装,跳过');
2165
+ return;
2166
+ }
2167
+ if (fs.existsSync(legacySkillDir)) {
2168
+ removePath(legacySkillDir);
2169
+ }
2170
+ if (!pkgManager) {
2171
+ pending.failures.push('UI UX Pro Max:无可用的包管理器,无法全局安装 uipro-cli。');
2172
+ return;
2173
+ }
2174
+
2175
+ const hasUipro = commandExists('uipro');
2176
+ if (!hasUipro) {
2177
+ info('安装 uipro-cli ...');
2178
+ const install = pkgManager === 'pnpm'
2179
+ ? runCommand('pnpm', ['add', '-g', 'uipro-cli'], { stdio: 'inherit' })
2180
+ : runCommand('npm', ['install', '-g', 'uipro-cli'], { stdio: 'inherit' });
2181
+ if (install.status !== 0 || !commandExists('uipro')) {
2182
+ pending.failures.push('uipro-cli 全局安装失败,请手动执行 npm install -g uipro-cli');
2183
+ return;
2184
+ }
2185
+ ok('uipro-cli 安装成功');
2186
+ }
2187
+
2188
+ const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'ai-spec-auto-uipro-'));
2189
+ info('下载 UI UX Pro Max 资源 ...');
2190
+ const init = runCommand('uipro', ['init', '--ai', 'cursor'], { cwd: tmpDir, stdio: 'inherit' });
2191
+ if (init.status !== 0) {
2192
+ pending.failures.push('uipro init 失败,请检查网络后重试。');
2193
+ removePath(tmpDir);
2194
+ return;
2195
+ }
2196
+ const output = resolveUiproInstallOutput(tmpDir);
2197
+ if (!output.dataDir || !fs.existsSync(output.dataDir)) {
2198
+ pending.failures.push('UI UX Pro Max 资源目录缺失,可能是 uipro-cli 版本或网络问题。');
2199
+ removePath(tmpDir);
2200
+ return;
2201
+ }
2202
+ ensureDir(skillDataDir);
2203
+ fs.cpSync(output.dataDir, skillDataDir, { recursive: true });
2204
+ writeUiproSkillFile(skillDir, output.promptFile);
2205
+ removePath(tmpDir);
2206
+ ok('UI UX Pro Max 安装完成');
2207
+ }
2208
+
2209
+ function getSelectedAiInitRules(options) {
2210
+ const selected = new Set(['01-项目概述.md', '03-项目结构.md']);
2211
+ if (options.rulesStrategy === 'custom') {
2212
+ for (const name of options.customRules) {
2213
+ selected.add(name);
2214
+ }
2215
+ }
2216
+ return [...selected];
2217
+ }
2218
+
2219
+ function buildManifestLocalPreferences(options, existingPreferences = null) {
2220
+ const base = existingPreferences && typeof existingPreferences === 'object'
2221
+ ? JSON.parse(JSON.stringify(existingPreferences))
2222
+ : {};
2223
+ if (options.rulesStrategy === 'custom') {
2224
+ const customRules = normalizeCustomRulesSelection(options.customRules.length > 0 ? options.customRules : DEFAULT_CUSTOM_RULE_SELECTION);
2225
+ base.project_init = { custom_rules: customRules };
2226
+ } else if (base.project_init) {
2227
+ delete base.project_init;
2228
+ }
2229
+ return Object.keys(base).length > 0 ? base : null;
2230
+ }
2231
+
2232
+ function formatUpdateRulesSummary(options) {
2233
+ if (options.updateRules !== 'yes') {
2234
+ return '跳过';
2235
+ }
2236
+ if (options.updateRuleMode === UPDATE_RULE_MODES.STANDARD) {
2237
+ return '标准更新(仅补缺失,保留已有规则)';
2238
+ }
2239
+ if (options.updateRuleMode === UPDATE_RULE_MODES.SELECTED) {
2240
+ return `自定义选择(${options.selectedUpdateRuleFiles.length} 个文件)`;
2241
+ }
2242
+ if (options.updateRuleMode === UPDATE_RULE_MODES.ALL) {
2243
+ return '全部更新(排除 README.md 与 12-Superpowers执行规范.md)';
2244
+ }
2245
+ return options.forceUpdateRules === 'yes'
2246
+ ? '强制覆盖(保留项目特有 01/03 与自定义)'
2247
+ : '仅补充缺失(保留已有规则)';
2248
+ }
2249
+
2250
+ function printTools(level, uiproSelected) {
2251
+ info('工具环境:');
2252
+ if (commandExists('git')) {
2253
+ const version = spawnSync('git', ['--version'], { encoding: 'utf8' }).stdout.trim().replace(/^git version\s+/, '');
2254
+ ok(` git ${version}`);
2255
+ } else {
2256
+ warn(' git 未安装');
2257
+ }
2258
+ if (commandExists('node')) {
2259
+ ok(` node ${spawnSync('node', ['--version'], { encoding: 'utf8' }).stdout.trim()}`);
2260
+ } else {
2261
+ warn(' node 未安装');
2262
+ }
2263
+ if (commandExists('npx')) {
2264
+ ok(' npx 可用');
2265
+ } else {
2266
+ warn(' npx 不可用');
2267
+ }
2268
+ if (level === 'L3') {
2269
+ if (spawnSync('npx', ['openspec', '--version'], { stdio: 'ignore' }).status === 0) {
2270
+ ok(' openspec 已安装');
2271
+ } else {
2272
+ warn(' openspec 未安装');
2273
+ }
2274
+ }
2275
+ if (uiproSelected === 'yes' || commandExists('python3')) {
2276
+ if (commandExists('python3')) {
2277
+ const py = spawnSync('python3', ['--version'], { encoding: 'utf8' });
2278
+ ok(` python3 ${(py.stdout || py.stderr).trim().replace(/^Python\s+/, '')}`);
2279
+ } else if (uiproSelected === 'yes') {
2280
+ warn(' python3 未安装(UI UX Pro Max 搜索脚本需要)');
2281
+ }
2282
+ }
2283
+ }
2284
+
2285
+ function printInstallReport(targetDir, options, pending) {
2286
+ const installMode = options.installMode || 'default-init';
2287
+ const resolvedIdes = normalizeIdeFilter(options.ideFilter);
2288
+ const ideSummary = resolvedIdes.length > 0 ? resolvedIdes.map((ide) => `.${ide}`).join(', ') : '(none)';
2289
+ const selectedAiInitRules = getSelectedAiInitRules(options);
2290
+ console.log('');
2291
+ console.log(color('════════════════════════════════════════', 'bold'));
2292
+ if (pending.failures.length > 0 || pending.configs.length > 0 || (pending.warnings || []).length > 0) {
2293
+ info('规范与配置文件已同步到项目。');
2294
+ warn(`存在 ${pending.failures.length + pending.configs.length + (pending.warnings || []).length} 项待处理(见文末汇总)。`);
2295
+ } else {
2296
+ ok('安装完成!');
2297
+ }
2298
+ console.log(color('════════════════════════════════════════', 'bold'));
2299
+ console.log('');
2300
+ info('安装配置:');
2301
+ console.log(` Profile: ${color((options.profiles && options.profiles.length > 0 ? options.profiles : [options.profile]).join(', '), 'bold')}`);
2302
+ console.log(` 安装模式: ${color(installMode, 'bold')}`);
2303
+ console.log(` 安装模型: ${color(options.level === DEFAULT_LEVEL ? 'default (full)' : 'compatibility override', 'bold')}`);
2304
+ if (options.level !== DEFAULT_LEVEL) {
2305
+ console.log(` 兼容层级: ${color(options.level, 'bold')}`);
2306
+ }
2307
+ console.log(` IDE: ${color(options.ideFilter, 'bold')}`);
2308
+ if (installMode === 'init-with-manifest') {
2309
+ console.log(` Profile来源: ${color(options.profileSource || 'manifest', 'bold')}`);
2310
+ console.log(` 规则来源: ${color(options.rulesSource || 'manifest 安装 + 安装模板沿用', 'bold')}`);
2311
+ console.log(` 规则内容偏好: ${color(options.rulesStrategy === 'custom' ? '根据项目自定义' : '沿用安装模板', 'bold')}`);
2312
+ }
2313
+ console.log(` UIPro: ${color(options.uipro, 'bold')}`);
2314
+ console.log(` AIInit: ${color('no', 'bold')}`);
2315
+ if (options.manifestSource) {
2316
+ console.log(` Manifest: ${color(options.manifestSource, 'bold')}`);
2317
+ }
2318
+ if (options.syncSummary) {
2319
+ console.log(` 首轮同步: roles ${options.syncSummary.roles}, skills ${options.syncSummary.skills}, rules ${options.syncSummary.rules}, flows ${options.syncSummary.flows}`);
2320
+ }
2321
+ console.log('');
2322
+ info('已部署内容:');
2323
+ console.log(` ${color('✔', 'green')} .agents/rules + skills + roles + flows + orchestration (profiles: ${(options.profiles && options.profiles.length > 0 ? options.profiles : [options.profile]).join(', ')})`);
2324
+ console.log(` ${options.installLint === 'yes' ? color('✔', 'green') : color('—', 'yellow')} lint/format 配置${options.installLint === 'yes' ? ' (.prettierrc, .eslintrc, .stylelintrc)' : '(已跳过)'}`);
2325
+ console.log(` ${options.installHusky === 'yes' ? color('✔', 'green') : color('—', 'yellow')} 提交校验${options.installHusky === 'yes' ? ' (.husky, .lintstagedrc, commitlint.config.js)' : '(已跳过)'}`);
2326
+ if (hasInstalledUiproData(targetDir)) {
2327
+ console.log(` ${color('✔', 'green')} UI UX Pro Max 设计智能技能 (67 styles, 161 palettes)`);
2328
+ }
2329
+ if (options.level !== 'L1') {
2330
+ console.log(` ${color('✔', 'green')} IDE 适配 (${ideSummary})`);
2331
+ }
2332
+ console.log('');
2333
+ info('提醒事项:');
2334
+ console.log(' 1. 当前包通过公共 npm 分发:npx @engineered/ai-spec-auto@latest <command>(内网遗留包见 README)');
2335
+ if (options.level !== 'L1' && resolvedIdes.includes('cursor')) {
2336
+ console.log(' 2. 配置 .cursor/mcp.json(按需启用 MCP)');
2337
+ console.log(` ${color('→', 'yellow')} Cursor 里各 MCP 默认关闭/未启用是预期行为,并非安装失败`);
2338
+ console.log(` ${color('→', 'yellow')} 先在 Cursor 设置 → MCP 中按需启用目标服务,再补齐凭证`);
2339
+ console.log(` ${color('→', 'yellow')} 将 project-id、access-token 等占位符替换成真实值,不需要的服务保持关闭即可`);
2340
+ console.log(' 3. 首次运行 /spec-start / /spec-continue / /spec-update 时,如 Cursor 提示执行 ai-spec-auto 命令');
2341
+ console.log(` ${color('→', 'yellow')} 请选择 Always allow for this workspace,避免宿主桥命令被权限弹窗打断`);
2342
+ }
2343
+ console.log('');
2344
+ console.log(color('────────────────────────────────────────────────────────────', 'bold'));
2345
+ console.log(` ${color('★ 项目初始化不会在安装后自动执行,请在 AI IDE 中按下面顺序继续:', 'bold')}`);
2346
+ console.log(` 1. 先执行 ${color('/project-init', 'bold')}(或输入“初始化项目规范” / “project-init”)`);
2347
+ console.log(` 2. 再执行 ${color('/spec-start', 'bold')} 开始第一个需求`);
2348
+ console.log(' /project-init 将生成或刷新:');
2349
+ for (const rule of selectedAiInitRules) {
2350
+ console.log(` • ${rule}`);
2351
+ }
2352
+ console.log(color('────────────────────────────────────────────────────────────', 'bold'));
2353
+ if (pending.failures.length > 0 || pending.configs.length > 0 || (pending.warnings || []).length > 0) {
2354
+ console.log('');
2355
+ if (pending.failures.length > 0) {
2356
+ console.log(color('════════════════════════════════════════', 'red'));
2357
+ console.log(color(' 待处理事项(安装或命令失败,请逐项处理)', 'red'));
2358
+ console.log(color('════════════════════════════════════════', 'red'));
2359
+ for (const item of pending.failures) {
2360
+ console.log(` ${color('•', 'red')} ${item}`);
2361
+ }
2362
+ }
2363
+ if ((pending.warnings || []).length > 0) {
2364
+ console.log(color('════════════════════════════════════════', 'yellow'));
2365
+ console.log(color(' 安装提示(非阻断)', 'yellow'));
2366
+ console.log(color('════════════════════════════════════════', 'yellow'));
2367
+ for (const item of pending.warnings) {
2368
+ console.log(` ${color('•', 'yellow')} ${item}`);
2369
+ }
2370
+ }
2371
+ if (pending.configs.length > 0) {
2372
+ console.log(color('════════════════════════════════════════', 'yellow'));
2373
+ console.log(color(' 配置提醒(非安装失败)', 'yellow'));
2374
+ console.log(color('════════════════════════════════════════', 'yellow'));
2375
+ for (const item of pending.configs) {
2376
+ console.log(` ${color('•', 'yellow')} ${item}`);
2377
+ }
2378
+ }
2379
+ }
2380
+ }
2381
+
2382
+ async function handleInitWithManifest(options, sourceDir, profilesRegistry, targetDir, pkgManager) {
2383
+ const sync = require('./sync');
2384
+ const pending = { failures: [], configs: [], warnings: [] };
2385
+ const previousInstallState = readInstallState(targetDir);
2386
+ const installStateAdditions = {
2387
+ createdConfigFiles: [],
2388
+ addedDevDependencies: [],
2389
+ prepareScript: '',
2390
+ };
2391
+ const syncOptions = {
2392
+ target: targetDir,
2393
+ manifest: options.manifest,
2394
+ dryRun: false,
2395
+ force: false,
2396
+ hubFetch: options.hubFetch,
2397
+ ...(options.profileExplicit ? { profile: options.profile } : {}),
2398
+ ...(options.ideExplicit ? { ide: options.ideFilter } : {}),
2399
+ ...(options.superpowersExplicit ? { superpowers: options.superpowers === 'yes' } : {}),
2400
+ ...(options.visualBridgeExplicit
2401
+ ? {
2402
+ visualBridge: {
2403
+ enabled: options.visualBridge === 'yes',
2404
+ },
2405
+ }
2406
+ : {}),
2407
+ ...(options.hubOrigin ? { hubOrigin: options.hubOrigin } : {}),
2408
+ };
2409
+ info('预解析 manifest 与 registry ...');
2410
+ const prepared = await sync.prepareSync(syncOptions);
2411
+ ok('Manifest / registry 预校验通过');
2412
+ await selectRulesStrategy(options, { mode: 'manifest' });
2413
+
2414
+ const manifestProfile = resolveProfileId(profilesRegistry, prepared.rawManifest?.profile || null);
2415
+ if (options.profileExplicit && manifestProfile && manifestProfile !== prepared.manifest.profile) {
2416
+ pending.warnings.push(`Manifest profile "${manifestProfile}" 已被显式参数 --profile ${options.profile} 覆盖,当前按 "${prepared.manifest.profile}" 安装。`);
2417
+ }
2418
+
2419
+ if (options.ideExplicit && prepared.rawManifest?.ides !== undefined) {
2420
+ const requestedIdes = normalizeIdeFilter(options.ideFilter);
2421
+ const manifestIdes = normalizeIdes(prepared.rawManifest.ides);
2422
+ if (!sameStringList(requestedIdes, manifestIdes)) {
2423
+ pending.warnings.push(`Manifest ides "${manifestIdes.join(',') || 'default'}" 已被显式参数 --ide ${requestedIdes.join(',')} 覆盖。`);
2424
+ }
2425
+ }
2426
+
2427
+ if (options.levelExplicit) {
2428
+ pending.warnings.push('init --manifest 固定按默认完整安装执行,--level 仅作兼容参数,当前不会影响场景资产同步。');
2429
+ }
2430
+ options.level = DEFAULT_LEVEL;
2431
+ options.profile = prepared.manifest.profile;
2432
+ options.ideFilter = prepared.manifest.ides.join(',');
2433
+ options.superpowers = prepared.manifest.superpowers?.enabled ? 'yes' : 'no';
2434
+ options.visualBridge = prepared.manifest.visual_bridge?.enabled ? 'yes' : 'no';
2435
+ options.installMode = 'init-with-manifest';
2436
+ options.manifestSource = prepared.manifestSource;
2437
+ options.profileSource = options.profileExplicit && manifestProfile
2438
+ ? `manifest(已由 --profile 覆盖,原值 ${manifestProfile})`
2439
+ : 'manifest';
2440
+ options.rulesSource = options.rulesStrategy === 'custom'
2441
+ ? 'manifest 安装 + 本地 project-init 偏好'
2442
+ : 'manifest 安装 + 安装模板沿用';
2443
+ options.syncSummary = {
2444
+ roles: prepared.resolvedResult.resolved.roles.length,
2445
+ skills: prepared.resolvedResult.resolved.skills.length,
2446
+ rules: prepared.resolvedResult.resolved.rules.length,
2447
+ flows: prepared.resolvedResult.resolved.installed_flows.length,
2448
+ };
2449
+ const localPreferences = buildManifestLocalPreferences(options, prepared.manifest.local_preferences);
2450
+ if (localPreferences) {
2451
+ prepared.manifest.local_preferences = localPreferences;
2452
+ } else {
2453
+ delete prepared.manifest.local_preferences;
2454
+ }
2455
+ if (options.superpowersExplicit) {
2456
+ prepared.manifest.superpowers = normalizeSuperpowersManifest({
2457
+ ...(prepared.manifest.superpowers || {}),
2458
+ enabled: options.superpowers === 'yes',
2459
+ }, null);
2460
+ }
2461
+ if (options.visualBridgeExplicit) {
2462
+ prepared.manifest.visual_bridge = normalizeVisualBridgeManifest({
2463
+ ...(prepared.manifest.visual_bridge || {}),
2464
+ enabled: options.visualBridge === 'yes',
2465
+ }, null);
2466
+ }
2467
+
2468
+ await selectBootstrapChoices(options);
2469
+
2470
+ const syncResult = await sync.runSync(syncOptions, prepared);
2471
+ options.profile = syncResult.target.profile;
2472
+ options.ideFilter = syncResult.target.ides.join(',');
2473
+ options.syncSummary = {
2474
+ roles: syncResult.resolved.roles.length,
2475
+ skills: syncResult.resolved.skills.length,
2476
+ rules: syncResult.resolved.rules.length,
2477
+ flows: syncResult.resolved.installed_flows.length,
2478
+ };
2479
+ for (const warning of syncResult.warnings) {
2480
+ if (!pending.warnings.includes(warning)) {
2481
+ pending.warnings.push(warning);
2482
+ }
2483
+ }
2484
+
2485
+ installStateAdditions.addedDevDependencies.push(...installLocalCli(targetDir, sourceDir, pkgManager, pending));
2486
+ if (options.installLint === 'yes') {
2487
+ installStateAdditions.createdConfigFiles.push(...copyConfigs(targetDir, sourceDir, profilesRegistry, options, true));
2488
+ const lintInstall = installLintDeps(targetDir, pkgManager, options, pending);
2489
+ installStateAdditions.addedDevDependencies.push(...lintInstall.addedPackages);
2490
+ installStateAdditions.prepareScript = installStateAdditions.prepareScript || lintInstall.prepareScript;
2491
+ }
2492
+ if (options.installHusky === 'yes') {
2493
+ const commitInstall = installCommitHooks(targetDir, pkgManager, pending);
2494
+ installStateAdditions.addedDevDependencies.push(...commitInstall.addedPackages);
2495
+ installStateAdditions.prepareScript = installStateAdditions.prepareScript || commitInstall.prepareScript;
2496
+ }
2497
+ if (options.uipro === 'yes') {
2498
+ setupUipro(targetDir, pkgManager, pending);
2499
+ }
2500
+ if (normalizeIdeFilter(options.ideFilter).includes('cursor')) {
2501
+ pending.configs.push('.cursor/mcp.json:在 Cursor 设置 → MCP 中按需启用服务后,再补齐 project-id、access-token 等凭证。');
2502
+ }
2503
+ setupOpenSpec(targetDir, sourceDir, options, pkgManager, pending);
2504
+ applySuperpowersBridge(targetDir, options, 'init-with-manifest', prepared.manifest.superpowers || null);
2505
+ applyVisualBridge(targetDir, options, 'init-with-manifest', prepared.manifest.visual_bridge || null);
2506
+ writeInstallState(targetDir, sourceDir, previousInstallState, installStateAdditions);
2507
+ printTools(options.level, options.uipro);
2508
+ printInstallReport(targetDir, options, pending);
2509
+ return pending.failures.length > 0 ? 1 : 0;
2510
+ }
2511
+
2512
+ async function handleInit(options) {
2513
+ const sourceDir = getSourceDir();
2514
+ const profilesRegistry = readProfilesRegistry(sourceDir);
2515
+ const installStateAdditions = {
2516
+ createdConfigFiles: [],
2517
+ addedDevDependencies: [],
2518
+ prepareScript: '',
2519
+ };
2520
+
2521
+ // 规范化 profiles:--profiles 优先,其次 --profile,最后 DEFAULT_PROFILE
2522
+ if (options.profiles && options.profiles.length > 0) {
2523
+ options.profiles = options.profiles.map((p) => resolveProfileId(profilesRegistry, p)).filter(Boolean);
2524
+ } else {
2525
+ const resolved = resolveProfileId(profilesRegistry, options.profile) || DEFAULT_PROFILE;
2526
+ options.profiles = [resolved];
2527
+ }
2528
+ options.profile = options.profiles[0];
2529
+
2530
+ testNodeEnv();
2531
+ const targetDir = await resolveMonorepoTarget(options.target, options);
2532
+ console.log('');
2533
+ info(`ai-spec-auto v${VERSION} | ${os.platform()} ${os.arch()} | Node ${process.version}`);
2534
+ info(`初始化项目: ${targetDir}`);
2535
+ console.log('');
2536
+
2537
+ if (fs.existsSync(path.join(targetDir, '.agents'))) {
2538
+ warn('目标项目已包含 .agents/ 目录');
2539
+ console.log(` 如果只需更新规范,请使用: ${color('npx @engineered/ai-spec-auto@latest update .', 'bold')}`);
2540
+ console.log('');
2541
+ if (!options.force && isInteractive()) {
2542
+ const goOn = await confirm('继续初始化将覆盖现有规范(01/03 和自定义规则除外),确认?', false);
2543
+ if (!goOn) {
2544
+ info('已取消');
2545
+ return 0;
2546
+ }
2547
+ }
2548
+ }
2549
+
2550
+ const pkgManager = detectPkgManager(targetDir);
2551
+ if (pkgManager) {
2552
+ ok(`使用包管理器: ${pkgManager}${commandExists(pkgManager) ? ` (${spawnSync(pkgManager, ['--version'], { encoding: 'utf8' }).stdout.trim()})` : ''}`);
2553
+ } else {
2554
+ warn('未检测到 npm 或 pnpm,后续依赖安装会跳过');
2555
+ }
2556
+ info(`使用 npm 包内规范库: ${sourceDir}`);
2557
+
2558
+ if (options.manifest) {
2559
+ return handleInitWithManifest(options, sourceDir, profilesRegistry, targetDir, pkgManager);
2560
+ }
2561
+
2562
+ await selectInitChoices(options, profilesRegistry);
2563
+ if (!['L1', 'L2', 'L3'].includes(options.level)) {
2564
+ options.level = DEFAULT_LEVEL;
2565
+ }
2566
+
2567
+ options.installMode = 'default-init';
2568
+ const pending = { failures: [], configs: [], warnings: [] };
2569
+ const previousInstallState = readInstallState(targetDir);
2570
+ copyAgents(targetDir, sourceDir, profilesRegistry, options);
2571
+ syncProtocolAssets(targetDir, sourceDir);
2572
+ installStateAdditions.addedDevDependencies.push(...installLocalCli(targetDir, sourceDir, pkgManager, pending));
2573
+ if (options.installLint === 'yes') {
2574
+ installStateAdditions.createdConfigFiles.push(...copyConfigs(targetDir, sourceDir, profilesRegistry, options, true));
2575
+ const lintInstall = installLintDeps(targetDir, pkgManager, options, pending);
2576
+ installStateAdditions.addedDevDependencies.push(...lintInstall.addedPackages);
2577
+ installStateAdditions.prepareScript = installStateAdditions.prepareScript || lintInstall.prepareScript;
2578
+ }
2579
+ if (options.installHusky === 'yes') {
2580
+ const commitInstall = installCommitHooks(targetDir, pkgManager, pending);
2581
+ installStateAdditions.addedDevDependencies.push(...commitInstall.addedPackages);
2582
+ installStateAdditions.prepareScript = installStateAdditions.prepareScript || commitInstall.prepareScript;
2583
+ }
2584
+ if (options.uipro === 'yes') {
2585
+ setupUipro(targetDir, pkgManager, pending);
2586
+ }
2587
+ const superpowersEnabled = resolveSuperpowersEnabled(targetDir, options, null);
2588
+ if (options.level !== 'L1') {
2589
+ createIdeLinks(targetDir, sourceDir, options, superpowersEnabled);
2590
+ if (normalizeIdeFilter(options.ideFilter).includes('cursor')) {
2591
+ pending.configs.push('.cursor/mcp.json:在 Cursor 设置 → MCP 中按需启用服务后,再补齐 project-id、access-token 等凭证。');
2592
+ }
2593
+ }
2594
+ if (options.level === 'L3') {
2595
+ setupOpenSpec(targetDir, sourceDir, options, pkgManager, pending);
2596
+ }
2597
+ if (options.superpowers === 'yes') {
2598
+ applySuperpowersBridge(targetDir, options, 'init', null);
2599
+ }
2600
+ if (options.visualBridge === 'yes') {
2601
+ applyVisualBridge(targetDir, options, 'init', {
2602
+ enabled: true,
2603
+ });
2604
+ }
2605
+ writeInstallState(targetDir, sourceDir, previousInstallState, installStateAdditions);
2606
+ writeProfileManifest(targetDir, options);
2607
+ printTools(options.level, options.uipro);
2608
+ printInstallReport(targetDir, options, pending);
2609
+ return pending.failures.length > 0 ? 1 : 0;
2610
+ }
2611
+
2612
+ async function handleUpdate(options) {
2613
+ const targetDir = path.resolve(options.target);
2614
+ const interactive = isInteractive();
2615
+ const useInteractiveRuleMode = interactive && options.rulesStrategy === 'ask' && options.forceUpdateRules === 'ask';
2616
+ const needsLegacyRuleStrategyPrompt = interactive && options.rulesStrategy === 'ask' && options.forceUpdateRules !== 'ask';
2617
+ if (!fs.existsSync(path.join(targetDir, '.agents'))) {
2618
+ throw new Error(`${targetDir} 未找到 .agents/,请先运行 init`);
2619
+ }
2620
+ const sourceDir = getSourceDir();
2621
+ const profilesRegistry = readProfilesRegistry(sourceDir);
2622
+ if (!options.profileExplicit) {
2623
+ options.profile = detectInstalledProfile(targetDir, profilesRegistry);
2624
+ } else {
2625
+ options.profile = resolveProfileId(profilesRegistry, options.profile) || DEFAULT_PROFILE;
2626
+ }
2627
+ if (!options.levelExplicit) {
2628
+ options.level = detectInstalledLevel(targetDir);
2629
+ }
2630
+ options.ideFilter = resolveTargetIdes(targetDir, options).join(',');
2631
+ const pkgManager = detectPkgManager(targetDir);
2632
+ info(`更新规范: ${targetDir}`);
2633
+ if (!interactive && options.rulesStrategy === 'ask') {
2634
+ await selectRulesStrategy(options);
2635
+ }
2636
+ if (options.rulesStrategy === 'ask') options.rulesStrategy = 'standard';
2637
+ if (hasAnyUiproAssets(targetDir) && options.updateUipro !== 'yes') {
2638
+ options.updateUipro = 'yes';
2639
+ }
2640
+ if (options.uipro === 'yes') {
2641
+ options.updateUipro = 'yes';
2642
+ }
2643
+
2644
+ if (interactive) {
2645
+ await selectUpdateModules(options);
2646
+ if (options.updateRules === 'yes') {
2647
+ if (useInteractiveRuleMode) {
2648
+ options.rulesStrategy = 'ask';
2649
+ await selectUpdateRuleMode(options, sourceDir, profilesRegistry);
2650
+ } else if (needsLegacyRuleStrategyPrompt) {
2651
+ options.rulesStrategy = 'ask';
2652
+ await selectRulesStrategy(options);
2653
+ }
2654
+ }
2655
+ }
2656
+
2657
+ if (options.updateRules === 'yes' && options.updateRuleMode === UPDATE_RULE_MODES.LEGACY && options.forceUpdateRules === 'ask') {
2658
+ if (interactive) {
2659
+ options.forceUpdateRules = await selectFromList(
2660
+ '是否强制更新已有规则?(默认否,已存在的规则文件将被保留)',
2661
+ [
2662
+ { value: 'no', label: '否(默认)', desc: '保留已存在的规则文件,仅补充缺失规则' },
2663
+ { value: 'yes', label: '是', desc: '覆盖所有已存在的规则文件(项目特有规则 01/03 仍保留)' },
2664
+ ],
2665
+ 0,
2666
+ );
2667
+ ok(options.forceUpdateRules === 'yes' ? '将强制覆盖已有规则' : '保留已有规则,仅补充缺失项');
2668
+ } else {
2669
+ options.forceUpdateRules = 'no';
2670
+ }
2671
+ }
2672
+ if (options.forceUpdateRules === 'ask') options.forceUpdateRules = 'no';
2673
+
2674
+ console.log('');
2675
+ console.log(color('── 变更摘要 ──', 'bold'));
2676
+ console.log(` Skills: ${options.updateSkills === 'yes' ? '更新' : '跳过'}`);
2677
+ console.log(` Rules: ${formatUpdateRulesSummary(options)}`);
2678
+ console.log(` Configs: ${options.updateConfigs === 'yes' ? '同步(已存在的不覆盖)' : '跳过'}`);
2679
+ console.log(` Commands: ${options.updateCommands === 'yes' ? '同步(覆盖已有命令)' : '同步(仅补新增)'}`);
2680
+ console.log(` IDE Links:${options.updateIdeLinks === 'yes' ? ' 重建' : ' 跳过'}`);
2681
+ console.log(` OpenSpec: ${options.level === 'L3' && options.updateOpenSpec === 'yes' ? '更新' : '跳过'}`);
2682
+ console.log(` UIPro: ${options.updateUipro === 'yes' ? '重新安装' : '跳过'}`);
2683
+ console.log(` Superpowers: ${options.refreshSuperpowers || options.superpowersExplicit ? '刷新/更新' : '保持当前状态'}`);
2684
+ console.log(` VisualBridge: ${options.visualBridgeExplicit || readVisualBridgeState(targetDir) ? '刷新/更新' : '保持当前状态'}`);
2685
+ console.log('');
2686
+
2687
+ const pending = { failures: [], configs: [] };
2688
+ const previousInstallState = readInstallState(targetDir);
2689
+ const installStateAdditions = {
2690
+ createdConfigFiles: [],
2691
+ addedDevDependencies: [],
2692
+ prepareScript: '',
2693
+ };
2694
+ if (options.updateSkills === 'yes' || options.updateRules === 'yes') {
2695
+ copyAgents(targetDir, sourceDir, profilesRegistry, options, {
2696
+ skipRules: options.updateRules !== 'yes',
2697
+ skipSkills: options.updateSkills !== 'yes',
2698
+ skipExistingRules: options.updateRules === 'yes'
2699
+ && options.forceUpdateRules !== 'yes'
2700
+ && options.updateRuleMode !== UPDATE_RULE_MODES.SELECTED
2701
+ && options.updateRuleMode !== UPDATE_RULE_MODES.ALL,
2702
+ updateRuleMode: options.updateRuleMode,
2703
+ selectedRuleFiles: options.selectedUpdateRuleFiles,
2704
+ });
2705
+ }
2706
+ syncProtocolAssets(targetDir, sourceDir);
2707
+ installStateAdditions.addedDevDependencies.push(...installLocalCli(targetDir, sourceDir, pkgManager, pending, { mode: 'update' }));
2708
+ if (options.updateConfigs === 'yes') {
2709
+ installStateAdditions.createdConfigFiles.push(...copyConfigs(targetDir, sourceDir, profilesRegistry, options, true));
2710
+ }
2711
+ if (options.level !== 'L1') {
2712
+ const superpowersEnabled = resolveSuperpowersEnabled(targetDir, options, null);
2713
+ if (options.updateIdeLinks === 'yes') {
2714
+ createIdeLinks(targetDir, sourceDir, options, superpowersEnabled);
2715
+ }
2716
+ for (const ide of normalizeIdeFilter(options.ideFilter)) {
2717
+ syncCommands(targetDir, sourceDir, ide, options.updateCommands === 'yes');
2718
+ }
2719
+ }
2720
+ if (options.level === 'L3' && options.updateOpenSpec === 'yes') {
2721
+ setupOpenSpec(targetDir, sourceDir, options, pkgManager, pending);
2722
+ }
2723
+ if (options.updateUipro === 'yes') {
2724
+ const { legacySkillDir, skillDir } = getUiproSkillPaths(targetDir);
2725
+ removePath(legacySkillDir);
2726
+ removePath(skillDir);
2727
+ setupUipro(targetDir, pkgManager, pending);
2728
+ }
2729
+ if (options.refreshSuperpowers || options.superpowersExplicit || readSuperpowersState(targetDir)) {
2730
+ applySuperpowersBridge(targetDir, options, 'update', null);
2731
+ }
2732
+ if (options.visualBridgeExplicit || readVisualBridgeState(targetDir)) {
2733
+ applyVisualBridge(targetDir, options, 'update', readInstalledManifestVisualBridge(targetDir));
2734
+ }
2735
+ writeInstallState(targetDir, sourceDir, previousInstallState, installStateAdditions);
2736
+ ok(`更新完成 (profile: ${options.profile}, compatibility level: ${options.level})`);
2737
+ if (pending.failures.length > 0) {
2738
+ pending.failures.forEach((item) => warn(item));
2739
+ }
2740
+ return pending.failures.length > 0 ? 1 : 0;
2741
+ }
2742
+
2743
+ function handleCheck(options) {
2744
+ const targetDir = path.resolve(options.target);
2745
+ let hasIssue = false;
2746
+ const syncManaged = isSyncManagedProject(targetDir);
2747
+ console.log('');
2748
+ info(`═══ 安装状态检查: ${targetDir} ═══`);
2749
+ console.log('');
2750
+ const agentsDir = path.join(targetDir, '.agents');
2751
+ if (fs.existsSync(agentsDir)) {
2752
+ ok('.agents/ 存在');
2753
+ if (fs.existsSync(path.join(agentsDir, 'rules'))) ok(' rules/ 存在');
2754
+ else { err(' rules/ 缺失'); hasIssue = true; }
2755
+ if (fs.existsSync(path.join(agentsDir, 'skills'))) ok(' skills/ 存在');
2756
+ else { err(' skills/ 缺失'); hasIssue = true; }
2757
+ } else {
2758
+ err('.agents/ 不存在');
2759
+ hasIssue = true;
2760
+ }
2761
+ const localCli = path.join(targetDir, 'node_modules', '.bin', isWindows() ? 'ai-spec-auto.cmd' : 'ai-spec-auto');
2762
+ if (fs.existsSync(localCli)) ok('./node_modules/.bin/ai-spec-auto 可用');
2763
+ else if (syncManaged) {
2764
+ warn('./node_modules/.bin/ai-spec-auto 缺失(当前项目已通过 sync --manifest 同步资源;仅在需要本地运行协议命令时再安装 CLI)');
2765
+ } else {
2766
+ err('./node_modules/.bin/ai-spec-auto 缺失');
2767
+ hasIssue = true;
2768
+ }
2769
+
2770
+ for (const ide of ALL_IDES) {
2771
+ const ideDir = path.join(targetDir, `.${ide}`);
2772
+ if (!fs.existsSync(ideDir)) {
2773
+ warn(`.${ide}/ 不存在`);
2774
+ continue;
2775
+ }
2776
+ const rulesLink = path.join(ideDir, 'rules');
2777
+ if (fs.existsSync(rulesLink)) ok(`.${ide}/rules 链接有效`);
2778
+ else { err(`.${ide}/rules 链接无效`); hasIssue = true; }
2779
+ const skillsDir = path.join(ideDir, 'skills');
2780
+ if (fs.existsSync(skillsDir)) {
2781
+ ok(`.${ide}/skills (${fs.readdirSync(skillsDir).length} 个链接)`);
2782
+ } else {
2783
+ warn(`.${ide}/skills 不存在`);
2784
+ }
2785
+ }
2786
+
2787
+ if (fs.existsSync(path.join(targetDir, 'openspec'))) {
2788
+ ok('openspec/ 存在');
2789
+ } else {
2790
+ info('openspec/ 不存在(默认完整安装会生成;兼容 L1/L2 可无)');
2791
+ }
2792
+ const hasProtocolCommandEntry = ALL_IDES.some((ide) => (
2793
+ fs.existsSync(path.join(targetDir, `.${ide}`, 'commands', 'spec-start.md')) ||
2794
+ fs.existsSync(path.join(targetDir, `.${ide}`, 'commands', 'spec-continue.md')) ||
2795
+ fs.existsSync(path.join(targetDir, `.${ide}`, 'commands', 'spec-update.md')) ||
2796
+ fs.existsSync(path.join(targetDir, `.${ide}`, 'commands', 'spec-orchestrate.md'))
2797
+ ));
2798
+ const requiresProtocolAssets = fs.existsSync(path.join(targetDir, 'openspec')) || hasProtocolCommandEntry;
2799
+ if (requiresProtocolAssets) {
2800
+ const missingProtocolAssets = [];
2801
+ if (!fs.existsSync(path.join(agentsDir, 'roles'))) missingProtocolAssets.push('.agents/roles');
2802
+ if (!fs.existsSync(path.join(agentsDir, 'flows'))) missingProtocolAssets.push('.agents/flows');
2803
+ if (!fs.existsSync(path.join(agentsDir, 'orchestration'))) missingProtocolAssets.push('.agents/orchestration');
2804
+ if (missingProtocolAssets.length > 0) {
2805
+ warn(`检测到 OpenSpec 或协议命令入口,但缺少 ${missingProtocolAssets.join('、')};建议运行: npx @engineered/ai-spec-auto@latest update .`);
2806
+ }
2807
+ }
2808
+ const staleCursorProtocolCommands = collectStaleCursorProtocolCommands(targetDir);
2809
+ if (staleCursorProtocolCommands.length > 0) {
2810
+ warn(`检测到 Cursor 协议命令模板可能过旧:${staleCursorProtocolCommands.join('、')};建议运行: npx @engineered/ai-spec-auto@latest update . 或重新执行 sync`);
2811
+ }
2812
+ printTools(detectInstalledLevel(targetDir), hasInstalledUiproData(targetDir) ? 'yes' : 'no');
2813
+ console.log('');
2814
+ if (hasIssue) {
2815
+ err('存在问题,建议运行: npx @engineered/ai-spec-auto@latest init .');
2816
+ return 1;
2817
+ }
2818
+ ok('全部检查通过');
2819
+ return 0;
2820
+ }
2821
+
2822
+ function uninstallPackageDeps(targetDir, packages) {
2823
+ const pkgManager = detectPkgManager(targetDir);
2824
+ if (!pkgManager) return;
2825
+ runCommand(pkgManager, ['uninstall', ...packages], { cwd: targetDir, stdio: 'ignore' });
2826
+ }
2827
+
2828
+ async function handleUninstall(options) {
2829
+ const targetDir = path.resolve(options.target);
2830
+ const sourceDir = getSourceDir();
2831
+ const installStatePath = getInstallStatePath(targetDir);
2832
+ const hasInstallState = fs.existsSync(installStatePath);
2833
+ const installState = hasInstallState ? readInstallState(targetDir) : normalizeInstallState(null);
2834
+ warn(`将移除 ${targetDir} 下的规范库文件`);
2835
+ console.log(' 包括: .agents/、IDE 链接、命令模板、.ai-spec/ 运行态,以及可证明由本工具创建的共享配置/依赖');
2836
+ console.log('');
2837
+ if (!options.force && isInteractive()) {
2838
+ const goOn = await confirm('确认?', false);
2839
+ if (!goOn) {
2840
+ info('已取消');
2841
+ return 0;
2842
+ }
2843
+ }
2844
+ const managedPaths = hasInstallState ? installState.managed_paths : listLegacyManagedPaths(targetDir, sourceDir);
2845
+ removeManagedPaths(targetDir, managedPaths);
2846
+ removeManagedAiSpecRuntime(targetDir);
2847
+ removePath(path.join(targetDir, '.ai-spec', 'manifest.json'));
2848
+ removePath(path.join(targetDir, '.ai-spec', 'lock.json'));
2849
+ removePath(path.join(targetDir, '.ai-spec', 'sources.json'));
2850
+ removePath(installStatePath);
2851
+
2852
+ if (hasInstallState) {
2853
+ removeManagedPaths(targetDir, installState.created_config_files);
2854
+ }
2855
+
2856
+ const pkgPath = path.join(targetDir, 'package.json');
2857
+ if (fs.existsSync(pkgPath)) {
2858
+ const pkg = readJson(pkgPath, 'package.json');
2859
+ if (hasInstallState && installState.package_json.prepare_script && pkg.scripts?.prepare === installState.package_json.prepare_script) {
2860
+ delete pkg.scripts.prepare;
2861
+ if (Object.keys(pkg.scripts).length === 0) delete pkg.scripts;
2862
+ writeJson(pkgPath, pkg);
2863
+ }
2864
+ }
2865
+ if (hasInstallState && installState.added_dev_dependencies.length > 0) {
2866
+ uninstallPackageDeps(targetDir, installState.added_dev_dependencies);
2867
+ }
2868
+ const huskyDir = path.join(targetDir, '.husky');
2869
+ if (fs.existsSync(huskyDir) && walkFiles(huskyDir).length === 0) {
2870
+ removePath(huskyDir);
2871
+ }
2872
+ cleanupEmptyIdeDirs(targetDir);
2873
+ upsertManagedAgentsBlock(targetDir, false);
2874
+ const aiSpecDir = path.join(targetDir, '.ai-spec');
2875
+ if (fs.existsSync(aiSpecDir)) {
2876
+ removePath(aiSpecDir);
2877
+ }
2878
+ ok('卸载完成');
2879
+ return 0;
2880
+ }
2881
+
2882
+ function printUsage() {
2883
+ console.log(`${color('ai-spec-auto', 'bold')} 安装工具\n`);
2884
+ console.log('推荐入口:');
2885
+ console.log(' npx @engineered/ai-spec-auto@latest init .');
2886
+ console.log(' npx @engineered/ai-spec-auto@latest init . --manifest <file-or-url>');
2887
+ console.log(' npx @engineered/ai-spec-auto@latest update .');
2888
+ console.log(' npx @engineered/ai-spec-auto@latest sync .');
2889
+ console.log(' npx @engineered/ai-spec-auto@latest check .');
2890
+ console.log('');
2891
+ console.log('说明:');
2892
+ console.log(' - 默认安装为完整安装(规范 + IDE 适配 + OpenSpec)');
2893
+ console.log(' - L1/L2/L3 仅保留为兼容参数,不再作为主路径概念');
2894
+ console.log(' - 公共 npm:npx @engineered/ai-spec-auto@latest <command>;内网 @ex 包见 README 折叠说明');
2895
+ console.log('');
2896
+ console.log('命令:');
2897
+ console.log(' init [dir] 首次安装到目标项目(支持 --manifest 首装即同步)');
2898
+ console.log(' update [dir] 更新规范,支持细粒度模块选择');
2899
+ console.log(' sync [dir] 按 manifest / profile 同步规范资产');
2900
+ console.log(' check [dir] 检查安装状态');
2901
+ console.log(' uninstall [dir] 卸载规范库');
2902
+ console.log('');
2903
+ console.log('常用选项:');
2904
+ console.log(' --profile <name> 技术栈(vue | react | nestjs | springboot | node-tooling)');
2905
+ console.log(' --profiles <a,b,...> 多技术栈(逗号分隔,如 vue,nestjs)');
2906
+ console.log(' --level <L1|L2|L3> 兼容参数,默认仍等价完整安装');
2907
+ console.log(' --standard-rules 使用标准规则集(manifest 模式下表示沿用安装模板)');
2908
+ console.log(' --custom-rules 启用自定义规则模式(manifest 模式下表示 /project-init 刷新偏好)');
2909
+ console.log(' --package <path> Monorepo 下指定子包');
2910
+ console.log(' --workspace-root Monorepo 下显式在根目录安装');
2911
+ console.log(' --uipro / --no-uipro 安装或跳过 UI UX Pro Max');
2912
+ console.log(' --superpowers / --no-superpowers 启用或关闭 superpowers 平台增强');
2913
+ console.log(' --visual-bridge / --no-visual-bridge 启用或关闭 visual 平台桥接配置');
2914
+ console.log(' --refresh-superpowers update 时仅刷新 superpowers 绑定状态');
2915
+ console.log(' --lint / --no-lint 安装或跳过 lint/format');
2916
+ console.log(' --husky / --no-husky 安装或跳过提交校验');
2917
+ console.log(' --manifest <path|url> init/sync 时指定安装清单');
2918
+ console.log(' --hub-origin <origin> 本地 manifest 缺失资产时指定 Hub 补充来源');
2919
+ console.log(' --no-hub-fetch 禁止通过 Hub 补充下载缺失资产');
2920
+ console.log(' --skip-skills update 时跳过 skills');
2921
+ console.log(' --force-update-rules update 时强制覆盖已有规则(默认保留)');
2922
+ console.log(' --no-force-update-rules update 时保留已有规则(默认行为)');
2923
+ console.log(' --skip-configs update 时跳过 configs');
2924
+ console.log(' --skip-commands update 时仅补新增命令模板');
2925
+ console.log(' --skip-ide-links update 时跳过 IDE 链接');
2926
+ console.log(' --skip-openspec update 时跳过 OpenSpec 更新');
2927
+ console.log(' --skip-uipro update 时跳过 UI UX Pro Max 更新');
2928
+ console.log(' --dry-run sync 时仅预览,不落盘');
2929
+ console.log('');
2930
+ }
2931
+
2932
+ async function main(argv) {
2933
+ try {
2934
+ if (argv[0] === 'sync') {
2935
+ const sync = require('./sync');
2936
+ return await sync.main(argv.slice(1));
2937
+ }
2938
+
2939
+ const options = parseArgs(argv);
2940
+ switch (options.command) {
2941
+ case 'help':
2942
+ printUsage();
2943
+ return 0;
2944
+ case 'init':
2945
+ return await handleInit(options);
2946
+ case 'update':
2947
+ return await handleUpdate(options);
2948
+ case 'check':
2949
+ return handleCheck(options);
2950
+ case 'uninstall':
2951
+ return await handleUninstall(options);
2952
+ default:
2953
+ printUsage();
2954
+ return 1;
2955
+ }
2956
+ } catch (error) {
2957
+ err(error.message);
2958
+ return 1;
2959
+ }
2960
+ }
2961
+
2962
+ module.exports = {
2963
+ main,
2964
+ __test__: {
2965
+ CUSTOMIZABLE_RULES,
2966
+ DEFAULT_CUSTOM_RULE_SELECTION,
2967
+ normalizeCustomRulesSelection,
2968
+ selectFromList,
2969
+ selectCustomRuleList,
2970
+ selectMultipleFromList,
2971
+ selectUpdateModules,
2972
+ selectUpdateRuleMode,
2973
+ selectUpdateRuleFiles,
2974
+ listSelectableUpdateRules,
2975
+ selectBootstrapChoices,
2976
+ buildDevDependencyInstallArgs,
2977
+ copyAgents,
2978
+ },
2979
+ };
2980
+
2981
+ if (require.main === module) {
2982
+ main(process.argv.slice(2)).then((code) => process.exit(code));
2983
+ }