add-coder 0.3.26 → 0.3.28

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 (323) hide show
  1. package/README.md +282 -155
  2. package/dist/index.js +481 -209
  3. package/package.json +15 -4
  4. package/templates/.add-coder-src-hash.json +186 -185
  5. package/templates/adapters/claude/hooks/doc-format-guard.ts +32 -0
  6. package/templates/adapters/claude/hooks/lib/claude-env.ts +7 -0
  7. package/templates/adapters/claude/hooks/notification.ts +24 -0
  8. package/templates/adapters/claude/hooks/permission-denied.ts +33 -0
  9. package/templates/adapters/claude/hooks/permission-gate.ts +12 -0
  10. package/templates/adapters/claude/hooks/post-tool-failure.ts +4 -0
  11. package/templates/adapters/claude/hooks/post-tool-use.ts +17 -0
  12. package/templates/adapters/claude/hooks/pre-compact.ts +10 -0
  13. package/templates/adapters/claude/hooks/pre-tool-use.ts +81 -0
  14. package/templates/adapters/claude/hooks/prompt-submit.ts +82 -0
  15. package/templates/adapters/claude/hooks/review-checklist.ts +4 -0
  16. package/templates/adapters/claude/hooks/session-end.ts +10 -0
  17. package/templates/adapters/claude/hooks/session-start.ts +24 -0
  18. package/templates/adapters/claude/hooks/stop-check.ts +15 -0
  19. package/templates/adapters/claude/hooks/stop-failure.ts +34 -0
  20. package/templates/adapters/claude/hooks/subagent-guard.ts +25 -0
  21. package/templates/adapters/claude/hooks/subagent-stop.ts +12 -0
  22. package/templates/adapters/claude/settings.json +13 -13
  23. package/templates/adapters/codex/config.toml.example +12 -0
  24. package/templates/adapters/codex/hooks/doc-format-guard.ts +30 -0
  25. package/templates/adapters/codex/hooks/notification.ts +26 -0
  26. package/templates/adapters/codex/hooks/permission-gate.ts +15 -0
  27. package/templates/adapters/codex/hooks/post-tool-failure.ts +14 -0
  28. package/templates/adapters/codex/hooks/post-tool-use.ts +85 -0
  29. package/templates/adapters/codex/hooks/pre-compact.ts +20 -0
  30. package/templates/adapters/codex/hooks/pre-tool-use.ts +141 -0
  31. package/templates/adapters/codex/hooks/prompt-submit.ts +38 -0
  32. package/templates/adapters/codex/hooks/review-checklist.ts +5 -0
  33. package/templates/adapters/codex/hooks/session-end.ts +19 -0
  34. package/templates/adapters/codex/hooks/session-start.ts +45 -0
  35. package/templates/adapters/codex/hooks/stop-check.ts +59 -0
  36. package/templates/adapters/codex/hooks/subagent-guard.ts +22 -0
  37. package/templates/adapters/codex/hooks/subagent-stop.ts +20 -0
  38. package/templates/adapters/codex/hooks.json +67 -1
  39. package/templates/adapters/qoder/hooks/doc-format-guard.ts +34 -0
  40. package/templates/adapters/qoder/hooks/lib/context-inject.ts +61 -0
  41. package/templates/adapters/qoder/hooks/lib/qoder-env.ts +23 -0
  42. package/templates/adapters/qoder/hooks/lib/review-checklist.ts +5 -0
  43. package/templates/adapters/qoder/hooks/notification.ts +34 -0
  44. package/templates/adapters/qoder/hooks/permission-gate.ts +26 -0
  45. package/templates/adapters/qoder/hooks/post-tool-failure.ts +30 -0
  46. package/templates/adapters/qoder/hooks/post-tool-use.ts +32 -0
  47. package/templates/adapters/qoder/hooks/pre-compact.ts +16 -0
  48. package/templates/adapters/qoder/hooks/pre-tool-use.ts +112 -0
  49. package/templates/adapters/qoder/hooks/prompt-submit.ts +66 -0
  50. package/templates/adapters/qoder/hooks/review-checklist.ts +24 -0
  51. package/templates/adapters/qoder/hooks/session-end.ts +27 -0
  52. package/templates/adapters/qoder/hooks/session-start.ts +83 -0
  53. package/templates/adapters/qoder/hooks/stop-check.ts +69 -0
  54. package/templates/adapters/qoder/hooks/subagent-guard.ts +16 -0
  55. package/templates/adapters/qoder/hooks/subagent-stop.ts +33 -0
  56. package/templates/adapters/qoder/settings.json +13 -13
  57. package/templates/adapters/trae/hooks/doc-format-guard.ts +30 -0
  58. package/templates/adapters/trae/hooks/notification.ts +22 -0
  59. package/templates/adapters/trae/hooks/permission-gate.ts +15 -0
  60. package/templates/adapters/trae/hooks/post-tool-failure.ts +14 -0
  61. package/templates/adapters/trae/hooks/post-tool-use.ts +24 -0
  62. package/templates/adapters/trae/hooks/pre-compact.ts +15 -0
  63. package/templates/adapters/trae/hooks/pre-tool-use.ts +40 -0
  64. package/templates/adapters/trae/hooks/prompt-submit.ts +29 -0
  65. package/templates/adapters/trae/hooks/review-checklist.ts +5 -0
  66. package/templates/adapters/trae/hooks/session-end.ts +16 -0
  67. package/templates/adapters/trae/hooks/session-start.ts +15 -0
  68. package/templates/adapters/trae/hooks/stop-check.ts +20 -0
  69. package/templates/adapters/trae/hooks/subagent-guard.ts +22 -0
  70. package/templates/adapters/trae/hooks/subagent-stop.ts +15 -0
  71. package/templates/adapters/trae/hooks.json +1 -1
  72. package/templates/adapters/vscode/hooks/doc-format-guard.ts +27 -0
  73. package/templates/adapters/vscode/hooks/notification.ts +22 -0
  74. package/templates/adapters/vscode/hooks/permission-gate.ts +15 -0
  75. package/templates/adapters/vscode/hooks/post-tool-failure.ts +32 -0
  76. package/templates/adapters/vscode/hooks/post-tool-use.ts +25 -0
  77. package/templates/adapters/vscode/hooks/pre-compact.ts +15 -0
  78. package/templates/adapters/vscode/hooks/pre-tool-use.ts +78 -0
  79. package/templates/adapters/vscode/hooks/prompt-submit.ts +74 -0
  80. package/templates/adapters/vscode/hooks/review-checklist.ts +5 -0
  81. package/templates/adapters/vscode/hooks/session-end.ts +16 -0
  82. package/templates/adapters/vscode/hooks/session-start.ts +24 -0
  83. package/templates/adapters/vscode/hooks/stop-check.ts +21 -0
  84. package/templates/adapters/vscode/hooks/subagent-guard.ts +21 -0
  85. package/templates/adapters/vscode/hooks/subagent-stop.ts +15 -0
  86. package/templates/core/docs/ADD-governance-claude-code.md +4 -4
  87. package/templates/core/docs/ADD-governance-codex.md +6 -6
  88. package/templates/core/docs/ADD-governance-qoder-cn.md +5 -5
  89. package/templates/core/docs/ADD-governance-trae.md +3 -3
  90. package/templates/core/docs/ADD-governance-vscode-copilot.md +22 -23
  91. package/templates/core/governance/audit-bridge.ts +47 -0
  92. package/templates/core/governance/common.ts +299 -0
  93. package/templates/core/governance/context-inject.ts +53 -0
  94. package/templates/core/governance/doc-format-guard.ts +440 -0
  95. package/templates/core/governance/notification-router.ts +62 -0
  96. package/templates/core/governance/notify.ts +66 -0
  97. package/templates/core/governance/permission-gate-router.ts +42 -0
  98. package/templates/core/governance/post-tool-failure-router.ts +56 -0
  99. package/templates/core/governance/post-tool-router.ts +212 -0
  100. package/templates/core/governance/pre-compact-guard.ts +41 -0
  101. package/templates/core/governance/pre-tool-guard.ts +309 -0
  102. package/templates/core/governance/preload-templates.ts +241 -0
  103. package/templates/core/governance/prompt-router.ts +222 -0
  104. package/templates/core/governance/review-checklist-guard.ts +186 -0
  105. package/templates/core/governance/rules.ts +720 -0
  106. package/templates/core/governance/session-end.ts +88 -0
  107. package/templates/core/governance/session-start-guard.ts +99 -0
  108. package/templates/core/governance/state-detect.ts +6 -0
  109. package/templates/core/governance/stop-router.ts +140 -0
  110. package/templates/core/governance/subagent-guard-router.ts +35 -0
  111. package/templates/core/governance/subagent-stop-router.ts +93 -0
  112. package/templates/core/governance/subagent-stop.ts +75 -0
  113. package/templates/core/governance/vocabulary.ts +109 -0
  114. package/templates/core/hooks/doc-format-guard.ts +21 -0
  115. package/templates/core/hooks/notification.ts +11 -0
  116. package/templates/core/hooks/permission-gate.ts +7 -0
  117. package/templates/core/hooks/post-tool-failure.ts +7 -0
  118. package/templates/core/hooks/post-tool-use.ts +23 -0
  119. package/templates/core/hooks/pre-compact.ts +9 -0
  120. package/templates/core/hooks/pre-tool-use.ts +30 -0
  121. package/templates/core/hooks/prompt-submit.ts +11 -0
  122. package/templates/core/hooks/review-checklist.ts +4 -0
  123. package/templates/core/hooks/session-end.ts +10 -0
  124. package/templates/core/hooks/session-start.ts +9 -0
  125. package/templates/core/hooks/stop-check.ts +14 -0
  126. package/templates/core/hooks/subagent-guard.ts +8 -0
  127. package/templates/core/hooks/subagent-stop.ts +9 -0
  128. package/templates/core/prisma/add.prisma +140 -82
  129. package/templates/core/rules/project_rules.md +27 -24
  130. package/templates/core/scripts/db-ensure.sh +8 -0
  131. package/templates/core/scripts/gen-plan-index.sh +4 -15
  132. package/templates/core/scripts/mcp-server/notifications/hitl.ts +9 -1
  133. package/templates/core/scripts/mcp-server/notifications/hook.ts +25 -9
  134. package/templates/core/scripts/mcp-server/resources/hitl-approval-widget.ts +34 -0
  135. package/templates/core/scripts/mcp-server/resources/hook-events-report.ts +14 -2
  136. package/templates/core/scripts/mcp-server/resources/index.ts +2 -0
  137. package/templates/core/scripts/mcp-server/sampling/review.ts +25 -10
  138. package/templates/core/scripts/mcp-server/shared/db-types.ts +33 -8
  139. package/templates/core/scripts/mcp-server/shared/dps-scoring.strategy.ts +1 -1
  140. package/templates/core/scripts/mcp-server/shared/env.ts +28 -8
  141. package/templates/core/scripts/mcp-server/shared/fs.ts +3 -0
  142. package/templates/core/scripts/mcp-server/shared/hitl-interaction.strategy.ts +2 -3
  143. package/templates/core/scripts/mcp-server/shared/hitl-lifecycle-mutation.ts +129 -0
  144. package/templates/core/scripts/mcp-server/shared/hitl-proposal-mutation.ts +98 -0
  145. package/templates/core/scripts/mcp-server/shared/hitl-ui.ts +3 -0
  146. package/templates/core/scripts/mcp-server/shared/plan-lifecycle-events.ts +62 -0
  147. package/templates/core/scripts/mcp-server/shared/plan-lifecycle-mutation.ts +78 -0
  148. package/templates/core/scripts/mcp-server/shared/plan-lifecycle-subscriber.ts +145 -0
  149. package/templates/core/scripts/mcp-server/shared/plan-lifecycle.ts +153 -0
  150. package/templates/core/scripts/mcp-server/shared/plan-round-events.ts +56 -0
  151. package/templates/core/scripts/mcp-server/shared/plan-round-mutation.ts +143 -0
  152. package/templates/core/scripts/mcp-server/shared/plan-round-store.ts +47 -0
  153. package/templates/core/scripts/mcp-server/shared/plan-round-subscriber.ts +99 -0
  154. package/templates/core/scripts/mcp-server/shared/plan-status-store.ts +37 -0
  155. package/templates/core/scripts/mcp-server/shared/plan-tracking-mutation.ts +71 -0
  156. package/templates/core/scripts/mcp-server/shared/prisma.ts +12 -6
  157. package/templates/core/scripts/mcp-server/shared/project-root-strategy.ts +1 -2
  158. package/templates/core/scripts/mcp-server/shared/quant-recheck.strategy.ts +16 -0
  159. package/templates/core/scripts/mcp-server/shared/redact.ts +28 -0
  160. package/templates/core/scripts/mcp-server/shared/response.ts +3 -2
  161. package/templates/core/scripts/mcp-server/shared/runtime-context.ts +77 -0
  162. package/templates/core/scripts/mcp-server/shared/schema-topology.ts +159 -0
  163. package/templates/core/scripts/mcp-server/tools/audit.ts +49 -7
  164. package/templates/core/scripts/mcp-server/tools/context.ts +117 -121
  165. package/templates/core/scripts/mcp-server/tools/contract.ts +34 -9
  166. package/templates/core/scripts/mcp-server/tools/gateway/check_add_route_status.ts +50 -3
  167. package/templates/core/scripts/mcp-server/tools/gateway/check_doc_similarity.ts +218 -0
  168. package/templates/core/scripts/mcp-server/tools/gateway/check_dps.ts +22 -7
  169. package/templates/core/scripts/mcp-server/tools/gateway/check_rahs.ts +86 -4
  170. package/templates/core/scripts/mcp-server/tools/gateway/check_spec_sync.ts +19 -6
  171. package/templates/core/scripts/mcp-server/tools/gateway/index.ts +2 -0
  172. package/templates/core/scripts/mcp-server/tools/hitl.ts +160 -75
  173. package/templates/core/scripts/mcp-server/tools/hook-event-report.ts +9 -2
  174. package/templates/core/scripts/mcp-server/tools/index.ts +69 -3
  175. package/templates/core/scripts/mcp-server/tools/plan.ts +152 -17
  176. package/templates/core/scripts/mcp-server/tools/review.ts +44 -15
  177. package/templates/core/scripts/mcp-server.ts +60 -1
  178. package/templates/core/scripts/plan-status-bridge.ts +26 -0
  179. package/templates/core/skills/add-paradigm/SKILL.md +2 -2
  180. package/templates/core/templates/add-route-template.schema.json +24 -56
  181. package/templates/core/templates/collab-contract-template.md +1 -1
  182. package/templates/core/templates/handoff-multi-round-template.schema.json +48 -0
  183. package/templates/core/templates/handoff-single-round-template.schema.json +92 -0
  184. package/templates/core/templates/handoff-single-round.schema.json +1 -1
  185. package/templates/core/templates/hitl-approval-widget.html +222 -37
  186. package/templates/core/templates/spec-template.schema.json +9 -46
  187. package/templates/core/templates/standard-plan-template.schema.json +1 -1
  188. package/templates/core/templates/tasks-template.schema.json +13 -54
  189. package/templates/adapters/claude/hooks/doc-format-guard.sh +0 -202
  190. package/templates/adapters/claude/hooks/lib/common.sh +0 -261
  191. package/templates/adapters/claude/hooks/lib/context-inject.sh +0 -96
  192. package/templates/adapters/claude/hooks/lib/notify.sh +0 -34
  193. package/templates/adapters/claude/hooks/lib/preload-templates.sh +0 -187
  194. package/templates/adapters/claude/hooks/lib/session-end.sh +0 -76
  195. package/templates/adapters/claude/hooks/lib/state-detect.sh +0 -117
  196. package/templates/adapters/claude/hooks/lib/subagent-stop.sh +0 -90
  197. package/templates/adapters/claude/hooks/lib/vocabulary.sh +0 -56
  198. package/templates/adapters/claude/hooks/notification.sh +0 -45
  199. package/templates/adapters/claude/hooks/permission-denied.sh +0 -42
  200. package/templates/adapters/claude/hooks/permission-gate.sh +0 -18
  201. package/templates/adapters/claude/hooks/post-tool-failure.sh +0 -10
  202. package/templates/adapters/claude/hooks/post-tool-use.sh +0 -35
  203. package/templates/adapters/claude/hooks/pre-compact.sh +0 -37
  204. package/templates/adapters/claude/hooks/pre-tool-use.sh +0 -153
  205. package/templates/adapters/claude/hooks/prompt-submit.sh +0 -99
  206. package/templates/adapters/claude/hooks/review-checklist.sh +0 -10
  207. package/templates/adapters/claude/hooks/session-end.sh +0 -21
  208. package/templates/adapters/claude/hooks/session-start.sh +0 -36
  209. package/templates/adapters/claude/hooks/stop-check.sh +0 -45
  210. package/templates/adapters/claude/hooks/stop-failure.sh +0 -44
  211. package/templates/adapters/claude/hooks/subagent-guard.sh +0 -36
  212. package/templates/adapters/claude/hooks/subagent-stop.sh +0 -20
  213. package/templates/adapters/codex/hooks/doc-format-guard.sh +0 -261
  214. package/templates/adapters/codex/hooks/lib/common.sh +0 -261
  215. package/templates/adapters/codex/hooks/lib/context-inject.sh +0 -96
  216. package/templates/adapters/codex/hooks/lib/notify.sh +0 -34
  217. package/templates/adapters/codex/hooks/lib/preload-templates.sh +0 -187
  218. package/templates/adapters/codex/hooks/lib/session-end.sh +0 -76
  219. package/templates/adapters/codex/hooks/lib/state-detect.sh +0 -117
  220. package/templates/adapters/codex/hooks/lib/subagent-stop.sh +0 -90
  221. package/templates/adapters/codex/hooks/lib/vocabulary.sh +0 -56
  222. package/templates/adapters/codex/hooks/notification.sh +0 -45
  223. package/templates/adapters/codex/hooks/permission-gate.sh +0 -18
  224. package/templates/adapters/codex/hooks/post-tool-failure.sh +0 -10
  225. package/templates/adapters/codex/hooks/post-tool-use.sh +0 -138
  226. package/templates/adapters/codex/hooks/pre-compact.sh +0 -37
  227. package/templates/adapters/codex/hooks/pre-tool-use.sh +0 -217
  228. package/templates/adapters/codex/hooks/prompt-submit.sh +0 -87
  229. package/templates/adapters/codex/hooks/review-checklist.sh +0 -10
  230. package/templates/adapters/codex/hooks/session-end.sh +0 -38
  231. package/templates/adapters/codex/hooks/session-start.sh +0 -53
  232. package/templates/adapters/codex/hooks/stop-check.sh +0 -45
  233. package/templates/adapters/codex/hooks/subagent-guard.sh +0 -15
  234. package/templates/adapters/codex/hooks/subagent-stop.sh +0 -47
  235. package/templates/adapters/qoder/hooks/doc-format-guard.sh +0 -260
  236. package/templates/adapters/qoder/hooks/lib/common.sh +0 -261
  237. package/templates/adapters/qoder/hooks/lib/context-inject.sh +0 -96
  238. package/templates/adapters/qoder/hooks/lib/notify.sh +0 -34
  239. package/templates/adapters/qoder/hooks/lib/preload-templates.sh +0 -187
  240. package/templates/adapters/qoder/hooks/lib/session-end.sh +0 -76
  241. package/templates/adapters/qoder/hooks/lib/state-detect.sh +0 -117
  242. package/templates/adapters/qoder/hooks/lib/subagent-stop.sh +0 -90
  243. package/templates/adapters/qoder/hooks/lib/vocabulary.sh +0 -56
  244. package/templates/adapters/qoder/hooks/notification.sh +0 -25
  245. package/templates/adapters/qoder/hooks/permission-gate.sh +0 -8
  246. package/templates/adapters/qoder/hooks/post-tool-failure.sh +0 -8
  247. package/templates/adapters/qoder/hooks/post-tool-use.sh +0 -121
  248. package/templates/adapters/qoder/hooks/pre-compact.sh +0 -37
  249. package/templates/adapters/qoder/hooks/pre-tool-use.sh +0 -154
  250. package/templates/adapters/qoder/hooks/prompt-submit.sh +0 -96
  251. package/templates/adapters/qoder/hooks/review-checklist.sh +0 -157
  252. package/templates/adapters/qoder/hooks/session-end.sh +0 -24
  253. package/templates/adapters/qoder/hooks/session-start.sh +0 -45
  254. package/templates/adapters/qoder/hooks/stop-check.sh +0 -48
  255. package/templates/adapters/qoder/hooks/subagent-guard.sh +0 -27
  256. package/templates/adapters/qoder/hooks/subagent-stop.sh +0 -26
  257. package/templates/adapters/trae/hooks/doc-format-guard.sh +0 -261
  258. package/templates/adapters/trae/hooks/lib/common.sh +0 -261
  259. package/templates/adapters/trae/hooks/lib/context-inject.sh +0 -96
  260. package/templates/adapters/trae/hooks/lib/notify.sh +0 -34
  261. package/templates/adapters/trae/hooks/lib/preload-templates.sh +0 -187
  262. package/templates/adapters/trae/hooks/lib/session-end.sh +0 -76
  263. package/templates/adapters/trae/hooks/lib/state-detect.sh +0 -117
  264. package/templates/adapters/trae/hooks/lib/subagent-stop.sh +0 -90
  265. package/templates/adapters/trae/hooks/lib/vocabulary.sh +0 -56
  266. package/templates/adapters/trae/hooks/notification.sh +0 -45
  267. package/templates/adapters/trae/hooks/permission-gate.sh +0 -18
  268. package/templates/adapters/trae/hooks/post-tool-failure.sh +0 -10
  269. package/templates/adapters/trae/hooks/post-tool-use.sh +0 -138
  270. package/templates/adapters/trae/hooks/pre-compact.sh +0 -37
  271. package/templates/adapters/trae/hooks/pre-tool-use.sh +0 -217
  272. package/templates/adapters/trae/hooks/prompt-submit.sh +0 -87
  273. package/templates/adapters/trae/hooks/review-checklist.sh +0 -10
  274. package/templates/adapters/trae/hooks/session-end.sh +0 -38
  275. package/templates/adapters/trae/hooks/session-start.sh +0 -53
  276. package/templates/adapters/trae/hooks/stop-check.sh +0 -45
  277. package/templates/adapters/trae/hooks/subagent-guard.sh +0 -15
  278. package/templates/adapters/trae/hooks/subagent-stop.sh +0 -47
  279. package/templates/adapters/vscode/hooks/doc-format-guard.sh +0 -201
  280. package/templates/adapters/vscode/hooks/lib/common.sh +0 -261
  281. package/templates/adapters/vscode/hooks/lib/context-inject.sh +0 -96
  282. package/templates/adapters/vscode/hooks/lib/notify.sh +0 -34
  283. package/templates/adapters/vscode/hooks/lib/preload-templates.sh +0 -187
  284. package/templates/adapters/vscode/hooks/lib/session-end.sh +0 -76
  285. package/templates/adapters/vscode/hooks/lib/state-detect.sh +0 -117
  286. package/templates/adapters/vscode/hooks/lib/subagent-stop.sh +0 -90
  287. package/templates/adapters/vscode/hooks/lib/vocabulary.sh +0 -56
  288. package/templates/adapters/vscode/hooks/notification.sh +0 -45
  289. package/templates/adapters/vscode/hooks/permission-gate.sh +0 -18
  290. package/templates/adapters/vscode/hooks/post-tool-failure.sh +0 -28
  291. package/templates/adapters/vscode/hooks/post-tool-use.sh +0 -35
  292. package/templates/adapters/vscode/hooks/pre-compact.sh +0 -37
  293. package/templates/adapters/vscode/hooks/pre-tool-use.sh +0 -153
  294. package/templates/adapters/vscode/hooks/prompt-submit.sh +0 -99
  295. package/templates/adapters/vscode/hooks/review-checklist.sh +0 -10
  296. package/templates/adapters/vscode/hooks/session-end.sh +0 -13
  297. package/templates/adapters/vscode/hooks/session-start.sh +0 -35
  298. package/templates/adapters/vscode/hooks/stop-check.sh +0 -53
  299. package/templates/adapters/vscode/hooks/subagent-guard.sh +0 -37
  300. package/templates/adapters/vscode/hooks/subagent-stop.sh +0 -12
  301. package/templates/core/hooks/doc-format-guard.sh +0 -261
  302. package/templates/core/hooks/lib/common.sh +0 -261
  303. package/templates/core/hooks/lib/context-inject.sh +0 -96
  304. package/templates/core/hooks/lib/notify.sh +0 -34
  305. package/templates/core/hooks/lib/preload-templates.sh +0 -187
  306. package/templates/core/hooks/lib/session-end.sh +0 -76
  307. package/templates/core/hooks/lib/state-detect.sh +0 -117
  308. package/templates/core/hooks/lib/subagent-stop.sh +0 -90
  309. package/templates/core/hooks/lib/vocabulary.sh +0 -56
  310. package/templates/core/hooks/notification.sh +0 -45
  311. package/templates/core/hooks/permission-gate.sh +0 -18
  312. package/templates/core/hooks/post-tool-failure.sh +0 -10
  313. package/templates/core/hooks/post-tool-use.sh +0 -138
  314. package/templates/core/hooks/pre-compact.sh +0 -37
  315. package/templates/core/hooks/pre-tool-use.sh +0 -217
  316. package/templates/core/hooks/prompt-submit.sh +0 -87
  317. package/templates/core/hooks/review-checklist.sh +0 -10
  318. package/templates/core/hooks/session-end.sh +0 -38
  319. package/templates/core/hooks/session-start.sh +0 -53
  320. package/templates/core/hooks/stop-check.sh +0 -45
  321. package/templates/core/hooks/subagent-guard.sh +0 -15
  322. package/templates/core/hooks/subagent-stop.sh +0 -47
  323. package/templates/shared/hooks-lib/common.sh +0 -22
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/cli/index.ts
4
- import { readFileSync as readFileSync12 } from "fs";
4
+ import { readFileSync as readFileSync13 } from "fs";
5
5
  import { Command } from "commander";
