@gong-ym/ai-spec-auto 0.2.14 → 0.2.15

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 (631) hide show
  1. package/.agents/commands/README.md +33 -33
  2. package/.agents/commands/claude/spec-start-review.md +88 -88
  3. package/.agents/commands/codex/spec-continue.md +74 -74
  4. package/.agents/commands/codex/spec-orchestrate.md +35 -35
  5. package/.agents/commands/codex/spec-start-review.md +88 -88
  6. package/.agents/commands/codex/spec-start.md +67 -67
  7. package/.agents/commands/codex/spec-status.md +22 -22
  8. package/.agents/commands/codex/spec-stop.md +29 -29
  9. package/.agents/commands/codex/spec-update.md +40 -40
  10. package/.agents/commands/common/branch-review.md +117 -117
  11. package/.agents/commands/common/project-init.md +25 -25
  12. package/.agents/commands/common/spec-continue.md +74 -74
  13. package/.agents/commands/common/spec-orchestrate.md +35 -35
  14. package/.agents/commands/common/spec-start-review.md +82 -82
  15. package/.agents/commands/common/spec-start.md +67 -67
  16. package/.agents/commands/common/spec-status.md +22 -22
  17. package/.agents/commands/common/spec-stop.md +29 -29
  18. package/.agents/commands/common/spec-update.md +60 -40
  19. package/.agents/commands/cursor/opsx-apply.md +55 -55
  20. package/.agents/commands/cursor/opsx-archive.md +48 -48
  21. package/.agents/commands/cursor/opsx-explore.md +45 -45
  22. package/.agents/commands/cursor/opsx-propose.md +59 -59
  23. package/.agents/commands/cursor/spec-continue.md +63 -63
  24. package/.agents/commands/cursor/spec-orchestrate.md +53 -53
  25. package/.agents/commands/cursor/spec-start-review.md +78 -78
  26. package/.agents/commands/cursor/spec-start.md +59 -59
  27. package/.agents/commands/cursor/spec-status.md +30 -30
  28. package/.agents/commands/cursor/spec-stop.md +29 -29
  29. package/.agents/commands/cursor/spec-update.md +41 -41
  30. package/.agents/flows/FRONTMATTER.md +263 -263
  31. package/.agents/flows/RUN_OUTPUT.md +263 -263
  32. package/.agents/flows/common/README.md +29 -29
  33. package/.agents/flows/common/bugfix-to-verification.md +95 -95
  34. package/.agents/flows/common/change-to-architecture-review.md +89 -89
  35. package/.agents/flows/common/change-to-release.md +94 -94
  36. package/.agents/flows/common/prd-to-delivery.md +184 -184
  37. package/.agents/flows/common/requirement-to-observability.md +97 -97
  38. package/.agents/orchestration/README.md +22 -22
  39. package/.agents/orchestration/expert-dispatch-spec.md +155 -155
  40. package/.agents/orchestration/expert-executor-spec.md +84 -84
  41. package/.agents/orchestration/expert-runtime-action-spec.md +73 -73
  42. package/.agents/orchestration/runtime-state-handoff-spec.md +264 -264
  43. package/.agents/orchestration/task-anchor-spec.md +212 -212
  44. package/.agents/orchestration/task-orchestrator-adapter-payload.md +153 -153
  45. package/.agents/orchestration/task-orchestrator-bootstrap-payload.md +145 -145
  46. package/.agents/orchestration/task-orchestrator-output-extractor-spec.md +93 -93
  47. package/.agents/orchestration/task-orchestrator-run-plan-template.md +312 -312
  48. package/.agents/orchestration/task-orchestrator-runtime-hooks.md +214 -214
  49. package/.agents/registry/README.md +63 -63
  50. package/.agents/registry/flows.json +125 -125
  51. package/.agents/registry/profiles.json +101 -101
  52. package/.agents/registry/roles.json +1265 -1265
  53. package/.agents/registry/rules.json +148 -148
  54. package/.agents/registry/scenario-packages.json +123 -123
  55. package/.agents/registry/skills.json +130 -130
  56. package/.agents/roles/INDEX.md +346 -346
  57. package/.agents/roles/common/README.md +76 -76
  58. package/.agents/roles/common/archive-change.md +80 -80
  59. package/.agents/roles/common/backend-implementer.md +92 -92
  60. package/.agents/roles/common/code-guardian.md +151 -151
  61. package/.agents/roles/common/frontend-implementer.md +146 -146
  62. package/.agents/roles/common/requirement-analyst.md +138 -138
  63. package/.agents/roles/common/task-orchestrator-routing.md +301 -301
  64. package/.agents/roles/common/task-orchestrator.md +224 -224
  65. package/.agents/roles/common/tooling-implementer.md +92 -92
  66. package/.agents/roles/domains/README.md +35 -35
  67. package/.agents/roles/domains/delivery/README.md +11 -11
  68. package/.agents/roles/domains/delivery/container-specialist.md +50 -50
  69. package/.agents/roles/domains/delivery/deployment-specialist.md +50 -50
  70. package/.agents/roles/domains/delivery/pipeline-specialist.md +50 -50
  71. package/.agents/roles/domains/demand-design/README.md +16 -16
  72. package/.agents/roles/domains/demand-design/api-contract-specialist.md +52 -52
  73. package/.agents/roles/domains/demand-design/design-collaborator.md +58 -58
  74. package/.agents/roles/domains/documentation/README.md +11 -11
  75. package/.agents/roles/domains/documentation/api-doc-specialist.md +50 -50
  76. package/.agents/roles/domains/documentation/component-doc-specialist.md +49 -49
  77. package/.agents/roles/domains/documentation/technical-writing-specialist.md +48 -48
  78. package/.agents/roles/domains/engineering/README.md +17 -17
  79. package/.agents/roles/domains/engineering/architecture-advisor.md +53 -53
  80. package/.agents/roles/domains/engineering/build-specialist.md +51 -51
  81. package/.agents/roles/domains/engineering/dependency-governor.md +52 -52
  82. package/.agents/roles/domains/governance/README.md +17 -17
  83. package/.agents/roles/domains/governance/api-governance-specialist.md +51 -51
  84. package/.agents/roles/domains/governance/lint-policy-specialist.md +49 -49
  85. package/.agents/roles/domains/governance/route-governance-specialist.md +52 -52
  86. package/.agents/roles/domains/observability/README.md +11 -11
  87. package/.agents/roles/domains/observability/error-tracker.md +50 -50
  88. package/.agents/roles/domains/observability/event-instrumentation-specialist.md +51 -51
  89. package/.agents/roles/domains/observability/rum-analyst.md +50 -50
  90. package/.agents/roles/domains/performance/README.md +11 -11
  91. package/.agents/roles/domains/performance/asset-optimizer.md +50 -50
  92. package/.agents/roles/domains/performance/performance-auditor.md +56 -56
  93. package/.agents/roles/domains/performance/vitals-analyst.md +50 -50
  94. package/.agents/roles/domains/security-a11y/README.md +11 -11
  95. package/.agents/roles/domains/security-a11y/a11y-auditor.md +50 -50
  96. package/.agents/roles/domains/security-a11y/aria-specialist.md +51 -51
  97. package/.agents/roles/domains/security-a11y/security-reviewer.md +49 -49
  98. package/.agents/roles/domains/testing/README.md +12 -12
  99. package/.agents/roles/domains/testing/coverage-analyst.md +50 -50
  100. package/.agents/roles/domains/testing/e2e-test-specialist.md +51 -51
  101. package/.agents/roles/domains/testing/unit-test-specialist.md +56 -56
  102. package/.agents/roles/domains/testing/verification-reviewer.md +67 -67
  103. package/.agents/rules/README.md +87 -87
  104. package/.agents/rules/common/02-/347/274/226/347/240/201/350/247/204/350/214/203.md +45 -45
  105. package/.agents/rules/common/08-/351/200/232/347/224/250/347/272/246/346/235/237.md +63 -63
  106. package/.agents/rules/common/10-/346/226/207/346/241/243/350/247/204/350/214/203.md +101 -101
  107. package/.agents/rules/common/12-Superpowers/346/211/247/350/241/214/350/247/204/350/214/203.md +46 -46
  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 -107
  109. package/.agents/rules/common/15-visual-gate-wait.md +90 -90
  110. package/.agents/rules/profiles/nestjs/01-/351/241/271/347/233/256/346/246/202/350/277/260.md +27 -27
  111. package/.agents/rules/profiles/nestjs/03-/351/241/271/347/233/256/347/273/223/346/236/204.md +20 -20
  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 -24
  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 -24
  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 -24
  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 -20
  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 -20
  117. package/.agents/rules/profiles/nestjs/11-/346/265/213/350/257/225/350/247/204/350/214/203.md +24 -24
  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 -20
  119. package/.agents/rules/profiles/node-tooling/01-/351/241/271/347/233/256/346/246/202/350/277/260.md +30 -30
  120. package/.agents/rules/profiles/node-tooling/03-/351/241/271/347/233/256/347/273/223/346/236/204.md +37 -37
  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 -42
  122. package/.agents/rules/profiles/node-tooling/05-Contract/344/270/216Schema/350/247/204/350/214/203.md +42 -42
  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 -30
  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 -60
  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 -45
  126. package/.agents/rules/profiles/node-tooling/11-/346/265/213/350/257/225/350/247/204/350/214/203.md +41 -41
  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 -55
  128. package/.agents/rules/profiles/react/01-/351/241/271/347/233/256/346/246/202/350/277/260.md +29 -29
  129. package/.agents/rules/profiles/react/03-/351/241/271/347/233/256/347/273/223/346/236/204.md +104 -104
  130. package/.agents/rules/profiles/react/04-/347/273/204/344/273/266/350/247/204/350/214/203.md +46 -46
  131. package/.agents/rules/profiles/react/05-API/350/247/204/350/214/203.md +67 -67
  132. package/.agents/rules/profiles/react/06-/350/267/257/347/224/261/350/247/204/350/214/203.md +54 -54
  133. package/.agents/rules/profiles/react/07-/347/212/266/346/200/201/347/256/241/347/220/206.md +226 -226
  134. package/.agents/rules/profiles/react/09-/346/240/267/345/274/217/350/247/204/350/214/203.md +71 -71
  135. package/.agents/rules/profiles/react/11-/346/265/213/350/257/225/350/247/204/350/214/203.md +80 -80
  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 -159
  137. package/.agents/rules/profiles/springboot/01-/351/241/271/347/233/256/346/246/202/350/277/260.md +31 -31
  138. package/.agents/rules/profiles/springboot/03-/351/241/271/347/233/256/347/273/223/346/236/204.md +37 -37
  139. package/.agents/rules/profiles/springboot/04-/345/210/206/345/261/202/350/247/204/350/214/203.md +33 -33
  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 -51
  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 -34
  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 -38
  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 -48
  144. package/.agents/rules/profiles/springboot/11-/346/265/213/350/257/225/350/247/204/350/214/203.md +43 -43
  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 -48
  146. package/.agents/rules/profiles/vue/01-/351/241/271/347/233/256/346/246/202/350/277/260.md +47 -47
  147. package/.agents/rules/profiles/vue/03-/351/241/271/347/233/256/347/273/223/346/236/204.md +106 -106
  148. package/.agents/rules/profiles/vue/04-/347/273/204/344/273/266/350/247/204/350/214/203.md +61 -61
  149. package/.agents/rules/profiles/vue/05-API/350/247/204/350/214/203.md +67 -67
  150. package/.agents/rules/profiles/vue/06-/350/267/257/347/224/261/350/247/204/350/214/203.md +69 -69
  151. package/.agents/rules/profiles/vue/07-/347/212/266/346/200/201/347/256/241/347/220/206.md +93 -93
  152. package/.agents/rules/profiles/vue/09-/346/240/267/345/274/217/350/247/204/350/214/203.md +67 -67
  153. package/.agents/rules/profiles/vue/11-/346/265/213/350/257/225/350/247/204/350/214/203.md +80 -80
  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 -159
  155. package/.agents/skills/README.md +171 -171
  156. package/.agents/skills/common/archive-change/SKILL.md +180 -180
  157. package/.agents/skills/common/branch-code-reviewer/SKILL.md +533 -533
  158. package/.agents/skills/common/branch-code-reviewer/references/business-risk-guide.md +293 -293
  159. package/.agents/skills/common/branch-code-reviewer/references/html-template-guide.md +121 -121
  160. package/.agents/skills/common/config-and-secret-scan/SKILL.md +99 -99
  161. package/.agents/skills/common/create-proposal/SKILL.md +192 -192
  162. package/.agents/skills/common/create-proposal/evals/evals.json +16 -16
  163. package/.agents/skills/common/create-proposal/evals/train_queries.json +18 -18
  164. package/.agents/skills/common/create-proposal/evals/validation_queries.json +18 -18
  165. package/.agents/skills/common/create-proposal/references/interaction-spec-template.md +42 -42
  166. package/.agents/skills/common/create-test/SKILL.md +292 -292
  167. package/.agents/skills/common/dependency-impact-graph/SKILL.md +80 -80
  168. package/.agents/skills/common/execute-task/SKILL.md +206 -206
  169. package/.agents/skills/common/execute-task/evals/evals.json +16 -16
  170. package/.agents/skills/common/execute-task/evals/train_queries.json +18 -18
  171. package/.agents/skills/common/execute-task/evals/validation_queries.json +18 -18
  172. package/.agents/skills/common/find-skills/SKILL.md +144 -144
  173. package/.agents/skills/common/install-ai-spec-auto/SKILL.md +260 -260
  174. package/.agents/skills/common/install-ai-spec-auto/evals/evals.json +17 -17
  175. package/.agents/skills/common/install-ai-spec-auto/evals/train_queries.json +18 -18
  176. package/.agents/skills/common/install-ai-spec-auto/evals/validation_queries.json +18 -18
  177. package/.agents/skills/common/project-init/SKILL.md +178 -178
  178. package/.agents/skills/common/project-init/evals/evals.json +16 -16
  179. package/.agents/skills/common/project-init/evals/train_queries.json +18 -18
  180. package/.agents/skills/common/project-init/evals/validation_queries.json +18 -18
  181. package/.agents/skills/common/project-init/references/custom-rule-generation.md +89 -89
  182. package/.agents/skills/common/project-init/references/deep-scan-rules.md +67 -67
  183. package/.agents/skills/common/project-init/references/output-contracts.md +71 -71
  184. package/.agents/skills/common/project-init/references/repo-fact-gathering.md +83 -83
  185. package/.agents/skills/common/project-init/references/scope-resolution.md +76 -76
  186. package/.agents/skills/common/project-init/scripts/inspect-project.js +112 -112
  187. package/.agents/skills/common/skill-creator/LICENSE.txt +201 -201
  188. package/.agents/skills/common/skill-creator/SKILL.md +370 -370
  189. package/.agents/skills/common/skill-creator/evals/evals.json +16 -16
  190. package/.agents/skills/common/skill-creator/evals/train_queries.json +18 -18
  191. package/.agents/skills/common/skill-creator/evals/validation_queries.json +18 -18
  192. package/.agents/skills/common/skill-creator/references/output-patterns.md +82 -82
  193. package/.agents/skills/common/skill-creator/references/workflows.md +27 -27
  194. package/.agents/skills/common/skill-creator/scripts/init_skill.py +209 -209
  195. package/.agents/skills/common/skill-creator/scripts/package_skill.py +110 -110
  196. package/.agents/skills/common/skill-creator/scripts/quick_validate.py +51 -51
  197. package/.agents/skills/common/skill-optimizer/SKILL.md +102 -102
  198. package/.agents/skills/common/skill-optimizer/evals/evals.json +16 -16
  199. package/.agents/skills/common/skill-optimizer/evals/train_queries.json +18 -18
  200. package/.agents/skills/common/skill-optimizer/evals/validation_queries.json +18 -18
  201. package/.agents/skills/common/skill-optimizer/references/design-patterns.md +26 -26
  202. package/.agents/skills/common/skill-optimizer/references/review-checklist.md +22 -22
  203. package/.agents/skills/common/using-superpowers/SKILL.md +151 -151
  204. package/.agents/skills/common/wait-for-gate-signal/SKILL.md +85 -85
  205. package/.agents/skills/domains/README.md +19 -19
  206. package/.agents/skills/domains/ui-ux-pro-max/SKILL.md +58 -58
  207. package/.agents/skills/domains/web/design-analysis/SKILL.md +89 -89
  208. package/.agents/skills/domains/web/design-analysis/rules/analysis-order.md +61 -61
  209. package/.agents/skills/domains/web/design-analysis/rules/analysis-priorities.md +136 -136
  210. package/.agents/skills/domains/web/design-analysis/rules/checklist-common-misses.md +107 -107
  211. package/.agents/skills/domains/web/design-analysis/rules/implementation-common-errors.md +204 -204
  212. package/.agents/skills/domains/web/design-analysis/rules/implementation-guidelines.md +211 -211
  213. package/.agents/skills/domains/web/design-analysis/rules/output-analysis-checklist.md +247 -247
  214. package/.agents/skills/domains/web/design-analysis/rules/tools-design-guidelines.md +108 -108
  215. package/.agents/skills/domains/web/design-analysis/rules/workflow-element-extraction.md +162 -162
  216. package/.agents/skills/domains/web/design-analysis/rules/workflow-layout-map.md +131 -131
  217. package/.agents/skills/domains/web/design-analysis/rules/workflow-output-checklist.md +70 -70
  218. package/.agents/skills/domains/web/design-analysis/rules/workflow-style-summary.md +91 -91
  219. package/.agents/skills/domains/web/route-permission-map/SKILL.md +103 -103
  220. package/.agents/skills/domains/web/ui-verification/SKILL.md +114 -114
  221. package/.agents/skills/domains/web/ui-verification/evals/evals.json +16 -16
  222. package/.agents/skills/domains/web/ui-verification/evals/train_queries.json +18 -18
  223. package/.agents/skills/domains/web/ui-verification/evals/validation_queries.json +18 -18
  224. package/.agents/skills/domains/web/ui-verification/rules/comparison-content-image.md +34 -34
  225. package/.agents/skills/domains/web/ui-verification/rules/comparison-content-text.md +30 -30
  226. package/.agents/skills/domains/web/ui-verification/rules/comparison-hierarchy.md +33 -33
  227. package/.agents/skills/domains/web/ui-verification/rules/comparison-layout.md +35 -35
  228. package/.agents/skills/domains/web/ui-verification/rules/errors-alignment.md +42 -42
  229. package/.agents/skills/domains/web/ui-verification/rules/errors-button-dimensions.md +28 -28
  230. package/.agents/skills/domains/web/ui-verification/rules/errors-button-position.md +25 -25
  231. package/.agents/skills/domains/web/ui-verification/rules/errors-css-priority.md +50 -50
  232. package/.agents/skills/domains/web/ui-verification/rules/errors-flex-column-width.md +46 -46
  233. package/.agents/skills/domains/web/ui-verification/rules/errors-flex-layout.md +46 -46
  234. package/.agents/skills/domains/web/ui-verification/rules/errors-grid-container-width.md +44 -44
  235. package/.agents/skills/domains/web/ui-verification/rules/errors-page-container-width.md +39 -39
  236. package/.agents/skills/domains/web/ui-verification/rules/tools-browser-navigation.md +53 -53
  237. package/.agents/skills/domains/web/ui-verification/rules/tools-design-guidelines.md +53 -53
  238. package/.agents/skills/domains/web/ui-verification/rules/workflow-checklist.md +27 -27
  239. package/.agents/skills/domains/web/ui-verification/rules/workflow-problem-list.md +56 -56
  240. package/.agents/skills/domains/web/ui-verification/rules/workflow-reflection.md +44 -44
  241. package/.agents/skills/domains/web/ui-verification/rules/writing-alignment.md +44 -44
  242. package/.agents/skills/domains/web/ui-verification/rules/writing-element-completeness.md +63 -63
  243. package/.agents/skills/domains/web/ui-verification/rules/writing-list-layout.md +75 -75
  244. package/.agents/skills/domains/web/ui-verification/rules/writing-page-container-width.md +37 -37
  245. package/.agents/skills/domains/web/web-design-guidelines/SKILL.md +40 -40
  246. package/.agents/skills/profiles/nestjs/README.md +4 -4
  247. package/.agents/skills/profiles/node-tooling/README.md +9 -9
  248. package/.agents/skills/profiles/react/create-api/SKILL.md +145 -145
  249. package/.agents/skills/profiles/react/create-component/SKILL.md +160 -160
  250. package/.agents/skills/profiles/react/create-route/SKILL.md +168 -168
  251. package/.agents/skills/profiles/react/create-store/SKILL.md +262 -262
  252. package/.agents/skills/profiles/react/theme-variables/SKILL.md +82 -82
  253. package/.agents/skills/profiles/react/vercel-composition-patterns/AGENTS.md +899 -899
  254. package/.agents/skills/profiles/react/vercel-composition-patterns/SKILL.md +81 -81
  255. package/.agents/skills/profiles/react/vercel-composition-patterns/rules/architecture-avoid-boolean-props.md +100 -100
  256. package/.agents/skills/profiles/react/vercel-composition-patterns/rules/architecture-compound-components.md +112 -112
  257. package/.agents/skills/profiles/react/vercel-composition-patterns/rules/patterns-children-over-render-props.md +87 -87
  258. package/.agents/skills/profiles/react/vercel-composition-patterns/rules/patterns-explicit-variants.md +100 -100
  259. package/.agents/skills/profiles/react/vercel-composition-patterns/rules/state-context-interface.md +191 -191
  260. package/.agents/skills/profiles/react/vercel-composition-patterns/rules/state-decouple-implementation.md +113 -113
  261. package/.agents/skills/profiles/react/vercel-composition-patterns/rules/state-lift-state.md +125 -125
  262. package/.agents/skills/profiles/react/vercel-react-best-practices/AGENTS.md +2934 -2934
  263. package/.agents/skills/profiles/react/vercel-react-best-practices/SKILL.md +136 -136
  264. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/advanced-event-handler-refs.md +55 -55
  265. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/advanced-init-once.md +42 -42
  266. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/advanced-use-latest.md +39 -39
  267. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/async-api-routes.md +38 -38
  268. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/async-defer-await.md +80 -80
  269. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/async-dependencies.md +51 -51
  270. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/async-parallel.md +28 -28
  271. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/async-suspense-boundaries.md +99 -99
  272. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/bundle-barrel-imports.md +59 -59
  273. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/bundle-conditional.md +31 -31
  274. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/bundle-defer-third-party.md +49 -49
  275. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/bundle-dynamic-imports.md +35 -35
  276. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/bundle-preload.md +50 -50
  277. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/client-event-listeners.md +74 -74
  278. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/client-localstorage-schema.md +71 -71
  279. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/client-passive-event-listeners.md +48 -48
  280. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/client-swr-dedup.md +56 -56
  281. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/js-batch-dom-css.md +107 -107
  282. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/js-cache-function-results.md +80 -80
  283. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/js-cache-property-access.md +28 -28
  284. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/js-cache-storage.md +70 -70
  285. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/js-combine-iterations.md +32 -32
  286. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/js-early-exit.md +50 -50
  287. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/js-hoist-regexp.md +45 -45
  288. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/js-index-maps.md +37 -37
  289. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/js-length-check-first.md +49 -49
  290. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/js-min-max-loop.md +82 -82
  291. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/js-set-map-lookups.md +24 -24
  292. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/js-tosorted-immutable.md +57 -57
  293. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/rendering-activity.md +26 -26
  294. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/rendering-animate-svg-wrapper.md +47 -47
  295. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/rendering-conditional-render.md +40 -40
  296. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/rendering-content-visibility.md +38 -38
  297. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/rendering-hoist-jsx.md +46 -46
  298. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/rendering-hydration-no-flicker.md +82 -82
  299. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/rendering-hydration-suppress-warning.md +30 -30
  300. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/rendering-svg-precision.md +28 -28
  301. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/rendering-usetransition-loading.md +75 -75
  302. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/rerender-defer-reads.md +39 -39
  303. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/rerender-dependencies.md +45 -45
  304. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/rerender-derived-state-no-effect.md +40 -40
  305. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/rerender-derived-state.md +29 -29
  306. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/rerender-functional-setstate.md +74 -74
  307. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/rerender-lazy-state-init.md +58 -58
  308. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/rerender-memo-with-default-value.md +38 -38
  309. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/rerender-memo.md +44 -44
  310. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/rerender-move-effect-to-event.md +45 -45
  311. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/rerender-simple-expression-in-memo.md +35 -35
  312. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/rerender-transitions.md +40 -40
  313. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/rerender-use-ref-transient-values.md +73 -73
  314. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/server-after-nonblocking.md +73 -73
  315. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/server-auth-actions.md +96 -96
  316. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/server-cache-lru.md +41 -41
  317. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/server-cache-react.md +76 -76
  318. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/server-dedup-props.md +65 -65
  319. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/server-parallel-fetching.md +83 -83
  320. package/.agents/skills/profiles/react/vercel-react-best-practices/rules/server-serialization.md +38 -38
  321. package/.agents/skills/profiles/springboot/README.md +10 -10
  322. package/.agents/skills/profiles/vue/create-api/SKILL.md +105 -105
  323. package/.agents/skills/profiles/vue/create-component/SKILL.md +76 -76
  324. package/.agents/skills/profiles/vue/create-route/SKILL.md +141 -141
  325. package/.agents/skills/profiles/vue/create-store/SKILL.md +97 -97
  326. package/.agents/skills/profiles/vue/create-view/SKILL.md +81 -81
  327. package/.agents/skills/profiles/vue/theme-variables/SKILL.md +73 -73
  328. package/.agents/skills/profiles/vue/vue-best-practices/SKILL.md +166 -166
  329. package/.agents/skills/profiles/vue/vue-best-practices/references/animation-class-based-technique.md +254 -254
  330. package/.agents/skills/profiles/vue/vue-best-practices/references/animation-state-driven-technique.md +291 -291
  331. package/.agents/skills/profiles/vue/vue-best-practices/references/component-async.md +97 -97
  332. package/.agents/skills/profiles/vue/vue-best-practices/references/component-data-flow.md +307 -307
  333. package/.agents/skills/profiles/vue/vue-best-practices/references/component-fallthrough-attrs.md +174 -174
  334. package/.agents/skills/profiles/vue/vue-best-practices/references/component-keep-alive.md +137 -137
  335. package/.agents/skills/profiles/vue/vue-best-practices/references/component-slots.md +216 -216
  336. package/.agents/skills/profiles/vue/vue-best-practices/references/component-suspense.md +228 -228
  337. package/.agents/skills/profiles/vue/vue-best-practices/references/component-teleport.md +108 -108
  338. package/.agents/skills/profiles/vue/vue-best-practices/references/component-transition-group.md +128 -128
  339. package/.agents/skills/profiles/vue/vue-best-practices/references/component-transition.md +125 -125
  340. package/.agents/skills/profiles/vue/vue-best-practices/references/composables.md +290 -290
  341. package/.agents/skills/profiles/vue/vue-best-practices/references/directives.md +162 -162
  342. package/.agents/skills/profiles/vue/vue-best-practices/references/perf-avoid-component-abstraction-in-lists.md +159 -159
  343. package/.agents/skills/profiles/vue/vue-best-practices/references/perf-v-once-v-memo-directives.md +182 -182
  344. package/.agents/skills/profiles/vue/vue-best-practices/references/perf-virtualize-large-lists.md +187 -187
  345. package/.agents/skills/profiles/vue/vue-best-practices/references/plugins.md +166 -166
  346. package/.agents/skills/profiles/vue/vue-best-practices/references/reactivity.md +344 -344
  347. package/.agents/skills/profiles/vue/vue-best-practices/references/render-functions.md +201 -201
  348. package/.agents/skills/profiles/vue/vue-best-practices/references/sfc.md +310 -310
  349. package/.agents/skills/profiles/vue/vue-best-practices/references/state-management.md +135 -135
  350. package/.agents/skills/profiles/vue/vue-best-practices/references/updated-hook-performance.md +187 -187
  351. package/.agents/templates/common/README.md +23 -23
  352. package/.agents/templates/common/bugfix.md +22 -22
  353. package/.agents/templates/common/create-expert-package.md +458 -458
  354. package/.agents/templates/common/mock-page.md +28 -28
  355. package/.agents/templates/common/new-component.md +25 -25
  356. package/.agents/templates/common/new-page.md +31 -31
  357. package/.cursor/mcp.json +35 -35
  358. package/.qoder/mcp.json +26 -26
  359. package/bin/archive-change.js +560 -474
  360. package/bin/check-command.js +62 -62
  361. package/bin/cli.js +0 -0
  362. package/bin/command-template-renderer.js +40 -40
  363. package/bin/context-command.js +102 -102
  364. package/bin/demo-runtime-smoke.js +760 -760
  365. package/bin/execution-semantics.js +821 -821
  366. package/bin/executor-command.js +93 -93
  367. package/bin/expert-dispatch.js +334 -334
  368. package/bin/expert-executor.js +1148 -1148
  369. package/bin/guard-command.js +52 -52
  370. package/bin/hub-command.js +876 -876
  371. package/bin/ide-command.js +242 -242
  372. package/bin/init-command.js +193 -193
  373. package/bin/install-workflow.js +35 -3
  374. package/bin/manifest-export.js +34 -34
  375. package/bin/profile-registry.js +90 -90
  376. package/bin/protocol-workflow.js +452 -446
  377. package/bin/repair-command.js +161 -161
  378. package/bin/repo-map.js +177 -177
  379. package/bin/report-command.js +236 -236
  380. package/bin/runtime-bootstrap.js +428 -428
  381. package/bin/runtime-embedded.js +101 -101
  382. package/bin/runtime-fallback.js +106 -106
  383. package/bin/runtime-launcher.js +116 -116
  384. package/bin/runtime-paths.js +177 -177
  385. package/bin/runtime-registry.js +289 -289
  386. package/bin/runtime-state.js +2541 -2541
  387. package/bin/scan.js +96 -96
  388. package/bin/self-upgrade.js +206 -206
  389. package/bin/skill-spec-validator.js +457 -457
  390. package/bin/spec-command.js +366 -366
  391. package/bin/superpowers.js +384 -384
  392. package/bin/sync-command.js +59 -59
  393. package/bin/sync.js +1904 -1904
  394. package/bin/task-orchestrator-adapter.js +341 -341
  395. package/bin/task-orchestrator-extractor.js +274 -274
  396. package/bin/task-orchestrator-runner.js +1208 -1208
  397. package/bin/telemetry/README.md +66 -66
  398. package/bin/telemetry/aspect.js +153 -153
  399. package/bin/telemetry/collect.js +67 -67
  400. package/bin/telemetry/config.js +114 -114
  401. package/bin/telemetry/defaults.json +5 -5
  402. package/bin/telemetry/healthcheck.js +195 -195
  403. package/bin/telemetry/identity.js +53 -53
  404. package/bin/telemetry/index.js +25 -25
  405. package/bin/telemetry/reporter.js +83 -83
  406. package/bin/telemetry/safe.js +39 -39
  407. package/bin/validate-registry.js +740 -740
  408. package/bin/visual-bridge-config.js +117 -117
  409. package/bin/visual-bridge.js +287 -287
  410. package/bin/visual-command.js +432 -432
  411. package/bin/worktree-command.js +194 -194
  412. package/configs/common/.editorconfig +15 -15
  413. package/configs/common/.husky/commit-msg +4 -4
  414. package/configs/common/.husky/pre-commit +4 -4
  415. package/configs/common/.lintstagedrc +11 -11
  416. package/configs/common/.prettierignore +11 -11
  417. package/configs/common/.prettierrc.json +11 -11
  418. package/configs/common/.stylelintignore +14 -14
  419. package/configs/common/.stylelintrc.json +21 -21
  420. package/configs/common/commitlint.config.js +3 -3
  421. package/configs/profiles/nestjs/.gitkeep +1 -1
  422. package/configs/profiles/node-tooling/.gitkeep +1 -1
  423. package/configs/profiles/react/.eslintignore +6 -6
  424. package/configs/profiles/react/.eslintrc.js +16 -16
  425. package/configs/profiles/react/.stylelintrc.json +18 -18
  426. package/configs/profiles/springboot/.gitkeep +1 -1
  427. package/configs/profiles/vue/.eslintignore +6 -6
  428. package/configs/profiles/vue/.eslintrc.cjs +17 -17
  429. package/contracts/README.md +28 -28
  430. package/contracts/fixtures/asset-package.fixture.json +26 -26
  431. package/contracts/fixtures/asset-usage-feedback.fixture.json +14 -14
  432. package/contracts/fixtures/evidence-report.fixture.json +28 -28
  433. package/contracts/fixtures/manifest.fixture.json +20 -20
  434. package/contracts/fixtures/run-event.fixture.json +15 -15
  435. package/contracts/schemas/asset-package.schema.json +76 -76
  436. package/contracts/schemas/asset-usage-feedback.schema.json +57 -57
  437. package/contracts/schemas/evidence-report.schema.json +60 -60
  438. package/contracts/schemas/manifest.schema.json +63 -63
  439. package/contracts/schemas/run-event.schema.json +72 -72
  440. package/install.ps1 +35 -35
  441. package/install.sh +17 -17
  442. package/internal/ai-protocol-workflow.js +5824 -5600
  443. package/internal/hub-client.js +98 -98
  444. package/internal/hub-sync-selection.js +69 -69
  445. package/internal/visual-hooks/README.md +481 -481
  446. package/internal/visual-hooks/config-loader.js +218 -218
  447. package/internal/visual-hooks/control-puller.js +206 -206
  448. package/internal/visual-hooks/gate-signal.js +150 -150
  449. package/internal/visual-hooks/inbox-consumer.js +469 -469
  450. package/internal/visual-hooks/index.js +197 -197
  451. package/internal/visual-hooks/push-client.js +189 -189
  452. package/internal/visual-hooks/receipt-pusher.js +176 -176
  453. package/internal/visual-hooks/runtime-state-pusher.js +128 -128
  454. package/openspec/config.yaml.template +52 -52
  455. package/openspec/schemas/expert-delivery/schema.yaml +68 -68
  456. package/openspec/schemas/expert-delivery/templates/checklist.md +39 -39
  457. package/openspec/schemas/expert-delivery/templates/design.md +61 -61
  458. package/openspec/schemas/expert-delivery/templates/iterations.md +25 -25
  459. package/openspec/schemas/expert-delivery/templates/proposal.md +45 -45
  460. package/openspec/schemas/expert-delivery/templates/spec.md +29 -29
  461. package/openspec/schemas/expert-delivery/templates/tasks.md +24 -24
  462. package/package.json +1 -1
  463. package/scripts/acceptance-zero-intrusion.sh +168 -168
  464. package/scripts/hub-sync-assets.config.example.json +296 -296
  465. package/scripts/hub-sync-assets.js +2038 -2038
  466. package/scripts/local-verify.sh +280 -280
  467. package/scripts/post-publish-auto-fix-check.js +404 -404
  468. package/scripts/post-publish-verify.sh +175 -175
  469. package/scripts/setup-cursor-manual-test.sh +107 -107
  470. package/scripts/setup-cursor-spec-archive-test.sh +111 -111
  471. package/scripts/setup-visual-integration.sh +225 -225
  472. package/scripts/test-integration.sh +176 -176
  473. package/scripts/update-test-project.sh +93 -93
  474. package/scripts/upload-four-web.sh +57 -57
  475. package/scripts/verify-install-ps1-bom.js +26 -26
  476. package/src/agent/agent-context.js +259 -259
  477. package/src/agent/agent-profile.js +185 -185
  478. package/src/agent/agent-templates.js +161 -161
  479. package/src/agent/agent-types.js +108 -108
  480. package/src/agent/collaboration-protocol.js +333 -333
  481. package/src/agent/conflict-handler.js +364 -364
  482. package/src/agent/file-permission.js +121 -121
  483. package/src/agent/index.js +38 -38
  484. package/src/agent/permission-audit.js +151 -151
  485. package/src/agent/review-repair-loop.js +270 -270
  486. package/src/agent/tool-permission.js +101 -101
  487. package/src/asset/asset-dependency.js +322 -322
  488. package/src/asset/asset-feedback.js +350 -350
  489. package/src/asset/asset-fork.js +300 -300
  490. package/src/asset/asset-install.js +278 -278
  491. package/src/asset/asset-installer.js +497 -497
  492. package/src/asset/asset-lifecycle.js +324 -324
  493. package/src/asset/asset-manager.js +245 -245
  494. package/src/asset/asset-package-manager.js +349 -349
  495. package/src/asset/asset-package.js +186 -186
  496. package/src/asset/asset-quality.js +262 -262
  497. package/src/asset/asset-registry.js +387 -387
  498. package/src/asset/asset-version.js +293 -293
  499. package/src/asset/index.js +86 -86
  500. package/src/cache/agent-profile-cache.js +59 -59
  501. package/src/cache/asset-cache.js +63 -63
  502. package/src/cache/global-cache.js +61 -61
  503. package/src/cache/manifest-cache.js +30 -30
  504. package/src/check/check-service.js +32 -32
  505. package/src/config/config-layer.js +343 -343
  506. package/src/config/config-loader.js +60 -60
  507. package/src/config/defaults.js +49 -49
  508. package/src/connectors/hub/asset-package.js +72 -72
  509. package/src/connectors/hub/asset-usage-feedback.js +46 -46
  510. package/src/connectors/hub/hub-connector.js +44 -44
  511. package/src/connectors/hub/index.js +21 -21
  512. package/src/connectors/visual/evidence-report.js +49 -49
  513. package/src/connectors/visual/index.js +15 -15
  514. package/src/connectors/visual/queue.js +41 -41
  515. package/src/connectors/visual/run-event.js +81 -81
  516. package/src/connectors/visual/visual-connector.js +77 -77
  517. package/src/context/context-budget.js +59 -59
  518. package/src/context/context-builder.js +285 -285
  519. package/src/context/context-loader.js +116 -116
  520. package/src/context/context-planner.js +158 -158
  521. package/src/context/types.js +96 -96
  522. package/src/contracts/index.js +63 -63
  523. package/src/executor/executor-registry.js +78 -78
  524. package/src/executor/executor-result-parser.js +44 -44
  525. package/src/executor/executor-runner.js +141 -141
  526. package/src/executor/executor-selector.js +139 -139
  527. package/src/executor/executor-timeout.js +36 -36
  528. package/src/executor/providers/base-provider-utils.js +189 -189
  529. package/src/executor/providers/claude-code-executor-provider.js +128 -128
  530. package/src/executor/providers/codex-executor-provider.js +126 -126
  531. package/src/executor/providers/cursor-executor-provider.js +99 -99
  532. package/src/executor/types.js +137 -137
  533. package/src/git/branch-manager.js +71 -71
  534. package/src/git/dirty-checker.js +43 -43
  535. package/src/git/dirty-strategy-handler.js +29 -29
  536. package/src/git/git-command.js +37 -37
  537. package/src/git/git-repository-detector.js +45 -45
  538. package/src/git/multi-repo-worktree-planner.js +88 -88
  539. package/src/git/policy.js +19 -19
  540. package/src/git/strategies/block-dirty-strategy.js +34 -34
  541. package/src/git/strategies/ignore-dirty-strategy.js +33 -33
  542. package/src/git/strategies/patch-snapshot-strategy.js +53 -53
  543. package/src/git/strategies/wip-commit-strategy.js +38 -38
  544. package/src/git/types.js +71 -71
  545. package/src/git/worktree-manager.js +85 -85
  546. package/src/governance/asset-review.js +351 -351
  547. package/src/governance/audit-log.js +368 -368
  548. package/src/governance/gray-release.js +312 -312
  549. package/src/governance/index.js +31 -31
  550. package/src/governance/policy-types.js +56 -56
  551. package/src/governance/rbac-types.js +171 -171
  552. package/src/governance/rbac.js +382 -382
  553. package/src/governance/rollback.js +360 -360
  554. package/src/governance/security-policy.js +354 -354
  555. package/src/hook/hook-config-writer.js +125 -125
  556. package/src/hub/hub-client.js +186 -186
  557. package/src/hub/hub-config.js +39 -39
  558. package/src/hub/project-facts.js +31 -31
  559. package/src/hub/runtime-feedback-reporter.js +55 -55
  560. package/src/ide/adapters/adapter-protocol.js +385 -385
  561. package/src/ide/adapters/claude-adapter.js +419 -419
  562. package/src/ide/adapters/codex-adapter.js +60 -60
  563. package/src/ide/adapters/cursor-adapter.js +484 -484
  564. package/src/ide/adapters/index.js +24 -24
  565. package/src/ide/anchors/markdown-anchor-writer.js +152 -152
  566. package/src/ide/ide-service.js +270 -270
  567. package/src/ide/ide-types.js +94 -94
  568. package/src/ide/links/link-mode-resolver.js +160 -160
  569. package/src/ide/registry/ide-registry-builder.js +165 -165
  570. package/src/incident/incident-writer.js +47 -47
  571. package/src/incident/types.js +22 -22
  572. package/src/init/ide-linker.js +126 -126
  573. package/src/init/ide-pointer-injector.js +75 -75
  574. package/src/init/init-applier.js +197 -197
  575. package/src/init/init-plan.js +294 -294
  576. package/src/init/init-service.js +65 -65
  577. package/src/init/manifest-installer.js +302 -302
  578. package/src/init/types.js +26 -26
  579. package/src/project/config-writer.js +83 -83
  580. package/src/project/context-index-writer.js +82 -82
  581. package/src/project/json-utils.js +72 -72
  582. package/src/project/local-state-writer.js +50 -50
  583. package/src/project/lock-file-writer.js +98 -98
  584. package/src/project/manifest-writer.js +126 -126
  585. package/src/project/policy-config-writer.js +91 -91
  586. package/src/project/project-config-writer.js +74 -74
  587. package/src/project/project-files.js +39 -39
  588. package/src/project/registry-index-writer.js +43 -43
  589. package/src/project/workspace-config-writer.js +63 -63
  590. package/src/run/index.js +11 -11
  591. package/src/run/run-id.js +32 -32
  592. package/src/run/run-service.js +269 -269
  593. package/src/run/run-store.js +80 -80
  594. package/src/scanner/aggregator/detection-aggregator.js +23 -23
  595. package/src/scanner/boundary/boundary-resolver.js +229 -229
  596. package/src/scanner/detectors/detector-registry.js +44 -44
  597. package/src/scanner/detectors/fastapi-detector.js +46 -46
  598. package/src/scanner/detectors/go-detector.js +46 -46
  599. package/src/scanner/detectors/nestjs-detector.js +57 -57
  600. package/src/scanner/detectors/nextjs-detector.js +52 -52
  601. package/src/scanner/detectors/react-vite-detector.js +52 -52
  602. package/src/scanner/detectors/react-webpack-detector.js +57 -57
  603. package/src/scanner/detectors/springboot-detector.js +46 -46
  604. package/src/scanner/detectors/springcloud-detector.js +46 -46
  605. package/src/scanner/detectors/springmvc-detector.js +46 -46
  606. package/src/scanner/detectors/vue-vite-detector.js +52 -52
  607. package/src/scanner/engine.js +72 -72
  608. package/src/scanner/facts/fact-extractor.js +211 -211
  609. package/src/scanner/types.js +30 -30
  610. package/src/security/asset-tamper-checker.js +188 -188
  611. package/src/security/checksum.js +40 -40
  612. package/src/spec/spec-writer.js +302 -302
  613. package/src/state-machine/circuit-breaker.js +112 -112
  614. package/src/state-machine/escape-hatch.js +49 -49
  615. package/src/state-machine/stage-runner.js +281 -281
  616. package/src/state-machine/state-machine.js +24 -24
  617. package/src/state-machine/transition-guard.js +36 -36
  618. package/src/state-machine/types.js +37 -37
  619. package/src/sync/sync-service.js +192 -192
  620. package/src/visual/agent-visual.js +142 -142
  621. package/src/visual/event-gateway.js +357 -357
  622. package/src/visual/event-mapper.js +128 -128
  623. package/src/visual/hook-dashboard.js +216 -216
  624. package/src/visual/index.js +27 -27
  625. package/src/visual/metrics.js +287 -287
  626. package/src/visual/privacy-filter.js +100 -100
  627. package/src/visual/risk-board.js +252 -252
  628. package/src/visual/timeline.js +245 -245
  629. package/src/visual/visual-client.js +94 -94
  630. package/src/visual/visual-config.js +40 -40
  631. package/src/visual/visual-reporter.js +88 -88