6
6
 
7
7
  // src/caijuehub/strategies/detect.strategy.ts
@@ -33,6 +33,13 @@ function detectIDE(projectRoot) {
33
33
  }
34
34
 
35
35
  // src/caijuehub/strategies/adapter.strategy.ts
36
+ var MAGIC_DIR_MAP = {
37
+ claude: ".claude",
38
+ qoder: ".qoder",
39
+ vscode: ".vscode",
40
+ trae: ".trae",
41
+ codex: ".codex"
42
+ };
36
43
  function resolveAdapters(target) {
37
44
  return [target];
38
45
  }
@@ -45,6 +52,15 @@ function resolveAdapters2(target) {
45
52
  return resolveAdapters(target);
46
53
  }
47
54
 
55
+ // src/shared/paths.ts
56
+ import { findUpSync } from "find-up";
57
+ var ADD_DIR = ".add";
58
+ function magicDirFor(adapter) {
59
+ const m = MAGIC_DIR_MAP[adapter];
60
+ if (!m) throw new Error(`\u672A\u77E5 adapter: ${adapter}`);
61
+ return m;
62
+ }
63
+
48
64
  // src/cli/config-loader.ts
49
65
  import { existsSync as existsSync2, readFileSync } from "fs";
50
66
  import { join as join2 } from "path";
@@ -372,8 +388,8 @@ function parseSchemaBlocks(content) {
372
388
  return blocks;
373
389
  }