@@ -1,876 +1,876 @@
1
- const crypto = require('crypto');
2
- const fs = require('fs');
3
- const path = require('path');
4
- const {
5
- fetchManifestExport,
6
- normalizeOrigin,
7
- postInstallReport,
8
- postRuntimeReport,
9
- requestJson,
10
- writeHubToken,
11
- } = require('../internal/hub-client');
12
-
13
- const REGISTRY_LOCK_FILE = path.join('.agents', 'registry', 'hub-lock.json');
14
- const LEGACY_LOCK_FILE = 'hub-lock.json';
15
- const REGISTRY_MANIFEST_FILE = path.join('.agents', 'registry', 'manifest.json');
16
- const HISTORY_ROOT = path.join('.ai-spec', 'history', 'hub-install');
17
-
18
- const ALLOWED_INSTALL_PREFIXES = [
19
- '.agents/rules/',
20
- '.agents/skills/',
21
- '.agents/roles/',
22
- '.agents/flows/',
23
- '.agents/registry/',
24
- '.cursor/',
25
- '.claude/',
26
- 'openspec/',
27
- '.ai-spec/config/',
28
- '.ai-spec/history/',
29
- ];
30
-
31
- const BLOCKED_INSTALL_PATHS = new Set([
32
- 'package.json',
33
- '.env',
34
- '.env.local',
35
- ]);
36
-
37
- function parseArgs(argv) {
38
- const args = [...argv];
39
- const command = args.shift() || 'help';
40
- const options = {
41
- command,
42
- target: '.',
43
- origin: process.env.AI_SPEC_HUB_ORIGIN || '',
44
- dryRun: false,
45
- allowHighRisk: false,
46
- json: false,
47
- force: false,
48
- yes: false,
49
- version: '',
50
- rollbackVersion: '',
51
- manifestId: '',
52
- token: '',
53
- kind: '',
54
- mode: 'standard',
55
- profile: '',
56
- ide: '',
57
- runId: '',
58
- stage: 'review',
59
- status: 'success',
60
- durationMs: 0,
61
- failedReason: '',
62
- repoUrl: '',
63
- };
64
-
65
- if (command === '--help' || command === '-h') {
66
- options.command = 'help';
67
- options.help = true;
68
- return options;
69
- }
70
-
71
- while (args.length > 0) {
72
- const arg = args.shift();
73
- switch (arg) {
74
- case '--hub':
75
- case '--hub-origin':
76
- case '--origin':
77
- options.origin = requireArg(arg, args);
78
- break;
79
- case '--target':
80
- options.target = requireArg(arg, args);
81
- break;
82
- case '--version':
83
- options.version = requireArg(arg, args);
84
- break;
85
- case '--token':
86
- options.token = requireArg(arg, args);
87
- break;
88
- case '--kind':
89
- options.kind = requireArg(arg, args);
90
- break;
91
- case '--mode':
92
- options.mode = requireArg(arg, args);
93
- break;
94
- case '--profile':
95
- options.profile = requireArg(arg, args);
96
- break;
97
- case '--ide':
98
- options.ide = requireArg(arg, args);
99
- break;
100
- case '--run-id':
101
- options.runId = requireArg(arg, args);
102
- break;
103
- case '--stage':
104
- options.stage = requireArg(arg, args);
105
- break;
106
- case '--status':
107
- options.status = requireArg(arg, args);
108
- break;
109
- case '--duration-ms':
110
- options.durationMs = Number(requireArg(arg, args));
111
- break;
112
- case '--failed-reason':
113
- options.failedReason = requireArg(arg, args);
114
- break;
115
- case '--repo-url':
116
- options.repoUrl = requireArg(arg, args);
117
- break;
118
- case '--dry-run':
119
- options.dryRun = true;
120
- break;
121
- case '--allow-high-risk':
122
- options.allowHighRisk = true;
123
- break;
124
- case '--json':
125
- options.json = true;
126
- break;
127
- case '--force':
128
- options.force = true;
129
- break;
130
- case '--yes':
131
- case '-y':
132
- options.yes = true;
133
- break;
134
- case '--help':
135
- case '-h':
136
- options.help = true;
137
- break;
138
- default:
139
- assignPositional(options, arg);
140
- }
141
- }
142
- return options;
143
- }
144
-
145
- function assignPositional(options, arg) {
146
- if (options.command === 'rollback') {
147
- if (!options.rollbackVersion && isSemver(arg)) {
148
- options.rollbackVersion = arg;
149
- return;
150
- }
151
- if (options.target === '.') {
152
- options.target = arg;
153
- return;
154
- }
155
- }
156
-
157
- if (options.command === 'upgrade' && !options.version && isSemver(arg)) {
158
- options.version = arg;
159
- return;
160
- }
161
-
162
- if (['diff', 'sync', 'upgrade', 'runtime-report'].includes(options.command) && options.target === '.') {
163
- options.target = arg;
164
- return;
165
- }
166
-
167
- if (!options.manifestId) {
168
- options.manifestId = arg;
169
- return;
170
- }
171
-
172
- if (options.target === '.') {
173
- options.target = arg;
174
- return;
175
- }
176
-
177
- throw new Error(`未知参数:${arg}`);
178
- }
179
-
180
- function requireArg(flag, args) {
181
- const next = args.shift();
182
- if (!next || next.startsWith('--')) {
183
- throw new Error(`选项 ${flag} 需要一个参数值`);
184
- }
185
- return next;
186
- }
187
-
188
- function isSemver(value) {
189
- return /^\d+\.\d+\.\d+$/.test(String(value || ''));
190
- }
191
-
192
- function ensureDir(dirPath) {
193
- fs.mkdirSync(dirPath, { recursive: true });
194
- }
195
-
196
- function readJson(filePath) {
197
- return JSON.parse(fs.readFileSync(filePath, 'utf8'));
198
- }
199
-
200
- function writeJson(filePath, value) {
201
- ensureDir(path.dirname(filePath));
202
- fs.writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`, 'utf8');
203
- }
204
-
205
- function sha256Text(text) {
206
- return crypto.createHash('sha256').update(text).digest('hex');
207
- }
208
-
209
- function readHubConfig(targetDir) {
210
- const filePath = path.join(targetDir, '.ai-spec', 'config', 'hub.json');
211
- if (!fs.existsSync(filePath)) return null;
212
- try {
213
- return readJson(filePath);
214
- } catch (_error) {
215
- return null;
216
- }
217
- }
218
-
219
- function resolveOrigin(options, targetDir) {
220
- const fromConfig = readHubConfig(targetDir);
221
- return normalizeOrigin(options.origin || fromConfig?.hub || fromConfig?.baseUrl || 'http://localhost:3000');
222
- }
223
-
224
- function normalizeExport(payload) {
225
- if (!payload || typeof payload !== 'object') {
226
- throw new Error('Hub Export 响应为空');
227
- }
228
- const data = payload.data && payload.success !== undefined ? payload.data : payload;
229
- if (data.contractVersion && data.contractVersion !== '1.0.0') {
230
- throw new Error(`不支持的 Hub 契约版本:${data.contractVersion}`);
231
- }
232
- if (!data.contractVersion) {
233
- throw new Error('Hub Export 缺少 contractVersion(契约版本)');
234
- }
235
- if (!data?.manifest?.id || !data.version || !Array.isArray(data.assets)) {
236
- throw new Error('Hub Export 响应缺少 manifest、version 或 assets');
237
- }
238
- if (data.manifest.status && data.manifest.status !== 'published') {
239
- throw new Error(`Manifest 未发布,当前状态:${data.manifest.status}`);
240
- }
241
-
242
- const filesByKey = new Map();
243
- for (const file of Array.isArray(data.files) ? data.files : []) {
244
- if (file && typeof file === 'object') {
245
- filesByKey.set(file.assetId || file.path, file);
246
- if (file.path) filesByKey.set(file.path, file);
247
- }
248
- }
249
-
250
- return {
251
- ...data,
252
- assets: data.assets.map((asset) => {
253
- const file = filesByKey.get(asset.assetId) || filesByKey.get(asset.installPath || asset.path);
254
- return {
255
- ...asset,
256
- content: asset.content ?? file?.content,
257
- contentFormat: asset.contentFormat ?? file?.contentFormat,
258
- };
259
- }),
260
- };
261
- }
262
-
263
- function registryLockPath(targetDir) {
264
- return path.join(targetDir, REGISTRY_LOCK_FILE);
265
- }
266
-
267
- function legacyLockPath(targetDir) {
268
- return path.join(targetDir, LEGACY_LOCK_FILE);
269
- }
270
-
271
- function findLockPath(targetDir) {
272
- const registry = registryLockPath(targetDir);
273
- if (fs.existsSync(registry)) return registry;
274
- const legacy = legacyLockPath(targetDir);
275
- if (fs.existsSync(legacy)) return legacy;
276
- return null;
277
- }
278
-
279
- function normalizeLock(raw) {
280
- if (!raw || typeof raw !== 'object') return null;
281
- const assets = Array.isArray(raw.assets)
282
- ? raw.assets.map((asset) => ({
283
- kind: String(asset.kind || 'unknown'),
284
- assetId: String(asset.assetId || asset.slug || asset.id || ''),
285
- slug: String(asset.slug || asset.assetId || asset.id || ''),
286
- version: String(asset.version || ''),
287
- required: asset.required !== false,
288
- checksum: String(asset.checksum || ''),
289
- installPath: String(asset.installPath || asset.path || ''),
290
- path: String(asset.path || asset.installPath || ''),
291
- riskLevel: String(asset.riskLevel || 'L0'),
292
- currentChecksum: typeof asset.currentChecksum === 'string' ? asset.currentChecksum : null,
293
- }))
294
- : [];
295
- return {
296
- raw,
297
- hubBaseUrl: typeof raw.hub === 'string' ? raw.hub : String(raw.hub?.baseUrl || raw.hub?.url || ''),
298
- manifestId: String(raw.manifest?.id || raw.manifestId || ''),
299
- manifestSlug: String(raw.manifest?.slug || raw.manifestId || raw.manifest?.id || ''),
300
- manifestVersion: String(raw.manifest?.version || raw.manifestVersion || ''),
301
- manifestChecksum: String(raw.manifest?.checksum || raw.manifestChecksum || ''),
302
- installMode: String(raw.install?.mode || raw.mode || ''),
303
- installedAt: String(raw.install?.installedAt || raw.installedAt || ''),
304
- assets,
305
- };
306
- }
307
-
308
- function readLockRecord(targetDir) {
309
- const filePath = findLockPath(targetDir);
310
- if (!filePath) return null;
311
- try {
312
- const raw = readJson(filePath);
313
- return { filePath, raw, lock: normalizeLock(raw) };
314
- } catch (_error) {
315
- return null;
316
- }
317
- }
318
-
319
- function resolveInstallPath(asset) {
320
- const relPath = asset.installPath || asset.path;
321
- if (relPath) return normalizeRelativePath(relPath);
322
- return `.agents/registry/${asset.kind}/${asset.assetId || asset.slug}.json`;
323
- }
324
-
325
- function normalizeRelativePath(relPath) {
326
- const normalized = path.posix.normalize(String(relPath).replace(/\\/g, '/'));
327
- if (path.isAbsolute(relPath) || normalized === '..' || normalized.startsWith('../')) {
328
- throw new Error(`安装路径非法:${relPath}`);
329
- }
330
- return normalized;
331
- }
332
-
333
- function assertAllowedInstallPath(relPath) {
334
- const normalized = normalizeRelativePath(relPath);
335
- if (
336
- BLOCKED_INSTALL_PATHS.has(normalized) ||
337
- normalized.startsWith('src/') ||
338
- normalized.startsWith('node_modules/') ||
339
- normalized.startsWith('.git/')
340
- ) {
341
- throw new Error(`安装路径被禁止:${relPath}`);
342
- }
343
- if (!ALLOWED_INSTALL_PREFIXES.some((prefix) => normalized.startsWith(prefix))) {
344
- throw new Error(`安装路径不在允许范围内:${relPath}`);
345
- }
346
- return normalized;
347
- }
348
-
349
- async function fetchAssetContent(asset) {
350
- if (typeof asset.content === 'string') {
351
- return asset.content;
352
- }
353
- if (asset.contentUrl) {
354
- const response = await fetch(asset.contentUrl);
355
- if (!response.ok) {
356
- throw new Error(`资产下载失败:${asset.assetId || asset.slug} (${response.status})`);
357
- }
358
- return response.text();
359
- }
360
- throw new Error(`资产 ${asset.assetId || asset.slug} 缺少 content 或 contentUrl`);
361
- }
362
-
363
- function verifyAssetChecksum(asset, content) {
364
- const expected = String(asset.checksum || '');
365
- if (!expected) return;
366
- const actual = sha256Text(content);
367
- if (expected.startsWith('sha256:') && expected !== `sha256:${actual}`) {
368
- throw new Error(`资产 ${asset.assetId || asset.slug} checksum 校验失败`);
369
- }
370
- if (/^[a-f0-9]{64}$/i.test(expected) && expected.toLowerCase() !== actual) {
371
- throw new Error(`资产 ${asset.assetId || asset.slug} checksum 校验失败`);
372
- }
373
- }
374
-
375
- function readInstalledChecksum(targetDir, relPath) {
376
- const filePath = path.join(targetDir, relPath);
377
- if (!fs.existsSync(filePath)) return null;
378
- return sha256Text(fs.readFileSync(filePath, 'utf8'));
379
- }
380
-
381
- function assertRiskAllowed(exportPayload, options) {
382
- const l4 = exportPayload.assets.find((asset) => asset.riskLevel === 'L4');
383
- if (l4) throw new Error(`资产 ${l4.assetId || l4.slug} 风险等级为 L4,禁止安装。`);
384
- const high = exportPayload.assets.find((asset) => asset.riskLevel === 'L3');
385
- if (high && !options.allowHighRisk && !options.yes) {
386
- throw new Error(`资产 ${high.assetId || high.slug} 风险等级为 L3,请显式添加 --allow-high-risk 或 --yes。`);
387
- }
388
- }
389
-
390
- function classifyUpgrade(current, next) {
391
- const [currentMajor, currentMinor, currentPatch] = String(current || '0.0.0').split('.').map(Number);
392
- const [nextMajor, nextMinor, nextPatch] = String(next || '0.0.0').split('.').map(Number);
393
- if (nextMajor !== currentMajor) return 'major';
394
- if (nextMinor !== currentMinor) return 'minor';
395
- if (nextPatch !== currentPatch) return 'patch';
396
- return 'same';
397
- }
398
-
399
- function compareSemver(current, next) {
400
- const a = String(current || '0.0.0').split('.').map(Number);
401
- const b = String(next || '0.0.0').split('.').map(Number);
402
- for (let index = 0; index < 3; index += 1) {
403
- const diff = (b[index] || 0) - (a[index] || 0);
404
- if (diff !== 0) return diff;
405
- }
406
- return 0;
407
- }
408
-
409
- function buildLock({ origin, exportPayload, mode, targetDir, options }) {
410
- const now = new Date().toISOString();
411
- const assets = exportPayload.assets.map((asset) => {
412
- const relPath = resolveInstallPath(asset);
413
- return {
414
- kind: asset.kind,
415
- id: asset.assetId || asset.slug,
416
- slug: asset.slug || asset.assetId,
417
- assetId: asset.assetId || asset.slug,
418
- version: asset.version,
419
- required: asset.required !== false,
420
- path: relPath,
421
- installPath: relPath,
422
- checksum: asset.checksum,
423
- currentChecksum: readInstalledChecksum(targetDir, relPath),
424
- riskLevel: asset.riskLevel || 'L0',
425
- };
426
- });
427
- return {
428
- lockVersion: '1.0.0',
429
- hub: {
430
- baseUrl: normalizeOrigin(origin),
431
- name: 'Xia Qiu Hub',
432
- },
433
- manifest: {
434
- id: exportPayload.manifest.id,
435
- slug: exportPayload.manifest.slug || exportPayload.manifest.id,
436
- version: exportPayload.version,
437
- checksum: exportPayload.checksum,
438
- },
439
- install: {
440
- mode,
441
- cliVersion: readCliVersion(),
442
- installedAt: now,
443
- profile: options.profile || undefined,
444
- ide: options.ide || undefined,
445
- },
446
- assets,
447
-
448
- // 兼容旧版 Visual / 脚本读取,主协议仍以上面的嵌套结构为准。
449
- manifestId: exportPayload.manifest.id,
450
- manifestVersion: exportPayload.version,
451
- manifestChecksum: exportPayload.checksum,
452
- installedAt: now,
453
- mode,
454
- };
455
- }
456
-
457
- function readCliVersion() {
458
- try {
459
- return require('../package.json').version || '0.0.0';
460
- } catch (_error) {
461
- return '0.0.0';
462
- }
463
- }
464
-
465
- function createHistorySnapshot(targetDir, previous, changed) {
466
- const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
467
- const snapshotDir = path.join(targetDir, HISTORY_ROOT, timestamp);
468
- const backupDir = path.join(snapshotDir, 'backup');
469
- const changedFiles = [];
470
- ensureDir(backupDir);
471
-
472
- for (const item of changed) {
473
- const source = path.join(targetDir, item.relPath);
474
- const backup = path.join(backupDir, item.relPath);
475
- const existed = fs.existsSync(source);
476
- if (existed) {
477
- ensureDir(path.dirname(backup));
478
- fs.copyFileSync(source, backup);
479
- }
480
- changedFiles.push({ path: item.relPath, existed });
481
- }
482
-
483
- const lockFile = findLockPath(targetDir);
484
- const lockBackup = path.join(backupDir, REGISTRY_LOCK_FILE);
485
- if (lockFile && fs.existsSync(lockFile)) {
486
- ensureDir(path.dirname(lockBackup));
487
- fs.copyFileSync(lockFile, lockBackup);
488
- }
489
-
490
- writeJson(path.join(snapshotDir, 'snapshot.json'), {
491
- createdAt: new Date().toISOString(),
492
- fromVersion: previous?.lock?.manifestVersion || null,
493
- manifestId: previous?.lock?.manifestId || previous?.lock?.manifestSlug || null,
494
- lockExisted: Boolean(lockFile),
495
- changedFiles,
496
- });
497
- return { snapshotDir, backupDir };
498
- }
499
-
500
- function assertNoLocalConflicts(targetDir, plan, previous, options) {
501
- if (options.force) return;
502
- const localByKey = new Map((previous?.lock?.assets || []).map((asset) => [`${asset.kind}:${asset.assetId}`, asset]));
503
- for (const item of plan) {
504
- const filePath = path.join(targetDir, item.relPath);
505
- if (!fs.existsSync(filePath)) continue;
506
- const actual = sha256Text(fs.readFileSync(filePath, 'utf8'));
507
- const local = localByKey.get(`${item.asset.kind}:${item.asset.assetId || item.asset.slug}`);
508
- if (local?.checksum && actual !== local.checksum && actual !== item.asset.checksum) {
509
- throw new Error(`检测到本地文件冲突:${item.relPath},请先处理或使用 --force。`);
510
- }
511
- }
512
- }
513
-
514
- async function install(options) {
515
- if (!options.manifestId) throw new Error('缺少 Manifest ID');
516
- const targetDir = path.resolve(options.target);
517
- const origin = resolveOrigin(options, targetDir);
518
- const exportPayload = normalizeExport(await fetchManifestExport({
519
- origin,
520
- manifestId: options.manifestId,
521
- version: options.version || undefined,
522
- }));
523
- assertRiskAllowed(exportPayload, options);
524
-
525
- const plan = exportPayload.assets.map((asset) => ({
526
- asset,
527
- relPath: assertAllowedInstallPath(resolveInstallPath(asset)),
528
- filePath: path.join(targetDir, assertAllowedInstallPath(resolveInstallPath(asset))),
529
- }));
530
- const previous = readLockRecord(targetDir);
531
- assertNoLocalConflicts(targetDir, plan, previous, options);
532
-
533
- const skipped = plan.filter((item) => {
534
- const actual = readInstalledChecksum(targetDir, item.relPath);
535
- return actual && actual === item.asset.checksum;
536
- });
537
- const changed = plan.filter((item) => !skipped.includes(item));
538
-
539
- if (previous?.lock?.manifestChecksum === exportPayload.checksum && changed.length === 0) {
540
- return {
541
- manifestId: exportPayload.manifest.id,
542
- version: exportPayload.version,
543
- message: '已是最新,无需重复安装',
544
- written: [],
545
- skipped: skipped.length,
546
- };
547
- }
548
-
549
- if (options.dryRun) {
550
- return {
551
- dryRun: true,
552
- manifestId: exportPayload.manifest.id,
553
- version: exportPayload.version,
554
- createOrUpdate: changed.map((item) => item.relPath),
555
- skipped: skipped.map((item) => item.relPath),
556
- warnings: exportPayload.assets.filter((asset) => asset.riskLevel === 'L3').map((asset) => `${asset.assetId || asset.slug} 为 L3 高风险资产`),
557
- };
558
- }
559
-
560
- const snapshot = createHistorySnapshot(targetDir, previous, changed);
561
- const written = [];
562
- try {
563
- for (const item of changed) {
564
- let content = '';
565
- try {
566
- content = await fetchAssetContent(item.asset);
567
- verifyAssetChecksum(item.asset, content);
568
- } catch (error) {
569
- if (item.asset.required !== false) throw error;
570
- console.warn(`警告:可选资产 ${item.asset.assetId || item.asset.slug} 下载失败,已跳过。`);
571
- continue;
572
- }
573
- ensureDir(path.dirname(item.filePath));
574
- fs.writeFileSync(item.filePath, content, 'utf8');
575
- written.push(item.relPath);
576
- }
577
-
578
- ensureDir(path.join(targetDir, '.agents', 'registry'));
579
- const lock = buildLock({ origin, exportPayload, mode: options.mode, targetDir, options });
580
- writeJson(registryLockPath(targetDir), lock);
581
- writeJson(path.join(targetDir, REGISTRY_MANIFEST_FILE), exportPayload);
582
-
583
- await postInstallReport({
584
- origin,
585
- report: {
586
- projectName: path.basename(targetDir),
587
- manifestId: lock.manifest.id,
588
- manifestVersion: lock.manifest.version,
589
- installMode: lock.install.mode,
590
- status: 'success',
591
- assets: lock.assets,
592
- message: '安装成功',
593
- },
594
- }).catch((error) => {
595
- console.warn(`警告:安装记录上报失败,已继续:${error.message}`);
596
- });
597
-
598
- return {
599
- dryRun: false,
600
- manifestId: lock.manifest.id,
601
- version: lock.manifest.version,
602
- lockFile: REGISTRY_LOCK_FILE,
603
- snapshot: path.relative(targetDir, snapshot.snapshotDir),
604
- written,
605
- skipped: skipped.length,
606
- };
607
- } catch (error) {
608
- for (const relPath of written) {
609
- try {
610
- fs.rmSync(path.join(targetDir, relPath), { force: true });
611
- } catch (_rollbackError) {
612
- // 回滚失败不覆盖原始错误。
613
- }
614
- }
615
- throw new Error(`安装失败,已回滚已写入文件:${error.message}`);
616
- }
617
- }
618
-
619
- async function diff(options) {
620
- const targetDir = path.resolve(options.target);
621
- const previous = readLockRecord(targetDir);
622
- if (!previous?.lock) throw new Error('未找到 .agents/registry/hub-lock.json,请先执行 hub install。');
623
- const origin = resolveOrigin(options, targetDir);
624
- const exportPayload = normalizeExport(await fetchManifestExport({
625
- origin,
626
- manifestId: options.manifestId || previous.lock.manifestSlug || previous.lock.manifestId,
627
- version: options.version || undefined,
628
- }));
629
- const remote = new Map(exportPayload.assets.map((asset) => [`${asset.kind}:${asset.assetId || asset.slug}`, asset]));
630
- const local = new Map((previous.lock.assets || []).map((asset) => [`${asset.kind}:${asset.assetId}`, asset]));
631
- const changes = [];
632
-
633
- for (const [key, asset] of remote) {
634
- const current = local.get(key);
635
- const relPath = resolveInstallPath(asset);
636
- const filePath = path.join(targetDir, relPath);
637
- if (!current) {
638
- changes.push({ type: 'missing', asset });
639
- continue;
640
- }
641
- if (!fs.existsSync(filePath)) {
642
- changes.push({ type: 'missing', asset: current });
643
- continue;
644
- }
645
- const actual = sha256Text(fs.readFileSync(filePath, 'utf8'));
646
- if (current.checksum && actual !== current.checksum && actual !== asset.checksum) {
647
- changes.push({ type: 'modified', asset: current, localChecksum: actual });
648
- }
649
- if (compareSemver(current.version, asset.version) > 0 || current.checksum !== asset.checksum) {
650
- changes.push({ type: 'outdated', from: current, to: asset });
651
- }
652
- }
653
-
654
- for (const [key, asset] of local) {
655
- if (!remote.has(key)) changes.push({ type: 'extra', asset });
656
- }
657
-
658
- return {
659
- manifestId: exportPayload.manifest.id,
660
- localVersion: previous.lock.manifestVersion,
661
- remoteVersion: exportPayload.version,
662
- changes,
663
- };
664
- }
665
-
666
- async function sync(options) {
667
- const targetDir = path.resolve(options.target);
668
- const previous = readLockRecord(targetDir);
669
- if (!previous?.lock && !options.manifestId) throw new Error('未找到 .agents/registry/hub-lock.json,请提供 Manifest ID。');
670
- const origin = resolveOrigin(options, targetDir);
671
- const manifestId = options.manifestId || previous.lock.manifestSlug || previous.lock.manifestId;
672
- const latest = normalizeExport(await fetchManifestExport({ origin, manifestId }));
673
- const upgradeType = classifyUpgrade(previous?.lock?.manifestVersion, latest.version);
674
- if (upgradeType === 'same') {
675
- return install({ ...options, origin, manifestId, version: latest.version });
676
- }
677
- if ((upgradeType === 'minor' || upgradeType === 'major') && !options.yes && !options.force) {
678
- throw new Error(`检测到 ${upgradeType} 升级:${previous.lock.manifestVersion} -> ${latest.version},请添加 --yes 确认。`);
679
- }
680
- return install({ ...options, origin, manifestId, version: latest.version });
681
- }
682
-
683
- async function upgrade(options) {
684
- const targetDir = path.resolve(options.target);
685
- const previous = readLockRecord(targetDir);
686
- if (!previous?.lock && !options.manifestId) throw new Error('未找到 .agents/registry/hub-lock.json,请提供 Manifest ID。');
687
- const origin = resolveOrigin(options, targetDir);
688
- const manifestId = options.manifestId || previous.lock.manifestSlug || previous.lock.manifestId;
689
- const target = normalizeExport(await fetchManifestExport({ origin, manifestId, version: options.version || undefined }));
690
- const upgradeType = classifyUpgrade(previous?.lock?.manifestVersion, target.version);
691
- if (upgradeType === 'major' && !options.yes && !options.force) {
692
- throw new Error(`检测到 major 升级:${previous.lock.manifestVersion} -> ${target.version},请添加 --yes 确认。`);
693
- }
694
- return install({ ...options, origin, manifestId, version: target.version });
695
- }
696
-
697
- async function rollback(options) {
698
- const targetDir = path.resolve(options.target);
699
- const backupsRoot = path.join(targetDir, HISTORY_ROOT);
700
- if (!fs.existsSync(backupsRoot)) return { restored: [], message: '没有可回滚的备份' };
701
- const snapshots = fs.readdirSync(backupsRoot).sort().reverse();
702
- let selected = null;
703
- for (const name of snapshots) {
704
- const snapshotPath = path.join(backupsRoot, name, 'snapshot.json');
705
- if (!fs.existsSync(snapshotPath)) continue;
706
- const snapshot = readJson(snapshotPath);
707
- if (!options.rollbackVersion || snapshot.fromVersion === options.rollbackVersion) {
708
- selected = { name, snapshot, backupDir: path.join(backupsRoot, name, 'backup') };
709
- break;
710
- }
711
- }
712
- if (!selected) throw new Error(`未找到可回滚版本:${options.rollbackVersion}`);
713
-
714
- const restored = [];
715
- for (const item of selected.snapshot.changedFiles || []) {
716
- const dest = path.join(targetDir, item.path);
717
- const backup = path.join(selected.backupDir, item.path);
718
- if (item.existed && fs.existsSync(backup)) {
719
- ensureDir(path.dirname(dest));
720
- fs.copyFileSync(backup, dest);
721
- restored.push(item.path);
722
- } else if (!item.existed) {
723
- fs.rmSync(dest, { force: true });
724
- restored.push(item.path);
725
- }
726
- }
727
-
728
- const lockBackup = path.join(selected.backupDir, REGISTRY_LOCK_FILE);
729
- if (selected.snapshot.lockExisted && fs.existsSync(lockBackup)) {
730
- ensureDir(path.dirname(registryLockPath(targetDir)));
731
- fs.copyFileSync(lockBackup, registryLockPath(targetDir));
732
- restored.push(REGISTRY_LOCK_FILE);
733
- } else if (!selected.snapshot.lockExisted) {
734
- fs.rmSync(registryLockPath(targetDir), { force: true });
735
- }
736
-
737
- return { restored, backup: selected.name, version: selected.snapshot.fromVersion || null };
738
- }
739
-
740
- async function search(options) {
741
- const q = options.manifestId || '';
742
- const origin = resolveOrigin(options, path.resolve(options.target));
743
- const params = new URLSearchParams();
744
- if (q) params.set('q', q);
745
- if (options.kind) params.set('kind', options.kind);
746
- return requestJson(`${normalizeOrigin(origin)}/api/hub/search?${params.toString()}`);
747
- }
748
-
749
- async function runtimeReport(options) {
750
- const targetDir = path.resolve(options.target);
751
- const previous = readLockRecord(targetDir);
752
- if (!previous?.lock) throw new Error('未找到 .agents/registry/hub-lock.json,请先执行 hub install。');
753
-
754
- const allowedStages = new Set(['requirement', 'design', 'implement', 'test', 'review', 'archive']);
755
- const allowedStatuses = new Set(['success', 'failed', 'partial']);
756
- if (!allowedStages.has(options.stage)) {
757
- throw new Error(`不支持的 stage:${options.stage}`);
758
- }
759
- if (!allowedStatuses.has(options.status)) {
760
- throw new Error(`不支持的 status:${options.status}`);
761
- }
762
- if (!Number.isFinite(options.durationMs) || options.durationMs < 0) {
763
- throw new Error('--duration-ms 必须是非负数字');
764
- }
765
-
766
- const origin = resolveOrigin(options, targetDir);
767
- const report = {
768
- projectName: path.basename(targetDir),
769
- repoUrl: options.repoUrl || undefined,
770
- manifestId: previous.lock.manifestId || previous.lock.manifestSlug || undefined,
771
- manifestVersion: previous.lock.manifestVersion || undefined,
772
- runId: options.runId || `manual-${Date.now()}`,
773
- stage: options.stage,
774
- status: options.status,
775
- usedAssets: (previous.lock.assets || []).map((asset) => ({
776
- kind: asset.kind,
777
- assetId: asset.assetId || asset.slug,
778
- version: asset.version,
779
- })),
780
- durationMs: Math.round(options.durationMs),
781
- failedReason: options.failedReason || undefined,
782
- };
783
-
784
- const response = await postRuntimeReport({ origin, report });
785
- return {
786
- reported: true,
787
- manifestId: report.manifestId,
788
- manifestVersion: report.manifestVersion,
789
- runId: report.runId,
790
- usedAssetCount: report.usedAssets.length,
791
- response,
792
- };
793
- }
794
-
795
- function login(options) {
796
- const targetDir = path.resolve(options.target);
797
- const origin = resolveOrigin(options, targetDir);
798
- const configPath = path.join(targetDir, '.ai-spec', 'config', 'hub.json');
799
- writeJson(configPath, {
800
- hub: origin,
801
- tokenSource: options.token ? 'config' : 'env-or-config',
802
- loginAt: new Date().toISOString(),
803
- });
804
- const tokenFile = options.token ? writeHubToken(options.token) : null;
805
- return { configFile: path.relative(targetDir, configPath), tokenFile, message: `Hub 登录成功:${origin}` };
806
- }
807
-
808
- function printUsage() {
809
- console.log(`用法:
810
- ai-spec-auto hub login --hub <url> [--token <token>]
811
- ai-spec-auto hub search <关键词> [--kind manifest] [--hub <url>]
812
- ai-spec-auto hub install <manifest-id> [target] [--hub <url>] [--mode standard] [--profile react] [--ide cursor] [--dry-run] [--force] [--yes]
813
- ai-spec-auto hub sync [target] [--yes]
814
- ai-spec-auto hub diff [target]
815
- ai-spec-auto hub upgrade [target] [--version <version>] [--yes]
816
- ai-spec-auto hub rollback [version] [target]
817
- ai-spec-auto hub runtime-report [target] [--run-id <id>] [--stage review] [--status success] [--duration-ms 0] [--failed-reason <reason>]
818
- `);
819
- }
820
-
821
- function printPretty(result) {
822
- console.log(JSON.stringify(result, null, 2));
823
- }
824
-
825
- async function main(argv) {
826
- try {
827
- const options = parseArgs(argv);
828
- if (options.help || options.command === 'help') {
829
- printUsage();
830
- return 0;
831
- }
832
-
833
- let result;
834
- switch (options.command) {
835
- case 'login':
836
- result = login(options);
837
- break;
838
- case 'search':
839
- result = await search(options);
840
- break;
841
- case 'install':
842
- result = await install(options);
843
- break;
844
- case 'sync':
845
- result = await sync(options);
846
- break;
847
- case 'diff':
848
- result = await diff(options);
849
- break;
850
- case 'upgrade':
851
- result = await upgrade(options);
852
- break;
853
- case 'rollback':
854
- result = await rollback(options);
855
- break;
856
- case 'runtime-report':
857
- result = await runtimeReport(options);
858
- break;
859
- default:
860
- throw new Error(`未知 hub 命令:${options.command}`);
861
- }
862
-
863
- if (options.json) process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
864
- else printPretty(result);
865
- return 0;
866
- } catch (error) {
867
- console.error(`hub(方案包)失败:${error.message}`);
868
- return 1;
869
- }
870
- }
871
-
872
- module.exports = { parseArgs, install, diff, sync, upgrade, rollback, runtimeReport, main };
873
-
874
- if (require.main === module) {
875
- process.exit(main(process.argv.slice(2)));
876
- }
1
+ const crypto = require('crypto');
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+ const {
5
+ fetchManifestExport,
6
+ normalizeOrigin,
7
+ postInstallReport,
8
+ postRuntimeReport,
9
+ requestJson,
10
+ writeHubToken,
11
+ } = require('../internal/hub-client');
12
+
13
+ const REGISTRY_LOCK_FILE = path.join('.agents', 'registry', 'hub-lock.json');
14
+ const LEGACY_LOCK_FILE = 'hub-lock.json';
15
+ const REGISTRY_MANIFEST_FILE = path.join('.agents', 'registry', 'manifest.json');
16
+ const HISTORY_ROOT = path.join('.ai-spec', 'history', 'hub-install');
17
+
18
+ const ALLOWED_INSTALL_PREFIXES = [
19
+ '.agents/rules/',
20
+ '.agents/skills/',
21
+ '.agents/roles/',
22
+ '.agents/flows/',
23
+ '.agents/registry/',
24
+ '.cursor/',
25
+ '.claude/',
26
+ 'openspec/',
27
+ '.ai-spec/config/',
28
+ '.ai-spec/history/',
29
+ ];
30
+
31
+ const BLOCKED_INSTALL_PATHS = new Set([
32
+ 'package.json',
33
+ '.env',
34
+ '.env.local',
35
+ ]);
36
+
37
+ function parseArgs(argv) {
38
+ const args = [...argv];
39
+ const command = args.shift() || 'help';
40
+ const options = {
41
+ command,
42
+ target: '.',
43
+ origin: process.env.AI_SPEC_HUB_ORIGIN || '',
44
+ dryRun: false,
45
+ allowHighRisk: false,
46
+ json: false,
47
+ force: false,
48
+ yes: false,
49
+ version: '',
50
+ rollbackVersion: '',
51
+ manifestId: '',
52
+ token: '',
53
+ kind: '',
54
+ mode: 'standard',
55
+ profile: '',
56
+ ide: '',
57
+ runId: '',
58
+ stage: 'review',
59
+ status: 'success',
60
+ durationMs: 0,
61
+ failedReason: '',
62
+ repoUrl: '',
63
+ };
64
+
65
+ if (command === '--help' || command === '-h') {
66
+ options.command = 'help';
67
+ options.help = true;
68
+ return options;
69
+ }
70
+
71
+ while (args.length > 0) {
72
+ const arg = args.shift();
73
+ switch (arg) {
74
+ case '--hub':
75
+ case '--hub-origin':
76
+ case '--origin':
77
+ options.origin = requireArg(arg, args);
78
+ break;
79
+ case '--target':
80
+ options.target = requireArg(arg, args);
81
+ break;
82
+ case '--version':
83
+ options.version = requireArg(arg, args);
84
+ break;
85
+ case '--token':
86
+ options.token = requireArg(arg, args);
87
+ break;
88
+ case '--kind':
89
+ options.kind = requireArg(arg, args);
90
+ break;
91
+ case '--mode':
92
+ options.mode = requireArg(arg, args);
93
+ break;
94
+ case '--profile':
95
+ options.profile = requireArg(arg, args);
96
+ break;
97
+ case '--ide':
98
+ options.ide = requireArg(arg, args);
99
+ break;
100
+ case '--run-id':
101
+ options.runId = requireArg(arg, args);
102
+ break;
103
+ case '--stage':
104
+ options.stage = requireArg(arg, args);
105
+ break;
106
+ case '--status':
107
+ options.status = requireArg(arg, args);
108
+ break;
109
+ case '--duration-ms':
110
+ options.durationMs = Number(requireArg(arg, args));
111
+ break;
112
+ case '--failed-reason':
113
+ options.failedReason = requireArg(arg, args);
114
+ break;
115
+ case '--repo-url':
116
+ options.repoUrl = requireArg(arg, args);
117
+ break;
118
+ case '--dry-run':
119
+ options.dryRun = true;
120
+ break;
121
+ case '--allow-high-risk':
122
+ options.allowHighRisk = true;
123
+ break;
124
+ case '--json':
125
+ options.json = true;
126
+ break;
127
+ case '--force':
128
+ options.force = true;
129
+ break;
130
+ case '--yes':
131
+ case '-y':
132
+ options.yes = true;
133
+ break;
134
+ case '--help':
135
+ case '-h':
136
+ options.help = true;
137
+ break;
138
+ default:
139
+ assignPositional(options, arg);
140
+ }
141
+ }
142
+ return options;
143
+ }
144
+
145
+ function assignPositional(options, arg) {
146
+ if (options.command === 'rollback') {
147
+ if (!options.rollbackVersion && isSemver(arg)) {
148
+ options.rollbackVersion = arg;
149
+ return;
150
+ }
151
+ if (options.target === '.') {
152
+ options.target = arg;
153
+ return;
154
+ }
155
+ }
156
+
157
+ if (options.command === 'upgrade' && !options.version && isSemver(arg)) {
158
+ options.version = arg;
159
+ return;
160
+ }
161
+
162
+ if (['diff', 'sync', 'upgrade', 'runtime-report'].includes(options.command) && options.target === '.') {
163
+ options.target = arg;
164
+ return;
165
+ }
166
+
167
+ if (!options.manifestId) {
168
+ options.manifestId = arg;
169
+ return;
170
+ }
171
+
172
+ if (options.target === '.') {
173
+ options.target = arg;
174
+ return;
175
+ }
176
+
177
+ throw new Error(`未知参数:${arg}`);
178
+ }
179
+
180
+ function requireArg(flag, args) {
181
+ const next = args.shift();
182
+ if (!next || next.startsWith('--')) {
183
+ throw new Error(`选项 ${flag} 需要一个参数值`);
184
+ }
185
+ return next;
186
+ }
187
+
188
+ function isSemver(value) {
189
+ return /^\d+\.\d+\.\d+$/.test(String(value || ''));
190
+ }
191
+
192
+ function ensureDir(dirPath) {
193
+ fs.mkdirSync(dirPath, { recursive: true });
194
+ }
195
+
196
+ function readJson(filePath) {
197
+ return JSON.parse(fs.readFileSync(filePath, 'utf8'));
198
+ }
199
+
200
+ function writeJson(filePath, value) {
201
+ ensureDir(path.dirname(filePath));
202
+ fs.writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`, 'utf8');
203
+ }
204
+
205
+ function sha256Text(text) {
206
+ return crypto.createHash('sha256').update(text).digest('hex');
207
+ }
208
+
209
+ function readHubConfig(targetDir) {
210
+ const filePath = path.join(targetDir, '.ai-spec', 'config', 'hub.json');
211
+ if (!fs.existsSync(filePath)) return null;
212
+ try {
213
+ return readJson(filePath);
214
+ } catch (_error) {
215
+ return null;
216
+ }
217
+ }
218
+
219
+ function resolveOrigin(options, targetDir) {
220
+ const fromConfig = readHubConfig(targetDir);
221
+ return normalizeOrigin(options.origin || fromConfig?.hub || fromConfig?.baseUrl || 'http://localhost:3000');
222
+ }
223
+
224
+ function normalizeExport(payload) {
225
+ if (!payload || typeof payload !== 'object') {
226
+ throw new Error('Hub Export 响应为空');
227
+ }
228
+ const data = payload.data && payload.success !== undefined ? payload.data : payload;
229
+ if (data.contractVersion && data.contractVersion !== '1.0.0') {
230
+ throw new Error(`不支持的 Hub 契约版本:${data.contractVersion}`);
231
+ }
232
+ if (!data.contractVersion) {
233
+ throw new Error('Hub Export 缺少 contractVersion(契约版本)');
234
+ }
235
+ if (!data?.manifest?.id || !data.version || !Array.isArray(data.assets)) {
236
+ throw new Error('Hub Export 响应缺少 manifest、version 或 assets');
237
+ }
238
+ if (data.manifest.status && data.manifest.status !== 'published') {
239
+ throw new Error(`Manifest 未发布,当前状态:${data.manifest.status}`);
240
+ }
241
+
242
+ const filesByKey = new Map();
243
+ for (const file of Array.isArray(data.files) ? data.files : []) {
244
+ if (file && typeof file === 'object') {
245
+ filesByKey.set(file.assetId || file.path, file);
246
+ if (file.path) filesByKey.set(file.path, file);
247
+ }
248
+ }
249
+
250
+ return {
251
+ ...data,
252
+ assets: data.assets.map((asset) => {
253
+ const file = filesByKey.get(asset.assetId) || filesByKey.get(asset.installPath || asset.path);
254
+ return {
255
+ ...asset,
256
+ content: asset.content ?? file?.content,
257
+ contentFormat: asset.contentFormat ?? file?.contentFormat,
258
+ };
259
+ }),
260
+ };
261
+ }
262
+
263
+ function registryLockPath(targetDir) {
264
+ return path.join(targetDir, REGISTRY_LOCK_FILE);
265
+ }
266
+
267
+ function legacyLockPath(targetDir) {
268
+ return path.join(targetDir, LEGACY_LOCK_FILE);
269
+ }
270
+
271
+ function findLockPath(targetDir) {
272
+ const registry = registryLockPath(targetDir);
273
+ if (fs.existsSync(registry)) return registry;
274
+ const legacy = legacyLockPath(targetDir);
275
+ if (fs.existsSync(legacy)) return legacy;
276
+ return null;
277
+ }
278
+
279
+ function normalizeLock(raw) {
280
+ if (!raw || typeof raw !== 'object') return null;
281
+ const assets = Array.isArray(raw.assets)
282
+ ? raw.assets.map((asset) => ({
283
+ kind: String(asset.kind || 'unknown'),
284
+ assetId: String(asset.assetId || asset.slug || asset.id || ''),
285
+ slug: String(asset.slug || asset.assetId || asset.id || ''),
286
+ version: String(asset.version || ''),
287
+ required: asset.required !== false,
288
+ checksum: String(asset.checksum || ''),
289
+ installPath: String(asset.installPath || asset.path || ''),
290
+ path: String(asset.path || asset.installPath || ''),
291
+ riskLevel: String(asset.riskLevel || 'L0'),
292
+ currentChecksum: typeof asset.currentChecksum === 'string' ? asset.currentChecksum : null,
293
+ }))
294
+ : [];
295
+ return {
296
+ raw,
297
+ hubBaseUrl: typeof raw.hub === 'string' ? raw.hub : String(raw.hub?.baseUrl || raw.hub?.url || ''),
298
+ manifestId: String(raw.manifest?.id || raw.manifestId || ''),
299
+ manifestSlug: String(raw.manifest?.slug || raw.manifestId || raw.manifest?.id || ''),
300
+ manifestVersion: String(raw.manifest?.version || raw.manifestVersion || ''),
301
+ manifestChecksum: String(raw.manifest?.checksum || raw.manifestChecksum || ''),
302
+ installMode: String(raw.install?.mode || raw.mode || ''),
303
+ installedAt: String(raw.install?.installedAt || raw.installedAt || ''),
304
+ assets,
305
+ };
306
+ }
307
+
308
+ function readLockRecord(targetDir) {
309
+ const filePath = findLockPath(targetDir);
310
+ if (!filePath) return null;
311
+ try {
312
+ const raw = readJson(filePath);
313
+ return { filePath, raw, lock: normalizeLock(raw) };
314
+ } catch (_error) {
315
+ return null;
316
+ }
317
+ }
318
+
319
+ function resolveInstallPath(asset) {
320
+ const relPath = asset.installPath || asset.path;
321
+ if (relPath) return normalizeRelativePath(relPath);
322
+ return `.agents/registry/${asset.kind}/${asset.assetId || asset.slug}.json`;
323
+ }
324
+
325
+ function normalizeRelativePath(relPath) {
326
+ const normalized = path.posix.normalize(String(relPath).replace(/\\/g, '/'));
327
+ if (path.isAbsolute(relPath) || normalized === '..' || normalized.startsWith('../')) {
328
+ throw new Error(`安装路径非法:${relPath}`);
329
+ }
330
+ return normalized;
331
+ }
332
+
333
+ function assertAllowedInstallPath(relPath) {
334
+ const normalized = normalizeRelativePath(relPath);
335
+ if (
336
+ BLOCKED_INSTALL_PATHS.has(normalized) ||
337
+ normalized.startsWith('src/') ||
338
+ normalized.startsWith('node_modules/') ||
339
+ normalized.startsWith('.git/')
340
+ ) {
341
+ throw new Error(`安装路径被禁止:${relPath}`);
342
+ }
343
+ if (!ALLOWED_INSTALL_PREFIXES.some((prefix) => normalized.startsWith(prefix))) {
344
+ throw new Error(`安装路径不在允许范围内:${relPath}`);
345
+ }
346
+ return normalized;
347
+ }
348
+
349
+ async function fetchAssetContent(asset) {
350
+ if (typeof asset.content === 'string') {
351
+ return asset.content;
352
+ }
353
+ if (asset.contentUrl) {
354
+ const response = await fetch(asset.contentUrl);
355
+ if (!response.ok) {
356
+ throw new Error(`资产下载失败:${asset.assetId || asset.slug} (${response.status})`);
357
+ }
358
+ return response.text();
359
+ }
360
+ throw new Error(`资产 ${asset.assetId || asset.slug} 缺少 content 或 contentUrl`);
361
+ }
362
+
363
+ function verifyAssetChecksum(asset, content) {
364
+ const expected = String(asset.checksum || '');
365
+ if (!expected) return;
366
+ const actual = sha256Text(content);
367
+ if (expected.startsWith('sha256:') && expected !== `sha256:${actual}`) {
368
+ throw new Error(`资产 ${asset.assetId || asset.slug} checksum 校验失败`);
369
+ }
370
+ if (/^[a-f0-9]{64}$/i.test(expected) && expected.toLowerCase() !== actual) {
371
+ throw new Error(`资产 ${asset.assetId || asset.slug} checksum 校验失败`);
372
+ }
373
+ }
374
+
375
+ function readInstalledChecksum(targetDir, relPath) {
376
+ const filePath = path.join(targetDir, relPath);
377
+ if (!fs.existsSync(filePath)) return null;
378
+ return sha256Text(fs.readFileSync(filePath, 'utf8'));
379
+ }
380
+
381
+ function assertRiskAllowed(exportPayload, options) {
382
+ const l4 = exportPayload.assets.find((asset) => asset.riskLevel === 'L4');
383
+ if (l4) throw new Error(`资产 ${l4.assetId || l4.slug} 风险等级为 L4,禁止安装。`);
384
+ const high = exportPayload.assets.find((asset) => asset.riskLevel === 'L3');
385
+ if (high && !options.allowHighRisk && !options.yes) {
386
+ throw new Error(`资产 ${high.assetId || high.slug} 风险等级为 L3,请显式添加 --allow-high-risk 或 --yes。`);
387
+ }
388
+ }
389
+
390
+ function classifyUpgrade(current, next) {
391
+ const [currentMajor, currentMinor, currentPatch] = String(current || '0.0.0').split('.').map(Number);
392
+ const [nextMajor, nextMinor, nextPatch] = String(next || '0.0.0').split('.').map(Number);
393
+ if (nextMajor !== currentMajor) return 'major';
394
+ if (nextMinor !== currentMinor) return 'minor';
395
+ if (nextPatch !== currentPatch) return 'patch';
396
+ return 'same';
397
+ }
398
+
399
+ function compareSemver(current, next) {
400
+ const a = String(current || '0.0.0').split('.').map(Number);
401
+ const b = String(next || '0.0.0').split('.').map(Number);
402
+ for (let index = 0; index < 3; index += 1) {
403
+ const diff = (b[index] || 0) - (a[index] || 0);
404
+ if (diff !== 0) return diff;
405
+ }
406
+ return 0;
407
+ }
408
+
409
+ function buildLock({ origin, exportPayload, mode, targetDir, options }) {
410
+ const now = new Date().toISOString();
411
+ const assets = exportPayload.assets.map((asset) => {
412
+ const relPath = resolveInstallPath(asset);
413
+ return {
414
+ kind: asset.kind,
415
+ id: asset.assetId || asset.slug,
416
+ slug: asset.slug || asset.assetId,
417
+ assetId: asset.assetId || asset.slug,
418
+ version: asset.version,
419
+ required: asset.required !== false,
420
+ path: relPath,
421
+ installPath: relPath,
422
+ checksum: asset.checksum,
423
+ currentChecksum: readInstalledChecksum(targetDir, relPath),
424
+ riskLevel: asset.riskLevel || 'L0',
425
+ };
426
+ });
427
+ return {
428
+ lockVersion: '1.0.0',
429
+ hub: {
430
+ baseUrl: normalizeOrigin(origin),
431
+ name: 'Xia Qiu Hub',
432
+ },
433
+ manifest: {
434
+ id: exportPayload.manifest.id,
435
+ slug: exportPayload.manifest.slug || exportPayload.manifest.id,
436
+ version: exportPayload.version,
437
+ checksum: exportPayload.checksum,
438
+ },
439
+ install: {
440
+ mode,
441
+ cliVersion: readCliVersion(),
442
+ installedAt: now,
443
+ profile: options.profile || undefined,
444
+ ide: options.ide || undefined,
445
+ },
446
+ assets,
447
+
448
+ // 兼容旧版 Visual / 脚本读取,主协议仍以上面的嵌套结构为准。
449
+ manifestId: exportPayload.manifest.id,
450
+ manifestVersion: exportPayload.version,
451
+ manifestChecksum: exportPayload.checksum,
452
+ installedAt: now,
453
+ mode,
454
+ };
455
+ }
456
+
457
+ function readCliVersion() {
458
+ try {
459
+ return require('../package.json').version || '0.0.0';
460
+ } catch (_error) {
461
+ return '0.0.0';
462
+ }
463
+ }
464
+
465
+ function createHistorySnapshot(targetDir, previous, changed) {
466
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
467
+ const snapshotDir = path.join(targetDir, HISTORY_ROOT, timestamp);
468
+ const backupDir = path.join(snapshotDir, 'backup');
469
+ const changedFiles = [];
470
+ ensureDir(backupDir);
471
+
472
+ for (const item of changed) {
473
+ const source = path.join(targetDir, item.relPath);
474
+ const backup = path.join(backupDir, item.relPath);
475
+ const existed = fs.existsSync(source);
476
+ if (existed) {
477
+ ensureDir(path.dirname(backup));
478
+ fs.copyFileSync(source, backup);
479
+ }
480
+ changedFiles.push({ path: item.relPath, existed });
481
+ }
482
+
483
+ const lockFile = findLockPath(targetDir);
484
+ const lockBackup = path.join(backupDir, REGISTRY_LOCK_FILE);
485
+ if (lockFile && fs.existsSync(lockFile)) {
486
+ ensureDir(path.dirname(lockBackup));
487
+ fs.copyFileSync(lockFile, lockBackup);
488
+ }
489
+
490
+ writeJson(path.join(snapshotDir, 'snapshot.json'), {
491
+ createdAt: new Date().toISOString(),
492
+ fromVersion: previous?.lock?.manifestVersion || null,
493
+ manifestId: previous?.lock?.manifestId || previous?.lock?.manifestSlug || null,
494
+ lockExisted: Boolean(lockFile),
495
+ changedFiles,
496
+ });
497
+ return { snapshotDir, backupDir };
498
+ }
499
+
500
+ function assertNoLocalConflicts(targetDir, plan, previous, options) {
501
+ if (options.force) return;
502
+ const localByKey = new Map((previous?.lock?.assets || []).map((asset) => [`${asset.kind}:${asset.assetId}`, asset]));
503
+ for (const item of plan) {
504
+ const filePath = path.join(targetDir, item.relPath);
505
+ if (!fs.existsSync(filePath)) continue;
506
+ const actual = sha256Text(fs.readFileSync(filePath, 'utf8'));
507
+ const local = localByKey.get(`${item.asset.kind}:${item.asset.assetId || item.asset.slug}`);
508
+ if (local?.checksum && actual !== local.checksum && actual !== item.asset.checksum) {
509
+ throw new Error(`检测到本地文件冲突:${item.relPath},请先处理或使用 --force。`);
510
+ }
511
+ }
512
+ }
513
+
514
+ async function install(options) {
515
+ if (!options.manifestId) throw new Error('缺少 Manifest ID');
516
+ const targetDir = path.resolve(options.target);
517
+ const origin = resolveOrigin(options, targetDir);
518
+ const exportPayload = normalizeExport(await fetchManifestExport({
519
+ origin,
520
+ manifestId: options.manifestId,
521
+ version: options.version || undefined,
522
+ }));
523
+ assertRiskAllowed(exportPayload, options);
524
+
525
+ const plan = exportPayload.assets.map((asset) => ({
526
+ asset,
527
+ relPath: assertAllowedInstallPath(resolveInstallPath(asset)),
528
+ filePath: path.join(targetDir, assertAllowedInstallPath(resolveInstallPath(asset))),
529
+ }));
530
+ const previous = readLockRecord(targetDir);
531
+ assertNoLocalConflicts(targetDir, plan, previous, options);
532
+
533
+ const skipped = plan.filter((item) => {
534
+ const actual = readInstalledChecksum(targetDir, item.relPath);
535
+ return actual && actual === item.asset.checksum;
536
+ });
537
+ const changed = plan.filter((item) => !skipped.includes(item));
538
+
539
+ if (previous?.lock?.manifestChecksum === exportPayload.checksum && changed.length === 0) {
540
+ return {
541
+ manifestId: exportPayload.manifest.id,
542
+ version: exportPayload.version,
543
+ message: '已是最新,无需重复安装',
544
+ written: [],
545
+ skipped: skipped.length,
546
+ };
547
+ }
548
+
549
+ if (options.dryRun) {
550
+ return {
551
+ dryRun: true,
552
+ manifestId: exportPayload.manifest.id,
553
+ version: exportPayload.version,
554
+ createOrUpdate: changed.map((item) => item.relPath),
555
+ skipped: skipped.map((item) => item.relPath),
556
+ warnings: exportPayload.assets.filter((asset) => asset.riskLevel === 'L3').map((asset) => `${asset.assetId || asset.slug} 为 L3 高风险资产`),
557
+ };
558
+ }
559
+
560
+ const snapshot = createHistorySnapshot(targetDir, previous, changed);
561
+ const written = [];
562
+ try {
563
+ for (const item of changed) {
564
+ let content = '';
565
+ try {
566
+ content = await fetchAssetContent(item.asset);
567
+ verifyAssetChecksum(item.asset, content);
568
+ } catch (error) {
569
+ if (item.asset.required !== false) throw error;
570
+ console.warn(`警告:可选资产 ${item.asset.assetId || item.asset.slug} 下载失败,已跳过。`);
571
+ continue;
572
+ }
573
+ ensureDir(path.dirname(item.filePath));
574
+ fs.writeFileSync(item.filePath, content, 'utf8');
575
+ written.push(item.relPath);
576
+ }
577
+
578
+ ensureDir(path.join(targetDir, '.agents', 'registry'));
579
+ const lock = buildLock({ origin, exportPayload, mode: options.mode, targetDir, options });
580
+ writeJson(registryLockPath(targetDir), lock);
581
+ writeJson(path.join(targetDir, REGISTRY_MANIFEST_FILE), exportPayload);
582
+
583
+ await postInstallReport({
584
+ origin,
585
+ report: {
586
+ projectName: path.basename(targetDir),
587
+ manifestId: lock.manifest.id,
588
+ manifestVersion: lock.manifest.version,
589
+ installMode: lock.install.mode,
590
+ status: 'success',
591
+ assets: lock.assets,
592
+ message: '安装成功',
593
+ },
594
+ }).catch((error) => {
595
+ console.warn(`警告:安装记录上报失败,已继续:${error.message}`);
596
+ });
597
+
598
+ return {
599
+ dryRun: false,
600
+ manifestId: lock.manifest.id,
601
+ version: lock.manifest.version,
602
+ lockFile: REGISTRY_LOCK_FILE,
603
+ snapshot: path.relative(targetDir, snapshot.snapshotDir),
604
+ written,
605
+ skipped: skipped.length,
606
+ };
607
+ } catch (error) {
608
+ for (const relPath of written) {
609
+ try {
610
+ fs.rmSync(path.join(targetDir, relPath), { force: true });
611
+ } catch (_rollbackError) {
612
+ // 回滚失败不覆盖原始错误。
613
+ }
614
+ }
615
+ throw new Error(`安装失败,已回滚已写入文件:${error.message}`);
616
+ }
617
+ }
618
+
619
+ async function diff(options) {
620
+ const targetDir = path.resolve(options.target);
621
+ const previous = readLockRecord(targetDir);
622
+ if (!previous?.lock) throw new Error('未找到 .agents/registry/hub-lock.json,请先执行 hub install。');
623
+ const origin = resolveOrigin(options, targetDir);
624
+ const exportPayload = normalizeExport(await fetchManifestExport({
625
+ origin,
626
+ manifestId: options.manifestId || previous.lock.manifestSlug || previous.lock.manifestId,
627
+ version: options.version || undefined,
628
+ }));
629
+ const remote = new Map(exportPayload.assets.map((asset) => [`${asset.kind}:${asset.assetId || asset.slug}`, asset]));
630
+ const local = new Map((previous.lock.assets || []).map((asset) => [`${asset.kind}:${asset.assetId}`, asset]));
631
+ const changes = [];
632
+
633
+ for (const [key, asset] of remote) {
634
+ const current = local.get(key);
635
+ const relPath = resolveInstallPath(asset);
636
+ const filePath = path.join(targetDir, relPath);
637
+ if (!current) {
638
+ changes.push({ type: 'missing', asset });
639
+ continue;
640
+ }
641
+ if (!fs.existsSync(filePath)) {
642
+ changes.push({ type: 'missing', asset: current });
643
+ continue;
644
+ }
645
+ const actual = sha256Text(fs.readFileSync(filePath, 'utf8'));
646
+ if (current.checksum && actual !== current.checksum && actual !== asset.checksum) {
647
+ changes.push({ type: 'modified', asset: current, localChecksum: actual });
648
+ }
649
+ if (compareSemver(current.version, asset.version) > 0 || current.checksum !== asset.checksum) {
650
+ changes.push({ type: 'outdated', from: current, to: asset });
651
+ }
652
+ }
653
+
654
+ for (const [key, asset] of local) {
655
+ if (!remote.has(key)) changes.push({ type: 'extra', asset });
656
+ }
657
+
658
+ return {
659
+ manifestId: exportPayload.manifest.id,
660
+ localVersion: previous.lock.manifestVersion,
661
+ remoteVersion: exportPayload.version,
662
+ changes,
663
+ };
664
+ }
665
+
666
+ async function sync(options) {
667
+ const targetDir = path.resolve(options.target);
668
+ const previous = readLockRecord(targetDir);
669
+ if (!previous?.lock && !options.manifestId) throw new Error('未找到 .agents/registry/hub-lock.json,请提供 Manifest ID。');
670
+ const origin = resolveOrigin(options, targetDir);
671
+ const manifestId = options.manifestId || previous.lock.manifestSlug || previous.lock.manifestId;
672
+ const latest = normalizeExport(await fetchManifestExport({ origin, manifestId }));
673
+ const upgradeType = classifyUpgrade(previous?.lock?.manifestVersion, latest.version);
674
+ if (upgradeType === 'same') {
675
+ return install({ ...options, origin, manifestId, version: latest.version });
676
+ }
677
+ if ((upgradeType === 'minor' || upgradeType === 'major') && !options.yes && !options.force) {
678
+ throw new Error(`检测到 ${upgradeType} 升级:${previous.lock.manifestVersion} -> ${latest.version},请添加 --yes 确认。`);
679
+ }
680
+ return install({ ...options, origin, manifestId, version: latest.version });
681
+ }
682
+
683
+ async function upgrade(options) {
684
+ const targetDir = path.resolve(options.target);
685
+ const previous = readLockRecord(targetDir);
686
+ if (!previous?.lock && !options.manifestId) throw new Error('未找到 .agents/registry/hub-lock.json,请提供 Manifest ID。');
687
+ const origin = resolveOrigin(options, targetDir);
688
+ const manifestId = options.manifestId || previous.lock.manifestSlug || previous.lock.manifestId;
689
+ const target = normalizeExport(await fetchManifestExport({ origin, manifestId, version: options.version || undefined }));
690
+ const upgradeType = classifyUpgrade(previous?.lock?.manifestVersion, target.version);
691
+ if (upgradeType === 'major' && !options.yes && !options.force) {
692
+ throw new Error(`检测到 major 升级:${previous.lock.manifestVersion} -> ${target.version},请添加 --yes 确认。`);
693
+ }
694
+ return install({ ...options, origin, manifestId, version: target.version });
695
+ }
696
+
697
+ async function rollback(options) {
698
+ const targetDir = path.resolve(options.target);
699
+ const backupsRoot = path.join(targetDir, HISTORY_ROOT);
700
+ if (!fs.existsSync(backupsRoot)) return { restored: [], message: '没有可回滚的备份' };
701
+ const snapshots = fs.readdirSync(backupsRoot).sort().reverse();
702
+ let selected = null;
703
+ for (const name of snapshots) {
704
+ const snapshotPath = path.join(backupsRoot, name, 'snapshot.json');
705
+ if (!fs.existsSync(snapshotPath)) continue;
706
+ const snapshot = readJson(snapshotPath);
707
+ if (!options.rollbackVersion || snapshot.fromVersion === options.rollbackVersion) {
708
+ selected = { name, snapshot, backupDir: path.join(backupsRoot, name, 'backup') };
709
+ break;
710
+ }
711
+ }
712
+ if (!selected) throw new Error(`未找到可回滚版本:${options.rollbackVersion}`);
713
+
714
+ const restored = [];
715
+ for (const item of selected.snapshot.changedFiles || []) {
716
+ const dest = path.join(targetDir, item.path);
717
+ const backup = path.join(selected.backupDir, item.path);
718
+ if (item.existed && fs.existsSync(backup)) {
719
+ ensureDir(path.dirname(dest));
720
+ fs.copyFileSync(backup, dest);
721
+ restored.push(item.path);
722
+ } else if (!item.existed) {
723
+ fs.rmSync(dest, { force: true });
724
+ restored.push(item.path);
725
+ }
726
+ }
727
+
728
+ const lockBackup = path.join(selected.backupDir, REGISTRY_LOCK_FILE);
729
+ if (selected.snapshot.lockExisted && fs.existsSync(lockBackup)) {
730
+ ensureDir(path.dirname(registryLockPath(targetDir)));
731
+ fs.copyFileSync(lockBackup, registryLockPath(targetDir));
732
+ restored.push(REGISTRY_LOCK_FILE);
733
+ } else if (!selected.snapshot.lockExisted) {
734
+ fs.rmSync(registryLockPath(targetDir), { force: true });
735
+ }
736
+
737
+ return { restored, backup: selected.name, version: selected.snapshot.fromVersion || null };
738
+ }
739
+
740
+ async function search(options) {
741
+ const q = options.manifestId || '';
742
+ const origin = resolveOrigin(options, path.resolve(options.target));
743
+ const params = new URLSearchParams();
744
+ if (q) params.set('q', q);
745
+ if (options.kind) params.set('kind', options.kind);
746
+ return requestJson(`${normalizeOrigin(origin)}/api/hub/search?${params.toString()}`);
747
+ }
748
+
749
+ async function runtimeReport(options) {
750
+ const targetDir = path.resolve(options.target);
751
+ const previous = readLockRecord(targetDir);
752
+ if (!previous?.lock) throw new Error('未找到 .agents/registry/hub-lock.json,请先执行 hub install。');
753
+
754
+ const allowedStages = new Set(['requirement', 'design', 'implement', 'test', 'review', 'archive']);
755
+ const allowedStatuses = new Set(['success', 'failed', 'partial']);
756
+ if (!allowedStages.has(options.stage)) {
757
+ throw new Error(`不支持的 stage:${options.stage}`);
758
+ }
759
+ if (!allowedStatuses.has(options.status)) {
760
+ throw new Error(`不支持的 status:${options.status}`);
761
+ }
762
+ if (!Number.isFinite(options.durationMs) || options.durationMs < 0) {
763
+ throw new Error('--duration-ms 必须是非负数字');
764
+ }
765
+
766
+ const origin = resolveOrigin(options, targetDir);
767
+ const report = {
768
+ projectName: path.basename(targetDir),
769
+ repoUrl: options.repoUrl || undefined,
770
+ manifestId: previous.lock.manifestId || previous.lock.manifestSlug || undefined,
771
+ manifestVersion: previous.lock.manifestVersion || undefined,
772
+ runId: options.runId || `manual-${Date.now()}`,
773
+ stage: options.stage,
774
+ status: options.status,
775
+ usedAssets: (previous.lock.assets || []).map((asset) => ({
776
+ kind: asset.kind,
777
+ assetId: asset.assetId || asset.slug,
778
+ version: asset.version,
779
+ })),
780
+ durationMs: Math.round(options.durationMs),
781
+ failedReason: options.failedReason || undefined,
782
+ };
783
+
784
+ const response = await postRuntimeReport({ origin, report });
785
+ return {
786
+ reported: true,
787
+ manifestId: report.manifestId,
788
+ manifestVersion: report.manifestVersion,
789
+ runId: report.runId,
790
+ usedAssetCount: report.usedAssets.length,
791
+ response,
792
+ };
793
+ }
794
+
795
+ function login(options) {
796
+ const targetDir = path.resolve(options.target);
797
+ const origin = resolveOrigin(options, targetDir);
798
+ const configPath = path.join(targetDir, '.ai-spec', 'config', 'hub.json');
799
+ writeJson(configPath, {
800
+ hub: origin,
801
+ tokenSource: options.token ? 'config' : 'env-or-config',
802
+ loginAt: new Date().toISOString(),
803
+ });
804
+ const tokenFile = options.token ? writeHubToken(options.token) : null;
805
+ return { configFile: path.relative(targetDir, configPath), tokenFile, message: `Hub 登录成功:${origin}` };
806
+ }
807
+
808
+ function printUsage() {
809
+ console.log(`用法:
810
+ ai-spec-auto hub login --hub <url> [--token <token>]
811
+ ai-spec-auto hub search <关键词> [--kind manifest] [--hub <url>]
812
+ ai-spec-auto hub install <manifest-id> [target] [--hub <url>] [--mode standard] [--profile react] [--ide cursor] [--dry-run] [--force] [--yes]
813
+ ai-spec-auto hub sync [target] [--yes]
814
+ ai-spec-auto hub diff [target]
815
+ ai-spec-auto hub upgrade [target] [--version <version>] [--yes]
816
+ ai-spec-auto hub rollback [version] [target]
817
+ ai-spec-auto hub runtime-report [target] [--run-id <id>] [--stage review] [--status success] [--duration-ms 0] [--failed-reason <reason>]
818
+ `);
819
+ }
820
+
821
+ function printPretty(result) {
822
+ console.log(JSON.stringify(result, null, 2));
823
+ }
824
+
825
+ async function main(argv) {
826
+ try {
827
+ const options = parseArgs(argv);
828
+ if (options.help || options.command === 'help') {
829
+ printUsage();
830
+ return 0;
831
+ }
832
+
833
+ let result;
834
+ switch (options.command) {
835
+ case 'login':
836
+ result = login(options);
837
+ break;
838
+ case 'search':
839
+ result = await search(options);
840
+ break;
841
+ case 'install':
842
+ result = await install(options);
843
+ break;
844
+ case 'sync':
845
+ result = await sync(options);
846
+ break;
847
+ case 'diff':
848
+ result = await diff(options);
849
+ break;
850
+ case 'upgrade':
851
+ result = await upgrade(options);
852
+ break;
853
+ case 'rollback':
854
+ result = await rollback(options);
855
+ break;
856
+ case 'runtime-report':
857
+ result = await runtimeReport(options);
858
+ break;
859
+ default:
860
+ throw new Error(`未知 hub 命令:${options.command}`);
861
+ }
862
+
863
+ if (options.json) process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
864
+ else printPretty(result);
865
+ return 0;
866
+ } catch (error) {
867
+ console.error(`hub(方案包)失败:${error.message}`);
868
+ return 1;
869
+ }
870
+ }
871
+
872
+ module.exports = { parseArgs, install, diff, sync, upgrade, rollback, runtimeReport, main };
873
+
874
+ if (require.main === module) {
875
+ process.exit(main(process.argv.slice(2)));
876
+ }