374
390
  function diffPrisma(basePath, targetPath) {
375
- const baseContent = existsSync4(basePath) ? readFileSync3(basePath, "utf-8") : "";
376
- const targetContent = existsSync4(targetPath) ? readFileSync3(targetPath, "utf-8") : "";
391
+ const baseContent = (existsSync4(basePath) ? readFileSync3(basePath, "utf-8") : "").replace(/\r\n/g, "\n");
392
+ const targetContent = (existsSync4(targetPath) ? readFileSync3(targetPath, "utf-8") : "").replace(/\r\n/g, "\n");
377
393
  if (!baseContent) return { hasDiff: false, baseSchema: basePath, targetPath, missing: [], fieldDiffs: [] };
378
394
  const baseBlocks = parseSchemaBlocks(baseContent);
379
395
  const targetBlocks = parseSchemaBlocks(targetContent);
@@ -425,11 +441,9 @@ function diffPrisma(basePath, targetPath) {
425
441
 
426
442
  // src/core/renderer.ts
427
443
  import { readFileSync as readFileSync4, readdirSync, statSync, existsSync as existsSync5, mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "fs";
428
- import { join as join4, relative, dirname as dirname2 } from "path";
429
- import { fileURLToPath } from "url";
444
+ import { join as join4, relative } from "path";
430
445
  import { parse } from "smol-toml";
431
- var __filename = fileURLToPath(import.meta.url);
432
- var __dirname = dirname2(__filename);
446
+ var __dirname = import.meta.dirname;
433
447
  var PLACEHOLDERS = {
434
448
  "{{projectName}}": "projectName",
435
449
  "{{projectRoot}}": "projectRoot",
@@ -500,9 +514,14 @@ function render(content, config) {
500
514
  result = renderStackPlaceholders(result, config);
501
515
  return result;
502
516
  }
503
- var TEMPLATES_ROOT = join4(__dirname, "../templates");
517
+ var TEMPLATES_ROOT = [
518
+ // Source mode: src/core/renderer.ts → ../../templates
519
+ join4(__dirname, "../../templates"),
520
+ // Bundled mode: dist/index.js → ../templates
521
+ join4(__dirname, "../templates")
522
+ ].find(existsSync5) ?? join4(__dirname, "../templates");
504
523
  var CORE_DIR = join4(TEMPLATES_ROOT, "core");
505
- var CORE_TARGET = ".add";
524
+ var CORE_TARGET = ADD_DIR;
506
525
  var SKIP_DIRS = /* @__PURE__ */ new Set(["prisma", "profiles"]);
507
526
  function loadProfileRegistry() {
508
527
  try {
@@ -558,7 +577,7 @@ function renderCore(config, dryRun) {
558
577
  const src = join4(CORE_DIR, "rules", "profiles", profile.file);
559
578
  if (existsSync5(src)) {
560
579
  const rendered = render(readFileSync4(src, "utf-8"), config);
561
- files.set(join4(".add", "rules", "profiles", profile.file), rendered);
580
+ files.set(join4(ADD_DIR, "rules", "profiles", profile.file), rendered);
562
581
  }
563
582
  }
564
583
  }
@@ -567,7 +586,17 @@ function renderCore(config, dryRun) {
567
586
  }
568
587
  return files;
569
588
  }
570
- function renderAdapterBase(config, magicPath, githubHooksToRoot, dryRun) {
589
+ function renderCoreForTargets(config, dryRun, targets, renderTarget = renderCore) {
590
+ const files = /* @__PURE__ */ new Map();
591
+ for (const target of new Set(targets)) {
592
+ const targetFiles = renderTarget({ ...config, magicDir: target }, dryRun);
593
+ for (const [relPath, content] of targetFiles) {
594
+ files.set(relPath.replace(/^\.add/, target), content);
595
+ }
596
+ }
597
+ return files;
598
+ }
599
+ function renderAdapterBase(config, magicPath, githubHooksToRoot, dryRun, includeCoreHooksLib = true) {
571
600
  const files = /* @__PURE__ */ new Map();
572
601
  const adapterDir = join4(TEMPLATES_ROOT, "adapters", magicPath.replace(".", ""));
573
602
  const coreHooksLib = join4(TEMPLATES_ROOT, "core", "hooks", "lib");
@@ -591,8 +620,10 @@ function renderAdapterBase(config, magicPath, githubHooksToRoot, dryRun) {
591
620
  }
592
621
  }
593
622
  }
623
+ if (includeCoreHooksLib) {
624
+ walk(coreHooksLib, "", join4(magicPath, "hooks", "lib"));
625
+ }
594
626
  walk(adapterDir, "");
595
- walk(coreHooksLib, "", join4(magicPath, "hooks", "lib"));
596
627
  if (dryRun) {
597
628
  console.log(`[dry-run] ${magicPath} adapter: ${files.size} files`);
598
629
  }
@@ -601,34 +632,161 @@ function renderAdapterBase(config, magicPath, githubHooksToRoot, dryRun) {
601
632
 
602
633
  // src/adapters/claude/renderer.ts
603
634
  function renderAdapter(config, targetDir, dryRun, magicDir) {
604
- return renderAdapterBase(config, magicDir, magicDir === ".vscode", dryRun);
635
+ return renderAdapterBase(config, magicDir, magicDir === magicDirFor("vscode"), dryRun, false);
605
636
  }
606
637
 
607
638
  // src/adapters/qoder/renderer.ts
608
639
  function renderAdapter2(config, targetDir, dryRun, magicDir) {
609
- return renderAdapterBase(config, magicDir, magicDir === ".vscode", dryRun);
640
+ return renderAdapterBase(config, magicDir, magicDir === magicDirFor("vscode"), dryRun, false);
610
641
  }
611
642
 
612
643
  // src/adapters/vscode/renderer.ts
613
644
  function renderAdapter3(config, targetDir, dryRun, magicDir) {
614
- return renderAdapterBase(config, magicDir, magicDir === ".vscode", dryRun);
645
+ return renderAdapterBase(config, magicDir, magicDir === magicDirFor("vscode"), dryRun, false);
615
646
  }
616
647
 
617
648
  // src/adapters/trae/renderer.ts
618
649
  function renderAdapter4(config, targetDir, dryRun, magicDir) {
619
- return renderAdapterBase(config, magicDir, magicDir === ".vscode", dryRun);
650
+ return renderAdapterBase(config, magicDir, magicDir === magicDirFor("vscode"), dryRun, false);
620
651
  }
621
652
 
622
653
  // src/adapters/codex/renderer.ts
654
+ import { existsSync as existsSync6, readFileSync as readFileSync5, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
655
+ import { join as join5, relative as relative2 } from "path";
656
+ var __dirname2 = import.meta.dirname;
657
+ var CODEX_SERVER_ID = "add_coder";
658
+ function normalizeServerId(name) {
659
+ return name.replace(/-/g, "_");
660
+ }
661
+ function codexCommandFor(platform) {
662
+ return platform === "win32" ? "cmd" : "npx";
663
+ }
664
+ function codexLaunchFor(platform, targetDir, magicDir) {
665
+ const serverPath = join5(targetDir, magicDir, "scripts", "mcp-server.ts");
666
+ const command = codexCommandFor(platform);
667
+ const args = platform === "win32" ? ["/c", "npx.cmd", "tsx", serverPath] : ["tsx", serverPath];
668
+ return { command, args, cwd: targetDir };
669
+ }
670
+ function tomlString(value) {
671
+ return JSON.stringify(value);
672
+ }
673
+ function tomlArray(values) {
674
+ return `[${values.map(tomlString).join(", ")}]`;
675
+ }
676
+ function renderCodexMcpTemplate(template, targetDir, magicDir, platform = process.platform) {
677
+ const launch = codexLaunchFor(platform, targetDir, magicDir);
678
+ return template.replaceAll("{{codexCommandToml}}", tomlString(launch.command)).replaceAll("{{codexArgsToml}}", tomlArray(launch.args)).replaceAll("{{codexProjectRootToml}}", tomlString(launch.cwd));
679
+ }
680
+ function mergeCodexProjectConfig(existing, snippet) {
681
+ const serverTable = /^\s*\[mcp_servers\.(?:add_coder|["']add_coder["'])\]\s*(?:#.*)?$/m;
682
+ if (serverTable.test(existing)) return existing;
683
+ const normalizedSnippet = snippet.trim();
684
+ if (!existing) return `${normalizedSnippet}
685
+ `;
686
+ const separator = existing.endsWith("\n") ? "\n" : "\n\n";
687
+ return `${existing}${separator}${normalizedSnippet}
688
+ `;
689
+ }
690
+ function findCoreSkillsDir() {
691
+ const candidates = [
692
+ join5(__dirname2, "../../../templates/core/skills"),
693
+ join5(__dirname2, "../templates/core/skills")
694
+ ];
695
+ const found = candidates.find(existsSync6);
696
+ if (!found) {
697
+ throw new Error(`[codex adapter] core skills \u771F\u6E90\u7F3A\u5931: ${candidates.join(" | ")}`);
698
+ }
699
+ return found;
700
+ }
701
+ function renderCodexSkills(config, skillsRoot = findCoreSkillsDir()) {
702
+ const files = /* @__PURE__ */ new Map();
703
+ function walk(dir) {
704
+ for (const name of readdirSync2(dir).sort()) {
705
+ const full = join5(dir, name);
706
+ if (statSync2(full).isDirectory()) {
707
+ walk(full);
708
+ continue;
709
+ }
710
+ const targetRel = join5(".agents", "skills", relative2(skillsRoot, full));
711
+ files.set(targetRel, render(readFileSync5(full, "utf-8"), config));
712
+ }
713
+ }
714
+ walk(skillsRoot);
715
+ return files;
716
+ }
623
717
  function renderAdapter5(config, targetDir, dryRun, magicDir) {
624
- return renderAdapterBase(config, magicDir, magicDir === ".vscode", dryRun);
718
+ const files = renderAdapterBase(config, magicDir, magicDir === magicDirFor("vscode"), dryRun, false);
719
+ const examplePath = join5(magicDir, "config.toml.example");
720
+ const projectConfigPath = join5(magicDir, "config.toml");
721
+ const example = files.get(examplePath);
722
+ if (!example) {
723
+ throw new Error(`[codex adapter] MCP \u914D\u7F6E\u6A21\u677F\u7F3A\u5931: ${examplePath}`);
724
+ }
725
+ const snippet = renderCodexMcpTemplate(example, targetDir, magicDir);
726
+ files.set(examplePath, snippet);
727
+ const existingPath = join5(targetDir, projectConfigPath);
728
+ const existing = existsSync6(existingPath) ? readFileSync5(existingPath, "utf-8") : "";
729
+ files.set(projectConfigPath, mergeCodexProjectConfig(existing, snippet));
730
+ for (const [relPath, content] of renderCodexSkills(config)) {
731
+ files.set(relPath, content);
732
+ }
733
+ if (!files.has(join5(".agents", "skills", "add-paradigm", "SKILL.md"))) {
734
+ throw new Error("[codex adapter] add-paradigm \u672A\u6620\u5C04\u5230 .agents/skills");
735
+ }
736
+ if (!files.has(join5(".agents", "skills", "session-init", "SKILL.md"))) {
737
+ throw new Error("[codex adapter] session-init \u672A\u6620\u5C04\u5230 .agents/skills");
738
+ }
739
+ if (normalizeServerId("add-coder") !== CODEX_SERVER_ID || !snippet.includes(`[mcp_servers.${CODEX_SERVER_ID}]`)) {
740
+ throw new Error(`[codex adapter] MCP server id \u6F02\u79FB: ${CODEX_SERVER_ID}`);
741
+ }
742
+ return files;
625
743
  }
626
744
 
627
745
  // src/lib/utils.ts
628
746
  import { createInterface as createInterface2 } from "readline";
629
- import { existsSync as existsSync6 } from "fs";
747
+ import { existsSync as existsSync7 } from "fs";
630
748
  import { resolve as resolve2 } from "path";
631
- function ask(q) {
749
+ var pipeQueue = null;
750
+ var pipeReady = null;
751
+ function collectPipeAnswers(input) {
752
+ return new Promise((resolveAnswers) => {
753
+ const answers = [];
754
+ let buffer = "";
755
+ let settled = false;
756
+ const finish = () => {
757
+ if (settled) return;
758
+ settled = true;
759
+ if (buffer.trim()) answers.push(buffer.trim());
760
+ resolveAnswers(answers);
761
+ };
762
+ input.setEncoding("utf-8");
763
+ input.on("data", (chunk) => {
764
+ buffer += chunk;
765
+ const lines = buffer.split("\n");
766
+ buffer = lines.pop() ?? "";
767
+ answers.push(...lines.filter((line) => line.trim() !== ""));
768
+ });
769
+ input.once("end", finish);
770
+ input.once("error", finish);
771
+ input.resume();
772
+ });
773
+ }
774
+ function takePipeAnswer(queue) {
775
+ return (queue.shift() ?? "").trim().toLowerCase();
776
+ }
777
+ function ensurePipeQueue() {
778
+ if (pipeReady) return pipeReady;
779
+ if (process.stdin.isTTY) return pipeReady = Promise.resolve();
780
+ pipeReady = collectPipeAnswers(process.stdin).then((answers) => {
781
+ pipeQueue = answers;
782
+ });
783
+ return pipeReady;
784
+ }
785
+ async function ask(q) {
786
+ await ensurePipeQueue();
787
+ if (pipeQueue) {
788
+ return takePipeAnswer(pipeQueue);
789
+ }
632
790
  const rl = createInterface2({ input: process.stdin, output: process.stdout });
633
791
  return new Promise((r) => {
634
792
  rl.question(q, (a) => {
@@ -638,16 +796,15 @@ function ask(q) {
638
796
  });
639
797
  }
640
798
  function detectPm(projectRoot) {
641
- return existsSync6(resolve2(projectRoot, "pnpm-lock.yaml")) ? "pnpm" : "npm";
799
+ return existsSync7(resolve2(projectRoot, "pnpm-lock.yaml")) ? "pnpm" : "npm";
642
800
  }
643
801
 
644
802
  // src/cli/prisma-injector.ts
645
- import { resolve as resolve6, dirname as dirname4 } from "path";
646
- import { fileURLToPath as fileURLToPath2 } from "url";
803
+ import { resolve as resolve6 } from "path";
647
804
 
648
805
  // src/caijuehub/strategies/prisma.strategy.ts
649
- import { copyFileSync, existsSync as existsSync9, readFileSync as readFileSync7, unlinkSync, writeFileSync as writeFileSync5, mkdirSync as mkdirSync5 } from "fs";
650
- import { resolve as resolve5, join as join6, dirname as dirname3 } from "path";
806
+ import { copyFileSync, existsSync as existsSync10, readFileSync as readFileSync8, unlinkSync, writeFileSync as writeFileSync5, mkdirSync as mkdirSync5 } from "fs";
807
+ import { resolve as resolve5, join as join7, dirname as dirname2 } from "path";
651
808
 
652
809
  // src/lib/run-command.ts
653
810
  import { spawnSync } from "child_process";
@@ -678,32 +835,32 @@ function commandExists(cmd, platform) {
678
835
  }
679
836
 
680
837
  // src/lib/db-backup.ts
681
- import { existsSync as existsSync7, mkdirSync as mkdirSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync3, readdirSync as readdirSync2, rmSync } from "fs";
682
- import { join as join5, resolve as resolve3 } from "path";
838
+ import { existsSync as existsSync8, mkdirSync as mkdirSync3, readFileSync as readFileSync6, writeFileSync as writeFileSync3, readdirSync as readdirSync3, rmSync } from "fs";
839
+ import { join as join6, resolve as resolve3 } from "path";
683
840
  var ADD_TABLES = ["AddUser", "DevOperation", "AuditLog", "HitlRecord", "PlanRecord", "ReviewRecord", "CollabContract"];
684
841
  function ensureGitignoreRule(projectRoot) {
685
842
  const gitignorePath = resolve3(projectRoot, ".gitignore");
686
- if (!existsSync7(gitignorePath)) return;
687
- const content = readFileSync5(gitignorePath, "utf-8");
688
- if (content.includes(".add/backups/")) return;
843
+ if (!existsSync8(gitignorePath)) return;
844
+ const content = readFileSync6(gitignorePath, "utf-8");
845
+ if (content.includes(`${ADD_DIR}/backups/`)) return;
689
846
  writeFileSync3(gitignorePath, `${content}${content.endsWith("\n") ? "" : "\n"}
690
847
  # add-coder \u6570\u636E\u5E93\u540C\u6B65\u5907\u4EFD
691
- .add/backups/
848
+ ${ADD_DIR}/backups/
692
849
  `, "utf-8");
693
- console.log("\u{1F4CB} \u5DF2\u6CE8\u5165\u5BBF\u4E3B .gitignore: .add/backups/");
850
+ console.log(`\u{1F4CB} \u5DF2\u6CE8\u5165\u5BBF\u4E3B .gitignore: ${ADD_DIR}/backups/`);
694
851
  }
695
852
  async function backupBeforeSync(projectRoot, opts) {
696
853
  const dir = resolve3(projectRoot, opts.backupDir);
697
854
  const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
698
- const bakDir = join5(dir, ts);
855
+ const bakDir = join6(dir, ts);
699
856
  ensureGitignoreRule(projectRoot);
700
857
  if (opts.datasource === "sqlite" || opts.dbUrl.startsWith("file:")) {
701
858
  const dbFile = opts.dbUrl.replace(/^file:/, "");
702
- if (!existsSync7(dbFile)) return handleBackupFailure(opts, `sqlite \u6587\u4EF6\u4E0D\u5B58\u5728: ${dbFile}`);
859
+ if (!existsSync8(dbFile)) return handleBackupFailure(opts, `sqlite \u6587\u4EF6\u4E0D\u5B58\u5728: ${dbFile}`);
703
860
  mkdirSync3(bakDir, { recursive: true });
704
- const copy = join5(bakDir, "dev.db");
705
- writeFileSync3(copy, readFileSync5(dbFile), "utf-8");
706
- writeFileSync3(join5(bakDir, "manifest.json"), JSON.stringify({ createdAt: ts, dbUrl: opts.dbUrl, files: ["dev.db"] }, null, 2), "utf-8");
861
+ const copy = join6(bakDir, "dev.db");
862
+ writeFileSync3(copy, readFileSync6(dbFile), "utf-8");
863
+ writeFileSync3(join6(bakDir, "manifest.json"), JSON.stringify({ createdAt: ts, dbUrl: opts.dbUrl, files: ["dev.db"] }, null, 2), "utf-8");
707
864
  console.log(`\u2705 \u5907\u4EFD\u5B8C\u6210: ${bakDir}`);
708
865
  pruneBackups(dir, opts.backupKeep);
709
866
  return bakDir;
@@ -712,8 +869,8 @@ async function backupBeforeSync(projectRoot, opts) {
712
869
  return handleBackupFailure(opts, "pg_dump \u672A\u5B89\u88C5\uFF08postgresql-client \u7F3A\u5931\uFF09");
713
870
  }
714
871
  mkdirSync3(bakDir, { recursive: true });
715
- const schemaFile = join5(bakDir, "schema.sql");
716
- const tablesFile = join5(bakDir, "add-tables.sql");
872
+ const schemaFile = join6(bakDir, "schema.sql");
873
+ const tablesFile = join6(bakDir, "add-tables.sql");
717
874
  const r1 = runCommand("pg_dump", ["--schema-only", "--no-owner", `--dbname=${opts.dbUrl}`], { timeout: 6e4 });
718
875
  if (r1.status !== 0) {
719
876
  return handleBackupFailure(opts, `pg_dump schema \u5931\u8D25: ${r1.stderr.trim().split("\n").slice(0, 3).join(" | ")}`);
@@ -724,7 +881,7 @@ async function backupBeforeSync(projectRoot, opts) {
724
881
  return handleBackupFailure(opts, `pg_dump ADD \u8868\u5931\u8D25: ${r2.stderr.trim().split("\n").slice(0, 3).join(" | ")}`);
725
882
  }
726
883
  writeFileSync3(tablesFile, r2.stdout, "utf-8");
727
- writeFileSync3(join5(bakDir, "manifest.json"), JSON.stringify({ createdAt: ts, dbUrl: opts.dbUrl, files: ["schema.sql", "add-tables.sql"] }, null, 2), "utf-8");
884
+ writeFileSync3(join6(bakDir, "manifest.json"), JSON.stringify({ createdAt: ts, dbUrl: opts.dbUrl, files: ["schema.sql", "add-tables.sql"] }, null, 2), "utf-8");
728
885
  console.log(`\u2705 \u5907\u4EFD\u5B8C\u6210: ${bakDir}`);
729
886
  pruneBackups(dir, opts.backupKeep);
730
887
  return bakDir;
@@ -742,17 +899,17 @@ async function handleBackupFailure(opts, reason) {
742
899
  return null;
743
900
  }
744
901
  function pruneBackups(dir, backupKeep) {
745
- if (!existsSync7(dir)) return;
746
- const entries = readdirSync2(dir).filter((n) => /^\d{4}-\d{2}-\d{2}T/.test(n)).sort();
902
+ if (!existsSync8(dir)) return;
903
+ const entries = readdirSync3(dir).filter((n) => /^\d{4}-\d{2}-\d{2}T/.test(n)).sort();
747
904
  const excess = entries.length - backupKeep;
748
905
  for (let i = 0; i < excess; i++) {
749
- rmSync(join5(dir, entries[i]), { recursive: true, force: true });
906
+ rmSync(join6(dir, entries[i]), { recursive: true, force: true });
750
907
  console.log(`\u{1F9F9} \u6E05\u7406\u65E7\u5907\u4EFD: ${entries[i]}`);
751
908
  }
752
909
  }
753
910
 
754
911
  // src/lib/ports-contract.ts
755
- import { existsSync as existsSync8, readFileSync as readFileSync6, writeFileSync as writeFileSync4, mkdirSync as mkdirSync4, readdirSync as readdirSync3 } from "fs";
912
+ import { existsSync as existsSync9, readFileSync as readFileSync7, writeFileSync as writeFileSync4, mkdirSync as mkdirSync4, readdirSync as readdirSync4 } from "fs";
756
913
  import { resolve as resolve4, basename } from "path";
757
914
 
758
915
  // src/caijuehub/strategies/ports.strategy.ts
@@ -773,10 +930,10 @@ import { createConnection } from "net";
773
930
  var PORTS_EXAMPLE_REL = "../templates/core/templates/ports.example.md";
774
931
  function ensurePortsContract(projectRoot, config, dryRun = false) {
775
932
  const target = resolve4(projectRoot, "docs", "ports.md");
776
- if (existsSync8(target)) return;
933
+ if (existsSync9(target)) return;
777
934
  let content;
778
935
  try {
779
- content = readFileSync6(resolve4(import.meta.dirname, PORTS_EXAMPLE_REL), "utf-8");
936
+ content = readFileSync7(resolve4(import.meta.dirname, PORTS_EXAMPLE_REL), "utf-8");
780
937
  } catch {
781
938
  console.warn(`\u26A0\uFE0F \u7AEF\u53E3\u5951\u7EA6\u6A21\u677F\u7F3A\u5931\uFF08\u8DF3\u8FC7\u751F\u6210 docs/ports.md\uFF09: ${PORTS_EXAMPLE_REL}`);
782
939
  return;
@@ -809,22 +966,22 @@ function parseContractPorts(content) {
809
966
  }
810
967
  function readRegisteredPorts(projectRoot) {
811
968
  const p = resolve4(projectRoot, "docs", "ports.md");
812
- if (!existsSync8(p)) return /* @__PURE__ */ new Set();
813
- return new Set(parseContractPorts(readFileSync6(p, "utf-8")));
969
+ if (!existsSync9(p)) return /* @__PURE__ */ new Set();
970
+ return new Set(parseContractPorts(readFileSync7(p, "utf-8")));
814
971
  }
815
972
  function readCrossProjectPorts(projectRoot) {
816
973
  const ports = /* @__PURE__ */ new Set();
817
974
  const parent = resolve4(projectRoot, "..");
818
975
  let names = [];
819
976
  try {
820
- names = readdirSync3(parent, { withFileTypes: true }).filter((d) => d.isDirectory() && d.name !== basename(projectRoot)).map((d) => d.name);
977
+ names = readdirSync4(parent, { withFileTypes: true }).filter((d) => d.isDirectory() && d.name !== basename(projectRoot)).map((d) => d.name);
821
978
  } catch {
822
979
  return ports;
823
980
  }
824
981
  for (const name of names) {
825
982
  const p = resolve4(parent, name, "docs", "ports.md");
826
- if (existsSync8(p)) {
827
- parseContractPorts(readFileSync6(p, "utf-8")).forEach((x) => ports.add(x));
983
+ if (existsSync9(p)) {
984
+ parseContractPorts(readFileSync7(p, "utf-8")).forEach((x) => ports.add(x));
828
985
  }
829
986
  }
830
987
  return ports;
@@ -843,11 +1000,11 @@ function scanPodmanPorts() {
843
1000
  }
844
1001
  function registerPort(projectRoot, port, svc) {
845
1002
  const contract = resolve4(projectRoot, "docs", "ports.md");
846
- if (!existsSync8(contract)) return;
1003
+ if (!existsSync9(contract)) return;
847
1004
  const env = svc.envKey ? ` \`${svc.envKey}\`` : "";
848
1005
  const line = `| ${port} | PostgreSQL\uFF08${svc.containerName}\uFF09 | ${svc.name}\uFF08add-coder \u81EA\u52A8\u767B\u8BB0\uFF09 | \u{1F7E2} \u4F7F\u7528\u4E2D | .env.development${env} |
849
1006
  `;
850
- writeFileSync4(contract, readFileSync6(contract, "utf-8") + line, "utf-8");
1007
+ writeFileSync4(contract, readFileSync7(contract, "utf-8") + line, "utf-8");
851
1008
  console.log(`\u{1F4CB} \u7AEF\u53E3\u5951\u7EA6\u767B\u8BB0: ${port}\uFF08${svc.containerName}\uFF09\u2192 docs/ports.md`);
852
1009
  }
853
1010
  async function allocatePortsWithContract(projectRoot, services) {
@@ -897,13 +1054,13 @@ var PRISMA_CONFIG = {
897
1054
  var ADD_TABLES2 = ["AddUser", "DevOperation", "AuditLog", "HitlRecord", "PlanRecord", "ReviewRecord", "CollabContract"];
898
1055
  function readEnvValue(projectRoot, key) {
899
1056
  const envPath = resolve5(projectRoot, ".env.development");
900
- if (!existsSync9(envPath)) return "";
901
- const m = readFileSync7(envPath, "utf-8").match(new RegExp(`^${key}=(.*)$`, "m"));
1057
+ if (!existsSync10(envPath)) return "";
1058
+ const m = readFileSync8(envPath, "utf-8").match(new RegExp(`^${key}=(.*)$`, "m"));
902
1059
  return m?.[1]?.trim() || "";
903
1060
  }
904
1061
  function appendEnvValue(projectRoot, key, value) {
905
1062
  const envPath = resolve5(projectRoot, ".env.development");
906
- const existing = existsSync9(envPath) ? readFileSync7(envPath, "utf-8") : "";
1063
+ const existing = existsSync10(envPath) ? readFileSync8(envPath, "utf-8") : "";
907
1064
  if (new RegExp(`^${key}=`, "m").test(existing)) return;
908
1065
  writeFileSync5(envPath, `${existing}${existing.endsWith("\n") ? "" : "\n"}${key}=${value}
909
1066
  `, "utf-8");
@@ -956,8 +1113,8 @@ async function ensureSplitDb(projectRoot, opts) {
956
1113
  }
957
1114
  appendEnvValue(projectRoot, "ADD_DATABASE_URL", `postgresql://${dbUser}:${dbPass}@127.0.0.1:${port}/${dbName}?schema=public`);
958
1115
  const addPrismaPath = resolve5(projectRoot, "prisma", "add.prisma");
959
- if (existsSync9(addPrismaPath)) {
960
- const content = readFileSync7(addPrismaPath, "utf-8");
1116
+ if (existsSync10(addPrismaPath)) {
1117
+ const content = readFileSync8(addPrismaPath, "utf-8");
961
1118
  if (!content.includes("datasource db")) {
962
1119
  writeFileSync5(addPrismaPath, `datasource db {
963
1120
  provider = "postgresql"
@@ -1023,9 +1180,9 @@ async function provisionDevUrl(projectRoot) {
1023
1180
  }
1024
1181
  function resolveAtlasBin(projectRoot) {
1025
1182
  const pkgBin = resolve5(projectRoot, "node_modules", "add-coder", "node_modules", ".bin", "atlas");
1026
- if (existsSync9(pkgBin)) return pkgBin;
1183
+ if (existsSync10(pkgBin)) return pkgBin;
1027
1184
  const local = resolve5(projectRoot, "node_modules", ".bin", "atlas");
1028
- if (existsSync9(local)) return local;
1185
+ if (existsSync10(local)) return local;
1029
1186
  return commandExists("atlas") ? "atlas" : null;
1030
1187
  }
1031
1188
  function hasNonAddTableChanges(diffSql) {
@@ -1061,8 +1218,8 @@ function buildExcludeArgs(projectRoot, splitDb) {
1061
1218
  async function runAtlasSync(projectRoot, targetUrl, splitDb, sync, opts) {
1062
1219
  const pm = detectPm(projectRoot);
1063
1220
  const schemaTo = splitDb ? resolve5(projectRoot, "prisma", "add.prisma") : resolve5(projectRoot, "prisma");
1064
- const baselinePath = join6(projectRoot, PRISMA_CONFIG.sync.backupDir, "baseline.sql");
1065
- mkdirSync5(dirname3(baselinePath), { recursive: true });
1221
+ const baselinePath = join7(projectRoot, PRISMA_CONFIG.sync.backupDir, "baseline.sql");
1222
+ mkdirSync5(dirname2(baselinePath), { recursive: true });
1066
1223
  const diffR = runCommand(pm, prismaDiffArgs(pm, schemaTo, null), { cwd: projectRoot, timeout: 6e4 });
1067
1224
  if (diffR.status !== 0) {
1068
1225
  console.warn(`\u26A0\uFE0F baseline \u751F\u6210\u5931\u8D25\uFF08${diffR.stderr.trim().slice(0, 150)}\uFF09\uFF0C\u964D\u7EA7 prisma-diff`);
@@ -1120,8 +1277,8 @@ async function runAtlasSync(projectRoot, targetUrl, splitDb, sync, opts) {
1120
1277
  function runPrismaDiffSync(projectRoot, targetUrl, splitDb) {
1121
1278
  const pm = detectPm(projectRoot);
1122
1279
  const schemaTo = splitDb ? resolve5(projectRoot, "prisma", "add.prisma") : resolve5(projectRoot, "prisma");
1123
- const diffPath = join6(projectRoot, PRISMA_CONFIG.sync.backupDir, "diff.sql");
1124
- mkdirSync5(dirname3(diffPath), { recursive: true });
1280
+ const diffPath = join7(projectRoot, PRISMA_CONFIG.sync.backupDir, "diff.sql");
1281
+ mkdirSync5(dirname2(diffPath), { recursive: true });
1125
1282
  const r = runCommand(pm, prismaDiffArgs(pm, schemaTo, targetUrl), { cwd: projectRoot, timeout: 6e4 });
1126
1283
  if (r.status !== 0) throw new Error(`prisma migrate diff \u5931\u8D25: ${r.stderr.trim().slice(0, 200)}`);
1127
1284
  writeFileSync5(diffPath, r.stdout, "utf-8");
@@ -1198,10 +1355,10 @@ function runPrismaInit(projectRoot, provider, schemaPath) {
1198
1355
  console.error(`\u2717 prisma init \u65E0\u6CD5\u6267\u884C: ${e instanceof Error ? e.message : String(e)}`);
1199
1356
  initResult = { status: null, stdout: "", stderr: "" };
1200
1357
  }
1201
- if (initResult.status !== 0 || !existsSync9(schemaPath)) {
1358
+ if (initResult.status !== 0 || !existsSync10(schemaPath)) {
1202
1359
  console.error(`\u26A0\uFE0F prisma init \u672A\u5B8C\u6210\uFF08\u9000\u51FA\u7801: ${initResult.status}\uFF09\uFF0C\u56DE\u9000\u624B\u52A8\u521B\u5EFA schema.prisma\u2014\u2014db push \u5C06\u9A8C\u8BC1\u5176\u53EF\u7528\u6027`);
1203
1360
  const prismaDir = resolve5(projectRoot, "prisma");
1204
- if (!existsSync9(prismaDir)) mkdirSync5(prismaDir, { recursive: true });
1361
+ if (!existsSync10(prismaDir)) mkdirSync5(prismaDir, { recursive: true });
1205
1362
  const content = `generator client {
1206
1363
  provider = "prisma-client-js"
1207
1364
  }
@@ -1212,7 +1369,7 @@ datasource db {
1212
1369
  `;
1213
1370
  writeFileSync5(schemaPath, content, "utf-8");
1214
1371
  const devEnvPath = resolve5(projectRoot, ".env.development");
1215
- if (!existsSync9(devEnvPath)) {
1372
+ if (!existsSync10(devEnvPath)) {
1216
1373
  const defaultUrl = provider === "sqlite" ? 'DATABASE_URL="file:./data/dev.db"' : '# \u8BF7\u7F16\u8F91\u4E3A\u4F60\u7684\u6570\u636E\u5E93\u8FDE\u63A5\u4FE1\u606F\nDATABASE_URL="postgresql://USER:PASSWORD@HOST:PORT/DB?schema=public"';
1217
1374
  writeFileSync5(devEnvPath, defaultUrl + "\n", "utf-8");
1218
1375
  console.log("\u5DF2\u521B\u5EFA .env.development");
@@ -1224,16 +1381,16 @@ datasource db {
1224
1381
  function postInitSetup(projectRoot, schemaPath, addPrismaTemplate, destPath) {
1225
1382
  const envPath = resolve5(projectRoot, ".env");
1226
1383
  const devEnvPath = resolve5(projectRoot, ".env.development");
1227
- if (existsSync9(envPath)) {
1228
- const envContent = readFileSync7(envPath, "utf-8");
1384
+ if (existsSync10(envPath)) {
1385
+ const envContent = readFileSync8(envPath, "utf-8");
1229
1386
  const dbUrl = envContent.match(/DATABASE_URL=.*/);
1230
1387
  if (dbUrl) {
1231
- const existing = existsSync9(devEnvPath) ? readFileSync7(devEnvPath, "utf-8") : "";
1388
+ const existing = existsSync10(devEnvPath) ? readFileSync8(devEnvPath, "utf-8") : "";
1232
1389
  if (!existing.includes("DATABASE_URL=")) {
1233
1390
  writeFileSync5(devEnvPath, `${existing}${existing ? "\n" : ""}${dbUrl[0]}
1234
1391
  `, "utf-8");
1235
1392
  }
1236
- if (existsSync9(envPath)) unlinkSync(envPath);
1393
+ if (existsSync10(envPath)) unlinkSync(envPath);
1237
1394
  console.log("\u5DF2\u5C06 DATABASE_URL \u8FC1\u79FB\u5230 .env.development");
1238
1395
  }
1239
1396
  }
@@ -1242,8 +1399,8 @@ function postInitSetup(projectRoot, schemaPath, addPrismaTemplate, destPath) {
1242
1399
  patchGeneratorOutput(schemaPath);
1243
1400
  }
1244
1401
  function patchGeneratorOutput(schemaPath) {
1245
- if (!existsSync9(schemaPath)) return;
1246
- let content = readFileSync7(schemaPath, "utf-8");
1402
+ if (!existsSync10(schemaPath)) return;
1403
+ let content = readFileSync8(schemaPath, "utf-8");
1247
1404
  const genBlock = content.match(/generator\s+\w+\s*\{[\s\S]*?\}/);
1248
1405
  if (!genBlock) {
1249
1406
  content += `
@@ -1268,7 +1425,7 @@ async function injectPrisma(projectRoot, addPrismaTemplate, options = {}) {
1268
1425
  const schemaPath = resolve5(prismaDir, "schema.prisma");
1269
1426
  const destPath = resolve5(prismaDir, "add.prisma");
1270
1427
  let justInited = false;
1271
- if (!existsSync9(prismaDir) || !existsSync9(schemaPath)) {
1428
+ if (!existsSync10(prismaDir) || !existsSync10(schemaPath)) {
1272
1429
  if (C.onMissing === "skip") {
1273
1430
  console.log("\u8DF3\u8FC7\uFF1A\u7F3A\u5C11 Prisma");
1274
1431
  return true;
@@ -1287,7 +1444,7 @@ async function injectPrisma(projectRoot, addPrismaTemplate, options = {}) {
1287
1444
  postInitSetup(projectRoot, schemaPath, addPrismaTemplate, destPath);
1288
1445
  justInited = true;
1289
1446
  }
1290
- if (existsSync9(destPath) && !justInited) {
1447
+ if (existsSync10(destPath) && !justInited) {
1291
1448
  if (options.dryRun) {
1292
1449
  console.log("[dry-run] \u5DF2\u6709 add.prisma");
1293
1450
  return true;
@@ -1304,8 +1461,8 @@ async function injectPrisma(projectRoot, addPrismaTemplate, options = {}) {
1304
1461
  console.log("\u8986\u76D6");
1305
1462
  } else if (choice === "d") {
1306
1463
  copyFileSync(destPath, destPath + ".bak");
1307
- console.log("=== \u5F53\u524D\uFF08\u5DF2\u5907\u4EFD\uFF09===\n" + readFileSync7(destPath, "utf-8"));
1308
- console.log("=== \u6A21\u677F ===\n" + readFileSync7(addPrismaTemplate, "utf-8"));
1464
+ console.log("=== \u5F53\u524D\uFF08\u5DF2\u5907\u4EFD\uFF09===\n" + readFileSync8(destPath, "utf-8"));
1465
+ console.log("=== \u6A21\u677F ===\n" + readFileSync8(addPrismaTemplate, "utf-8"));
1309
1466
  if (await ask("\u786E\u8BA4\u8986\u76D6\uFF1F[y/N] ") !== "y") {
1310
1467
  console.log("\u5DF2\u8DF3\u8FC7");
1311
1468
  return true;
@@ -1353,22 +1510,22 @@ ${detail}` : ""}`);
1353
1510
  }
1354
1511
 
1355
1512
  // src/cli/prisma-injector.ts
1356
- var __filename2 = fileURLToPath2(import.meta.url);
1357
- var __dirname2 = dirname4(__filename2);
1358
- var ADD_PRISMA_TEMPLATE = resolve6(__dirname2, "../templates/core/prisma/add.prisma");
1513
+ var __dirname3 = import.meta.dirname;
1514
+ var ADD_PRISMA_TEMPLATE = resolve6(__dirname3, "../templates/core/prisma/add.prisma");
1359
1515
  async function injectPrisma2(projectRoot, options = {}) {
1360
1516
  return injectPrisma(projectRoot, ADD_PRISMA_TEMPLATE, options);
1361
1517
  }
1362
1518
 
1363
1519
  // src/cli/commands/init.ts
1364
- import { readFileSync as readFileSync9, writeFileSync as writeFileSync6, existsSync as existsSync11, mkdirSync as mkdirSync6, copyFileSync as copyFileSync2, readdirSync as readdirSync4 } from "fs";
1520
+ import { readFileSync as readFileSync10, writeFileSync as writeFileSync6, existsSync as existsSync12, mkdirSync as mkdirSync6, copyFileSync as copyFileSync2, readdirSync as readdirSync5 } from "fs";
1365
1521
  import { createHash } from "crypto";
1366
- import { resolve as resolve8 } from "path";
1522
+ import { resolve as resolve8, dirname as dirname3, join as pathJoin } from "path";
1523
+ import { homedir as homedir2 } from "os";
1367
1524
  import { createConnection as createConnection2 } from "net";
1368
1525
 
1369
1526
  // src/lib/model-predownload.ts
1370
- import { existsSync as existsSync10, readFileSync as readFileSync8 } from "fs";
1371
- import { join as join7, resolve as resolve7 } from "path";
1527
+ import { existsSync as existsSync11, readFileSync as readFileSync9 } from "fs";
1528
+ import { join as join8, resolve as resolve7 } from "path";
1372
1529
  import { homedir } from "os";
1373
1530
  import { parse as parse2 } from "smol-toml";
1374
1531
  var DEFAULT_DOWNLOAD_TIMEOUT_MS = 5 * 60 * 1e3;
@@ -1377,13 +1534,13 @@ var TOML_CANDIDATES = [
1377
1534
  resolve7(import.meta.dirname, "../caijuehub/dps-scoring-rules.toml")
1378
1535
  ];
1379
1536
  function resolveEmbeddingModel() {
1380
- const tomlPath = TOML_CANDIDATES.find((p) => existsSync10(p));
1537
+ const tomlPath = TOML_CANDIDATES.find((p) => existsSync11(p));
1381
1538
  if (!tomlPath) {
1382
1539
  throw new Error(
1383
1540
  `dps-scoring-rules.toml \u672A\u627E\u5230\uFF08\u671F\u671B\u8DEF\u5F84: ${TOML_CANDIDATES.join(" \u6216 ")}\uFF09`
1384
1541
  );
1385
1542
  }
1386
- const cfg = parse2(readFileSync8(tomlPath, "utf-8"));
1543
+ const cfg = parse2(readFileSync9(tomlPath, "utf-8"));
1387
1544
  const model = cfg.embedding?.model;
1388
1545
  if (typeof model !== "string" || model.length === 0) {
1389
1546
  throw new Error(`dps-scoring-rules.toml [embedding] model \u672A\u914D\u7F6E\uFF08${tomlPath}\uFF09`);
@@ -1393,8 +1550,8 @@ function resolveEmbeddingModel() {
1393
1550
  function resolveCacheDir() {
1394
1551
  const hubCache = process.env.HF_HUB_CACHE;
1395
1552
  if (hubCache) return hubCache;
1396
- const home = process.env.HF_HOME || join7(homedir(), ".cache", "huggingface");
1397
- return join7(home, "hub");
1553
+ const home = process.env.HF_HOME || join8(homedir(), ".cache", "huggingface");
1554
+ return join8(home, "hub");
1398
1555
  }
1399
1556
  function modelCacheName(model) {
1400
1557
  const parts = model.split("/");
@@ -1404,7 +1561,7 @@ function modelCacheName(model) {
1404
1561
  }
1405
1562
  function isModelCached(model) {
1406
1563
  const cacheDir = resolveCacheDir();
1407
- return existsSync10(join7(cacheDir, modelCacheName(model), "snapshots"));
1564
+ return existsSync11(join8(cacheDir, modelCacheName(model), "snapshots"));
1408
1565
  }
1409
1566
  async function ensureEmbeddingModel(options) {
1410
1567
  const force = options?.force ?? false;
@@ -1417,8 +1574,8 @@ async function ensureEmbeddingModel(options) {
1417
1574
  const { pipeline, env } = await import("@huggingface/transformers");
1418
1575
  env.cacheDir = resolveCacheDir();
1419
1576
  const cacheDir = env.cacheDir;
1420
- const snapshotsDir = join7(cacheDir, modelCacheName(model), "snapshots");
1421
- if (!force && existsSync10(snapshotsDir)) {
1577
+ const snapshotsDir = join8(cacheDir, modelCacheName(model), "snapshots");
1578
+ if (!force && existsSync11(snapshotsDir)) {
1422
1579
  return { status: "already-cached", model, cacheDir };
1423
1580
  }
1424
1581
  env.remoteHost = "https://hf-mirror.com";
@@ -1450,8 +1607,17 @@ var ADAPTER_RENDERERS = {
1450
1607
  trae: renderAdapter4,
1451
1608
  codex: renderAdapter5
1452
1609
  };
1453
- var MAGIC_DIR_MAP = { claude: ".claude", qoder: ".qoder", vscode: ".vscode", trae: ".trae", codex: ".codex" };
1610
+ function coreTargetsForAdapter(target, magicDir) {
1611
+ return target === "codex" ? [magicDir] : [ADD_DIR, magicDir];
1612
+ }
1613
+ function needsClaudeAgentHost(adapters) {
1614
+ return adapters.includes("vscode") || adapters.includes("trae");
1615
+ }
1454
1616
  async function initCommand(options) {
1617
+ if (options.printMcpConfig || options.writeUserConfig) {
1618
+ await handleCodexMcpConfig(options);
1619
+ return;
1620
+ }
1455
1621
  const ctx = await prepare(options);
1456
1622
  writeComposeEnv(ctx);
1457
1623
  if (!options.dryRun) {
@@ -1474,9 +1640,54 @@ async function initCommand(options) {
1474
1640
  }
1475
1641
  }
1476
1642
  }
1643
+ async function handleCodexMcpConfig(options) {
1644
+ const projectRoot = process.cwd();
1645
+ const magicDir = magicDirFor(options.adapter ?? "codex");
1646
+ const config = await loadConfig(projectRoot, options.config, { force: true });
1647
+ config.projectRoot = projectRoot;
1648
+ config.magicDir = magicDir;
1649
+ const files = renderAdapter5(config, projectRoot, !!options.dryRun, magicDir);
1650
+ const tomlEntry = [...files.entries()].find(
1651
+ ([rel]) => rel.endsWith("config.toml.example") || rel.endsWith("config.toml")
1652
+ );
1653
+ if (!tomlEntry) {
1654
+ console.error("\u2717 config.toml \u6E32\u67D3\u4EA7\u7269\u7F3A\u5931\uFF08\u68C0\u67E5 templates/adapters/codex/config.toml.example\uFF09");
1655
+ process.exit(1);
1656
+ }
1657
+ const toml = tomlEntry[1];
1658
+ if (options.printMcpConfig) {
1659
+ console.log("\n# \u5C06\u4EE5\u4E0B\u7247\u6BB5\u7C98\u8D34\u5230 ~/.codex/config.toml\uFF08\u6216\u4F7F\u7528 --write-user-config \u81EA\u52A8\u5199\u5165\uFF09");
1660
+ console.log(toml);
1661
+ }
1662
+ if (options.writeUserConfig) {
1663
+ const userConfigPath = pathJoin(homedir2(), ".codex", "config.toml");
1664
+ if (options.dryRun) {
1665
+ console.log(`[dry-run] \u5C06\u5199\u5165 ${userConfigPath}`);
1666
+ return;
1667
+ }
1668
+ if (existsSync12(userConfigPath) && readFileSync10(userConfigPath, "utf-8").includes("mcp_servers.add_coder")) {
1669
+ console.log(`\u26A0\uFE0F ${userConfigPath} \u5DF2\u5B58\u5728 mcp_servers.add_coder \u914D\u7F6E\uFF0C\u8DF3\u8FC7\uFF08\u907F\u514D\u91CD\u590D\uFF09`);
1670
+ return;
1671
+ }
1672
+ const answer = await ask(`\u5199\u5165 ${userConfigPath}\uFF1F\uFF08y/N\uFF09 \u2192 `);
1673
+ if (answer.trim().toLowerCase() !== "y") {
1674
+ console.log("\u5DF2\u53D6\u6D88\u5199\u5165");
1675
+ return;
1676
+ }
1677
+ if (existsSync12(userConfigPath)) {
1678
+ const backup = `${userConfigPath}.bak-${Date.now()}`;
1679
+ copyFileSync2(userConfigPath, backup);
1680
+ console.log(`\u5DF2\u5907\u4EFD\u539F\u914D\u7F6E \u2192 ${backup}`);
1681
+ }
1682
+ mkdirSync6(dirname3(userConfigPath), { recursive: true });
1683
+ writeFileSync6(userConfigPath, `
1684
+ ${toml}`, { flag: "a" });
1685
+ console.log(`\u2705 \u5DF2\u5199\u5165 ${userConfigPath}\uFF0C\u91CD\u542F Codex \u751F\u6548`);
1686
+ }
1687
+ }
1477
1688
  async function resolveAdapter(projectRoot, specified) {
1478
1689
  if (specified) {
1479
- if (!MAGIC_DIR_MAP[specified]) throw new Error(`\u672A\u77E5 adapter: ${specified}`);
1690
+ if (!magicDirFor(specified)) throw new Error(`\u672A\u77E5 adapter: ${specified}`);
1480
1691
  console.log(`\u76EE\u6807 IDE: ${specified} (--adapter)`);
1481
1692
  return specified;
1482
1693
  }
@@ -1639,18 +1850,18 @@ main().catch((e) => { console.error(e); process.exit(1); });
1639
1850
  console.log(`[dry-run] \u5C06\u5199\u5165 ${scriptPath}`);
1640
1851
  return;
1641
1852
  }
1642
- if (!existsSync11(scriptsDir)) mkdirSync6(scriptsDir, { recursive: true });
1853
+ if (!existsSync12(scriptsDir)) mkdirSync6(scriptsDir, { recursive: true });
1643
1854
  writeFileSync6(scriptPath, content, "utf-8");
1644
1855
  console.log("\u5DF2\u751F\u6210 scripts/export-db.ts");
1645
1856
  }
1646
1857
  function injectDbExportScript(projectRoot, dryRun) {
1647
1858
  const pkgPath = resolve8(projectRoot, "package.json");
1648
- if (!existsSync11(pkgPath)) return;
1859
+ if (!existsSync12(pkgPath)) return;
1649
1860
  if (dryRun) {
1650
1861
  console.log("[dry-run] \u5C06\u6CE8\u5165 db:export");
1651
1862
  return;
1652
1863
  }
1653
- const pkg = JSON.parse(readFileSync9(pkgPath, "utf-8"));
1864
+ const pkg = JSON.parse(readFileSync10(pkgPath, "utf-8"));
1654
1865
  if (!pkg.scripts) pkg.scripts = {};
1655
1866
  if (!pkg.scripts["db:export"]) {
1656
1867
  pkg.scripts["db:export"] = "npx tsx scripts/export-db.ts";
@@ -1677,7 +1888,7 @@ async function resolveStack(options) {
1677
1888
  async function prepare(options) {
1678
1889
  const projectRoot = process.cwd();
1679
1890
  const target = await resolveAdapter(projectRoot, options.adapter);
1680
- const magicDir = MAGIC_DIR_MAP[target];
1891
+ const magicDir = magicDirFor(target);
1681
1892
  const config = await loadConfig(projectRoot, options.config, { force: options.force });
1682
1893
  config.projectRoot = projectRoot;
1683
1894
  config.magicDir = magicDir;
@@ -1698,14 +1909,14 @@ function writeComposeEnv(ctx) {
1698
1909
  if (!db.reuseExisting) {
1699
1910
  const composeName = db.container === "podman" ? "podman-compose.add.yml" : "docker-compose.add.yml";
1700
1911
  const composePath = resolve8(projectRoot, composeName);
1701
- if (!options.dryRun && (!existsSync11(composePath) || options.force)) {
1912
+ if (!options.dryRun && (!existsSync12(composePath) || options.force)) {
1702
1913
  writeFileSync6(composePath, composeContent(config.projectName || "add-project"), "utf-8");
1703
1914
  console.log(`\u5DF2\u521B\u5EFA ${composeName}`);
1704
1915
  }
1705
1916
  }
1706
1917
  const devEnvPath = resolve8(projectRoot, ".env.development");
1707
- if (!options.dryRun && existsSync11(devEnvPath)) {
1708
- const existing = readFileSync9(devEnvPath, "utf-8");
1918
+ if (!options.dryRun && existsSync12(devEnvPath)) {
1919
+ const existing = readFileSync10(devEnvPath, "utf-8");
1709
1920
  if (!/^DATABASE_USER=/m.test(existing)) {
1710
1921
  writeFileSync6(devEnvPath, existing + `
1711
1922
  DATABASE_USER=${db.user || "admin"}
@@ -1720,32 +1931,39 @@ PROJECT_NAME=${config.projectName || "add-project"}
1720
1931
  async function renderAndWrite(ctx) {
1721
1932
  const { projectRoot, options, magicDir, config, target } = ctx;
1722
1933
  const dry = !!options.dryRun;
1723
- const coreFiles = renderCore(config, dry);
1724
- console.log(`Core \u6A21\u677F: ${coreFiles.size} \u6587\u4EF6`);
1725
- const allFiles = /* @__PURE__ */ new Map();
1726
- for (const [relPath, content] of coreFiles) {
1727
- for (const t of [".add", magicDir]) {
1728
- const targetPath = relPath.replace(/^\.add/, t);
1729
- if (!allFiles.has(targetPath)) allFiles.set(targetPath, content);
1730
- }
1731
- }
1934
+ const coreTargets = coreTargetsForAdapter(target, magicDir);
1935
+ const allFiles = renderCoreForTargets(config, dry, coreTargets);
1936
+ console.log(`Core \u6A21\u677F: ${allFiles.size} \u4E2A\u5E76\u5217\u76EE\u6807\u6587\u4EF6`);
1732
1937
  const resolved = resolveAdapters2(target);
1733
1938
  for (const adapter of resolved) {
1734
1939
  const renderFn = ADAPTER_RENDERERS[adapter];
1735
1940
  if (renderFn) {
1736
- const adapterFiles = renderFn(config, projectRoot, dry, magicDir);
1941
+ const adapterMagicDir = magicDirFor(adapter);
1942
+ const adapterFiles = renderFn(
1943
+ { ...config, magicDir: adapterMagicDir },
1944
+ projectRoot,
1945
+ dry,
1946
+ adapterMagicDir
1947
+ );
1737
1948
  for (const [p, c] of adapterFiles) allFiles.set(p, c);
1738
1949
  console.log(`${adapter} adapter: ${adapterFiles.size} \u6587\u4EF6`);
1739
1950
  }
1740
1951
  }
1741
- if (resolved.includes("vscode") || resolved.includes("trae") || resolved.includes("codex")) {
1742
- const claudeFiles = renderAdapter(config, projectRoot, dry, ".claude");
1952
+ if (needsClaudeAgentHost(resolved)) {
1953
+ const claudeMagicDir = magicDirFor("claude");
1954
+ const claudeFiles = renderAdapter(
1955
+ { ...config, magicDir: claudeMagicDir },
1956
+ projectRoot,
1957
+ dry,
1958
+ claudeMagicDir
1959
+ );
1743
1960
  for (const [p, c] of claudeFiles) allFiles.set(p, c);
1744
1961
  console.log(`claude adapter (via Agent Host): ${claudeFiles.size} \u6587\u4EF6`);
1745
1962
  }
1746
- for (const d of [".add", magicDir]) {
1963
+ const reviewTargets = target === "codex" ? [magicDir] : [ADD_DIR, magicDir];
1964
+ for (const d of reviewTargets) {
1747
1965
  const reviewsDir = resolve8(projectRoot, d, "reviews");
1748
- if (!existsSync11(reviewsDir)) {
1966
+ if (!existsSync12(reviewsDir)) {
1749
1967
  if (dry) {
1750
1968
  console.log(`[dry-run] \u5C06\u521B\u5EFA ${reviewsDir}/`);
1751
1969
  } else {
@@ -1757,7 +1975,7 @@ async function renderAndWrite(ctx) {
1757
1975
  const hashMap = {};
1758
1976
  let npmVer = "";
1759
1977
  try {
1760
- npmVer = JSON.parse(readFileSync9(resolve8(projectRoot, "node_modules", "add-coder", "templates", ".add-coder-src-hash.json"), "utf-8"))._version ?? "";
1978
+ npmVer = JSON.parse(readFileSync10(resolve8(projectRoot, "node_modules", "add-coder", "templates", ".add-coder-src-hash.json"), "utf-8"))._version ?? "";
1761
1979
  } catch {
1762
1980
  }
1763
1981
  for (const [rp, c] of allFiles) {
@@ -1796,7 +2014,7 @@ ${bashRun.stderr.trim().split("\n").slice(0, 5).join("\n")}` : ""}`;
1796
2014
  }
1797
2015
  if (db.engine === "postgresql" && db.container === "manual") {
1798
2016
  const dbScript = resolve8(projectRoot, magicDir, "scripts", "db-ensure.sh");
1799
- if (existsSync11(dbScript)) {
2017
+ if (existsSync12(dbScript)) {
1800
2018
  try {
1801
2019
  const bashRun = runCommand("bash", [dbScript, "postgresql", "manual"], { cwd: projectRoot, stdio: "inherit" });
1802
2020
  if (bashRun.status !== 0) fail = `db-ensure.sh \u9000\u51FA\u7801: ${bashRun.status}${bashRun.stderr ? `
@@ -1846,12 +2064,12 @@ function deployDocs(ctx) {
1846
2064
  for (const d of ["00-\u9700\u6C42", "01-\u67B6\u6784", "02-\u89C4\u8303"]) {
1847
2065
  const srcDir = resolve8(groundingSrc, d);
1848
2066
  const destDir = resolve8(docsBase, d);
1849
- if (!existsSync11(destDir)) mkdirSync6(destDir, { recursive: true });
1850
- if (!existsSync11(srcDir)) continue;
1851
- for (const f of readdirSync4(srcDir)) {
2067
+ if (!existsSync12(destDir)) mkdirSync6(destDir, { recursive: true });
2068
+ if (!existsSync12(srcDir)) continue;
2069
+ for (const f of readdirSync5(srcDir)) {
1852
2070
  const src = resolve8(srcDir, f);
1853
2071
  const dest = resolve8(destDir, f);
1854
- if (existsSync11(dest)) continue;
2072
+ if (existsSync12(dest)) continue;
1855
2073
  try {
1856
2074
  copyFileSync2(src, dest);
1857
2075
  } catch {
@@ -1867,7 +2085,7 @@ function finalize(ctx, result, dbFail) {
1867
2085
  return;
1868
2086
  }
1869
2087
  if (db.engine === "sqlite") console.log("\u6570\u636E\u5907\u4EFD: npm run db:export \u2192 data/exports/");
1870
- const pkg = JSON.parse(readFileSync9(resolve8(import.meta.dirname, "../package.json"), "utf-8"));
2088
+ const pkg = JSON.parse(readFileSync10(resolve8(import.meta.dirname, "../package.json"), "utf-8"));
1871
2089
  const peerNames = Object.keys(pkg.peerDependencies || {});
1872
2090
  if (peerNames.length > 0) {
1873
2091
  console.log(`
@@ -1895,8 +2113,8 @@ function finalize(ctx, result, dbFail) {
1895
2113
  }
1896
2114
 
1897
2115
  // src/cli/commands/sync.ts
1898
- import { existsSync as existsSync12, readFileSync as readFileSync10, writeFileSync as writeFileSync7, mkdirSync as mkdirSync7 } from "fs";
1899
- import { resolve as resolve9, dirname as dirname5 } from "path";
2116
+ import { existsSync as existsSync13, readFileSync as readFileSync11, writeFileSync as writeFileSync7, mkdirSync as mkdirSync7 } from "fs";
2117
+ import { resolve as resolve9, dirname as dirname4 } from "path";
1900
2118
  import { createHash as createHash2 } from "crypto";
1901
2119
 
1902
2120
  // src/lib/path-normalize.ts
@@ -1931,10 +2149,9 @@ var ADAPTER_RENDERERS2 = {
1931
2149
  trae: renderAdapter4,
1932
2150
  codex: renderAdapter5
1933
2151
  };
1934
- var MAGIC_DIR_MAP2 = { claude: ".claude", qoder: ".qoder", vscode: ".vscode", trae: ".trae", codex: ".codex" };
1935
2152
  function resolveAdapter2(projectRoot, specified) {
1936
2153
  if (specified) {
1937
- if (!MAGIC_DIR_MAP2[specified]) throw new Error(`\u672A\u77E5 adapter: ${specified}`);
2154
+ if (!magicDirFor(specified)) throw new Error(`\u672A\u77E5 adapter: ${specified}`);
1938
2155
  console.log(`\u76EE\u6807 IDE: ${specified} (--adapter)`);
1939
2156
  return specified;
1940
2157
  }
@@ -1952,9 +2169,25 @@ function isUserData(p) {
1952
2169
  function hash8(c) {
1953
2170
  return createHash2("sha256").update(c).digest("hex").slice(0, SYNC_CONFIG.HASH_HEX_LENGTH);
1954
2171
  }
2172
+ function classifyPatchCandidate(currentContent, incomingContent, storedHash) {
2173
+ const currentHash = hash8(currentContent);
2174
+ if (currentHash === hash8(incomingContent)) return "same";
2175
+ if (storedHash && currentHash === storedHash) return "update";
2176
+ return "conflict";
2177
+ }
2178
+ function coreTargetsForAdapter2(target, magicDir = magicDirFor(target)) {
2179
+ return target === "codex" ? [magicDir] : [ADD_DIR, magicDir];
2180
+ }
2181
+ function needsClaudeAgentHost2(adapters) {
2182
+ return adapters.includes("vscode") || adapters.includes("trae");
2183
+ }
2184
+ function resolvePatchConflictMode(yes = false, isTTY = Boolean(process.stdin.isTTY)) {
2185
+ if (yes) return "all";
2186
+ return isTTY ? "interactive" : "skip";
2187
+ }
1955
2188
  function loadHashFile(root, magic) {
1956
2189
  try {
1957
- const raw = JSON.parse(readFileSync10(resolve9(root, magic, SYNC_CONFIG.HASH_OUTPUT_FILE), "utf-8"));
2190
+ const raw = JSON.parse(readFileSync11(resolve9(root, magic, SYNC_CONFIG.HASH_OUTPUT_FILE), "utf-8"));
1958
2191
  const normalized = {};
1959
2192
  for (const [k, v] of Object.entries(raw)) normalized[normalizeRelPath(k)] = v;
1960
2193
  return normalized;
@@ -1964,7 +2197,7 @@ function loadHashFile(root, magic) {
1964
2197
  }
1965
2198
  function loadVersionFile(root, magic) {
1966
2199
  try {
1967
- return readFileSync10(resolve9(root, magic, SYNC_CONFIG.VERSION_SENTINEL), "utf-8").trim();
2200
+ return readFileSync11(resolve9(root, magic, SYNC_CONFIG.VERSION_SENTINEL), "utf-8").trim();
1968
2201
  } catch {
1969
2202
  return "";
1970
2203
  }
@@ -1972,21 +2205,40 @@ function loadVersionFile(root, magic) {
1972
2205
  function saveVersionFile(root, magic, version2) {
1973
2206
  writeFileSync7(resolve9(root, magic, SYNC_CONFIG.VERSION_SENTINEL), version2 + "\n", "utf-8");
1974
2207
  }
2208
+ function loadTemplateVersion(root) {
2209
+ const candidates = [
2210
+ resolve9(root, "node_modules", "add-coder", "templates", ".add-coder-src-hash.json"),
2211
+ resolve9(import.meta.dirname, "../templates/.add-coder-src-hash.json"),
2212
+ resolve9(import.meta.dirname, "../../../templates/.add-coder-src-hash.json")
2213
+ ];
2214
+ for (const candidate of candidates) {
2215
+ try {
2216
+ return JSON.parse(readFileSync11(candidate, "utf-8"))._version ?? "";
2217
+ } catch {
2218
+ }
2219
+ }
2220
+ return "";
2221
+ }
1975
2222
  function saveHashFile(root, magic, files) {
1976
2223
  const m = {};
1977
2224
  for (const [p, c] of files) m[p] = c;
1978
2225
  writeFileSync7(resolve9(root, magic, SYNC_CONFIG.HASH_OUTPUT_FILE), JSON.stringify(m, null, 2) + "\n", "utf-8");
1979
2226
  }
1980
- function mergeFullHash(outHash, candidates, readDiskHash) {
2227
+ function mergeFullHash(outHash, candidates, readDiskHash, preserveKeys = /* @__PURE__ */ new Set()) {
1981
2228
  const finalHash = /* @__PURE__ */ new Map();
1982
2229
  for (const [k, v] of Object.entries(outHash)) finalHash.set(k, v);
1983
2230
  for (const { relPath, absPath } of candidates) {
1984
2231
  const key = normalizeRelPath(relPath);
2232
+ if (preserveKeys.has(key)) continue;
1985
2233
  const h = readDiskHash(absPath);
1986
2234
  if (h !== null) finalHash.set(key, h);
1987
2235
  }
1988
2236
  return finalHash;
1989
2237
  }
2238
+ function pruneHashToCandidates(hashes, candidatePaths) {
2239
+ const allowed = new Set([...candidatePaths].map(normalizeRelPath));
2240
+ return new Map([...hashes].filter(([key]) => allowed.has(normalizeRelPath(key))));
2241
+ }
1990
2242
  async function maybeModelDownload(options) {
1991
2243
  let model;
1992
2244
  try {
@@ -2010,30 +2262,35 @@ async function maybeModelDownload(options) {
2010
2262
  async function syncCommand(options = {}) {
2011
2263
  const projectRoot = process.cwd();
2012
2264
  const target = resolveAdapter2(projectRoot, options.adapter);
2013
- const magicDir = MAGIC_DIR_MAP2[target];
2265
+ const magicDir = magicDirFor(target);
2014
2266
  const config = await loadConfig(projectRoot);
2015
2267
  config.projectRoot = projectRoot;
2016
2268
  config.magicDir = magicDir;
2017
- const coreFiles = renderCore(config, false);
2018
- const CORE_TARGETS = [".add", magicDir];
2019
- const allFiles = /* @__PURE__ */ new Map();
2020
- for (const [relPath, content] of coreFiles) {
2021
- for (const t of CORE_TARGETS) {
2022
- const targetPath = relPath.replace(/^\.add/, t);
2023
- if (!allFiles.has(targetPath)) allFiles.set(targetPath, content);
2024
- }
2025
- }
2269
+ const coreTargets = coreTargetsForAdapter2(target, magicDir);
2270
+ const allFiles = renderCoreForTargets(config, false, coreTargets);
2026
2271
  const resolved = resolveAdapters2(target);
2027
2272
  for (const adapter of resolved) {
2028
2273
  const renderFn = ADAPTER_RENDERERS2[adapter];
2029
2274
  if (renderFn) {
2030
- const adapterFiles = renderFn(config, projectRoot, false, magicDir);
2275
+ const adapterMagicDir = magicDirFor(adapter);
2276
+ const adapterFiles = renderFn(
2277
+ { ...config, magicDir: adapterMagicDir },
2278
+ projectRoot,
2279
+ false,
2280
+ adapterMagicDir
2281
+ );
2031
2282
  for (const [p, c] of adapterFiles) allFiles.set(p, c);
2032
2283
  console.log(`${adapter} adapter: ${adapterFiles.size} \u6587\u4EF6`);
2033
2284
  }
2034
2285
  }
2035
- if (resolved.includes("vscode") || resolved.includes("trae") || resolved.includes("codex")) {
2036
- const claudeFiles = renderAdapter(config, projectRoot, false, ".claude");
2286
+ if (needsClaudeAgentHost2(resolved)) {
2287
+ const claudeMagicDir = magicDirFor("claude");
2288
+ const claudeFiles = renderAdapter(
2289
+ { ...config, magicDir: claudeMagicDir },
2290
+ projectRoot,
2291
+ false,
2292
+ claudeMagicDir
2293
+ );
2037
2294
  for (const [p, c] of claudeFiles) allFiles.set(p, c);
2038
2295
  console.log(`claude adapter (via Agent Host): ${claudeFiles.size} \u6587\u4EF6`);
2039
2296
  }
@@ -2048,12 +2305,7 @@ async function syncCommand(options = {}) {
2048
2305
  candidates.set(p, c);
2049
2306
  }
2050
2307
  const outHash = loadHashFile(projectRoot, magicDir);
2051
- const srcHashPath = resolve9(projectRoot, "node_modules", "add-coder", "templates", ".add-coder-src-hash.json");
2052
- let npmVersion = "";
2053
- try {
2054
- npmVersion = JSON.parse(readFileSync10(srcHashPath, "utf-8"))._version ?? "";
2055
- } catch {
2056
- }
2308
+ const npmVersion = loadTemplateVersion(projectRoot);
2057
2309
  const installedVersion = loadVersionFile(projectRoot, magicDir);
2058
2310
  const isFirstPatch = !installedVersion;
2059
2311
  const isUpgrade = installedVersion && npmVersion && installedVersion !== npmVersion;
@@ -2072,18 +2324,19 @@ async function syncCommand(options = {}) {
2072
2324
  for (const [relPath, content] of candidates) {
2073
2325
  const key = normalizeRelPath(relPath);
2074
2326
  const absPath = resolve9(projectRoot, relPath);
2075
- if (!existsSync12(absPath)) {
2327
+ if (!existsSync13(absPath)) {
2076
2328
  missingFiles.set(key, content);
2077
2329
  } else if (establishBaseline) {
2078
2330
  missingFiles.set(key, content);
2079
2331
  } else {
2080
- const curH = hash8(readFileSync10(absPath, "utf-8"));
2081
- const storedH = outHash[key];
2082
- if (storedH && curH === storedH) {
2083
- sameCount++;
2084
- } else {
2085
- conflictFiles.set(key, content);
2086
- }
2332
+ const classification = classifyPatchCandidate(
2333
+ readFileSync11(absPath, "utf-8"),
2334
+ content,
2335
+ outHash[key]
2336
+ );
2337
+ if (classification === "same") sameCount++;
2338
+ else if (classification === "update") missingFiles.set(key, content);
2339
+ else conflictFiles.set(key, content);
2087
2340
  }
2088
2341
  }
2089
2342
  console.log(`patch: ${allFiles.size} \u2192 skip user ${skipped} | missing ${missingFiles.size} | conflict ${conflictFiles.size} | same ${sameCount}`);
@@ -2091,23 +2344,39 @@ async function syncCommand(options = {}) {
2091
2344
  const r = await writeFiles2(projectRoot, missingFiles, { force: true, yes: true });
2092
2345
  console.log(` missing: \u65B0\u5EFA ${r.created}`);
2093
2346
  }
2347
+ const unresolvedConflictKeys = /* @__PURE__ */ new Set();
2094
2348
  if (conflictFiles.size > 0) {
2095
- const sel = await selectFiles(projectRoot, conflictFiles);
2349
+ const conflictMode = resolvePatchConflictMode(options.yes);
2350
+ let sel = /* @__PURE__ */ new Map();
2351
+ if (conflictMode === "all") {
2352
+ sel = conflictFiles;
2353
+ } else if (conflictMode === "interactive") {
2354
+ sel = await selectFiles(projectRoot, conflictFiles);
2355
+ } else {
2356
+ console.warn(` conflict: \u975E\u4EA4\u4E92\u7EC8\u7AEF\u9ED8\u8BA4\u8DF3\u8FC7 ${conflictFiles.size} \u4E2A\u51B2\u7A81\u6587\u4EF6\uFF1B\u5982\u9700\u5168\u90E8\u8986\u76D6\u8BF7\u91CD\u8DD1\u5E76\u663E\u5F0F\u4F20\u5165 --yes`);
2357
+ }
2096
2358
  if (sel.size > 0) {
2097
2359
  const r = await writeFiles2(projectRoot, sel, { force: true, yes: true });
2098
2360
  console.log(` conflict: \u8986\u76D6 ${r.overwritten}, \u8DF3\u8FC7 ${conflictFiles.size - sel.size}`);
2099
- } else {
2361
+ } else if (conflictMode === "interactive") {
2100
2362
  console.log(` conflict: \u7528\u6237\u53D6\u6D88\uFF0C${conflictFiles.size} \u4E2A\u6587\u4EF6\u672A\u5199\u5165`);
2101
2363
  }
2364
+ for (const key of conflictFiles.keys()) {
2365
+ if (!sel.has(key)) unresolvedConflictKeys.add(key);
2366
+ }
2102
2367
  }
2103
2368
  if (missingFiles.size === 0 && conflictFiles.size === 0) {
2104
2369
  console.log(SYNC_CONFIG.PROMPT_PATCH_DONE);
2105
2370
  }
2106
- const finalHash = mergeFullHash(
2371
+ let finalHash = mergeFullHash(
2107
2372
  outHash,
2108
2373
  [...candidates].map(([relPath]) => ({ relPath, absPath: resolve9(projectRoot, relPath) })),
2109
- (absPath) => existsSync12(absPath) ? hash8(readFileSync10(absPath, "utf-8")) : null
2374
+ (absPath) => existsSync13(absPath) ? hash8(readFileSync11(absPath, "utf-8")) : null,
2375
+ unresolvedConflictKeys
2110
2376
  );
2377
+ if (target === "codex") {
2378
+ finalHash = pruneHashToCandidates(finalHash, candidates.keys());
2379
+ }
2111
2380
  saveHashFile(projectRoot, magicDir, finalHash);
2112
2381
  saveVersionFile(projectRoot, magicDir, npmVersion);
2113
2382
  await checkPrismaDiff(projectRoot, options);
@@ -2117,7 +2386,7 @@ async function syncCommand(options = {}) {
2117
2386
  }
2118
2387
  const missing = /* @__PURE__ */ new Map();
2119
2388
  for (const [relPath, content] of allFiles) {
2120
- if (!existsSync12(resolve9(projectRoot, relPath))) {
2389
+ if (!existsSync13(resolve9(projectRoot, relPath))) {
2121
2390
  missing.set(relPath, content);
2122
2391
  }
2123
2392
  }
@@ -2141,7 +2410,7 @@ async function syncCommand(options = {}) {
2141
2410
  }
2142
2411
  function printMigrateGuidance(targetPath, changed) {
2143
2412
  const g = SYNC_PRISMA_CONFIG.POST_SYNC;
2144
- const isManaged = existsSync12(resolve9(dirname5(targetPath), "migrations"));
2413
+ const isManaged = existsSync13(resolve9(dirname4(targetPath), "migrations"));
2145
2414
  const actions = isManaged ? g.MANAGED_ACTIONS : g.UNMANAGED_ACTIONS;
2146
2415
  console.log(` \u25B6 \u5DF2\u5199\u5165 ${changed} \u5904\u53D8\u66F4\uFF0C${g.HEADER}`);
2147
2416
  for (const a of actions) {
@@ -2161,20 +2430,26 @@ function printMigrateGuidance(targetPath, changed) {
2161
2430
  }
2162
2431
  console.log(` \u26A0\uFE0F \u6240\u6709\u573A\u666F\u6536\u5C3E: ${g.FINAL}`);
2163
2432
  }
2164
- async function ensureAtlasCapability(projectRoot) {
2433
+ async function ensureAtlasCapability(projectRoot, yes = false) {
2165
2434
  const bin = resolveAtlasBin(projectRoot);
2166
2435
  if (bin) {
2167
2436
  console.log(` \u2705 Atlas \u80FD\u529B\u5C31\u7EEA: ${bin}`);
2168
2437
  return;
2169
2438
  }
2439
+ if (process.platform === "win32") {
2440
+ console.warn(" \u26A0\uFE0F Atlas \u5728 Windows \u4E0D\u53EF\u7528\uFF08@ariga/atlas install.js \u4E0D\u652F\u6301 win32\uFF09");
2441
+ console.log(" \u964D\u7EA7\u8DEF\u5F84\uFF1A\u6570\u636E\u5E93\u540C\u6B65\u4F7F\u7528 prisma-diff\uFF08\u514D shadow\uFF09\uFF1B\u5982\u9700 Atlas \u8BF7\u5728 WSL \u73AF\u5883\u624B\u52A8\u5B89\u88C5");
2442
+ console.log(" \u6587\u6863: README.md \u2192 \u7AE0\u8282\u300CAtlas \u6570\u636E\u5E93\u540C\u6B65\u80FD\u529B\u300D/ DEVELOPMENT.md \xA7\u4E5D");
2443
+ return;
2444
+ }
2170
2445
  console.warn(" \u26A0\uFE0F Atlas \u4E0D\u53EF\u7528\u2014\u2014\u6570\u636E\u5E93\u540C\u6B65\uFF08Atlas diff/apply\uFF09\u80FD\u529B\u7F3A\u5931");
2171
- const a = await ask(" \u662F\u5426\u81EA\u52A8\u5B89\u88C5 @ariga/atlas\uFF08npm \u4F9D\u8D56\uFF0C\u8D70 registry\uFF09\uFF1F[Y/n] ");
2446
+ const a = yes ? "y" : await ask(" \u662F\u5426\u81EA\u52A8\u5B89\u88C5 @ariga/atlas\uFF08npm \u4F9D\u8D56\uFF0C\u8D70 registry\uFF09\uFF1F[Y/n] ");
2172
2447
  if (a === "n" || a === "no") {
2173
2448
  console.log(" \u5DF2\u8DF3\u8FC7\u3002\u6570\u636E\u5E93\u540C\u6B65\u5C06\u964D\u7EA7 prisma-diff\uFF08\u514D shadow\uFF09\uFF1B\u53EF\u968F\u65F6\u8865\u88C5\u6062\u590D Atlas");
2174
2449
  console.log(" \u6587\u6863: README.md \u2192 \u7AE0\u8282\u300CAtlas \u6570\u636E\u5E93\u540C\u6B65\u80FD\u529B\u300D/ DEVELOPMENT.md \xA7\u4E5D");
2175
2450
  return;
2176
2451
  }
2177
- const pm = existsSync12(resolve9(projectRoot, "pnpm-lock.yaml")) ? "pnpm" : "npm";
2452
+ const pm = existsSync13(resolve9(projectRoot, "pnpm-lock.yaml")) ? "pnpm" : "npm";
2178
2453
  console.log(` \u5B89\u88C5 ${pm} add -D @ariga/atlas ...`);
2179
2454
  const r = runCommand(pm, ["add", "-D", "@ariga/atlas"], { cwd: projectRoot, timeout: 18e4 });
2180
2455
  if (r.status !== 0) {
@@ -2187,8 +2462,8 @@ async function ensureAtlasCapability(projectRoot) {
2187
2462
  }
2188
2463
  function checkHostAtlasSegment(projectRoot) {
2189
2464
  const script = resolve9(projectRoot, "scripts", "db-ensure.sh");
2190
- if (!existsSync12(script)) return;
2191
- const content = readFileSync10(script, "utf-8");
2465
+ if (!existsSync13(script)) return;
2466
+ const content = readFileSync11(script, "utf-8");
2192
2467
  if (content.includes("atlas_sync")) return;
2193
2468
  console.warn(" \u26A0\uFE0F \u5BBF\u4E3B scripts/db-ensure.sh \u672A\u5305\u542B Atlas \u540C\u6B65\u6BB5\uFF08\u65E5\u5E38 db-ensure \u5C06\u7F3A\u5C11 ADD \u6CBB\u7406\u6A21\u578B\u540C\u6B65\uFF09");
2194
2469
  console.warn(" \u804C\u8D23\u8FB9\u754C: add-coder \u53EA\u540C\u6B65 ADD \u6CBB\u7406\u6A21\u578B(7 \u8868)\uFF1B\u5BBF\u4E3B\u4E1A\u52A1\u8868 diff \u63A8\u8350 Atlas \u4F46\u4E0D\u5F3A\u6C42");
@@ -2202,13 +2477,13 @@ async function checkPrismaDiff(projectRoot, options) {
2202
2477
  if (!options.patch) return;
2203
2478
  const basePath = resolve9(projectRoot, SYNC_PRISMA_CONFIG.BASE_SCHEMA);
2204
2479
  const targetPath = resolve9(projectRoot, SYNC_PRISMA_CONFIG.TARGET_PATTERN);
2205
- if (!existsSync12(basePath)) {
2480
+ if (!existsSync13(basePath)) {
2206
2481
  console.log(`
2207
2482
  \u26A0\uFE0F \u57FA\u51C6 schema \u4E0D\u5B58\u5728: ${basePath}`);
2208
2483
  console.log(` \u8BF7\u786E\u4FDD add-coder \u5DF2\u6B63\u786E\u5B89\u88C5\u3002`);
2209
2484
  return;
2210
2485
  }
2211
- await ensureAtlasCapability(projectRoot);
2486
+ await ensureAtlasCapability(projectRoot, options.yes);
2212
2487
  checkHostAtlasSegment(projectRoot);
2213
2488
  const result = diffPrisma(basePath, targetPath);
2214
2489
  if (!result.hasDiff) {
@@ -2216,7 +2491,7 @@ async function checkPrismaDiff(projectRoot, options) {
2216
2491
  \u2705 Prisma schema \u4E0E add-coder \u6807\u51C6\u4E00\u81F4\uFF0C\u65E0\u9700\u540C\u6B65\u3002`);
2217
2492
  return;
2218
2493
  }
2219
- const targetExists = existsSync12(targetPath);
2494
+ const targetExists = existsSync13(targetPath);
2220
2495
  let modifiedCount = 0;
2221
2496
  console.log(`
2222
2497
  \u26A0\uFE0F Prisma schema \u5DEE\u5F02\u68C0\u6D4B:`);
@@ -2235,7 +2510,7 @@ async function checkPrismaDiff(projectRoot, options) {
2235
2510
  }
2236
2511
  },
2237
2512
  async confirm() {
2238
- const ans = await ask(`
2513
+ const ans = options.yes ? "a" : await ask(`
2239
2514
  \u8F93\u5165\u7F16\u53F7\u9009\u62E9\u6027\u6CE8\u5165\uFF08\u5982 1,3\uFF09\uFF0Ca \u5168\u90E8\u6CE8\u5165\uFF0C\u56DE\u8F66\u8DF3\u8FC7: `);
2240
2515
  if (ans === "a" || ans === "all") {
2241
2516
  selected = [...result.missing];
@@ -2273,7 +2548,7 @@ async function checkPrismaDiff(projectRoot, options) {
2273
2548
  }
2274
2549
  },
2275
2550
  async confirm() {
2276
- const ans = await ask(` \u662F\u5426\u7528\u57FA\u51C6\u5B9A\u4E49\u8986\u76D6\u8FD9\u4E9B\u51B2\u7A81\u5B57\u6BB5\uFF1F(y/n): `);
2551
+ const ans = options.yes ? "y" : await ask(` \u662F\u5426\u7528\u57FA\u51C6\u5B9A\u4E49\u8986\u76D6\u8FD9\u4E9B\u51B2\u7A81\u5B57\u6BB5\uFF1F(y/n): `);
2277
2552
  return ans === "y" || ans === "yes";
2278
2553
  },
2279
2554
  execute() {
@@ -2294,7 +2569,7 @@ async function checkPrismaDiff(projectRoot, options) {
2294
2569
  }
2295
2570
  },
2296
2571
  async confirm() {
2297
- const ans = await ask(` \u662F\u5426\u8865\u5145\u8FD9\u4E9B\u7F3A\u5931\u5B57\u6BB5\uFF1F(y/n): `);
2572
+ const ans = options.yes ? "y" : await ask(` \u662F\u5426\u8865\u5145\u8FD9\u4E9B\u7F3A\u5931\u5B57\u6BB5\uFF1F(y/n): `);
2298
2573
  return ans === "y" || ans === "yes";
2299
2574
  },
2300
2575
  execute() {
@@ -2335,7 +2610,7 @@ async function checkPrismaDiff(projectRoot, options) {
2335
2610
  } else {
2336
2611
  console.log(`
2337
2612
  \u76EE\u6807 schema \u6587\u4EF6\u4E0D\u5B58\u5728: ${result.targetPath}`);
2338
- mkdirSync7(dirname5(targetPath), { recursive: true });
2613
+ mkdirSync7(dirname4(targetPath), { recursive: true });
2339
2614
  writeFileSync7(targetPath, "// add.prisma \u2014 ADD \u6CBB\u7406\u6A21\u578B\n\n", "utf-8");
2340
2615
  const n = injectMissingModels(targetPath, result.missing);
2341
2616
  console.log(` \u2705 \u5DF2\u521B\u5EFA\u5E76\u6CE8\u5165 ${n} \u4E2A\u6A21\u578B/\u679A\u4E3E`);
@@ -2367,7 +2642,7 @@ async function handleDiffAction(action, ctx) {
2367
2642
  }
2368
2643
  }
2369
2644
  function injectMissingModels(targetPath, models) {
2370
- let content = readFileSync10(targetPath, "utf-8");
2645
+ let content = readFileSync11(targetPath, "utf-8");
2371
2646
  content = content.replace(/\n*$/, "\n");
2372
2647
  content += `
2373
2648
  // ===== \u7531 add-coder sync --patch \u81EA\u52A8\u6CE8\u5165 (${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}) =====
@@ -2380,7 +2655,7 @@ function injectMissingModels(targetPath, models) {
2380
2655
  return models.length;
2381
2656
  }
2382
2657
  function getBaseFieldLines(basePath, modelName) {
2383
- const content = readFileSync10(basePath, "utf-8");
2658
+ const content = readFileSync11(basePath, "utf-8");
2384
2659
  const blocks = parseSchemaBlocks(content);
2385
2660
  const block = blocks.get(`model:${modelName}`) ?? blocks.get(`enum:${modelName}`);
2386
2661
  if (!block) return {};
@@ -2403,7 +2678,7 @@ function injectFieldLines(targetPath, basePath, modelName, fieldKeys) {
2403
2678
  console.warn(`\u26A0\uFE0F \u6CE8\u5165\u5931\u8D25\uFF1A${modelName} \u5728\u57FA\u51C6\u4E2D\u672A\u627E\u5230\u5B57\u6BB5\u5B9A\u4E49\uFF08${fieldKeys.length} \u4E2A\u5B57\u6BB5\u672A\u5199\u5165\uFF09`);
2404
2679
  return 0;
2405
2680
  }
2406
- const content = readFileSync10(targetPath, "utf-8");
2681
+ const content = readFileSync11(targetPath, "utf-8");
2407
2682
  const lines = content.split("\n");
2408
2683
  const blocks = parseSchemaBlocks(content);
2409
2684
  const target = blocks.get(`model:${modelName}`) ?? blocks.get(`enum:${modelName}`);
@@ -2450,7 +2725,7 @@ function injectFieldLines(targetPath, basePath, modelName, fieldKeys) {
2450
2725
  function overwriteFieldLines(targetPath, basePath, modelName, conflicts) {
2451
2726
  const baseFields = getBaseFieldLines(basePath, modelName);
2452
2727
  if (Object.keys(baseFields).length === 0) return 0;
2453
- let content = readFileSync10(targetPath, "utf-8");
2728
+ let content = readFileSync11(targetPath, "utf-8");
2454
2729
  let count = 0;
2455
2730
  for (const { fieldName } of conflicts) {
2456
2731
  const baseLine = baseFields[fieldName];
@@ -2466,7 +2741,7 @@ function overwriteFieldLines(targetPath, basePath, modelName, conflicts) {
2466
2741
  }
2467
2742
 
2468
2743
  // src/cli/commands/status.ts
2469
- import { existsSync as existsSync13 } from "fs";
2744
+ import { existsSync as existsSync14 } from "fs";
2470
2745
  import { resolve as resolve10 } from "path";
2471
2746
  async function statusCommand() {
2472
2747
  const projectRoot = process.cwd();
@@ -2476,7 +2751,7 @@ async function statusCommand() {
2476
2751
  const missing = [];
2477
2752
  const present = [];
2478
2753
  for (const [relPath] of coreFiles) {
2479
- if (existsSync13(resolve10(projectRoot, relPath))) {
2754
+ if (existsSync14(resolve10(projectRoot, relPath))) {
2480
2755
  present.push(relPath);
2481
2756
  } else {
2482
2757
  missing.push(relPath);
@@ -2494,32 +2769,30 @@ async function statusCommand() {
2494
2769
  }
2495
2770
 
2496
2771
  // src/cli/commands/stack.ts
2497
- import { readdirSync as readdirSync5, readFileSync as readFileSync11, writeFileSync as writeFileSync8, existsSync as existsSync14, mkdirSync as mkdirSync8 } from "fs";
2498
- import { resolve as resolve11, join as join8 } from "path";
2772
+ import { readdirSync as readdirSync6, readFileSync as readFileSync12, writeFileSync as writeFileSync8, existsSync as existsSync15, mkdirSync as mkdirSync8 } from "fs";
2773
+ import { resolve as resolve11, join as join9 } from "path";
2499
2774
  import { createHash as createHash3 } from "crypto";
2500
- var MAGIC_DIR_MAP3 = { claude: ".claude", qoder: ".qoder", vscode: ".vscode", trae: ".trae", codex: ".codex" };
2501
2775
  var HASH_OUTPUT_FILE = ".add-coder-hash.json";
2502
2776
  function hash82(c) {
2503
2777
  return createHash3("sha256").update(c).digest("hex").slice(0, 8);
2504
2778
  }
2505
2779
  function resolveMagicDir(projectRoot, specified) {
2506
2780
  if (specified) {
2507
- if (!MAGIC_DIR_MAP3[specified]) throw new Error(`\u672A\u77E5 adapter: ${specified}`);
2508
- return MAGIC_DIR_MAP3[specified];
2781
+ return magicDirFor(specified);
2509
2782
  }
2510
2783
  const detected = detectIDE2(projectRoot);
2511
2784
  const adapter = detected !== "auto" ? detected : "qoder";
2512
- return MAGIC_DIR_MAP3[adapter];
2785
+ return magicDirFor(adapter);
2513
2786
  }
2514
2787
  function listCustomProfiles(projectRoot, magicDir, registryNames) {
2515
2788
  const dir = resolve11(projectRoot, magicDir, "rules", "profiles");
2516
- if (!existsSync14(dir)) return [];
2517
- return readdirSync5(dir).filter((f) => f.endsWith(".md") && !registryNames.has(f.replace(/-profile\.md$/, ""))).sort();
2789
+ if (!existsSync15(dir)) return [];
2790
+ return readdirSync6(dir).filter((f) => f.endsWith(".md") && !registryNames.has(f.replace(/-profile\.md$/, ""))).sort();
2518
2791
  }
2519
2792
  function profileExists(projectRoot, magicDir, name) {
2520
2793
  const registry = loadProfileRegistry();
2521
2794
  if (registry.some((p) => p.name === name)) return true;
2522
- return existsSync14(resolve11(projectRoot, magicDir, "rules", "profiles", `${name}-profile.md`));
2795
+ return existsSync15(resolve11(projectRoot, magicDir, "rules", "profiles", `${name}-profile.md`));
2523
2796
  }
2524
2797
  function stackCommand(sub, name, options = {}) {
2525
2798
  const projectRoot = process.cwd();
@@ -2557,11 +2830,11 @@ function stackCommand(sub, name, options = {}) {
2557
2830
  return;
2558
2831
  }
2559
2832
  const profilePath = resolve11(projectRoot, magicDir, "rules", "profiles", `${current}-profile.md`);
2560
- const stat = existsSync14(profilePath) ? readFileSync11(profilePath, "utf-8").length : 0;
2833
+ const stat = existsSync15(profilePath) ? readFileSync12(profilePath, "utf-8").length : 0;
2561
2834
  console.log(`\u6280\u672F\u6808: ${current}`);
2562
- console.log(`profile \u6587\u4EF6: ${profilePath}${existsSync14(profilePath) ? ` (${stat} \u5B57\u7B26)` : "\uFF08\u7F3A\u5931\uFF09"}`);
2835
+ console.log(`profile \u6587\u4EF6: ${profilePath}${existsSync15(profilePath) ? ` (${stat} \u5B57\u7B26)` : "\uFF08\u7F3A\u5931\uFF09"}`);
2563
2836
  try {
2564
- const raw = JSON.parse(readFileSync11(resolve11(projectRoot, magicDir, "stack.json"), "utf-8"));
2837
+ const raw = JSON.parse(readFileSync12(resolve11(projectRoot, magicDir, "stack.json"), "utf-8"));
2565
2838
  if (raw.updatedAt) console.log(`\u66F4\u65B0\u65F6\u95F4: ${raw.updatedAt}`);
2566
2839
  } catch {
2567
2840
  }
@@ -2587,8 +2860,8 @@ function buildConfig(projectRoot, magicDir, stack) {
2587
2860
  };
2588
2861
  try {
2589
2862
  const pkgPath = resolve11(projectRoot, "package.json");
2590
- if (existsSync14(pkgPath)) {
2591
- const pkg = JSON.parse(readFileSync11(pkgPath, "utf-8"));
2863
+ if (existsSync15(pkgPath)) {
2864
+ const pkg = JSON.parse(readFileSync12(pkgPath, "utf-8"));
2592
2865
  if (pkg.name) config.projectName = pkg.name;
2593
2866
  }
2594
2867
  } catch {
@@ -2604,7 +2877,8 @@ function applyStack(projectRoot, magicDir, name) {
2604
2877
  }
2605
2878
  const config = buildConfig(projectRoot, magicDir, name);
2606
2879
  saveStack(projectRoot, magicDir, name);
2607
- const coreFiles = renderCore(config, false);
2880
+ const coreTargets = magicDir === magicDirFor("codex") ? [magicDir] : [ADD_DIR, magicDir];
2881
+ const coreFiles = renderCoreForTargets(config, false, coreTargets);
2608
2882
  const stackRelated = /* @__PURE__ */ new Map();
2609
2883
  for (const [relPath, content] of coreFiles) {
2610
2884
  const rp = normalizeRelPath(relPath);
@@ -2614,18 +2888,16 @@ function applyStack(projectRoot, magicDir, name) {
2614
2888
  }
2615
2889
  const hashMap = {};
2616
2890
  try {
2617
- Object.assign(hashMap, JSON.parse(readFileSync11(resolve11(projectRoot, magicDir, HASH_OUTPUT_FILE), "utf-8")));
2891
+ Object.assign(hashMap, JSON.parse(readFileSync12(resolve11(projectRoot, magicDir, HASH_OUTPUT_FILE), "utf-8")));
2618
2892
  } catch {
2619
2893
  }
2620
2894
  let written = 0;
2621
2895
  for (const [relPath, content] of stackRelated) {
2622
- for (const t of [".add", magicDir]) {
2623
- const targetPath = resolve11(projectRoot, relPath.replace(/^\.add/, t));
2624
- mkdirSync8(join8(targetPath, ".."), { recursive: true });
2625
- writeFileSync8(targetPath, content, "utf-8");
2626
- hashMap[relPath.replace(/^\.add/, t)] = hash82(content);
2627
- written++;
2628
- }
2896
+ const targetPath = resolve11(projectRoot, relPath);
2897
+ mkdirSync8(join9(targetPath, ".."), { recursive: true });
2898
+ writeFileSync8(targetPath, content, "utf-8");
2899
+ hashMap[relPath] = hash82(content);
2900
+ written++;
2629
2901
  }
2630
2902
  const fail = (msg) => {
2631
2903
  console.error(`\u2717 stack set \u5931\u8D25: ${msg}`);
@@ -2634,23 +2906,23 @@ function applyStack(projectRoot, magicDir, name) {
2634
2906
  const registry = loadProfileRegistry();
2635
2907
  const isBuiltin = registry.some((p) => p.name === name);
2636
2908
  const profilePathMagic = resolve11(projectRoot, magicDir, "rules", "profiles", `${name}-profile.md`);
2637
- const profilePathAdd = resolve11(projectRoot, ".add", "rules", "profiles", `${name}-profile.md`);
2909
+ const profilePathAdd = resolve11(projectRoot, ADD_DIR, "rules", "profiles", `${name}-profile.md`);
2638
2910
  const projectRulesPath = resolve11(projectRoot, magicDir, "rules", "project_rules.md");
2639
- const projectRulesContent = existsSync14(projectRulesPath) ? readFileSync11(projectRulesPath, "utf-8") : "";
2911
+ const projectRulesContent = existsSync15(projectRulesPath) ? readFileSync12(projectRulesPath, "utf-8") : "";
2640
2912
  if (written === 0) fail(`\u672A\u6E32\u67D3\u4EFB\u4F55 stack \u76F8\u5173\u6587\u4EF6\uFF08${name}\uFF09\u2014\u2014Windows \u8DEF\u5F84\u5339\u914D\u5931\u6548\u9057\u7559\u95EE\u9898`);
2641
- if (isBuiltin && !existsSync14(profilePathAdd)) fail(`profile \u672A\u5199\u5165 .add: ${profilePathAdd}`);
2642
- if (!existsSync14(profilePathMagic)) fail(`profile \u672A\u5199\u5165 ${magicDir}: ${profilePathMagic}`);
2913
+ if (magicDir !== magicDirFor("codex") && isBuiltin && !existsSync15(profilePathAdd)) fail(`profile \u672A\u5199\u5165 ${ADD_DIR}: ${profilePathAdd}`);
2914
+ if (!existsSync15(profilePathMagic)) fail(`profile \u672A\u5199\u5165 ${magicDir}: ${profilePathMagic}`);
2643
2915
  if (!projectRulesContent.includes("**\u5F53\u524D\u6280\u672F\u6808**") || !projectRulesContent.includes(name)) fail(`project_rules.md \u672A\u5305\u542B ${name} \u5F15\u7528`);
2644
2916
  writeFileSync8(resolve11(projectRoot, magicDir, HASH_OUTPUT_FILE), JSON.stringify(hashMap, null, 2) + "\n", "utf-8");
2645
2917
  console.log(`\u2705 \u6280\u672F\u6808\u5DF2\u8BBE\u7F6E\u4E3A ${name}`);
2646
2918
  console.log(` ${magicDir}/stack.json \u2192 ${name}`);
2647
- console.log(` ${magicDir}/rules/profiles/${name}-profile.md ${existsSync14(resolve11(projectRoot, magicDir, "rules", "profiles", `${name}-profile.md`)) ? "\u5DF2\u5C31\u4F4D" : "\uFF08\u81EA\u5B9A\u4E49 profile\uFF0C\u9879\u76EE\u4FA7\u6587\u4EF6\uFF09"}`);
2919
+ console.log(` ${magicDir}/rules/profiles/${name}-profile.md ${existsSync15(resolve11(projectRoot, magicDir, "rules", "profiles", `${name}-profile.md`)) ? "\u5DF2\u5C31\u4F4D" : "\uFF08\u81EA\u5B9A\u4E49 profile\uFF0C\u9879\u76EE\u4FA7\u6587\u4EF6\uFF09"}`);
2648
2920
  console.log(` project_rules.md \u5F15\u7528\u884C\u5DF2\u66F4\u65B0 + hash \u5DF2\u5237\u65B0\uFF08${written} \u4E2A\u6587\u4EF6\uFF09`);
2649
2921
  }
2650
2922
 
2651
2923
  // src/cli/index.ts
2652
2924
  var { version } = JSON.parse(
2653
- readFileSync12(new URL("../package.json", import.meta.url), "utf-8")
2925
+ readFileSync13(new URL("../package.json", import.meta.url), "utf-8")
2654
2926
  );
2655
2927
  async function modelDownloadCommand(options) {
2656
2928
  try {
@@ -2665,8 +2937,8 @@ async function modelDownloadCommand(options) {
2665
2937
  }
2666
2938
  var program = new Command();
2667
2939
  program.name("add-coder").description("\u521D\u59CB\u5316 ADD \u8303\u5F0F\u5DE5\u4F5C\u6D41\u6A21\u677F").version(version);
2668
- program.command("init").description("\u521D\u59CB\u5316 ADD \u6A21\u677F\u5230\u5F53\u524D\u9879\u76EE").option("--adapter <type>", "\u76EE\u6807 IDE: claude | qoder | vscode | trae | codex").option("--config <path>", "\u6307\u5B9A\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").option("--force", "\u8986\u76D6\u5DF2\u6709\u6587\u4EF6\uFF0C\u4E0D\u4EA4\u4E92").option("--dry-run", "\u9884\u89C8\u6A21\u5F0F\uFF0C\u4E0D\u5B9E\u9645\u5199\u5165").option("--stack <name>", "\u6280\u672F\u6808\u7EA6\u675F profile \u540D\uFF08\u5982 machineserver\uFF0C\u53EF\u9009\uFF09").option("--skip-model", "\u8DF3\u8FC7 embedding \u6A21\u578B\u9884\u4E0B\u8F7D").action(initCommand);
2669
- program.command("sync").description("\u589E\u91CF\u540C\u6B65\u7F3A\u5931\u6587\u4EF6\uFF08--patch \u8986\u76D6\u5DF2\u6709\u6A21\u677F\uFF09").option("--adapter <type>", "\u76EE\u6807 IDE: claude | qoder | vscode | trae | codex").option("--patch", "\u8986\u76D6\u5DF2\u6709\u6A21\u677F\u6587\u4EF6\uFF08\u4E0D\u78B0 plans/specs/reviews\uFF09").option("-i, --interactive", "\u4EA4\u4E92\u5F0F\u9009\u62E9\u8981\u540C\u6B65\u7684\u6587\u4EF6").option("--model", "\u68C0\u6D4B\u5230\u7F3A\u5931\u65F6\u4E0B\u8F7D embedding \u6A21\u578B").action(syncCommand);
2940
+ program.command("init").description("\u521D\u59CB\u5316 ADD \u6A21\u677F\u5230\u5F53\u524D\u9879\u76EE").option("--adapter <type>", "\u76EE\u6807 IDE: claude | qoder | vscode | trae | codex").option("--config <path>", "\u6307\u5B9A\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").option("--force", "\u8986\u76D6\u5DF2\u6709\u6587\u4EF6\uFF0C\u4E0D\u4EA4\u4E92").option("--dry-run", "\u9884\u89C8\u6A21\u5F0F\uFF0C\u4E0D\u5B9E\u9645\u5199\u5165").option("--stack <name>", "\u6280\u672F\u6808\u7EA6\u675F profile \u540D\uFF08\u5982 machineserver\uFF0C\u53EF\u9009\uFF09").option("--skip-model", "\u8DF3\u8FC7 embedding \u6A21\u578B\u9884\u4E0B\u8F7D").option("--print-mcp-config", "Codex: stdout \u8F93\u51FA config.toml \u7247\u6BB5\uFF08\u4E0D\u5199\u76D8\uFF0C\u4E0D\u521D\u59CB\u5316\u9879\u76EE\uFF09").option("--write-user-config", "Codex: \u663E\u5F0F\u786E\u8BA4\u540E\u5199\u5165 ~/.codex/config.toml\uFF08\u5148\u5907\u4EFD\uFF09").action(initCommand);
2941
+ program.command("sync").description("\u589E\u91CF\u540C\u6B65\u7F3A\u5931\u6587\u4EF6\uFF08--patch \u8986\u76D6\u5DF2\u6709\u6A21\u677F\uFF09").option("--adapter <type>", "\u76EE\u6807 IDE: claude | qoder | vscode | trae | codex").option("--patch", "\u8986\u76D6\u5DF2\u6709\u6A21\u677F\u6587\u4EF6\uFF08\u4E0D\u78B0 plans/specs/reviews\uFF09").option("-i, --interactive", "\u4EA4\u4E92\u5F0F\u9009\u62E9\u8981\u540C\u6B65\u7684\u6587\u4EF6").option("--model", "\u68C0\u6D4B\u5230\u7F3A\u5931\u65F6\u4E0B\u8F7D embedding \u6A21\u578B").option("--yes", "\u5168\u90E8\u786E\u8BA4\u8DF3\u8FC7\u8BE2\u95EE\uFF08RPT-06/#19\uFF09").action(syncCommand);
2670
2942
  program.command("status").description("\u68C0\u67E5 ADD \u6A21\u677F\u5B8C\u6574\u6027").action(statusCommand);
2671
2943
  program.command("stack").description("\u7BA1\u7406\u6280\u672F\u6808\u7EA6\u675F profile\uFF08list / set <name> / show / --clear\uFF09").argument("[sub]", "list | set | show").argument("[name]", "set <name>: profile \u540D\uFF08\u5185\u7F6E\u6216\u81EA\u5B9A\u4E49\uFF09").option("--adapter <type>", "\u76EE\u6807 IDE: claude | qoder | vscode | trae | codex").option("--clear", "\u6E05\u9664\u6280\u672F\u6808\u8BBE\u7F6E\uFF08\u4E2D\u6027\uFF09").action(
2672
2944
  (sub, name, options) => stackCommand(sub, name, options)