@event4u/agent-config 6.1.0 → 7.0.0

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 (1537) hide show
  1. package/.claude-plugin/marketplace.json +35 -3
  2. package/AGENTS.md +8 -7
  3. package/CHANGELOG.md +408 -0
  4. package/CONTRIBUTING.md +1 -1
  5. package/README.md +17 -15
  6. package/dist/agent-src/commands/agent-status.md +2 -2
  7. package/dist/agent-src/commands/agents/audit.md +3 -3
  8. package/dist/agent-src/commands/agents/init.md +1 -1
  9. package/dist/agent-src/commands/agents/optimize.md +4 -4
  10. package/dist/agent-src/commands/analyze/decision.md +108 -0
  11. package/dist/agent-src/commands/analyze/incident.md +120 -0
  12. package/dist/agent-src/commands/analyze/near-miss.md +113 -0
  13. package/dist/agent-src/commands/analyze/postmortem.md +130 -0
  14. package/dist/agent-src/commands/analyze/premortem.md +104 -0
  15. package/dist/agent-src/commands/analyze.md +124 -0
  16. package/dist/agent-src/commands/brand/identity.md +27 -0
  17. package/dist/agent-src/commands/brand/review.md +27 -0
  18. package/dist/agent-src/commands/brand/strategy.md +27 -0
  19. package/dist/agent-src/commands/brand/tokens.md +28 -0
  20. package/dist/agent-src/commands/brand/voice.md +27 -0
  21. package/dist/agent-src/commands/brand.md +58 -0
  22. package/dist/agent-src/commands/check-current-md.md +3 -3
  23. package/dist/agent-src/commands/condense.md +2 -2
  24. package/dist/agent-src/commands/council/debate.md +2 -2
  25. package/dist/agent-src/commands/council/default.md +45 -18
  26. package/dist/agent-src/commands/fix/portability.md +3 -3
  27. package/dist/agent-src/commands/fix/refs.md +3 -3
  28. package/dist/agent-src/commands/implement-ticket.md +36 -6
  29. package/dist/agent-src/commands/knowledge/cross-repo.md +1 -1
  30. package/dist/agent-src/commands/memory/add.md +1 -1
  31. package/dist/agent-src/commands/mission/upgrade.md +182 -0
  32. package/dist/agent-src/commands/optimize/skills.md +2 -2
  33. package/dist/agent-src/commands/orchestrate.md +1 -1
  34. package/dist/agent-src/commands/pr/create.md +6 -4
  35. package/dist/agent-src/commands/review-changes.md +8 -0
  36. package/dist/agent-src/commands/roadmap/materialize.md +73 -0
  37. package/dist/agent-src/commands/skill/preview.md +1 -1
  38. package/dist/agent-src/commands/skills/discover.md +1 -1
  39. package/dist/agent-src/commands/threat-model.md +4 -4
  40. package/dist/agent-src/commands/upstream-contribute.md +3 -3
  41. package/dist/agent-src/commands/video/from-script.md +2 -2
  42. package/dist/agent-src/commands/video/from-song.md +3 -3
  43. package/dist/agent-src/commands/video/scene.md +1 -1
  44. package/dist/agent-src/commands/video/storyboard.md +1 -1
  45. package/dist/agent-src/commands/video.md +3 -3
  46. package/dist/agent-src/contexts/communication/rules-auto/source-of-truth-mechanics.md +3 -3
  47. package/dist/agent-src/contexts/communication/rules-auto/user-interaction-mechanics.md +1 -1
  48. package/dist/agent-src/contexts/execution/evidence-discipline.md +153 -0
  49. package/dist/agent-src/contexts/execution/project-intelligence.md +264 -0
  50. package/dist/agent-src/contexts/execution/roadmap-process-loop.md +2 -1
  51. package/dist/agent-src/personas/ai-video-technical-director.md +1 -1
  52. package/dist/agent-src/personas/brand-strategist.md +74 -0
  53. package/dist/agent-src/personas/design-director.md +74 -0
  54. package/dist/agent-src/rules/brand-consistency.md +77 -0
  55. package/dist/agent-src/rules/brand-source-of-truth.md +57 -0
  56. package/dist/agent-src/rules/direct-answers.md +2 -0
  57. package/dist/agent-src/rules/domain-safety-disclaimer.md +2 -0
  58. package/dist/agent-src/rules/git-history-discipline.md +1 -0
  59. package/dist/agent-src/rules/icon-consistency.md +53 -0
  60. package/dist/agent-src/rules/image-likeness-and-rights.md +67 -0
  61. package/dist/agent-src/rules/lethal-trifecta-guard.md +1 -1
  62. package/dist/agent-src/rules/persona-governance.md +2 -2
  63. package/dist/agent-src/rules/provider-lifecycle-discipline.md +3 -1
  64. package/dist/agent-src/rules/roadmap-progress-sync.md +10 -0
  65. package/dist/agent-src/rules/security-sensitive-stop.md +9 -3
  66. package/dist/agent-src/rules/size-enforcement.md +1 -1
  67. package/dist/agent-src/rules/source-confidentiality.md +3 -3
  68. package/dist/agent-src/rules/source-discovery-gate.md +98 -0
  69. package/dist/agent-src/rules/think-before-action.md +1 -0
  70. package/dist/agent-src/rules/ui-audit-gate.md +2 -0
  71. package/dist/agent-src/rules/untrusted-input-defense.md +1 -1
  72. package/dist/agent-src/rules/user-interaction.md +1 -1
  73. package/dist/agent-src/scripts/archive_completed_roadmaps.ts +392 -0
  74. package/dist/agent-src/scripts/update_roadmap_progress.ts +824 -0
  75. package/dist/agent-src/skills/adr-create/SKILL.md +5 -5
  76. package/dist/agent-src/skills/agent-security-review/evals/triggers.json +1 -0
  77. package/dist/agent-src/skills/agents-md-thin-root/SKILL.md +1 -1
  78. package/dist/agent-src/skills/ai-council/SKILL.md +1 -1
  79. package/dist/agent-src/skills/analysis-autonomous-mode/SKILL.md +9 -13
  80. package/dist/agent-src/skills/blade-ui/SKILL.md +12 -5
  81. package/dist/agent-src/skills/blameless-post-mortem/SKILL.md +199 -0
  82. package/dist/agent-src/skills/brand/ATTRIBUTION.md +38 -0
  83. package/dist/agent-src/skills/brand/SKILL.md +115 -0
  84. package/dist/agent-src/skills/brand/data/archetypes.csv +13 -0
  85. package/dist/agent-src/skills/brand/data/color-psychology.csv +14 -0
  86. package/dist/agent-src/skills/brand/data/logo-style-fit.csv +13 -0
  87. package/dist/agent-src/skills/brand/data/manifest.json +226 -0
  88. package/dist/agent-src/skills/brand/data/messaging-frameworks.csv +13 -0
  89. package/dist/agent-src/skills/brand/data/naming-patterns.csv +13 -0
  90. package/dist/agent-src/skills/brand/data/typography-principles.csv +13 -0
  91. package/dist/agent-src/skills/brand/data/voice-tone.csv +13 -0
  92. package/dist/agent-src/skills/brand/evals/triggers.json +17 -0
  93. package/dist/agent-src/skills/brand-asset-generation/SKILL.md +89 -0
  94. package/dist/agent-src/skills/brand-asset-generation/evals/triggers.json +17 -0
  95. package/dist/agent-src/skills/brand-audit/SKILL.md +67 -0
  96. package/dist/agent-src/skills/brand-audit/evals/triggers.json +17 -0
  97. package/dist/agent-src/skills/brand-identity/SKILL.md +101 -0
  98. package/dist/agent-src/skills/brand-identity/evals/triggers.json +17 -0
  99. package/dist/agent-src/skills/brand-strategy/SKILL.md +83 -0
  100. package/dist/agent-src/skills/brand-strategy/evals/triggers.json +17 -0
  101. package/dist/agent-src/skills/brand-to-tokens/SKILL.md +102 -0
  102. package/dist/agent-src/skills/brand-to-tokens/evals/triggers.json +17 -0
  103. package/dist/agent-src/skills/brand-to-tokens/templates/marp-brand-deck.md.example +46 -0
  104. package/dist/agent-src/skills/brand-to-tokens/templates/reveal-brand-deck.yaml +32 -0
  105. package/dist/agent-src/skills/canvas-design/evals/triggers.json +1 -0
  106. package/dist/agent-src/skills/check-refs/SKILL.md +5 -5
  107. package/dist/agent-src/skills/code-review/SKILL.md +6 -15
  108. package/dist/agent-src/skills/command-writing/SKILL.md +2 -2
  109. package/dist/agent-src/skills/complexity-first-planning/evals/triggers.json +1 -0
  110. package/dist/agent-src/skills/context-authoring/SKILL.md +2 -2
  111. package/dist/agent-src/skills/context-document/SKILL.md +35 -2
  112. package/dist/agent-src/skills/corpus-grounding/evals/triggers.json +1 -0
  113. package/dist/agent-src/skills/corpus-grounding/scripts/bm25_search.ts +482 -0
  114. package/dist/agent-src/skills/corpus-grounding/scripts/decision_engine.ts +803 -0
  115. package/dist/agent-src/skills/corpus-grounding/scripts/ground.ts +541 -0
  116. package/dist/agent-src/skills/corpus-grounding/scripts/schema_validator.ts +309 -0
  117. package/dist/agent-src/skills/database/SKILL.md +26 -4
  118. package/dist/agent-src/skills/decision-record/SKILL.md +1 -1
  119. package/dist/agent-src/skills/decision-record/evals/triggers.json +17 -0
  120. package/dist/agent-src/skills/decision-review/SKILL.md +179 -0
  121. package/dist/agent-src/skills/description-assist/SKILL.md +1 -1
  122. package/dist/agent-src/skills/design-intelligence/SKILL.md +1 -1
  123. package/dist/agent-src/skills/design-intelligence/data/manifest.json +23 -6
  124. package/dist/agent-src/skills/design-intelligence/evals/triggers.json +1 -0
  125. package/dist/agent-src/skills/design-tokens/evals/triggers.json +1 -0
  126. package/dist/agent-src/skills/design-tokens/scripts/tokens.ts +888 -0
  127. package/dist/agent-src/skills/doc-coauthoring/evals/triggers.json +1 -0
  128. package/dist/agent-src/skills/eloquent/evals/triggers.json +1 -0
  129. package/dist/agent-src/skills/emit-tickets/SKILL.md +198 -0
  130. package/dist/agent-src/skills/estimate-ticket/evals/triggers.json +1 -0
  131. package/dist/agent-src/skills/git-workflow/SKILL.md +33 -0
  132. package/dist/agent-src/skills/guideline-writing/SKILL.md +2 -2
  133. package/dist/agent-src/skills/iconography/SKILL.md +88 -0
  134. package/dist/agent-src/skills/iconography/evals/triggers.json +17 -0
  135. package/dist/agent-src/skills/image-analyser/evals/triggers.json +1 -0
  136. package/dist/agent-src/skills/image-creator/evals/triggers.json +1 -0
  137. package/dist/agent-src/skills/image-editing/SKILL.md +100 -0
  138. package/dist/agent-src/skills/image-editing/evals/triggers.json +17 -0
  139. package/dist/agent-src/skills/image-generation/SKILL.md +95 -0
  140. package/dist/agent-src/skills/image-generation/evals/triggers.json +17 -0
  141. package/dist/agent-src/skills/image-provider-routing/SKILL.md +96 -0
  142. package/dist/agent-src/skills/image-provider-routing/evals/triggers.json +17 -0
  143. package/dist/agent-src/skills/launch-readiness/SKILL.md +21 -0
  144. package/dist/agent-src/skills/learning-to-rule-or-skill/SKILL.md +12 -8
  145. package/dist/agent-src/skills/lint-skills/SKILL.md +5 -5
  146. package/dist/agent-src/skills/logo-generation/SKILL.md +98 -0
  147. package/dist/agent-src/skills/logo-generation/evals/triggers.json +17 -0
  148. package/dist/agent-src/skills/markitdown/SKILL.md +1 -1
  149. package/dist/agent-src/skills/md-language-check/SKILL.md +1 -1
  150. package/dist/agent-src/skills/motion-choreographer/SKILL.md +1 -1
  151. package/dist/agent-src/skills/php-coder/evals/triggers.json +1 -0
  152. package/dist/agent-src/skills/prediction-pool-optimizer/evals/triggers.json +1 -0
  153. package/dist/agent-src/skills/premortem/SKILL.md +137 -0
  154. package/dist/agent-src/skills/prompt-engineering-image/SKILL.md +115 -0
  155. package/dist/agent-src/skills/prompt-engineering-image/evals/triggers.json +17 -0
  156. package/dist/agent-src/skills/prompt-validator/evals/triggers.json +1 -0
  157. package/dist/agent-src/skills/react-shadcn-ui/SKILL.md +12 -5
  158. package/dist/agent-src/skills/react-shadcn-ui/scripts/shadcn_add.ts +388 -0
  159. package/dist/agent-src/skills/reasoning-orchestrator/SKILL.md +1 -1
  160. package/dist/agent-src/skills/reasoning-orchestrator/evals/triggers.json +1 -0
  161. package/dist/agent-src/skills/refine-ticket/evals/triggers.json +1 -0
  162. package/dist/agent-src/skills/roadmap-management/SKILL.md +16 -3
  163. package/dist/agent-src/skills/roadmap-writing/SKILL.md +76 -0
  164. package/dist/agent-src/skills/root-cause-frameworks/SKILL.md +146 -0
  165. package/dist/agent-src/skills/rule-refactor/SKILL.md +9 -9
  166. package/dist/agent-src/skills/rule-writing/SKILL.md +7 -7
  167. package/dist/agent-src/skills/script-writing/SKILL.md +2 -2
  168. package/dist/agent-src/skills/security-audit/SKILL.md +5 -0
  169. package/dist/agent-src/skills/skill-improvement-pipeline/SKILL.md +19 -3
  170. package/dist/agent-src/skills/skill-management/SKILL.md +3 -3
  171. package/dist/agent-src/skills/skill-reviewer/SKILL.md +1 -1
  172. package/dist/agent-src/skills/skill-writing/SKILL.md +5 -5
  173. package/dist/agent-src/skills/skill-writing/evals/triggers.json +1 -0
  174. package/dist/agent-src/skills/source-discovery/SKILL.md +182 -0
  175. package/dist/agent-src/skills/standards-from-config/SKILL.md +93 -0
  176. package/dist/agent-src/skills/systematic-debugging/SKILL.md +7 -0
  177. package/dist/agent-src/skills/tailwind-engineer/scripts/tailwind_config_gen.ts +561 -0
  178. package/dist/agent-src/skills/threat-modeling/SKILL.md +1 -0
  179. package/dist/agent-src/skills/typography-system/SKILL.md +138 -0
  180. package/dist/agent-src/skills/typography-system/evals/triggers.json +17 -0
  181. package/dist/agent-src/skills/upstream-contribute/SKILL.md +3 -3
  182. package/dist/agent-src/skills/verify-repair-loop/SKILL.md +209 -0
  183. package/dist/agent-src/skills/verify-repair-loop/evals/output-schema.yml +20 -0
  184. package/dist/agent-src/skills/verify-repair-loop/evals/triggers.json +17 -0
  185. package/dist/agent-src/templates/agent-settings.md +7 -0
  186. package/dist/agent-src/templates/contexts/knowledge-card.md +69 -0
  187. package/dist/agent-src/templates/contexts/lesson-card.md +73 -0
  188. package/dist/agent-src/templates/roadmaps.md +29 -1
  189. package/dist/agent-src/templates/scripts/README.md +6 -6
  190. package/dist/agent-src/templates/scripts/check_memory.ts +640 -0
  191. package/dist/agent-src/templates/scripts/check_memory_proposal.ts +351 -0
  192. package/dist/agent-src/templates/scripts/implement_ticket/__main__.ts +27 -0
  193. package/dist/agent-src/templates/scripts/memory_hash.ts +333 -0
  194. package/dist/agent-src/templates/scripts/memory_lookup.ts +1067 -0
  195. package/dist/agent-src/templates/scripts/memory_report.ts +846 -0
  196. package/dist/agent-src/templates/scripts/memory_signal.ts +422 -0
  197. package/dist/agent-src/templates/scripts/memory_status.ts +191 -0
  198. package/dist/agent-src/templates/scripts/pr_review_routing.ts +523 -0
  199. package/dist/agent-src/templates/scripts/pr_risk_review.ts +0 -0
  200. package/dist/agent-src/templates/scripts/telemetry/aggregator.ts +0 -0
  201. package/dist/agent-src/templates/scripts/telemetry/boundary.ts +164 -0
  202. package/dist/agent-src/templates/scripts/telemetry/engagement.ts +479 -0
  203. package/dist/agent-src/templates/scripts/telemetry/report_renderer.ts +394 -0
  204. package/dist/agent-src/templates/scripts/telemetry/settings.ts +210 -0
  205. package/dist/agent-src/templates/scripts/telemetry_record.ts +255 -0
  206. package/dist/agent-src/templates/scripts/telemetry_report.ts +189 -0
  207. package/dist/agent-src/templates/scripts/telemetry_status.ts +312 -0
  208. package/dist/agent-src/templates/scripts/tier_usage_report.ts +597 -0
  209. package/dist/agent-src/templates/scripts/work_engine/__main__.ts +14 -0
  210. package/dist/agent-src/templates/scripts/work_engine/_lib/agent_settings.ts +1118 -0
  211. package/dist/agent-src/templates/scripts/work_engine/_lib/user_global_paths.ts +329 -0
  212. package/dist/agent-src/templates/scripts/work_engine/cli.ts +206 -0
  213. package/dist/agent-src/templates/scripts/work_engine/cli_args.ts +249 -0
  214. package/dist/agent-src/templates/scripts/work_engine/delivery_state.ts +225 -0
  215. package/dist/agent-src/templates/scripts/work_engine/directives/backend/analyze.ts +125 -0
  216. package/dist/agent-src/templates/scripts/work_engine/directives/backend/implement.ts +189 -0
  217. package/dist/agent-src/templates/scripts/work_engine/directives/backend/index.ts +94 -0
  218. package/dist/agent-src/templates/scripts/work_engine/directives/backend/memory.ts +193 -0
  219. package/dist/agent-src/templates/scripts/work_engine/directives/backend/plan.ts +267 -0
  220. package/dist/agent-src/templates/scripts/work_engine/directives/backend/refine.ts +518 -0
  221. package/dist/agent-src/templates/scripts/work_engine/directives/backend/report.ts +379 -0
  222. package/dist/agent-src/templates/scripts/work_engine/directives/backend/test.ts +268 -0
  223. package/dist/agent-src/templates/scripts/work_engine/directives/backend/verify.ts +258 -0
  224. package/dist/agent-src/templates/scripts/work_engine/directives/index.ts +32 -0
  225. package/dist/agent-src/templates/scripts/work_engine/directives/mixed/contract.ts +243 -0
  226. package/dist/agent-src/templates/scripts/work_engine/directives/mixed/index.ts +108 -0
  227. package/dist/agent-src/templates/scripts/work_engine/directives/mixed/stitch.ts +259 -0
  228. package/dist/agent-src/templates/scripts/work_engine/directives/mixed/ui.ts +216 -0
  229. package/dist/agent-src/templates/scripts/work_engine/directives/ui/_passthrough.ts +40 -0
  230. package/dist/agent-src/templates/scripts/work_engine/directives/ui/app_spec.ts +241 -0
  231. package/dist/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +216 -0
  232. package/dist/agent-src/templates/scripts/work_engine/directives/ui/audit.ts +506 -0
  233. package/dist/agent-src/templates/scripts/work_engine/directives/ui/design.ts +325 -0
  234. package/dist/agent-src/templates/scripts/work_engine/directives/ui/index.ts +102 -0
  235. package/dist/agent-src/templates/scripts/work_engine/directives/ui/polish.ts +462 -0
  236. package/dist/agent-src/templates/scripts/work_engine/directives/ui/review.ts +474 -0
  237. package/dist/agent-src/templates/scripts/work_engine/directives/ui/scaffold.ts +352 -0
  238. package/dist/agent-src/templates/scripts/work_engine/directives/ui_trivial/_skipped.ts +33 -0
  239. package/dist/agent-src/templates/scripts/work_engine/directives/ui_trivial/apply.ts +213 -0
  240. package/dist/agent-src/templates/scripts/work_engine/directives/ui_trivial/index.ts +111 -0
  241. package/dist/agent-src/templates/scripts/work_engine/directives/ui_trivial/refine.ts +126 -0
  242. package/dist/agent-src/templates/scripts/work_engine/directives/ui_trivial/report.ts +112 -0
  243. package/dist/agent-src/templates/scripts/work_engine/directives/ui_trivial/test.ts +164 -0
  244. package/dist/agent-src/templates/scripts/work_engine/dispatcher.ts +515 -0
  245. package/dist/agent-src/templates/scripts/work_engine/emitters.ts +119 -0
  246. package/dist/agent-src/templates/scripts/work_engine/errors.ts +24 -0
  247. package/dist/agent-src/templates/scripts/work_engine/hook_bootstrap.ts +104 -0
  248. package/dist/agent-src/templates/scripts/work_engine/hooks/builtin/_chat_history_base.ts +176 -0
  249. package/dist/agent-src/templates/scripts/work_engine/hooks/builtin/chat_history_append.ts +41 -0
  250. package/dist/agent-src/templates/scripts/work_engine/hooks/builtin/chat_history_halt_append.ts +89 -0
  251. package/dist/agent-src/templates/scripts/work_engine/hooks/builtin/decision_gate.ts +193 -0
  252. package/dist/agent-src/templates/scripts/work_engine/hooks/builtin/decision_trace.ts +304 -0
  253. package/dist/agent-src/templates/scripts/work_engine/hooks/builtin/directive_set_guard.ts +110 -0
  254. package/dist/agent-src/templates/scripts/work_engine/hooks/builtin/halt_surface_audit.ts +118 -0
  255. package/dist/agent-src/templates/scripts/work_engine/hooks/builtin/index.ts +17 -0
  256. package/dist/agent-src/templates/scripts/work_engine/hooks/builtin/memory_visibility.ts +161 -0
  257. package/dist/agent-src/templates/scripts/work_engine/hooks/builtin/state_shape_validation.ts +45 -0
  258. package/dist/agent-src/templates/scripts/work_engine/hooks/builtin/trace.ts +134 -0
  259. package/dist/agent-src/templates/scripts/work_engine/hooks/context.ts +94 -0
  260. package/dist/agent-src/templates/scripts/work_engine/hooks/events.ts +58 -0
  261. package/dist/agent-src/templates/scripts/work_engine/hooks/exceptions.ts +85 -0
  262. package/dist/agent-src/templates/scripts/work_engine/hooks/index.ts +27 -0
  263. package/dist/agent-src/templates/scripts/work_engine/hooks/registry.ts +66 -0
  264. package/dist/agent-src/templates/scripts/work_engine/hooks/runner.ts +90 -0
  265. package/dist/agent-src/templates/scripts/work_engine/hooks/settings.ts +260 -0
  266. package/dist/agent-src/templates/scripts/work_engine/input_builders.ts +260 -0
  267. package/dist/agent-src/templates/scripts/work_engine/intent/classify.ts +466 -0
  268. package/dist/agent-src/templates/scripts/work_engine/migration/v0_to_v1.ts +531 -0
  269. package/dist/agent-src/templates/scripts/work_engine/orchestration.ts +366 -0
  270. package/dist/agent-src/templates/scripts/work_engine/persona_policy.ts +97 -0
  271. package/dist/agent-src/templates/scripts/work_engine/resolvers/diff.ts +135 -0
  272. package/dist/agent-src/templates/scripts/work_engine/resolvers/file.ts +175 -0
  273. package/dist/agent-src/templates/scripts/work_engine/resolvers/prompt.ts +115 -0
  274. package/dist/agent-src/templates/scripts/work_engine/scoring/confidence.ts +415 -0
  275. package/dist/agent-src/templates/scripts/work_engine/scoring/decision_engine.ts +466 -0
  276. package/dist/agent-src/templates/scripts/work_engine/scoring/decision_trace.ts +298 -0
  277. package/dist/agent-src/templates/scripts/work_engine/scoring/memory_visibility.ts +444 -0
  278. package/dist/agent-src/templates/scripts/work_engine/stack/detect.ts +252 -0
  279. package/dist/agent-src/templates/scripts/work_engine/stack/runner.ts +745 -0
  280. package/dist/agent-src/templates/scripts/work_engine/state.ts +1151 -0
  281. package/dist/agent-src/templates/scripts/work_engine/state_io.ts +413 -0
  282. package/dist/agent-src/templates/tickets.md +120 -0
  283. package/dist/cli/commands/commands.js +2 -2
  284. package/dist/cli/commands/commands.js.map +1 -1
  285. package/dist/cli/commands/doctorShell.js +4 -22
  286. package/dist/cli/commands/doctorShell.js.map +1 -1
  287. package/dist/cli/commands/packs.js +1 -1
  288. package/dist/cli/commands/packs.js.map +1 -1
  289. package/dist/cli/commands/recordTriggerEval.js +179 -0
  290. package/dist/cli/commands/recordTriggerEval.js.map +1 -0
  291. package/dist/cli/commands/recordTriggerEval.test.js +113 -0
  292. package/dist/cli/commands/recordTriggerEval.test.js.map +1 -0
  293. package/dist/cli/commands/workspaces.js +1 -1
  294. package/dist/cli/commands/workspaces.js.map +1 -1
  295. package/dist/cli/main.js +22 -1
  296. package/dist/cli/main.js.map +1 -1
  297. package/dist/cli/python/knowledge_ingest.js +1048 -0
  298. package/dist/cli/python/knowledge_ingest.js.map +1 -0
  299. package/dist/cli/python/workspace_analytics.js +1085 -0
  300. package/dist/cli/python/workspace_analytics.js.map +1 -0
  301. package/dist/cli/python/workspace_crypto.js +544 -0
  302. package/dist/cli/python/workspace_crypto.js.map +1 -0
  303. package/dist/cli/python/workspace_documents.js +1216 -0
  304. package/dist/cli/python/workspace_documents.js.map +1 -0
  305. package/dist/cli/python/workspace_drive.js +574 -0
  306. package/dist/cli/python/workspace_drive.js.map +1 -0
  307. package/dist/cli/python/workspace_drive_health.js +628 -0
  308. package/dist/cli/python/workspace_drive_health.js.map +1 -0
  309. package/dist/cli/python/workspace_explain.js +765 -0
  310. package/dist/cli/python/workspace_explain.js.map +1 -0
  311. package/dist/cli/python/workspace_hosts.js +349 -0
  312. package/dist/cli/python/workspace_hosts.js.map +1 -0
  313. package/dist/cli/python/workspace_inbox.js +692 -0
  314. package/dist/cli/python/workspace_inbox.js.map +1 -0
  315. package/dist/cli/python/workspace_render.js +816 -0
  316. package/dist/cli/python/workspace_render.js.map +1 -0
  317. package/dist/cli/python/workspace_roles.js +487 -0
  318. package/dist/cli/python/workspace_roles.js.map +1 -0
  319. package/dist/cli/python/workspace_secrets.js +180 -0
  320. package/dist/cli/python/workspace_secrets.js.map +1 -0
  321. package/dist/cli/python/workspace_sessions.js +1079 -0
  322. package/dist/cli/python/workspace_sessions.js.map +1 -0
  323. package/dist/cli/python/workspace_skills.js +417 -0
  324. package/dist/cli/python/workspace_skills.js.map +1 -0
  325. package/dist/cli/registry.js +2 -0
  326. package/dist/cli/registry.js.map +1 -1
  327. package/dist/discovery/deprecation-report.md +1 -1
  328. package/dist/discovery/discovery-manifest.json +1174 -123
  329. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  330. package/dist/discovery/discovery-manifest.summary.md +9 -6
  331. package/dist/discovery/orphan-report.md +1 -1
  332. package/dist/discovery/packs.json +163 -15
  333. package/dist/discovery/trust-report.md +4 -4
  334. package/dist/discovery/workspaces.json +73 -12
  335. package/dist/install/install.mjs +13934 -0
  336. package/dist/mcp/registry-manifest.json +4 -4
  337. package/dist/router.json +1 -1
  338. package/dist/server/routes/wizard.js +50 -21
  339. package/dist/server/routes/wizard.js.map +1 -1
  340. package/dist/server/routes/workspace.js +44 -25
  341. package/dist/server/routes/workspace.js.map +1 -1
  342. package/dist/server/schemas/settings.js +15 -0
  343. package/dist/server/schemas/settings.js.map +1 -1
  344. package/docs/SKILL_CENSUS.md +344 -0
  345. package/docs/architecture/augment-projection.md +1 -1
  346. package/docs/architecture/multi-tool-projection.md +3 -3
  347. package/docs/architecture.md +37 -6
  348. package/docs/benchmark.md +24 -27
  349. package/docs/capability-matrix.md +32 -0
  350. package/docs/catalog.md +50 -9
  351. package/docs/command-naming-audit.md +60 -0
  352. package/docs/contracts/STABILITY.md +32 -0
  353. package/docs/contracts/agents-md-tech-stack.md +1 -1
  354. package/docs/contracts/ai-council-config.md +22 -22
  355. package/docs/contracts/analysis-memory-loop.md +149 -0
  356. package/docs/contracts/benchmark-ab-contract.md +3 -3
  357. package/docs/contracts/branch-protection-policy.md +27 -0
  358. package/docs/contracts/brand-token-consumption.md +69 -0
  359. package/docs/contracts/command-clusters.md +2 -1
  360. package/docs/contracts/command-surface-tiers.md +13 -0
  361. package/docs/contracts/discovery-manifest.schema.json +24 -5
  362. package/docs/contracts/implement-ticket-flow.md +9 -9
  363. package/docs/contracts/install-layout.md +249 -0
  364. package/docs/contracts/kernel-membership.md +1 -1
  365. package/docs/contracts/linear-ai-rules-inclusion.md +2 -2
  366. package/docs/contracts/linter-structural-model.md +1 -1
  367. package/docs/contracts/mcp-discovery-phase-notice.md +1 -1
  368. package/docs/contracts/multi-tool-projection-fidelity.md +1 -1
  369. package/docs/contracts/namespace.md +2 -2
  370. package/docs/contracts/no-runtime-boundary.md +56 -0
  371. package/docs/contracts/package-self-orientation.md +24 -0
  372. package/docs/contracts/provider-lifecycle.md +3 -3
  373. package/docs/contracts/reasoning-discipline-protocol.md +83 -0
  374. package/docs/contracts/rule-classification.md +3 -3
  375. package/docs/contracts/skill-domains.md +1 -1
  376. package/docs/contracts/smoke-contracts.md +1 -1
  377. package/docs/contracts/surface-tiers.md +81 -0
  378. package/docs/contracts/ticket-bundle-format.md +228 -0
  379. package/docs/cookbook.md +152 -0
  380. package/docs/customization.md +12 -1
  381. package/docs/decisions/ADR-013-discovery-frontmatter-contract.md +16 -0
  382. package/docs/decisions/ADR-056-unvalidated-video-adapters-disposition.md +1 -1
  383. package/docs/decisions/ADR-059-render-resume-filesystem-as-state.md +1 -1
  384. package/docs/decisions/ADR-060-comfyui-sandbox-model.md +1 -1
  385. package/docs/decisions/ADR-061-corpus-grounding-layer.md +48 -1
  386. package/docs/decisions/ADR-096-analysis-workbench.md +110 -0
  387. package/docs/decisions/ADR-097-mission-recipe-privilege-boundary.md +121 -0
  388. package/docs/decisions/ADR-098-evidence-first-structure-discovery.md +154 -0
  389. package/docs/decisions/ADR-099-file-first-pattern-library.md +87 -0
  390. package/docs/decisions/ADR-100-global-knowledge-card-sharing.md +133 -0
  391. package/docs/decisions/ADR-101-ticket-bundle-emission.md +109 -0
  392. package/docs/decisions/ADR-102-ticket-handoff-paste-and-mcp.md +72 -0
  393. package/docs/decisions/ADR-103-global-knowledge-default-off-until-measured.md +92 -0
  394. package/docs/decisions/ADR-200-python-to-typescript-migration.md +193 -0
  395. package/docs/decisions/INDEX.md +9 -0
  396. package/docs/distribution/mcp-submission-checklist.md +3 -3
  397. package/docs/featured-commands.md +1 -1
  398. package/docs/featured-skills.md +1 -1
  399. package/docs/getting-started-by-role.md +2 -0
  400. package/docs/getting-started.md +2 -2
  401. package/docs/guidelines/agent-infra/failure-signatures.md +35 -0
  402. package/docs/guidelines/agent-infra/frontier-reasoning-operating-profile.md +5 -0
  403. package/docs/guidelines/agent-infra/size-and-scope.md +17 -0
  404. package/docs/guidelines/agent-infra/skill-quality-checklist.md +2 -2
  405. package/docs/guides/frontend-design-corpus-refresh.md +83 -0
  406. package/docs/guides/skill-preview.md +1 -1
  407. package/docs/hook-payload-capture.md +4 -4
  408. package/docs/mcp.md +1 -1
  409. package/docs/migration/consumer-template-consumption-model.md +145 -0
  410. package/docs/migration/divergences/README.md +55 -0
  411. package/docs/migration/divergences/_template.md +50 -0
  412. package/docs/migration/divergences/bench-stats-float-precision.md +72 -0
  413. package/docs/migration/divergences/mcp-telemetry-node-sqlite.md +61 -0
  414. package/docs/migration/divergences/pack-mcp-content-gzip-body.md +53 -0
  415. package/docs/migration/divergences/src-scripts-build_cloud_bundle.md +63 -0
  416. package/docs/migration/divergences/src-scripts-check_memory.md +91 -0
  417. package/docs/migration/divergences/src-scripts-inventory_abstraction_budget.md +65 -0
  418. package/docs/migration/divergences/src-scripts-lint_marketplace.md +57 -0
  419. package/docs/migration/divergences/src-scripts-lint_mcp_registry_manifest.md +70 -0
  420. package/docs/migration/divergences/src-scripts-spotcheck_thin_root.md +60 -0
  421. package/docs/migration/divergences/src-scripts-validate_agent_settings.md +58 -0
  422. package/docs/migration/node-floor.md +86 -0
  423. package/docs/migration/yaml-roundtrip-spike.md +163 -0
  424. package/docs/personas.md +6 -1
  425. package/docs/role-experiences.md +19 -0
  426. package/docs/setup/per-ide/windsurf.md +1 -1
  427. package/docs/skills-catalog.md +24 -3
  428. package/docs/threat-model.md +28 -0
  429. package/llms.txt +23 -2
  430. package/package.json +10 -15
  431. package/src/config/agent-settings.template.yml +64 -1
  432. package/src/config/discovery/packs.yml +31 -0
  433. package/src/config/discovery/unassigned-artefacts.yml +6 -0
  434. package/src/config/discovery/workspaces.yml +2 -2
  435. package/src/config/gitignore-block.txt +7 -0
  436. package/src/scripts/_cli/cmd_doctor.ts +2306 -0
  437. package/src/scripts/_cli/cmd_explain.ts +748 -0
  438. package/src/scripts/_cli/cmd_export.ts +375 -0
  439. package/src/scripts/_cli/cmd_migrate.ts +951 -0
  440. package/src/scripts/_cli/cmd_prune.ts +610 -0
  441. package/src/scripts/_cli/cmd_refresh.ts +530 -0
  442. package/src/scripts/_cli/cmd_settings_check.ts +407 -0
  443. package/src/scripts/_cli/cmd_settings_migrate.ts +344 -0
  444. package/src/scripts/_cli/cmd_sync.ts +381 -0
  445. package/src/scripts/_cli/cmd_uninstall.ts +833 -0
  446. package/src/scripts/_cli/cmd_update.ts +585 -0
  447. package/src/scripts/_cli/cmd_upgrade.ts +390 -0
  448. package/src/scripts/_cli/cmd_validate.ts +394 -0
  449. package/src/scripts/_cli/cmd_versions.ts +492 -0
  450. package/src/scripts/_cli/explain_last/assumptions.ts +114 -0
  451. package/src/scripts/_cli/explain_last/council.ts +197 -0
  452. package/src/scripts/_cli/explain_last/halt.ts +73 -0
  453. package/src/scripts/_cli/explain_last/index.ts +155 -0
  454. package/src/scripts/_cli/explain_last/inputs.ts +211 -0
  455. package/src/scripts/_cli/explain_last/memory.ts +231 -0
  456. package/src/scripts/_cli/explain_last/provider.ts +82 -0
  457. package/src/scripts/_cli/explain_last/render.ts +54 -0
  458. package/src/scripts/_cli/explain_last/route.ts +70 -0
  459. package/src/scripts/_cli/explain_last/scrubber.ts +138 -0
  460. package/src/scripts/_cli/explain_last/sections/assumptions.ts +51 -0
  461. package/src/scripts/_cli/explain_last/sections/council.ts +56 -0
  462. package/src/scripts/_cli/explain_last/sections/halt.ts +60 -0
  463. package/src/scripts/_cli/explain_last/sections/header.ts +50 -0
  464. package/src/scripts/_cli/explain_last/sections/index.ts +21 -0
  465. package/src/scripts/_cli/explain_last/sections/inputs.ts +63 -0
  466. package/src/scripts/_cli/explain_last/sections/memory.ts +124 -0
  467. package/src/scripts/_cli/explain_last/sections/pack.ts +42 -0
  468. package/src/scripts/_cli/explain_last/sections/provider.ts +51 -0
  469. package/src/scripts/_cli/explain_last/sections/route.ts +48 -0
  470. package/src/scripts/_cli/explain_last/state_loader.ts +119 -0
  471. package/src/scripts/_dispatch.bash +179 -163
  472. package/src/scripts/_lib/agent_settings.ts +1123 -0
  473. package/src/scripts/_lib/agent_src.ts +654 -0
  474. package/src/scripts/_lib/agents_overlay.ts +183 -0
  475. package/src/scripts/_lib/bench_ab_cache.ts +399 -0
  476. package/src/scripts/_lib/bench_ab_scoring.ts +352 -0
  477. package/src/scripts/_lib/bench_ab_scoring_v2.ts +751 -0
  478. package/src/scripts/_lib/bench_cost.ts +396 -0
  479. package/src/scripts/_lib/bench_quality.ts +237 -0
  480. package/src/scripts/_lib/bench_report.ts +255 -0
  481. package/src/scripts/_lib/bench_telegraph.ts +516 -0
  482. package/src/scripts/_lib/bench_telegraph_report.ts +272 -0
  483. package/src/scripts/_lib/changelog_eras.ts +398 -0
  484. package/src/scripts/_lib/claude_desktop_bundler.ts +324 -0
  485. package/src/scripts/_lib/cli_wrapper.ts +89 -0
  486. package/src/scripts/_lib/fs_atomic.ts +172 -0
  487. package/src/scripts/_lib/global_deploy_inventory.ts +639 -0
  488. package/src/scripts/_lib/install_layout.ts +87 -0
  489. package/src/scripts/_lib/install_regenerator.ts +157 -0
  490. package/src/scripts/_lib/installed_lock.ts +451 -0
  491. package/src/scripts/_lib/installed_tools.ts +518 -0
  492. package/src/scripts/_lib/json_pointers.ts +388 -0
  493. package/src/scripts/_lib/knowledge_global.ts +770 -0
  494. package/src/scripts/_lib/knowledge_global_promote.ts +453 -0
  495. package/src/scripts/_lib/knowledge_global_redaction.ts +448 -0
  496. package/src/scripts/_lib/link_crypto.ts +325 -0
  497. package/src/scripts/_lib/linked_projects.ts +613 -0
  498. package/src/scripts/_lib/model_tier.ts +65 -0
  499. package/src/scripts/_lib/module_detection.ts +275 -0
  500. package/src/scripts/_lib/node_sqlite.d.ts +32 -0
  501. package/src/scripts/_lib/pin_resolver.ts +264 -0
  502. package/src/scripts/_lib/py_random.ts +212 -0
  503. package/src/scripts/_lib/script_output.ts +147 -0
  504. package/src/scripts/_lib/security_lint.ts +623 -0
  505. package/src/scripts/_lib/surface_tiers.ts +127 -0
  506. package/src/scripts/_lib/token_count.ts +126 -0
  507. package/src/scripts/_lib/update_check.ts +297 -0
  508. package/src/scripts/_lib/user_global_paths.ts +329 -0
  509. package/src/scripts/_lib/value_ladder.ts +882 -0
  510. package/src/scripts/_lib/value_report.ts +617 -0
  511. package/src/scripts/_lib/zip_min.ts +175 -0
  512. package/src/scripts/adoption_report.ts +357 -0
  513. package/src/scripts/adoption_snapshot.ts +392 -0
  514. package/src/scripts/adoption_status.ts +424 -0
  515. package/src/scripts/adr/regenerate_index.ts +257 -0
  516. package/src/scripts/ai-image/adapters/flux.sh +45 -0
  517. package/src/scripts/ai-image/adapters/gemini-image.sh +45 -0
  518. package/src/scripts/ai-image/adapters/ideogram.sh +45 -0
  519. package/src/scripts/ai-image/adapters/recraft.sh +47 -0
  520. package/src/scripts/ai-video/adapters/comfyui.sh +3 -3
  521. package/src/scripts/ai-video/adapters/fal.sh +3 -3
  522. package/src/scripts/ai-video/adapters/gemini-veo.sh +3 -3
  523. package/src/scripts/ai-video/adapters/higgsfield.sh +3 -3
  524. package/src/scripts/ai-video/adapters/kling.sh +3 -3
  525. package/src/scripts/ai-video/adapters/musetalk.sh +2 -2
  526. package/src/scripts/ai-video/adapters/openai-images.sh +3 -3
  527. package/src/scripts/ai-video/adapters/replicate.sh +3 -3
  528. package/src/scripts/ai-video/adapters/sora.sh +3 -3
  529. package/src/scripts/ai-video/adapters/syncso.sh +3 -3
  530. package/src/scripts/ai-video/audio-adapters/allin1.sh +2 -2
  531. package/src/scripts/ai-video/audio-adapters/whisperx.sh +2 -2
  532. package/src/scripts/ai-video/lib/audio-adapter-contract.md +1 -1
  533. package/src/scripts/ai-video/lib/embed-provenance.sh +2 -2
  534. package/src/scripts/ai-video/lib/ingest-song.sh +2 -2
  535. package/src/scripts/ai-video/lib/parse-blueprint.sh +1 -1
  536. package/src/scripts/ai-video/lib/resume-scan.sh +2 -2
  537. package/src/scripts/ai-video/smoke-trace.sh +16 -7
  538. package/src/scripts/ai-video/stitch.sh +2 -2
  539. package/src/scripts/ai_council/_default_prices.ts +73 -0
  540. package/src/scripts/ai_council/advisors.ts +244 -0
  541. package/src/scripts/ai_council/airgap.ts +249 -0
  542. package/src/scripts/ai_council/budget_guard.ts +492 -0
  543. package/src/scripts/ai_council/bundler.ts +376 -0
  544. package/src/scripts/ai_council/cli_hints.ts +120 -0
  545. package/src/scripts/ai_council/clients.ts +2214 -0
  546. package/src/scripts/ai_council/compile_corpus.ts +681 -0
  547. package/src/scripts/ai_council/confidence_gate.ts +230 -0
  548. package/src/scripts/ai_council/config.ts +1729 -0
  549. package/src/scripts/ai_council/consensus.ts +551 -0
  550. package/src/scripts/ai_council/events_log.ts +327 -0
  551. package/src/scripts/ai_council/learn_low_impact_preview.ts +317 -0
  552. package/src/scripts/ai_council/low_impact.ts +1069 -0
  553. package/src/scripts/ai_council/low_impact_corpus.ts +662 -0
  554. package/src/scripts/ai_council/low_impact_intake.ts +222 -0
  555. package/src/scripts/ai_council/modes.ts +169 -0
  556. package/src/scripts/ai_council/necessity.ts +933 -0
  557. package/src/scripts/ai_council/orchestrator.ts +1689 -0
  558. package/src/scripts/ai_council/pricing.ts +267 -0
  559. package/src/scripts/ai_council/probation_gate.ts +282 -0
  560. package/src/scripts/ai_council/project_context.ts +308 -0
  561. package/src/scripts/ai_council/prompts.ts +600 -0
  562. package/src/scripts/ai_council/redact_low_impact_entry.ts +291 -0
  563. package/src/scripts/ai_council/replay.ts +314 -0
  564. package/src/scripts/ai_council/session.ts +558 -0
  565. package/src/scripts/ai_council/shadow_dispatch.ts +509 -0
  566. package/src/scripts/ai_council/solo_dispatch.ts +281 -0
  567. package/src/scripts/analysis_freshness.ts +343 -0
  568. package/src/scripts/annotate_discovery.ts +288 -0
  569. package/src/scripts/apply_modules_config.ts +537 -0
  570. package/src/scripts/audit_adr_coverage.ts +357 -0
  571. package/src/scripts/audit_auto_rules.ts +415 -0
  572. package/src/scripts/audit_cloud_compatibility.ts +608 -0
  573. package/src/scripts/audit_command_surface.ts +1227 -0
  574. package/src/scripts/audit_initial_context.ts +694 -0
  575. package/src/scripts/audit_likelihood.ts +434 -0
  576. package/src/scripts/audit_mcp_tools.ts +252 -0
  577. package/src/scripts/audit_overlap.ts +421 -0
  578. package/src/scripts/audit_skill_descriptions.ts +402 -0
  579. package/src/scripts/audit_skill_overlap.ts +576 -0
  580. package/src/scripts/audit_user_type_axis.ts +264 -0
  581. package/src/scripts/backfill_model_tier.ts +349 -0
  582. package/src/scripts/bench_ab_cache_dispatch.ts +126 -0
  583. package/src/scripts/bench_ab_clone.ts +610 -0
  584. package/src/scripts/bench_ab_diff.ts +609 -0
  585. package/src/scripts/bench_ab_integrity.ts +261 -0
  586. package/src/scripts/bench_ab_run.ts +417 -0
  587. package/src/scripts/bench_ab_task_runner.ts +1382 -0
  588. package/src/scripts/bench_ab_tracka_run.ts +436 -0
  589. package/src/scripts/bench_ab_v2_run.ts +585 -0
  590. package/src/scripts/bench_ab_v2_stats.ts +1018 -0
  591. package/src/scripts/bench_baseline_ready.ts +326 -0
  592. package/src/scripts/bench_condense_memory.ts +479 -0
  593. package/src/scripts/bench_drift_check.ts +503 -0
  594. package/src/scripts/bench_per_tool.ts +591 -0
  595. package/src/scripts/bench_rtk_savings.ts +710 -0
  596. package/src/scripts/bench_run.ts +509 -0
  597. package/src/scripts/bench_runner.ts +519 -0
  598. package/src/scripts/build_cloud_bundle.ts +692 -0
  599. package/src/scripts/build_discovery_manifest.ts +1371 -0
  600. package/src/scripts/build_linear_digest.ts +368 -0
  601. package/src/scripts/build_mcp_registry_manifest.ts +351 -0
  602. package/src/scripts/build_rule_trigger_matrix.ts +469 -0
  603. package/src/scripts/capture_showcase_session.ts +735 -0
  604. package/src/scripts/chat_history.ts +2301 -0
  605. package/src/scripts/check_always_budget.ts +694 -0
  606. package/src/scripts/check_artefact_checksums.ts +281 -0
  607. package/src/scripts/check_augment_description_cap.ts +133 -0
  608. package/src/scripts/check_augmentignore.ts +108 -0
  609. package/src/scripts/check_beta_review_markers.ts +234 -0
  610. package/src/scripts/check_bite_sized_granularity.ts +116 -0
  611. package/src/scripts/check_cluster_patterns.ts +285 -0
  612. package/src/scripts/check_command_count_messaging.ts +224 -0
  613. package/src/scripts/check_condensation.ts +900 -0
  614. package/src/scripts/check_condensed_paths.ts +414 -0
  615. package/src/scripts/check_context_paths.ts +388 -0
  616. package/src/scripts/check_council_config_location.ts +260 -0
  617. package/src/scripts/check_council_layout.ts +180 -0
  618. package/src/scripts/check_council_references.ts +345 -0
  619. package/src/scripts/check_discovery_determinism.ts +124 -0
  620. package/src/scripts/check_gate_paths.ts +230 -0
  621. package/src/scripts/check_iron_law_prominence.ts +298 -0
  622. package/src/scripts/check_kernel_rule_bundle.ts +242 -0
  623. package/src/scripts/check_knowledge_cards.ts +759 -0
  624. package/src/scripts/check_md_language.ts +291 -0
  625. package/src/scripts/check_memory.ts +845 -0
  626. package/src/scripts/check_memory_proposal.ts +351 -0
  627. package/src/scripts/check_module_management_neutral.ts +238 -0
  628. package/src/scripts/check_no_conflict_markers.ts +298 -0
  629. package/src/scripts/check_no_conflict_markers_allowlist.json +4 -0
  630. package/src/scripts/check_no_external_sources.ts +351 -0
  631. package/src/scripts/check_no_local_settings_committed.ts +69 -0
  632. package/src/scripts/check_no_new_legacy_path.ts +188 -0
  633. package/src/scripts/check_no_roadmap_refs.ts +304 -0
  634. package/src/scripts/check_one_off_location.ts +165 -0
  635. package/src/scripts/check_overlay_cascade_subdirs.ts +188 -0
  636. package/src/scripts/check_portability.ts +860 -0
  637. package/src/scripts/check_proposal.ts +0 -0
  638. package/src/scripts/check_public_catalog_links.ts +204 -0
  639. package/src/scripts/check_public_links.ts +357 -0
  640. package/src/scripts/check_references.ts +963 -0
  641. package/src/scripts/check_release_includes_discovery.ts +94 -0
  642. package/src/scripts/check_release_pr_shape.ts +222 -0
  643. package/src/scripts/check_release_published.ts +235 -0
  644. package/src/scripts/check_release_trunk_sync.ts +203 -0
  645. package/src/scripts/check_reply_consistency.ts +359 -0
  646. package/src/scripts/check_roadmap_trackable.ts +268 -0
  647. package/src/scripts/check_role_doc_links.ts +187 -0
  648. package/src/scripts/check_safety_floor_untouched.ts +160 -0
  649. package/src/scripts/check_skill_requires.ts +205 -0
  650. package/src/scripts/check_structural_breaking.ts +170 -0
  651. package/src/scripts/check_surface_tiers.ts +567 -0
  652. package/src/scripts/check_template_pin_drift.ts +222 -0
  653. package/src/scripts/check_test_coverage_diff.ts +235 -0
  654. package/src/scripts/check_token_optimizer_freshness.ts +183 -0
  655. package/src/scripts/check_trigger_evals.ts +375 -0
  656. package/src/scripts/check_update_banner.ts +143 -0
  657. package/src/scripts/ci_status.ts +0 -0
  658. package/src/scripts/ci_summary.ts +235 -0
  659. package/src/scripts/ci_time_ratio.ts +526 -0
  660. package/src/scripts/command_suggester/cooldown.ts +176 -0
  661. package/src/scripts/command_suggester/index.ts +41 -0
  662. package/src/scripts/command_suggester/loader.ts +205 -0
  663. package/src/scripts/command_suggester/match.ts +294 -0
  664. package/src/scripts/command_suggester/rank.ts +201 -0
  665. package/src/scripts/command_suggester/render.ts +122 -0
  666. package/src/scripts/command_suggester/sanitize.ts +114 -0
  667. package/src/scripts/command_suggester/settings.ts +186 -0
  668. package/src/scripts/command_suggester/types.ts +0 -0
  669. package/src/scripts/compile_router.ts +297 -0
  670. package/src/scripts/condense.sh +7 -1
  671. package/src/scripts/condense.ts +2035 -0
  672. package/src/scripts/condense_memory.ts +334 -0
  673. package/src/scripts/config/index.ts +15 -0
  674. package/src/scripts/config/packs.ts +310 -0
  675. package/src/scripts/config/presets.ts +369 -0
  676. package/src/scripts/config/profile_explain.ts +114 -0
  677. package/src/scripts/config/profiles.ts +277 -0
  678. package/src/scripts/config/session_profiles.ts +1064 -0
  679. package/src/scripts/context_hygiene_hook.ts +272 -0
  680. package/src/scripts/cost_by_conversation.ts +444 -0
  681. package/src/scripts/cost_summary.ts +407 -0
  682. package/src/scripts/council_cli.ts +2827 -0
  683. package/src/scripts/council_prune.ts +153 -0
  684. package/src/scripts/cross_repo_retrieve.ts +694 -0
  685. package/src/scripts/discovery_stats.ts +218 -0
  686. package/src/scripts/evidence_report.ts +580 -0
  687. package/src/scripts/external_sources_denylist.json +1 -0
  688. package/src/scripts/extract_audit_patterns.ts +394 -0
  689. package/src/scripts/first_run_gate_hook.ts +246 -0
  690. package/src/scripts/gen_discovery_baseline.ts +297 -0
  691. package/src/scripts/generate_capabilities_index.ts +496 -0
  692. package/src/scripts/generate_capability_matrix.ts +430 -0
  693. package/src/scripts/generate_catalog.ts +178 -0
  694. package/src/scripts/generate_command_flows.ts +316 -0
  695. package/src/scripts/generate_cookbook.ts +302 -0
  696. package/src/scripts/generate_index.ts +500 -0
  697. package/src/scripts/generate_ownership_matrix.ts +646 -0
  698. package/src/scripts/generate_pack_manifests.ts +1025 -0
  699. package/src/scripts/generate_role_experiences_catalog.ts +265 -0
  700. package/src/scripts/hermetic-install.sh +22 -11
  701. package/src/scripts/hook_manifest.yaml +24 -10
  702. package/src/scripts/hooks/augment-chat-history.sh +3 -10
  703. package/src/scripts/hooks/augment-context-hygiene.sh +3 -10
  704. package/src/scripts/hooks/augment-dispatcher.sh +3 -10
  705. package/src/scripts/hooks/augment-onboarding-gate.sh +3 -10
  706. package/src/scripts/hooks/augment-roadmap-progress.sh +3 -10
  707. package/src/scripts/hooks/block_no_verify.ts +413 -0
  708. package/src/scripts/hooks/cline-dispatcher.sh +3 -10
  709. package/src/scripts/hooks/cowork-dispatcher.sh +3 -14
  710. package/src/scripts/hooks/cursor-dispatcher.sh +3 -10
  711. package/src/scripts/hooks/dispatch_hook.ts +851 -0
  712. package/src/scripts/hooks/dispatch_issues.ts +226 -0
  713. package/src/scripts/hooks/envelope.ts +140 -0
  714. package/src/scripts/hooks/gemini-dispatcher.sh +3 -8
  715. package/src/scripts/hooks/replay_hook.ts +364 -0
  716. package/src/scripts/hooks/state_io.ts +293 -0
  717. package/src/scripts/hooks/windsurf-dispatcher.sh +3 -9
  718. package/src/scripts/hooks_doctor.ts +418 -0
  719. package/src/scripts/hooks_status.ts +292 -0
  720. package/src/scripts/injection_scan_hook.ts +285 -0
  721. package/src/scripts/install +36 -22
  722. package/src/scripts/install-hooks.sh +20 -14
  723. package/src/scripts/install.sh +38 -14
  724. package/src/scripts/install.ts +4515 -0
  725. package/src/scripts/inventory_abstraction_budget.ts +1104 -0
  726. package/src/scripts/inventory_frontmatter.ts +320 -0
  727. package/src/scripts/inventory_meta_layers.ts +516 -0
  728. package/src/scripts/iron_law_sha.ts +233 -0
  729. package/src/scripts/knowledge_global_cli.ts +1105 -0
  730. package/src/scripts/linked_projects_list.ts +310 -0
  731. package/src/scripts/lint_agent_security.ts +224 -0
  732. package/src/scripts/lint_agent_skill_names.ts +241 -0
  733. package/src/scripts/lint_agents_layout.ts +205 -0
  734. package/src/scripts/lint_agents_md.ts +294 -0
  735. package/src/scripts/lint_archived_skills.ts +309 -0
  736. package/src/scripts/lint_artefact_frontmatter.ts +359 -0
  737. package/src/scripts/lint_bench_ab.ts +319 -0
  738. package/src/scripts/lint_bench_corpus.ts +421 -0
  739. package/src/scripts/lint_command_flow_coverage.ts +231 -0
  740. package/src/scripts/lint_command_routing.ts +377 -0
  741. package/src/scripts/lint_command_tiers.ts +345 -0
  742. package/src/scripts/lint_command_verbs.ts +379 -0
  743. package/src/scripts/lint_commit_subjects.ts +243 -0
  744. package/src/scripts/lint_context_spine_usage.ts +198 -0
  745. package/src/scripts/lint_discovery_manifest.ts +540 -0
  746. package/src/scripts/lint_discovery_vocabulary.ts +393 -0
  747. package/src/scripts/lint_empty_roadmaps.ts +147 -0
  748. package/src/scripts/lint_eval_freshness.ts +335 -0
  749. package/src/scripts/lint_examples.ts +183 -0
  750. package/src/scripts/lint_explain_trace.ts +381 -0
  751. package/src/scripts/lint_featured_skills.ts +0 -0
  752. package/src/scripts/lint_flows.ts +701 -0
  753. package/src/scripts/lint_framework_leakage.ts +497 -0
  754. package/src/scripts/lint_framework_leakage_allowlist.json +8 -1
  755. package/src/scripts/lint_frontmatter_boilerplate.ts +356 -0
  756. package/src/scripts/lint_ghostwriter_source.ts +389 -0
  757. package/src/scripts/lint_global_paths.ts +420 -0
  758. package/src/scripts/lint_handoffs.ts +362 -0
  759. package/src/scripts/lint_hidden_unicode.ts +350 -0
  760. package/src/scripts/lint_hook_concern_budget.ts +319 -0
  761. package/src/scripts/lint_hook_manifest.ts +354 -0
  762. package/src/scripts/lint_instruction_smuggling.ts +173 -0
  763. package/src/scripts/lint_load_context.ts +371 -0
  764. package/src/scripts/lint_marketplace.ts +286 -0
  765. package/src/scripts/lint_marketplace_install_completeness.ts +309 -0
  766. package/src/scripts/lint_mcp_config_security.ts +225 -0
  767. package/src/scripts/lint_mcp_registry_manifest.ts +350 -0
  768. package/src/scripts/lint_media_policy_linkage.ts +224 -0
  769. package/src/scripts/lint_missions.ts +774 -0
  770. package/src/scripts/lint_model_tier_coverage.ts +151 -0
  771. package/src/scripts/lint_namespace.ts +295 -0
  772. package/src/scripts/lint_namespace_collisions.ts +203 -0
  773. package/src/scripts/lint_new_skill_gate.ts +462 -0
  774. package/src/scripts/lint_no_new_atomic_commands.ts +342 -0
  775. package/src/scripts/lint_one_off_age.ts +348 -0
  776. package/src/scripts/lint_orchestration_dsl.ts +377 -0
  777. package/src/scripts/lint_orchestrator_auto_detect.ts +177 -0
  778. package/src/scripts/lint_pack_boundaries.ts +366 -0
  779. package/src/scripts/lint_pack_dependencies.ts +541 -0
  780. package/src/scripts/lint_pack_first_win.ts +202 -0
  781. package/src/scripts/lint_persona_governance.ts +292 -0
  782. package/src/scripts/lint_positioning.ts +257 -0
  783. package/src/scripts/lint_profile_overlay_set_only.ts +324 -0
  784. package/src/scripts/lint_readme_jargon.ts +189 -0
  785. package/src/scripts/lint_readme_size.ts +73 -0
  786. package/src/scripts/lint_regression.ts +497 -0
  787. package/src/scripts/lint_roadmap_ci_steps.ts +252 -0
  788. package/src/scripts/lint_roadmap_complexity.ts +295 -0
  789. package/src/scripts/lint_roadmap_later_disposition.ts +357 -0
  790. package/src/scripts/lint_role_experiences.ts +410 -0
  791. package/src/scripts/lint_rule_interactions.ts +281 -0
  792. package/src/scripts/lint_rule_tiers.ts +169 -0
  793. package/src/scripts/lint_showcase_sessions.ts +254 -0
  794. package/src/scripts/lint_skill_frontmatter_safety.ts +279 -0
  795. package/src/scripts/lint_skill_originality.ts +586 -0
  796. package/src/scripts/lint_skill_originality_allowlist.json +20 -0
  797. package/src/scripts/lint_skill_tools.ts +320 -0
  798. package/src/scripts/lint_ticket_buildable.ts +1027 -0
  799. package/src/scripts/lint_topics_yaml.ts +203 -0
  800. package/src/scripts/lint_trust_coherence.ts +377 -0
  801. package/src/scripts/lint_value_dashboard.ts +314 -0
  802. package/src/scripts/lint_workflow_security.ts +637 -0
  803. package/src/scripts/lint_workflow_security_allowlist.json +20 -0
  804. package/src/scripts/lint_workspace_boundary.ts +248 -0
  805. package/src/scripts/mcp_parity_smoke.ts +638 -0
  806. package/src/scripts/mcp_render.ts +346 -0
  807. package/src/scripts/mcp_server/__main__.ts +28 -0
  808. package/src/scripts/mcp_server/catalog.ts +154 -0
  809. package/src/scripts/mcp_server/index.ts +24 -0
  810. package/src/scripts/mcp_server/metadata.ts +83 -0
  811. package/src/scripts/mcp_server/prompts.ts +711 -0
  812. package/src/scripts/mcp_server/resources.ts +343 -0
  813. package/src/scripts/mcp_server/server.ts +439 -0
  814. package/src/scripts/mcp_server/telemetry.ts +154 -0
  815. package/src/scripts/mcp_server/tools.ts +1031 -0
  816. package/src/scripts/mcp_setup.sh +25 -52
  817. package/src/scripts/mcp_telemetry_health.ts +362 -0
  818. package/src/scripts/mcp_telemetry_query.ts +371 -0
  819. package/src/scripts/mcp_telemetry_store.ts +422 -0
  820. package/src/scripts/measure_augment_budget.ts +453 -0
  821. package/src/scripts/measure_density.ts +618 -0
  822. package/src/scripts/measure_frugality_savings.ts +353 -0
  823. package/src/scripts/measure_markitdown_lift.ts +299 -0
  824. package/src/scripts/measure_patterns.ts +682 -0
  825. package/src/scripts/measure_projection_bytes.ts +425 -0
  826. package/src/scripts/measure_rule_budget.ts +627 -0
  827. package/src/scripts/measure_skill_reduction.ts +442 -0
  828. package/src/scripts/media/lib/adapter-common.sh +247 -0
  829. package/src/scripts/media/lib/adapter-contract.md +329 -0
  830. package/src/scripts/media/lib/fixtures/comfyui/result.json +1 -0
  831. package/src/scripts/media/lib/fixtures/fal/result.json +1 -0
  832. package/src/scripts/media/lib/fixtures/flux/asset-0001.png +0 -0
  833. package/src/scripts/media/lib/fixtures/flux/result.json +1 -0
  834. package/src/scripts/media/lib/fixtures/gemini-image/asset-0001.png +0 -0
  835. package/src/scripts/media/lib/fixtures/gemini-image/result.json +1 -0
  836. package/src/scripts/media/lib/fixtures/gemini-veo/result.json +1 -0
  837. package/src/scripts/media/lib/fixtures/higgsfield/result.json +1 -0
  838. package/src/scripts/media/lib/fixtures/ideogram/asset-0001.png +0 -0
  839. package/src/scripts/media/lib/fixtures/ideogram/result.json +1 -0
  840. package/src/scripts/media/lib/fixtures/kling/result.json +1 -0
  841. package/src/scripts/media/lib/fixtures/musetalk/result.json +1 -0
  842. package/src/scripts/media/lib/fixtures/openai-images/result.json +1 -0
  843. package/src/scripts/media/lib/fixtures/recraft/asset-0001.svg +1 -0
  844. package/src/scripts/media/lib/fixtures/recraft/result.json +1 -0
  845. package/src/scripts/media/lib/fixtures/replicate/result.json +1 -0
  846. package/src/scripts/media/lib/fixtures/sora/result.json +1 -0
  847. package/src/scripts/media/lib/fixtures/syncso/result.json +1 -0
  848. package/src/scripts/media/lib/load-config.sh +180 -0
  849. package/src/scripts/media/lib/redact.sh +85 -0
  850. package/src/scripts/memory_hash.ts +331 -0
  851. package/src/scripts/memory_lookup.ts +1278 -0
  852. package/src/scripts/memory_report.ts +845 -0
  853. package/src/scripts/memory_signal.ts +417 -0
  854. package/src/scripts/memory_status.ts +189 -0
  855. package/src/scripts/migrate_command_suggestions.ts +341 -0
  856. package/src/scripts/migrate_frontmatter_defaults.ts +539 -0
  857. package/src/scripts/migration_status.ts +301 -0
  858. package/src/scripts/mine_session.ts +645 -0
  859. package/src/scripts/minimal_safe_diff_hook.ts +355 -0
  860. package/src/scripts/move_artefact.ts +869 -0
  861. package/src/scripts/new_skill.ts +404 -0
  862. package/src/scripts/onboarding_gate_hook.ts +224 -0
  863. package/src/scripts/pack_dependency_allowlist.json +1 -1
  864. package/src/scripts/pack_mcp_content.ts +552 -0
  865. package/src/scripts/parity/README.md +140 -0
  866. package/src/scripts/parity/compare.ts +189 -0
  867. package/src/scripts/parity/coverage_diff.ts +199 -0
  868. package/src/scripts/parity/phase-manifest.json +93 -0
  869. package/src/scripts/parity/phase_gate.ts +270 -0
  870. package/src/scripts/parity/replay.ts +320 -0
  871. package/src/scripts/pattern_share.ts +363 -0
  872. package/src/scripts/plan_physical_move.ts +605 -0
  873. package/src/scripts/prediction-pool/poisson_sim.ts +537 -0
  874. package/src/scripts/prediction-pool/pool_winsim.ts +677 -0
  875. package/src/scripts/prediction-pool/score_ev.ts +546 -0
  876. package/src/scripts/print_required_checks.ts +249 -0
  877. package/src/scripts/probe_projection_fidelity.ts +468 -0
  878. package/src/scripts/probe_skill_registration.ts +787 -0
  879. package/src/scripts/profile_staleness_hook.ts +169 -0
  880. package/src/scripts/profile_use.ts +227 -0
  881. package/src/scripts/project_thin_rules.ts +387 -0
  882. package/src/scripts/propose_modules_config.ts +311 -0
  883. package/src/scripts/prototype_lint_contradictions.ts +414 -0
  884. package/src/scripts/prove_pack_extractable.ts +388 -0
  885. package/src/scripts/readme_linter.ts +913 -0
  886. package/src/scripts/redact_hook_capture.ts +325 -0
  887. package/src/scripts/refine_ticket_detect.ts +703 -0
  888. package/src/scripts/release.ts +1697 -0
  889. package/src/scripts/render_benchmark_md.ts +664 -0
  890. package/src/scripts/render_value_md.ts +506 -0
  891. package/src/scripts/repro/repro_marketplace_install_gap.sh +1 -1
  892. package/src/scripts/roadmap_progress_hook.ts +410 -0
  893. package/src/scripts/router_telemetry.ts +972 -0
  894. package/src/scripts/run.ts +98 -0
  895. package/src/scripts/run_skill_evals.ts +477 -0
  896. package/src/scripts/runtime_dispatcher.ts +586 -0
  897. package/src/scripts/runtime_handler.ts +231 -0
  898. package/src/scripts/runtime_registry.ts +394 -0
  899. package/src/scripts/schemas/command.schema.json +3 -2
  900. package/src/scripts/schemas/mission-catalog.schema.json +112 -0
  901. package/src/scripts/schemas/mission.schema.json +87 -0
  902. package/src/scripts/schemas/pack.schema.json +6 -0
  903. package/src/scripts/schemas/rule.schema.json +1 -0
  904. package/src/scripts/schemas/skill.schema.json +1 -0
  905. package/src/scripts/schemas/ticket-manifest.schema.json +35 -0
  906. package/src/scripts/schemas/ticket.schema.json +60 -0
  907. package/src/scripts/score_skill_selection.ts +570 -0
  908. package/src/scripts/security_audit_config.ts +423 -0
  909. package/src/scripts/skill_collision_clusters.ts +448 -0
  910. package/src/scripts/skill_discovery.ts +690 -0
  911. package/src/scripts/skill_linter.ts +4276 -0
  912. package/src/scripts/skill_overlap.ts +414 -0
  913. package/src/scripts/skill_preview.ts +548 -0
  914. package/src/scripts/skill_tools/audit_persona_coverage.ts +427 -0
  915. package/src/scripts/skill_tools/audit_user_type_coverage.ts +507 -0
  916. package/src/scripts/skill_tools/index.ts +28 -0
  917. package/src/scripts/skill_tools/run_block_d_eval.ts +373 -0
  918. package/src/scripts/skill_tools/score_skill_relevance.ts +475 -0
  919. package/src/scripts/skill_tools/suggest_skill_for_task.ts +288 -0
  920. package/src/scripts/skill_trigger_eval.ts +1046 -0
  921. package/src/scripts/skill_usage_collect.ts +465 -0
  922. package/src/scripts/skill_usage_report.ts +364 -0
  923. package/src/scripts/smoke/kernel.sh +4 -5
  924. package/src/scripts/smoke/router.sh +76 -76
  925. package/src/scripts/smoke/schema.sh +2 -2
  926. package/src/scripts/smoke/skills.sh +73 -52
  927. package/src/scripts/smoke_path_resolution.ts +194 -0
  928. package/src/scripts/smoke_quickstart.ts +224 -0
  929. package/src/scripts/snapshot_agent_outputs.ts +375 -0
  930. package/src/scripts/spotcheck_thin_root.ts +247 -0
  931. package/src/scripts/surface-tiers.yml +68 -0
  932. package/src/scripts/sync_agent_settings.ts +763 -0
  933. package/src/scripts/sync_github_metadata.ts +550 -0
  934. package/src/scripts/sync_gitignore.ts +630 -0
  935. package/src/scripts/sync_yaml_rt.ts +910 -0
  936. package/src/scripts/telegraph_stats.ts +447 -0
  937. package/src/scripts/tool_registry.ts +330 -0
  938. package/src/scripts/tools/adapter_errors.ts +93 -0
  939. package/src/scripts/tools/base_adapter.ts +147 -0
  940. package/src/scripts/tools/github_adapter.ts +229 -0
  941. package/src/scripts/tools/jira_adapter.ts +196 -0
  942. package/src/scripts/trigger_coverage.ts +251 -0
  943. package/src/scripts/update_counts.ts +284 -0
  944. package/src/scripts/update_prices.ts +219 -0
  945. package/src/scripts/validate_agent_settings.ts +265 -0
  946. package/src/scripts/validate_decision_engine.ts +366 -0
  947. package/src/scripts/validate_discovery_manifest.ts +160 -0
  948. package/src/scripts/validate_frontmatter.ts +1030 -0
  949. package/src/scripts/validate_pack_yaml.ts +0 -0
  950. package/src/scripts/validate_safe_paths.ts +164 -0
  951. package/src/scripts/validate_telegraph_carveouts.ts +485 -0
  952. package/src/scripts/verify_before_complete_hook.ts +306 -0
  953. package/src/scripts/verify_physical_move.ts +411 -0
  954. package/src/scripts/wrapper_freshness_hook.ts +179 -0
  955. package/dist/agent-src/scripts/archive_completed_roadmaps.py +0 -171
  956. package/dist/agent-src/scripts/update_roadmap_progress.py +0 -537
  957. package/dist/agent-src/skills/corpus-grounding/scripts/bm25_search.py +0 -212
  958. package/dist/agent-src/skills/corpus-grounding/scripts/decision_engine.py +0 -438
  959. package/dist/agent-src/skills/corpus-grounding/scripts/ground.py +0 -166
  960. package/dist/agent-src/skills/corpus-grounding/scripts/schema_validator.py +0 -160
  961. package/dist/agent-src/skills/design-tokens/scripts/tokens.py +0 -296
  962. package/dist/agent-src/skills/react-shadcn-ui/scripts/shadcn_add.py +0 -299
  963. package/dist/agent-src/skills/tailwind-engineer/scripts/tailwind_config_gen.py +0 -463
  964. package/dist/agent-src/templates/scripts/check_memory.py +0 -282
  965. package/dist/agent-src/templates/scripts/check_memory_proposal.py +0 -180
  966. package/dist/agent-src/templates/scripts/implement_ticket/__init__.py +0 -94
  967. package/dist/agent-src/templates/scripts/implement_ticket/__main__.py +0 -15
  968. package/dist/agent-src/templates/scripts/memory_hash.py +0 -75
  969. package/dist/agent-src/templates/scripts/memory_lookup.py +0 -436
  970. package/dist/agent-src/templates/scripts/memory_report.py +0 -314
  971. package/dist/agent-src/templates/scripts/memory_signal.py +0 -165
  972. package/dist/agent-src/templates/scripts/memory_status.py +0 -76
  973. package/dist/agent-src/templates/scripts/pr_review_routing.py +0 -340
  974. package/dist/agent-src/templates/scripts/pr_risk_review.py +0 -211
  975. package/dist/agent-src/templates/scripts/telemetry/__init__.py +0 -42
  976. package/dist/agent-src/templates/scripts/telemetry/aggregator.py +0 -169
  977. package/dist/agent-src/templates/scripts/telemetry/boundary.py +0 -171
  978. package/dist/agent-src/templates/scripts/telemetry/engagement.py +0 -297
  979. package/dist/agent-src/templates/scripts/telemetry/report_renderer.py +0 -197
  980. package/dist/agent-src/templates/scripts/telemetry/settings.py +0 -177
  981. package/dist/agent-src/templates/scripts/telemetry_record.py +0 -179
  982. package/dist/agent-src/templates/scripts/telemetry_report.py +0 -161
  983. package/dist/agent-src/templates/scripts/telemetry_status.py +0 -142
  984. package/dist/agent-src/templates/scripts/tier_usage_report.py +0 -183
  985. package/dist/agent-src/templates/scripts/work_engine/__init__.py +0 -58
  986. package/dist/agent-src/templates/scripts/work_engine/__main__.py +0 -9
  987. package/dist/agent-src/templates/scripts/work_engine/_lib/__init__.py +0 -7
  988. package/dist/agent-src/templates/scripts/work_engine/_lib/agent_settings.py +0 -840
  989. package/dist/agent-src/templates/scripts/work_engine/_lib/user_global_paths.py +0 -249
  990. package/dist/agent-src/templates/scripts/work_engine/cli.py +0 -195
  991. package/dist/agent-src/templates/scripts/work_engine/cli_args.py +0 -116
  992. package/dist/agent-src/templates/scripts/work_engine/delivery_state.py +0 -137
  993. package/dist/agent-src/templates/scripts/work_engine/directives/__init__.py +0 -33
  994. package/dist/agent-src/templates/scripts/work_engine/directives/backend/__init__.py +0 -98
  995. package/dist/agent-src/templates/scripts/work_engine/directives/backend/analyze.py +0 -98
  996. package/dist/agent-src/templates/scripts/work_engine/directives/backend/implement.py +0 -145
  997. package/dist/agent-src/templates/scripts/work_engine/directives/backend/memory.py +0 -136
  998. package/dist/agent-src/templates/scripts/work_engine/directives/backend/plan.py +0 -175
  999. package/dist/agent-src/templates/scripts/work_engine/directives/backend/refine.py +0 -396
  1000. package/dist/agent-src/templates/scripts/work_engine/directives/backend/report.py +0 -227
  1001. package/dist/agent-src/templates/scripts/work_engine/directives/backend/test.py +0 -180
  1002. package/dist/agent-src/templates/scripts/work_engine/directives/backend/verify.py +0 -170
  1003. package/dist/agent-src/templates/scripts/work_engine/directives/mixed/__init__.py +0 -116
  1004. package/dist/agent-src/templates/scripts/work_engine/directives/mixed/contract.py +0 -254
  1005. package/dist/agent-src/templates/scripts/work_engine/directives/mixed/stitch.py +0 -229
  1006. package/dist/agent-src/templates/scripts/work_engine/directives/mixed/ui.py +0 -231
  1007. package/dist/agent-src/templates/scripts/work_engine/directives/ui/__init__.py +0 -113
  1008. package/dist/agent-src/templates/scripts/work_engine/directives/ui/_passthrough.py +0 -44
  1009. package/dist/agent-src/templates/scripts/work_engine/directives/ui/apply.py +0 -241
  1010. package/dist/agent-src/templates/scripts/work_engine/directives/ui/audit.py +0 -414
  1011. package/dist/agent-src/templates/scripts/work_engine/directives/ui/design.py +0 -335
  1012. package/dist/agent-src/templates/scripts/work_engine/directives/ui/polish.py +0 -513
  1013. package/dist/agent-src/templates/scripts/work_engine/directives/ui/review.py +0 -471
  1014. package/dist/agent-src/templates/scripts/work_engine/directives/ui_trivial/__init__.py +0 -119
  1015. package/dist/agent-src/templates/scripts/work_engine/directives/ui_trivial/_skipped.py +0 -37
  1016. package/dist/agent-src/templates/scripts/work_engine/directives/ui_trivial/apply.py +0 -165
  1017. package/dist/agent-src/templates/scripts/work_engine/directives/ui_trivial/refine.py +0 -66
  1018. package/dist/agent-src/templates/scripts/work_engine/directives/ui_trivial/report.py +0 -62
  1019. package/dist/agent-src/templates/scripts/work_engine/directives/ui_trivial/test.py +0 -115
  1020. package/dist/agent-src/templates/scripts/work_engine/dispatcher.py +0 -331
  1021. package/dist/agent-src/templates/scripts/work_engine/emitters.py +0 -68
  1022. package/dist/agent-src/templates/scripts/work_engine/errors.py +0 -19
  1023. package/dist/agent-src/templates/scripts/work_engine/hook_bootstrap.py +0 -91
  1024. package/dist/agent-src/templates/scripts/work_engine/hooks/__init__.py +0 -54
  1025. package/dist/agent-src/templates/scripts/work_engine/hooks/builtin/__init__.py +0 -35
  1026. package/dist/agent-src/templates/scripts/work_engine/hooks/builtin/_chat_history_base.py +0 -59
  1027. package/dist/agent-src/templates/scripts/work_engine/hooks/builtin/chat_history_append.py +0 -43
  1028. package/dist/agent-src/templates/scripts/work_engine/hooks/builtin/chat_history_halt_append.py +0 -41
  1029. package/dist/agent-src/templates/scripts/work_engine/hooks/builtin/decision_gate.py +0 -162
  1030. package/dist/agent-src/templates/scripts/work_engine/hooks/builtin/decision_trace.py +0 -163
  1031. package/dist/agent-src/templates/scripts/work_engine/hooks/builtin/directive_set_guard.py +0 -53
  1032. package/dist/agent-src/templates/scripts/work_engine/hooks/builtin/halt_surface_audit.py +0 -50
  1033. package/dist/agent-src/templates/scripts/work_engine/hooks/builtin/memory_visibility.py +0 -141
  1034. package/dist/agent-src/templates/scripts/work_engine/hooks/builtin/state_shape_validation.py +0 -52
  1035. package/dist/agent-src/templates/scripts/work_engine/hooks/builtin/trace.py +0 -84
  1036. package/dist/agent-src/templates/scripts/work_engine/hooks/context.py +0 -66
  1037. package/dist/agent-src/templates/scripts/work_engine/hooks/events.py +0 -44
  1038. package/dist/agent-src/templates/scripts/work_engine/hooks/exceptions.py +0 -79
  1039. package/dist/agent-src/templates/scripts/work_engine/hooks/registry.py +0 -60
  1040. package/dist/agent-src/templates/scripts/work_engine/hooks/runner.py +0 -73
  1041. package/dist/agent-src/templates/scripts/work_engine/hooks/settings.py +0 -196
  1042. package/dist/agent-src/templates/scripts/work_engine/input_builders.py +0 -163
  1043. package/dist/agent-src/templates/scripts/work_engine/intent/__init__.py +0 -47
  1044. package/dist/agent-src/templates/scripts/work_engine/intent/classify.py +0 -280
  1045. package/dist/agent-src/templates/scripts/work_engine/migration/__init__.py +0 -8
  1046. package/dist/agent-src/templates/scripts/work_engine/migration/v0_to_v1.py +0 -231
  1047. package/dist/agent-src/templates/scripts/work_engine/orchestration.py +0 -193
  1048. package/dist/agent-src/templates/scripts/work_engine/persona_policy.py +0 -85
  1049. package/dist/agent-src/templates/scripts/work_engine/resolvers/__init__.py +0 -22
  1050. package/dist/agent-src/templates/scripts/work_engine/resolvers/diff.py +0 -106
  1051. package/dist/agent-src/templates/scripts/work_engine/resolvers/file.py +0 -113
  1052. package/dist/agent-src/templates/scripts/work_engine/resolvers/prompt.py +0 -90
  1053. package/dist/agent-src/templates/scripts/work_engine/scoring/__init__.py +0 -14
  1054. package/dist/agent-src/templates/scripts/work_engine/scoring/confidence.py +0 -300
  1055. package/dist/agent-src/templates/scripts/work_engine/scoring/decision_engine.py +0 -351
  1056. package/dist/agent-src/templates/scripts/work_engine/scoring/decision_trace.py +0 -141
  1057. package/dist/agent-src/templates/scripts/work_engine/scoring/memory_visibility.py +0 -283
  1058. package/dist/agent-src/templates/scripts/work_engine/stack/__init__.py +0 -31
  1059. package/dist/agent-src/templates/scripts/work_engine/stack/detect.py +0 -187
  1060. package/dist/agent-src/templates/scripts/work_engine/stack/runner.py +0 -481
  1061. package/dist/agent-src/templates/scripts/work_engine/state.py +0 -694
  1062. package/dist/agent-src/templates/scripts/work_engine/state_io.py +0 -202
  1063. package/dist/cli/python/resolvePython.js +0 -38
  1064. package/dist/cli/python/resolvePython.js.map +0 -1
  1065. package/src/scripts/__pycache__/validate_frontmatter.cpython-312.pyc +0 -0
  1066. package/src/scripts/_archive/_backfill_skill_domains.py +0 -140
  1067. package/src/scripts/_archive/_bootstrap_tier_frontmatter.py +0 -151
  1068. package/src/scripts/_archive/_p43_bodies.py +0 -235
  1069. package/src/scripts/_archive/_p43_condense.py +0 -118
  1070. package/src/scripts/_archive/_p4_migrate.py +0 -199
  1071. package/src/scripts/_archive/_phase2_shim_helper.py +0 -109
  1072. package/src/scripts/_archive/_pilot_council_question.py +0 -57
  1073. package/src/scripts/_cli/__init__.py +0 -0
  1074. package/src/scripts/_cli/cmd_doctor.py +0 -1669
  1075. package/src/scripts/_cli/cmd_explain.py +0 -355
  1076. package/src/scripts/_cli/cmd_export.py +0 -157
  1077. package/src/scripts/_cli/cmd_migrate.py +0 -524
  1078. package/src/scripts/_cli/cmd_prune.py +0 -322
  1079. package/src/scripts/_cli/cmd_refresh.py +0 -179
  1080. package/src/scripts/_cli/cmd_settings_check.py +0 -171
  1081. package/src/scripts/_cli/cmd_settings_migrate.py +0 -147
  1082. package/src/scripts/_cli/cmd_sync.py +0 -166
  1083. package/src/scripts/_cli/cmd_uninstall.py +0 -476
  1084. package/src/scripts/_cli/cmd_update.py +0 -279
  1085. package/src/scripts/_cli/cmd_upgrade.py +0 -172
  1086. package/src/scripts/_cli/cmd_validate.py +0 -177
  1087. package/src/scripts/_cli/cmd_versions.py +0 -160
  1088. package/src/scripts/_cli/explain_last/__init__.py +0 -122
  1089. package/src/scripts/_cli/explain_last/assumptions.py +0 -59
  1090. package/src/scripts/_cli/explain_last/council.py +0 -105
  1091. package/src/scripts/_cli/explain_last/halt.py +0 -44
  1092. package/src/scripts/_cli/explain_last/inputs.py +0 -128
  1093. package/src/scripts/_cli/explain_last/memory.py +0 -94
  1094. package/src/scripts/_cli/explain_last/provider.py +0 -52
  1095. package/src/scripts/_cli/explain_last/render.py +0 -52
  1096. package/src/scripts/_cli/explain_last/route.py +0 -59
  1097. package/src/scripts/_cli/explain_last/scrubber.py +0 -105
  1098. package/src/scripts/_cli/explain_last/sections/__init__.py +0 -35
  1099. package/src/scripts/_cli/explain_last/sections/assumptions.py +0 -21
  1100. package/src/scripts/_cli/explain_last/sections/council.py +0 -27
  1101. package/src/scripts/_cli/explain_last/sections/halt.py +0 -31
  1102. package/src/scripts/_cli/explain_last/sections/header.py +0 -24
  1103. package/src/scripts/_cli/explain_last/sections/inputs.py +0 -27
  1104. package/src/scripts/_cli/explain_last/sections/memory.py +0 -21
  1105. package/src/scripts/_cli/explain_last/sections/pack.py +0 -16
  1106. package/src/scripts/_cli/explain_last/sections/provider.py +0 -26
  1107. package/src/scripts/_cli/explain_last/sections/route.py +0 -22
  1108. package/src/scripts/_cli/explain_last/state_loader.py +0 -76
  1109. package/src/scripts/_emit_domain_table.py +0 -35
  1110. package/src/scripts/_lib/__init__.py +0 -5
  1111. package/src/scripts/_lib/__pycache__/__init__.cpython-312.pyc +0 -0
  1112. package/src/scripts/_lib/__pycache__/agent_src.cpython-312.pyc +0 -0
  1113. package/src/scripts/_lib/agent_settings.py +0 -840
  1114. package/src/scripts/_lib/agent_src.py +0 -491
  1115. package/src/scripts/_lib/agents_overlay.py +0 -120
  1116. package/src/scripts/_lib/bench_ab_cache.py +0 -162
  1117. package/src/scripts/_lib/bench_ab_scoring.py +0 -209
  1118. package/src/scripts/_lib/bench_ab_scoring_v2.py +0 -227
  1119. package/src/scripts/_lib/bench_cost.py +0 -138
  1120. package/src/scripts/_lib/bench_quality.py +0 -118
  1121. package/src/scripts/_lib/bench_report.py +0 -149
  1122. package/src/scripts/_lib/bench_telegraph.py +0 -273
  1123. package/src/scripts/_lib/bench_telegraph_report.py +0 -151
  1124. package/src/scripts/_lib/changelog_eras.py +0 -330
  1125. package/src/scripts/_lib/claude_desktop_bundler.py +0 -238
  1126. package/src/scripts/_lib/cli_wrapper.py +0 -64
  1127. package/src/scripts/_lib/fs_atomic.py +0 -116
  1128. package/src/scripts/_lib/global_deploy_inventory.py +0 -312
  1129. package/src/scripts/_lib/install_regenerator.py +0 -134
  1130. package/src/scripts/_lib/installed_lock.py +0 -256
  1131. package/src/scripts/_lib/installed_tools.py +0 -381
  1132. package/src/scripts/_lib/json_pointers.py +0 -260
  1133. package/src/scripts/_lib/link_crypto.py +0 -206
  1134. package/src/scripts/_lib/linked_projects.py +0 -238
  1135. package/src/scripts/_lib/model_tier.py +0 -52
  1136. package/src/scripts/_lib/module_detection.py +0 -223
  1137. package/src/scripts/_lib/pin_resolver.py +0 -152
  1138. package/src/scripts/_lib/script_output.py +0 -144
  1139. package/src/scripts/_lib/security_lint.py +0 -228
  1140. package/src/scripts/_lib/token_count.py +0 -95
  1141. package/src/scripts/_lib/update_check.py +0 -207
  1142. package/src/scripts/_lib/user_global_paths.py +0 -249
  1143. package/src/scripts/_lib/value_ladder.py +0 -696
  1144. package/src/scripts/_lib/value_report.py +0 -455
  1145. package/src/scripts/_phase4_bucket.py +0 -210
  1146. package/src/scripts/_pilot_measure.py +0 -53
  1147. package/src/scripts/_tmp_scan_framework_leakage.py +0 -119
  1148. package/src/scripts/adoption_report.py +0 -195
  1149. package/src/scripts/adoption_snapshot.py +0 -219
  1150. package/src/scripts/adoption_status.py +0 -166
  1151. package/src/scripts/adr/regenerate_index.py +0 -79
  1152. package/src/scripts/ai-video/lib/adapter-common.sh +0 -231
  1153. package/src/scripts/ai-video/lib/adapter-contract.md +0 -329
  1154. package/src/scripts/ai-video/lib/fixtures/comfyui/result.json +0 -1
  1155. package/src/scripts/ai-video/lib/fixtures/fal/result.json +0 -1
  1156. package/src/scripts/ai-video/lib/fixtures/gemini-veo/result.json +0 -1
  1157. package/src/scripts/ai-video/lib/fixtures/higgsfield/result.json +0 -1
  1158. package/src/scripts/ai-video/lib/fixtures/kling/result.json +0 -1
  1159. package/src/scripts/ai-video/lib/fixtures/musetalk/result.json +0 -1
  1160. package/src/scripts/ai-video/lib/fixtures/openai-images/result.json +0 -1
  1161. package/src/scripts/ai-video/lib/fixtures/replicate/result.json +0 -1
  1162. package/src/scripts/ai-video/lib/fixtures/sora/result.json +0 -1
  1163. package/src/scripts/ai-video/lib/fixtures/syncso/result.json +0 -1
  1164. package/src/scripts/ai-video/lib/load-config.sh +0 -180
  1165. package/src/scripts/ai-video/lib/redact.sh +0 -85
  1166. package/src/scripts/ai_council/__init__.py +0 -40
  1167. package/src/scripts/ai_council/_default_prices.py +0 -50
  1168. package/src/scripts/ai_council/advisors.py +0 -148
  1169. package/src/scripts/ai_council/airgap.py +0 -165
  1170. package/src/scripts/ai_council/budget_guard.py +0 -202
  1171. package/src/scripts/ai_council/bundler.py +0 -263
  1172. package/src/scripts/ai_council/cli_hints.py +0 -123
  1173. package/src/scripts/ai_council/clients.py +0 -1385
  1174. package/src/scripts/ai_council/compile_corpus.py +0 -179
  1175. package/src/scripts/ai_council/confidence_gate.py +0 -156
  1176. package/src/scripts/ai_council/config.py +0 -1419
  1177. package/src/scripts/ai_council/consensus.py +0 -329
  1178. package/src/scripts/ai_council/events_log.py +0 -141
  1179. package/src/scripts/ai_council/learn_low_impact_preview.py +0 -252
  1180. package/src/scripts/ai_council/low_impact.py +0 -714
  1181. package/src/scripts/ai_council/low_impact_corpus.py +0 -466
  1182. package/src/scripts/ai_council/low_impact_intake.py +0 -163
  1183. package/src/scripts/ai_council/modes.py +0 -131
  1184. package/src/scripts/ai_council/necessity.py +0 -782
  1185. package/src/scripts/ai_council/one_off_archive/2026-05/_one_off_2a4_acceptance.py +0 -208
  1186. package/src/scripts/ai_council/one_off_archive/2026-05/_one_off_add_quiet.py +0 -149
  1187. package/src/scripts/ai_council/one_off_archive/2026-05/_one_off_budget_v2_audit.py +0 -206
  1188. package/src/scripts/ai_council/one_off_archive/2026-05/_one_off_context_layer_v1_estimate.py +0 -67
  1189. package/src/scripts/ai_council/one_off_archive/2026-05/_one_off_context_layer_v1_review.py +0 -292
  1190. package/src/scripts/ai_council/one_off_archive/2026-05/_one_off_followups_review.py +0 -259
  1191. package/src/scripts/ai_council/one_off_archive/2026-05/_one_off_inject_quiet_flag.py +0 -33
  1192. package/src/scripts/ai_council/one_off_archive/2026-05/_one_off_measure_v2.sh +0 -36
  1193. package/src/scripts/ai_council/one_off_archive/2026-05/_one_off_measure_verbosity.sh +0 -26
  1194. package/src/scripts/ai_council/one_off_archive/2026-05/_one_off_nondestructive_inline_audit.py +0 -209
  1195. package/src/scripts/ai_council/one_off_archive/2026-05/_one_off_per_task.sh +0 -41
  1196. package/src/scripts/ai_council/one_off_archive/2026-05/_one_off_phase4_dispatch_latency.py +0 -108
  1197. package/src/scripts/ai_council/one_off_archive/2026-05/_one_off_phase6_trigger_jaccard.py +0 -92
  1198. package/src/scripts/ai_council/one_off_archive/2026-05/_one_off_phase_2a_budget_rebalance.py +0 -257
  1199. package/src/scripts/ai_council/one_off_archive/2026-05/_one_off_phase_2a_post_revert.py +0 -197
  1200. package/src/scripts/ai_council/one_off_archive/2026-05/_one_off_rebalancing_audit.py +0 -149
  1201. package/src/scripts/ai_council/one_off_archive/2026-05/_one_off_roundtrip.py +0 -111
  1202. package/src/scripts/ai_council/one_off_archive/2026-05/_one_off_rule_hardening_v1.py +0 -251
  1203. package/src/scripts/ai_council/one_off_archive/2026-05/_one_off_silent_taskfiles.py +0 -98
  1204. package/src/scripts/ai_council/one_off_archive/2026-05/_one_off_structural_open_questions.py +0 -232
  1205. package/src/scripts/ai_council/one_off_archive/2026-05/_one_off_structural_optimization.py +0 -144
  1206. package/src/scripts/ai_council/one_off_archive/2026-05/_one_off_structural_v3_gaps.py +0 -252
  1207. package/src/scripts/ai_council/one_off_archive/2026-05/_one_off_structural_v3_review.py +0 -240
  1208. package/src/scripts/ai_council/one_off_archive/2026-05/_one_off_tier_retrofit.py +0 -180
  1209. package/src/scripts/ai_council/orchestrator.py +0 -1206
  1210. package/src/scripts/ai_council/pricing.py +0 -215
  1211. package/src/scripts/ai_council/probation_gate.py +0 -152
  1212. package/src/scripts/ai_council/project_context.py +0 -159
  1213. package/src/scripts/ai_council/prompts.py +0 -567
  1214. package/src/scripts/ai_council/redact_low_impact_entry.py +0 -155
  1215. package/src/scripts/ai_council/replay.py +0 -155
  1216. package/src/scripts/ai_council/session.py +0 -366
  1217. package/src/scripts/ai_council/shadow_dispatch.py +0 -235
  1218. package/src/scripts/ai_council/solo_dispatch.py +0 -226
  1219. package/src/scripts/annotate_discovery.py +0 -149
  1220. package/src/scripts/apply_modules_config.py +0 -290
  1221. package/src/scripts/audit_adr_coverage.py +0 -173
  1222. package/src/scripts/audit_auto_rules.py +0 -175
  1223. package/src/scripts/audit_cloud_compatibility.py +0 -362
  1224. package/src/scripts/audit_command_surface.py +0 -682
  1225. package/src/scripts/audit_initial_context.py +0 -237
  1226. package/src/scripts/audit_likelihood.py +0 -148
  1227. package/src/scripts/audit_mcp_tools.py +0 -146
  1228. package/src/scripts/audit_overlap.py +0 -145
  1229. package/src/scripts/audit_skill_descriptions.py +0 -180
  1230. package/src/scripts/audit_skill_overlap.py +0 -207
  1231. package/src/scripts/audit_user_type_axis.py +0 -140
  1232. package/src/scripts/backfill_model_tier.py +0 -184
  1233. package/src/scripts/bench_ab_cache_dispatch.py +0 -68
  1234. package/src/scripts/bench_ab_clone.py +0 -220
  1235. package/src/scripts/bench_ab_diff.py +0 -220
  1236. package/src/scripts/bench_ab_integrity.py +0 -143
  1237. package/src/scripts/bench_ab_run.py +0 -235
  1238. package/src/scripts/bench_ab_task_runner.py +0 -814
  1239. package/src/scripts/bench_ab_tracka_run.py +0 -202
  1240. package/src/scripts/bench_ab_v2_run.py +0 -247
  1241. package/src/scripts/bench_ab_v2_stats.py +0 -347
  1242. package/src/scripts/bench_baseline_ready.py +0 -108
  1243. package/src/scripts/bench_condense_memory.py +0 -168
  1244. package/src/scripts/bench_drift_check.py +0 -151
  1245. package/src/scripts/bench_per_tool.py +0 -216
  1246. package/src/scripts/bench_rtk_savings.py +0 -320
  1247. package/src/scripts/bench_run.py +0 -272
  1248. package/src/scripts/bench_runner.py +0 -158
  1249. package/src/scripts/build_cloud_bundle.py +0 -458
  1250. package/src/scripts/build_discovery_manifest.py +0 -757
  1251. package/src/scripts/build_linear_digest.py +0 -260
  1252. package/src/scripts/build_mcp_registry_manifest.py +0 -181
  1253. package/src/scripts/build_rule_trigger_matrix.py +0 -350
  1254. package/src/scripts/capture_showcase_session.py +0 -361
  1255. package/src/scripts/chat_history.py +0 -1799
  1256. package/src/scripts/check_always_budget.py +0 -532
  1257. package/src/scripts/check_artefact_checksums.py +0 -111
  1258. package/src/scripts/check_augment_description_cap.py +0 -79
  1259. package/src/scripts/check_augmentignore.py +0 -72
  1260. package/src/scripts/check_beta_review_markers.py +0 -127
  1261. package/src/scripts/check_bite_sized_granularity.py +0 -98
  1262. package/src/scripts/check_cluster_patterns.py +0 -206
  1263. package/src/scripts/check_command_count_messaging.py +0 -152
  1264. package/src/scripts/check_condensation.py +0 -375
  1265. package/src/scripts/check_condensed_paths.py +0 -231
  1266. package/src/scripts/check_context_paths.py +0 -202
  1267. package/src/scripts/check_council_layout.py +0 -125
  1268. package/src/scripts/check_council_references.py +0 -228
  1269. package/src/scripts/check_discovery_determinism.py +0 -70
  1270. package/src/scripts/check_gate_paths.py +0 -128
  1271. package/src/scripts/check_iron_law_prominence.py +0 -145
  1272. package/src/scripts/check_kernel_rule_bundle.py +0 -151
  1273. package/src/scripts/check_md_language.py +0 -161
  1274. package/src/scripts/check_memory.py +0 -429
  1275. package/src/scripts/check_memory_proposal.py +0 -182
  1276. package/src/scripts/check_module_management_neutral.py +0 -147
  1277. package/src/scripts/check_no_external_sources.py +0 -101
  1278. package/src/scripts/check_no_local_settings_committed.py +0 -51
  1279. package/src/scripts/check_no_new_legacy_path.py +0 -100
  1280. package/src/scripts/check_no_roadmap_refs.py +0 -155
  1281. package/src/scripts/check_one_off_location.py +0 -81
  1282. package/src/scripts/check_overlay_cascade_subdirs.py +0 -129
  1283. package/src/scripts/check_portability.py +0 -574
  1284. package/src/scripts/check_proposal.py +0 -269
  1285. package/src/scripts/check_public_catalog_links.py +0 -125
  1286. package/src/scripts/check_public_links.py +0 -185
  1287. package/src/scripts/check_references.py +0 -559
  1288. package/src/scripts/check_release_includes_discovery.py +0 -61
  1289. package/src/scripts/check_release_pr_shape.py +0 -123
  1290. package/src/scripts/check_release_published.py +0 -145
  1291. package/src/scripts/check_release_trunk_sync.py +0 -152
  1292. package/src/scripts/check_reply_consistency.py +0 -169
  1293. package/src/scripts/check_roadmap_trackable.py +0 -114
  1294. package/src/scripts/check_role_doc_links.py +0 -110
  1295. package/src/scripts/check_safety_floor_untouched.py +0 -125
  1296. package/src/scripts/check_skill_requires.py +0 -147
  1297. package/src/scripts/check_template_pin_drift.py +0 -129
  1298. package/src/scripts/check_test_coverage_diff.py +0 -180
  1299. package/src/scripts/check_token_optimizer_freshness.py +0 -146
  1300. package/src/scripts/check_update_banner.py +0 -86
  1301. package/src/scripts/ci_status.py +0 -301
  1302. package/src/scripts/ci_summary.py +0 -131
  1303. package/src/scripts/ci_time_ratio.py +0 -168
  1304. package/src/scripts/command_suggester/__init__.py +0 -51
  1305. package/src/scripts/command_suggester/cooldown.py +0 -132
  1306. package/src/scripts/command_suggester/loader.py +0 -73
  1307. package/src/scripts/command_suggester/match.py +0 -180
  1308. package/src/scripts/command_suggester/rank.py +0 -120
  1309. package/src/scripts/command_suggester/render.py +0 -86
  1310. package/src/scripts/command_suggester/sanitize.py +0 -113
  1311. package/src/scripts/command_suggester/settings.py +0 -127
  1312. package/src/scripts/command_suggester/types.py +0 -78
  1313. package/src/scripts/compile_router.py +0 -232
  1314. package/src/scripts/condense.py +0 -1919
  1315. package/src/scripts/condense_memory.py +0 -178
  1316. package/src/scripts/config/__init__.py +0 -9
  1317. package/src/scripts/config/packs.py +0 -157
  1318. package/src/scripts/config/presets.py +0 -224
  1319. package/src/scripts/config/profile_explain.py +0 -89
  1320. package/src/scripts/config/profiles.py +0 -191
  1321. package/src/scripts/config/session_profiles.py +0 -542
  1322. package/src/scripts/context_hygiene_hook.py +0 -181
  1323. package/src/scripts/cost_by_conversation.py +0 -78
  1324. package/src/scripts/cost_summary.py +0 -97
  1325. package/src/scripts/council_cli.py +0 -2571
  1326. package/src/scripts/council_prune.py +0 -81
  1327. package/src/scripts/cross_repo_retrieve.py +0 -172
  1328. package/src/scripts/discovery_stats.py +0 -70
  1329. package/src/scripts/extract_audit_patterns.py +0 -202
  1330. package/src/scripts/first_run_gate_hook.py +0 -179
  1331. package/src/scripts/gen_discovery_baseline.py +0 -127
  1332. package/src/scripts/generate_catalog.py +0 -116
  1333. package/src/scripts/generate_command_flows.py +0 -191
  1334. package/src/scripts/generate_index.py +0 -303
  1335. package/src/scripts/generate_ownership_matrix.py +0 -378
  1336. package/src/scripts/generate_pack_manifests.py +0 -340
  1337. package/src/scripts/hooks/__init__.py +0 -1
  1338. package/src/scripts/hooks/dispatch_hook.py +0 -461
  1339. package/src/scripts/hooks/dispatch_issues.py +0 -136
  1340. package/src/scripts/hooks/envelope.py +0 -98
  1341. package/src/scripts/hooks/replay_hook.py +0 -144
  1342. package/src/scripts/hooks/state_io.py +0 -145
  1343. package/src/scripts/hooks_doctor.py +0 -223
  1344. package/src/scripts/hooks_status.py +0 -157
  1345. package/src/scripts/injection_scan_hook.py +0 -145
  1346. package/src/scripts/install.py +0 -5258
  1347. package/src/scripts/inventory_abstraction_budget.py +0 -622
  1348. package/src/scripts/inventory_frontmatter.py +0 -164
  1349. package/src/scripts/inventory_meta_layers.py +0 -288
  1350. package/src/scripts/iron_law_sha.py +0 -107
  1351. package/src/scripts/linked_projects_list.py +0 -91
  1352. package/src/scripts/lint_agent_security.py +0 -112
  1353. package/src/scripts/lint_agent_skill_names.py +0 -150
  1354. package/src/scripts/lint_agents_layout.py +0 -197
  1355. package/src/scripts/lint_agents_md.py +0 -210
  1356. package/src/scripts/lint_archived_skills.py +0 -159
  1357. package/src/scripts/lint_artefact_frontmatter.py +0 -188
  1358. package/src/scripts/lint_bench_ab.py +0 -173
  1359. package/src/scripts/lint_bench_corpus.py +0 -255
  1360. package/src/scripts/lint_command_flow_coverage.py +0 -132
  1361. package/src/scripts/lint_command_routing.py +0 -160
  1362. package/src/scripts/lint_command_tiers.py +0 -216
  1363. package/src/scripts/lint_command_verbs.py +0 -206
  1364. package/src/scripts/lint_commit_subjects.py +0 -139
  1365. package/src/scripts/lint_context_spine_usage.py +0 -137
  1366. package/src/scripts/lint_discovery_manifest.py +0 -176
  1367. package/src/scripts/lint_discovery_vocabulary.py +0 -222
  1368. package/src/scripts/lint_empty_roadmaps.py +0 -80
  1369. package/src/scripts/lint_examples.py +0 -102
  1370. package/src/scripts/lint_explain_trace.py +0 -80
  1371. package/src/scripts/lint_featured_skills.py +0 -144
  1372. package/src/scripts/lint_flows.py +0 -215
  1373. package/src/scripts/lint_framework_leakage.py +0 -375
  1374. package/src/scripts/lint_frontmatter_boilerplate.py +0 -77
  1375. package/src/scripts/lint_ghostwriter_source.py +0 -242
  1376. package/src/scripts/lint_global_paths.py +0 -148
  1377. package/src/scripts/lint_handoffs.py +0 -217
  1378. package/src/scripts/lint_hidden_unicode.py +0 -132
  1379. package/src/scripts/lint_hook_concern_budget.py +0 -207
  1380. package/src/scripts/lint_hook_manifest.py +0 -217
  1381. package/src/scripts/lint_instruction_smuggling.py +0 -107
  1382. package/src/scripts/lint_load_context.py +0 -196
  1383. package/src/scripts/lint_marketplace.py +0 -180
  1384. package/src/scripts/lint_marketplace_install_completeness.py +0 -198
  1385. package/src/scripts/lint_mcp_config_security.py +0 -124
  1386. package/src/scripts/lint_mcp_registry_manifest.py +0 -69
  1387. package/src/scripts/lint_media_policy_linkage.py +0 -140
  1388. package/src/scripts/lint_model_tier_coverage.py +0 -73
  1389. package/src/scripts/lint_namespace.py +0 -135
  1390. package/src/scripts/lint_namespace_collisions.py +0 -103
  1391. package/src/scripts/lint_new_skill_gate.py +0 -144
  1392. package/src/scripts/lint_no_new_atomic_commands.py +0 -180
  1393. package/src/scripts/lint_one_off_age.py +0 -184
  1394. package/src/scripts/lint_orchestration_dsl.py +0 -217
  1395. package/src/scripts/lint_orchestrator_auto_detect.py +0 -111
  1396. package/src/scripts/lint_pack_boundaries.py +0 -147
  1397. package/src/scripts/lint_pack_dependencies.py +0 -137
  1398. package/src/scripts/lint_pack_first_win.py +0 -121
  1399. package/src/scripts/lint_persona_governance.py +0 -164
  1400. package/src/scripts/lint_positioning.py +0 -143
  1401. package/src/scripts/lint_profile_overlay_set_only.py +0 -179
  1402. package/src/scripts/lint_readme_jargon.py +0 -131
  1403. package/src/scripts/lint_readme_size.py +0 -33
  1404. package/src/scripts/lint_regression.py +0 -251
  1405. package/src/scripts/lint_roadmap_ci_steps.py +0 -186
  1406. package/src/scripts/lint_roadmap_complexity.py +0 -220
  1407. package/src/scripts/lint_role_experiences.py +0 -255
  1408. package/src/scripts/lint_rule_interactions.py +0 -170
  1409. package/src/scripts/lint_rule_tiers.py +0 -90
  1410. package/src/scripts/lint_showcase_sessions.py +0 -148
  1411. package/src/scripts/lint_skill_frontmatter_safety.py +0 -144
  1412. package/src/scripts/lint_skill_tools.py +0 -168
  1413. package/src/scripts/lint_topics_yaml.py +0 -89
  1414. package/src/scripts/lint_trust_coherence.py +0 -212
  1415. package/src/scripts/lint_value_dashboard.py +0 -218
  1416. package/src/scripts/lint_workspace_boundary.py +0 -122
  1417. package/src/scripts/mcp_parity_smoke.py +0 -316
  1418. package/src/scripts/mcp_render.py +0 -173
  1419. package/src/scripts/mcp_server/__init__.py +0 -19
  1420. package/src/scripts/mcp_server/__main__.py +0 -12
  1421. package/src/scripts/mcp_server/catalog.py +0 -125
  1422. package/src/scripts/mcp_server/metadata.py +0 -75
  1423. package/src/scripts/mcp_server/prompts.py +0 -442
  1424. package/src/scripts/mcp_server/requirements.txt +0 -4
  1425. package/src/scripts/mcp_server/resources.py +0 -201
  1426. package/src/scripts/mcp_server/server.py +0 -270
  1427. package/src/scripts/mcp_server/telemetry.py +0 -128
  1428. package/src/scripts/mcp_server/tools.py +0 -926
  1429. package/src/scripts/mcp_telemetry_health.py +0 -214
  1430. package/src/scripts/mcp_telemetry_query.py +0 -203
  1431. package/src/scripts/mcp_telemetry_store.py +0 -211
  1432. package/src/scripts/measure_augment_budget.py +0 -214
  1433. package/src/scripts/measure_density.py +0 -232
  1434. package/src/scripts/measure_frugality_savings.py +0 -164
  1435. package/src/scripts/measure_markitdown_lift.py +0 -127
  1436. package/src/scripts/measure_patterns.py +0 -376
  1437. package/src/scripts/measure_projection_bytes.py +0 -159
  1438. package/src/scripts/measure_rule_budget.py +0 -347
  1439. package/src/scripts/measure_skill_reduction.py +0 -102
  1440. package/src/scripts/memory_hash.py +0 -75
  1441. package/src/scripts/memory_lookup.py +0 -436
  1442. package/src/scripts/memory_report.py +0 -314
  1443. package/src/scripts/memory_signal.py +0 -165
  1444. package/src/scripts/memory_status.py +0 -76
  1445. package/src/scripts/migrate_command_suggestions.py +0 -151
  1446. package/src/scripts/migrate_frontmatter_defaults.py +0 -245
  1447. package/src/scripts/mine_session.py +0 -356
  1448. package/src/scripts/minimal_safe_diff_hook.py +0 -245
  1449. package/src/scripts/move_artefact.py +0 -143
  1450. package/src/scripts/new_skill.py +0 -148
  1451. package/src/scripts/onboarding_gate_hook.py +0 -142
  1452. package/src/scripts/pack_mcp_content.py +0 -293
  1453. package/src/scripts/plan_physical_move.py +0 -353
  1454. package/src/scripts/prediction-pool/poisson_sim.py +0 -167
  1455. package/src/scripts/prediction-pool/pool_winsim.py +0 -236
  1456. package/src/scripts/prediction-pool/score_ev.py +0 -188
  1457. package/src/scripts/print_required_checks.py +0 -196
  1458. package/src/scripts/probe_projection_fidelity.py +0 -202
  1459. package/src/scripts/probe_skill_registration.py +0 -413
  1460. package/src/scripts/profile_staleness_hook.py +0 -69
  1461. package/src/scripts/profile_use.py +0 -164
  1462. package/src/scripts/project_thin_rules.py +0 -168
  1463. package/src/scripts/propose_modules_config.py +0 -145
  1464. package/src/scripts/prototype_lint_contradictions.py +0 -150
  1465. package/src/scripts/prove_pack_extractable.py +0 -187
  1466. package/src/scripts/readme_linter.py +0 -589
  1467. package/src/scripts/redact_hook_capture.py +0 -148
  1468. package/src/scripts/refine_ticket_detect.py +0 -646
  1469. package/src/scripts/release.py +0 -1091
  1470. package/src/scripts/render_benchmark_md.py +0 -401
  1471. package/src/scripts/render_value_md.py +0 -347
  1472. package/src/scripts/requirements-evals.txt +0 -8
  1473. package/src/scripts/roadmap_progress_hook.py +0 -274
  1474. package/src/scripts/router_telemetry.py +0 -470
  1475. package/src/scripts/run_skill_evals.py +0 -185
  1476. package/src/scripts/runtime_dispatcher.py +0 -276
  1477. package/src/scripts/runtime_handler.py +0 -148
  1478. package/src/scripts/runtime_registry.py +0 -166
  1479. package/src/scripts/score_skill_selection.py +0 -198
  1480. package/src/scripts/security_audit_config.py +0 -153
  1481. package/src/scripts/setup_eval_venv.sh +0 -58
  1482. package/src/scripts/skill_collision_clusters.py +0 -162
  1483. package/src/scripts/skill_discovery.py +0 -254
  1484. package/src/scripts/skill_linter.py +0 -3694
  1485. package/src/scripts/skill_overlap.py +0 -204
  1486. package/src/scripts/skill_preview.py +0 -179
  1487. package/src/scripts/skill_tools/__init__.py +0 -22
  1488. package/src/scripts/skill_tools/audit_persona_coverage.py +0 -147
  1489. package/src/scripts/skill_tools/audit_user_type_coverage.py +0 -148
  1490. package/src/scripts/skill_tools/run_block_d_eval.py +0 -129
  1491. package/src/scripts/skill_tools/score_skill_relevance.py +0 -169
  1492. package/src/scripts/skill_tools/suggest_skill_for_task.py +0 -113
  1493. package/src/scripts/skill_trigger_eval.py +0 -682
  1494. package/src/scripts/skill_usage_collect.py +0 -191
  1495. package/src/scripts/skill_usage_report.py +0 -162
  1496. package/src/scripts/smoke_path_resolution.py +0 -93
  1497. package/src/scripts/smoke_quickstart.py +0 -144
  1498. package/src/scripts/snapshot_agent_outputs.py +0 -144
  1499. package/src/scripts/spotcheck_thin_root.py +0 -134
  1500. package/src/scripts/sync_agent_settings.py +0 -180
  1501. package/src/scripts/sync_github_metadata.py +0 -147
  1502. package/src/scripts/sync_gitignore.py +0 -291
  1503. package/src/scripts/sync_yaml_rt.py +0 -734
  1504. package/src/scripts/telegraph_stats.py +0 -119
  1505. package/src/scripts/tool_registry.py +0 -146
  1506. package/src/scripts/tools/__init__.py +0 -1
  1507. package/src/scripts/tools/adapter_errors.py +0 -63
  1508. package/src/scripts/tools/base_adapter.py +0 -91
  1509. package/src/scripts/tools/github_adapter.py +0 -128
  1510. package/src/scripts/tools/jira_adapter.py +0 -115
  1511. package/src/scripts/trigger_coverage.py +0 -129
  1512. package/src/scripts/update_counts.py +0 -199
  1513. package/src/scripts/update_prices.py +0 -125
  1514. package/src/scripts/validate_agent_settings.py +0 -124
  1515. package/src/scripts/validate_decision_engine.py +0 -136
  1516. package/src/scripts/validate_discovery_manifest.py +0 -94
  1517. package/src/scripts/validate_frontmatter.py +0 -647
  1518. package/src/scripts/validate_pack_yaml.py +0 -179
  1519. package/src/scripts/validate_safe_paths.py +0 -118
  1520. package/src/scripts/validate_telegraph_carveouts.py +0 -129
  1521. package/src/scripts/verify_before_complete_hook.py +0 -216
  1522. package/src/scripts/verify_physical_move.py +0 -185
  1523. package/src/scripts/wrapper_freshness_hook.py +0 -86
  1524. /package/dist/agent-src/skills/design-intelligence/data/{typography.csv → font-pairings-reference.csv} +0 -0
  1525. /package/src/scripts/{ai-video → media}/lib/fixtures/allin1/analysis.json +0 -0
  1526. /package/src/scripts/{ai-video → media}/lib/fixtures/comfyui/scene-0001.mp4 +0 -0
  1527. /package/src/scripts/{ai-video → media}/lib/fixtures/fal/scene-0001.mp4 +0 -0
  1528. /package/src/scripts/{ai-video → media}/lib/fixtures/gemini-veo/scene-0001.mp4 +0 -0
  1529. /package/src/scripts/{ai-video → media}/lib/fixtures/higgsfield/scene-0001.mp4 +0 -0
  1530. /package/src/scripts/{ai-video → media}/lib/fixtures/kling/scene-0001.mp4 +0 -0
  1531. /package/src/scripts/{ai-video → media}/lib/fixtures/musetalk/lipsync-0001.mp4 +0 -0
  1532. /package/src/scripts/{ai-video → media}/lib/fixtures/openai-images/scene-0001.png +0 -0
  1533. /package/src/scripts/{ai-video → media}/lib/fixtures/replicate/scene-0001.mp4 +0 -0
  1534. /package/src/scripts/{ai-video → media}/lib/fixtures/sora/scene-0001.mp4 +0 -0
  1535. /package/src/scripts/{ai-video → media}/lib/fixtures/syncso/lipsync-0001.mp4 +0 -0
  1536. /package/src/scripts/{ai-video → media}/lib/fixtures/whisperx/transcript.json +0 -0
  1537. /package/src/scripts/{ai-video → media}/lib/telemetry.sh +0 -0
@@ -0,0 +1,4515 @@
1
+ #!/usr/bin/env tsx
2
+ /**
3
+ * Agent Config — Project Bridge Installer (TypeScript twin).
4
+ *
5
+ * TypeScript twin of `src/scripts/install.py` (ADR-200, py2ts migration).
6
+ * The CLI contract mirrors the Python original EXACTLY — same flags, same
7
+ * exit codes, same stdout/stderr split, byte-identical emitted output,
8
+ * same filesystem effects, same subprocess argv/cwd/env. No behaviour
9
+ * changes — latent quirks are replicated and flagged inline, not fixed.
10
+ *
11
+ * Generates project bridge files (.agent-settings.yml, .vscode/settings.json,
12
+ * etc.) so that supported AI tools can discover agent-config from the project.
13
+ *
14
+ * On first run in a project that still has the legacy flat-file
15
+ * `.agent-settings` (key=value), the installer migrates it to the new YAML
16
+ * format in `.agent-settings.yml`, leaves a one-shot backup as
17
+ * `.agent-settings.backup.key-value`, and deletes the legacy file. This runs
18
+ * exactly once; subsequent runs are idempotent.
19
+ *
20
+ * Usage:
21
+ * tsx src/scripts/install.ts # defaults: rule_loading_tier=balanced
22
+ * tsx src/scripts/install.ts --profile=minimal # set rule_loading_tier=minimal (kernel only)
23
+ * tsx src/scripts/install.ts --force # accepted (no-op): installs always overwrite
24
+ * tsx src/scripts/install.ts --skip-bridges # only create .agent-settings.yml
25
+ * tsx src/scripts/install.ts --project <dir> # override project root
26
+ *
27
+ * Idempotent — safe to run multiple times. A run always refreshes every
28
+ * deployed file with the current package content; user configuration
29
+ * (.agent-settings.yml) is merged by the settings layer, never clobbered.
30
+ *
31
+ * --- Parity notes (ADR-200) ---
32
+ *
33
+ * - `fail()` mirrors Python's `sys.exit(1)`: it prints the doctor hint then
34
+ * throws a `SystemExitError(1)` sentinel caught at the CLI entry guard,
35
+ * which sets `process.exitCode`. We never call `process.exit()` (per the
36
+ * migration contract) — every termination flows through a settable
37
+ * `process.exitCode`.
38
+ * - argparse errors (`parser.error`, unknown flags, bad `--scope` choice)
39
+ * throw `ArgparseExit(2)` — argparse's exit code for usage errors.
40
+ * - `-h`/`--help` throws `ArgparseExit(0)` after printing usage.
41
+ * - JSON byte-parity: `json.dumps(..., indent=4, ensure_ascii=False)` →
42
+ * `_jsonDumpsIndent(data, 4, false)`; `indent=2, sort_keys=False` →
43
+ * `_jsonDumpsIndent(data, 2, false)`; `separators=(",",":")` (compact
44
+ * NDJSON) → `_jsonDumpsCompact`. Dict insertion order is preserved (JS
45
+ * object key order matches Python dict order for our string keys).
46
+ * - `_lib.*` and `config.*` imports resolve to the `.ts` twins (never a
47
+ * `.py`). The Python dual-path try/except (`scripts._lib.X` vs `_lib.X`)
48
+ * collapses to a single static import here.
49
+ * - The lazy `_load_*_module()` helpers in the Python are eager static
50
+ * imports here; the sys.path bootstrap they performed is a Python-only
51
+ * import-resolution detail with no observable effect.
52
+ * - `scripts._cli.cmd_migrate` now has a `.ts` twin, so `_run_migrate_to_global`
53
+ * calls `cmd_migrate.main([], { cwd })` directly in-process — exactly as the
54
+ * Python original ran it via `sys.stdout`. The migrator's `out`/`err` sinks
55
+ * default to `process.stdout`/`process.stderr`, preserving the prior
56
+ * `stdio:['ignore','inherit','pipe']` observable contract (stdout text + exit
57
+ * code). Inline reason at the call site.
58
+ * - `webbrowser.open` → a platform open spawn (`open`/`xdg-open`/`start`),
59
+ * best-effort and never fatal, matching the Python's bare-except guard.
60
+ * - `threading.Thread` draining child stderr → an async line reader; the
61
+ * 80-line cap + observable stderr-tail behaviour is preserved.
62
+ * - `signal.SIGTERM`/`SIGKILL` + `os.kill(pid, 0)` liveness → `process.kill`.
63
+ */
64
+
65
+ import { spawnSync } from 'node:child_process';
66
+ import * as crypto from 'node:crypto';
67
+ import * as fs from 'node:fs';
68
+ import * as os from 'node:os';
69
+ import * as path from 'node:path';
70
+ import process from 'node:process';
71
+ import { fileURLToPath, pathToFileURL } from 'node:url';
72
+ import type * as YamlModule from 'yaml';
73
+
74
+ import { build_merge_entries } from './_lib/json_pointers.js';
75
+ import * as installed_lock from './_lib/installed_lock.js';
76
+ import * as surface_tiers from './_lib/surface_tiers.js';
77
+ import * as global_deploy_inventory from './_lib/global_deploy_inventory.js';
78
+ import * as installed_tools from './_lib/installed_tools.js';
79
+ import * as user_global_paths from './_lib/user_global_paths.js';
80
+ import * as claude_desktop_bundler from './_lib/claude_desktop_bundler.js';
81
+ import { find_project_root_with_anchor, load_agent_settings } from './_lib/agent_settings.js';
82
+ import { detect_module_roots } from './_lib/module_detection.js';
83
+ import {
84
+ TIER_TO_CLAUDE_MODEL,
85
+ read_model_tier,
86
+ render_native_model_md,
87
+ } from './_lib/model_tier.js';
88
+ import { main as cmdMigrateMain } from './_cli/cmd_migrate.js';
89
+
90
+ // ---------------------------------------------------------------------------
91
+ // Python-runtime parity helpers
92
+ // ---------------------------------------------------------------------------
93
+
94
+ const _HERE = fileURLToPath(import.meta.url);
95
+
96
+ /** Mirror of Python `sys.exit(code)` raised by `fail()`. Caught at the CLI entry. */
97
+ class SystemExitError extends Error {
98
+ constructor(public readonly code: number) {
99
+ super(`system-exit-${code}`);
100
+ }
101
+ }
102
+
103
+ /** argparse usage-error / help exit (code 2 / 0). */
104
+ class ArgparseExit extends Error {
105
+ constructor(public readonly code: number) {
106
+ super(`argparse-exit-${code}`);
107
+ }
108
+ }
109
+
110
+ /** `os.path.expanduser` — expand a leading `~` / `~user` (we only handle `~`). */
111
+ function expanduser(p: string): string {
112
+ if (p === '~') return os.homedir();
113
+ if (p.startsWith('~/') || p.startsWith('~\\')) {
114
+ return path.join(os.homedir(), p.slice(2));
115
+ }
116
+ return p;
117
+ }
118
+
119
+ /** `Path.resolve()` — absolute, symlink-resolved where possible. */
120
+ function resolvePath(p: string): string {
121
+ try {
122
+ return fs.realpathSync(path.resolve(p));
123
+ } catch {
124
+ return path.resolve(p);
125
+ }
126
+ }
127
+
128
+ function isFile(p: string): boolean {
129
+ try {
130
+ return fs.statSync(p).isFile();
131
+ } catch {
132
+ return false;
133
+ }
134
+ }
135
+
136
+ function isDir(p: string): boolean {
137
+ try {
138
+ return fs.statSync(p).isDirectory();
139
+ } catch {
140
+ return false;
141
+ }
142
+ }
143
+
144
+ function pathExists(p: string): boolean {
145
+ try {
146
+ fs.statSync(p);
147
+ return true;
148
+ } catch {
149
+ return false;
150
+ }
151
+ }
152
+
153
+ function isSymlink(p: string): boolean {
154
+ try {
155
+ return fs.lstatSync(p).isSymbolicLink();
156
+ } catch {
157
+ return false;
158
+ }
159
+ }
160
+
161
+ function readText(p: string): string {
162
+ return fs.readFileSync(p, 'utf-8');
163
+ }
164
+
165
+ function writeText(p: string, content: string): void {
166
+ fs.writeFileSync(p, content, 'utf-8');
167
+ }
168
+
169
+ /** `path.mkdir(parents=True, exist_ok=True)`. */
170
+ function mkdirp(p: string): void {
171
+ fs.mkdirSync(p, { recursive: true });
172
+ }
173
+
174
+ /**
175
+ * `sorted(Path.glob(pattern))` with PyYAML/pathlib component-wise ordering.
176
+ * We only need a non-recursive `*.glob("<pat>")`; pathlib sorts the matched
177
+ * paths lexicographically by their string form, which for sibling entries in
178
+ * one directory is plain string sort — matched here.
179
+ */
180
+ function sortedGlobStems(directory: string, suffix: string): string[] {
181
+ let entries: string[];
182
+ try {
183
+ entries = fs.readdirSync(directory);
184
+ } catch {
185
+ return [];
186
+ }
187
+ const stems: string[] = [];
188
+ for (const name of entries) {
189
+ if (name.endsWith(suffix)) {
190
+ stems.push(name.slice(0, name.length - suffix.length));
191
+ }
192
+ }
193
+ // pathlib glob yields full paths; sorted() compares the path strings.
194
+ // The directory prefix is common, so sorting by full path == sorting by
195
+ // filename, == sorting by stem+suffix. Sort by the full filename to match.
196
+ stems.sort((a, b) => {
197
+ const fa = a + suffix;
198
+ const fb = b + suffix;
199
+ return fa < fb ? -1 : fa > fb ? 1 : 0;
200
+ });
201
+ return stems;
202
+ }
203
+
204
+ /** Number of `.zip` files directly under `directory` (count only). */
205
+ function countZips(directory: string): number {
206
+ if (!isDir(directory)) return 0;
207
+ let n = 0;
208
+ for (const name of fs.readdirSync(directory)) {
209
+ if (name.endsWith('.zip')) n += 1;
210
+ }
211
+ return n;
212
+ }
213
+
214
+ /** `hashlib.sha256(data).hexdigest()` of a file's bytes, or null when unreadable. */
215
+ function sha256OfFile(p: string): string | null {
216
+ let data: Buffer;
217
+ try {
218
+ data = fs.readFileSync(p);
219
+ } catch {
220
+ return null;
221
+ }
222
+ return crypto.createHash('sha256').update(data).digest('hex');
223
+ }
224
+
225
+ /**
226
+ * Atomic write mirroring the Python `tempfile.mkstemp(...) → os.fdopen → write
227
+ * → os.chmod(0o644) → os.replace` pattern in `_write_consumer_bridge_marker`
228
+ * and `_write_per_tool_project_anchors`. Temp file lives in the same dir so
229
+ * the rename is atomic; cleanup-on-failure mirrors the Python `except` arm.
230
+ */
231
+ function atomicWrite0644(target: string, body: string, prefix: string): void {
232
+ const dir = path.dirname(target);
233
+ const tmpName = path.join(
234
+ dir,
235
+ `${prefix}${process.pid}.${crypto.randomBytes(6).toString('hex')}.yml.tmp`,
236
+ );
237
+ let fd: number | null = null;
238
+ try {
239
+ fd = fs.openSync(tmpName, 'wx', 0o644);
240
+ fs.writeFileSync(fd, body, 'utf-8');
241
+ fs.closeSync(fd);
242
+ fd = null;
243
+ fs.chmodSync(tmpName, 0o644);
244
+ fs.renameSync(tmpName, target);
245
+ } catch (err) {
246
+ if (fd !== null) {
247
+ try {
248
+ fs.closeSync(fd);
249
+ } catch {
250
+ /* fd may already be closed */
251
+ }
252
+ }
253
+ try {
254
+ fs.unlinkSync(tmpName);
255
+ } catch {
256
+ /* best-effort cleanup */
257
+ }
258
+ throw err;
259
+ }
260
+ }
261
+
262
+ /**
263
+ * `datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")` — second-precision
264
+ * UTC stamp with a `Z` suffix.
265
+ */
266
+ function utcStamp(now?: Date): string {
267
+ const d = now ?? new Date();
268
+ const pad = (n: number, w = 2) => String(n).padStart(w, '0');
269
+ return (
270
+ `${d.getUTCFullYear()}-${pad(d.getUTCMonth() + 1)}-${pad(d.getUTCDate())}` +
271
+ `T${pad(d.getUTCHours())}:${pad(d.getUTCMinutes())}:${pad(d.getUTCSeconds())}Z`
272
+ );
273
+ }
274
+
275
+ // --- JSON byte-parity (ensure_ascii=False; insertion order) ---
276
+
277
+ function _jsonStrNoAscii(s: string): string {
278
+ // json.dumps(ensure_ascii=False): escape control chars + " + \, keep >=0x20
279
+ let out = '"';
280
+ for (const ch of s) {
281
+ const code = ch.codePointAt(0) as number;
282
+ switch (ch) {
283
+ case '"':
284
+ out += '\\"';
285
+ break;
286
+ case '\\':
287
+ out += '\\\\';
288
+ break;
289
+ case '\n':
290
+ out += '\\n';
291
+ break;
292
+ case '\r':
293
+ out += '\\r';
294
+ break;
295
+ case '\t':
296
+ out += '\\t';
297
+ break;
298
+ case '\b':
299
+ out += '\\b';
300
+ break;
301
+ case '\f':
302
+ out += '\\f';
303
+ break;
304
+ default:
305
+ if (code < 0x20) {
306
+ out += '\\u' + code.toString(16).padStart(4, '0');
307
+ } else {
308
+ out += ch;
309
+ }
310
+ }
311
+ }
312
+ return out + '"';
313
+ }
314
+
315
+ function _jsonScalar(value: unknown): string | null {
316
+ if (value === null || value === undefined) return 'null';
317
+ if (typeof value === 'boolean') return value ? 'true' : 'false';
318
+ if (typeof value === 'number') {
319
+ if (!Number.isFinite(value)) {
320
+ if (Number.isNaN(value)) return 'NaN';
321
+ return value > 0 ? 'Infinity' : '-Infinity';
322
+ }
323
+ // Our payloads carry only integers; render as-is.
324
+ return String(value);
325
+ }
326
+ if (typeof value === 'string') return _jsonStrNoAscii(value);
327
+ return null;
328
+ }
329
+
330
+ function _dumpIndent(value: unknown, indent: number, depth: number): string {
331
+ const scalar = _jsonScalar(value);
332
+ if (scalar !== null) return scalar;
333
+ const pad = ' '.repeat(indent * (depth + 1));
334
+ const closePad = ' '.repeat(indent * depth);
335
+ if (Array.isArray(value)) {
336
+ if (value.length === 0) return '[]';
337
+ const items = value.map((v) => pad + _dumpIndent(v, indent, depth + 1));
338
+ return `[\n${items.join(',\n')}\n${closePad}]`;
339
+ }
340
+ if (typeof value === 'object' && value !== null) {
341
+ const obj = value as Record<string, unknown>;
342
+ const keys = Object.keys(obj);
343
+ if (keys.length === 0) return '{}';
344
+ const items = keys.map(
345
+ (k) => `${pad}${_jsonStrNoAscii(k)}: ${_dumpIndent(obj[k], indent, depth + 1)}`,
346
+ );
347
+ return `{\n${items.join(',\n')}\n${closePad}}`;
348
+ }
349
+ return _jsonStrNoAscii(String(value));
350
+ }
351
+
352
+ /** `json.dumps(data, indent=N, ensure_ascii=False)` (sort_keys=False). */
353
+ function jsonDumpsIndent(value: unknown, indent: number): string {
354
+ return _dumpIndent(value, indent, 0);
355
+ }
356
+
357
+ /** `json.dumps(obj, separators=(",", ":"))` — compact, ensure_ascii=False here. */
358
+ function jsonDumpsCompact(value: unknown): string {
359
+ const scalar = _jsonScalar(value);
360
+ if (scalar !== null) return scalar;
361
+ if (Array.isArray(value)) {
362
+ return '[' + value.map((v) => jsonDumpsCompact(v)).join(',') + ']';
363
+ }
364
+ if (typeof value === 'object' && value !== null) {
365
+ const obj = value as Record<string, unknown>;
366
+ return (
367
+ '{' +
368
+ Object.keys(obj)
369
+ .map((k) => `${_jsonStrNoAscii(k)}:${jsonDumpsCompact(obj[k])}`)
370
+ .join(',') +
371
+ '}'
372
+ );
373
+ }
374
+ return _jsonStrNoAscii(String(value));
375
+ }
376
+
377
+ /** Lazy YAML safe_load mirroring PyYAML (version 1.1), `{}` on every error. */
378
+ function yamlSafeLoad(text: string): unknown {
379
+ let YAML: typeof YamlModule;
380
+ try {
381
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
382
+ YAML = require('yaml') as typeof YamlModule;
383
+ } catch {
384
+ return null; // ImportError → callers collapse to {}
385
+ }
386
+ try {
387
+ const data = YAML.parse(text, { version: '1.1' });
388
+ return data;
389
+ } catch {
390
+ return undefined; // YAMLError sentinel — callers collapse to {}
391
+ }
392
+ }
393
+
394
+ // ---------------------------------------------------------------------------
395
+ // Module-level constants (install.py:51-116)
396
+ // ---------------------------------------------------------------------------
397
+
398
+ const DEFAULT_PROFILE = 'balanced';
399
+ const SUPPORTED_PROFILES: readonly string[] = ['minimal', 'balanced', 'full'];
400
+ const RULE_LOADING_TIER_PLACEHOLDER = '__RULE_LOADING_TIER__';
401
+ const USER_TYPE_PLACEHOLDER = '__USER_TYPE__';
402
+ const USER_TYPES_DIR = 'user-types';
403
+
404
+ const SETTINGS_FILE = '.agent-settings.yml';
405
+ const LEGACY_SETTINGS_FILE = '.agent-settings';
406
+ const LEGACY_BACKUP_FILE = '.agent-settings.backup.key-value';
407
+
408
+ const SETTINGS_SUBDIR: readonly string[] = ['agents', 'settings'];
409
+
410
+ function _canonical_settings_target(project_root: string): string {
411
+ return path.join(project_root, ...SETTINGS_SUBDIR, SETTINGS_FILE);
412
+ }
413
+
414
+ function _resolve_settings_read(project_root: string): string {
415
+ const canonical = _canonical_settings_target(project_root);
416
+ if (pathExists(canonical)) return canonical;
417
+ const legacy = path.join(project_root, SETTINGS_FILE);
418
+ if (pathExists(legacy)) return legacy;
419
+ return canonical;
420
+ }
421
+
422
+ const LEGACY_RENAME_MAP: Record<string, string> = {
423
+ cost_profile: 'rule_loading_tier',
424
+ ide: 'personal.ide',
425
+ open_edited_files: 'personal.open_edited_files',
426
+ user_name: 'personal.user_name',
427
+ rtk_installed: 'personal.rtk_installed',
428
+ minimal_output: 'personal.minimal_output',
429
+ play_by_play: 'personal.play_by_play',
430
+ pr_comment_bot_icon: 'project.pr_comment_bot_icon',
431
+ pr_template: 'project.pr_template',
432
+ upstream_repo: 'project.upstream_repo',
433
+ improvement_pr_branch_prefix: 'project.improvement_pr_branch_prefix',
434
+ github_pr_reply_method: 'github.pr_reply_method',
435
+ eloquent_access_style: 'eloquent.access_style',
436
+ skill_improvement_pipeline: 'pipelines.skill_improvement',
437
+ subagent_implementer_model: 'subagents.implementer_model',
438
+ subagent_judge_model: 'subagents.judge_model',
439
+ subagent_max_parallel: 'subagents.max_parallel',
440
+ };
441
+
442
+ // --- Output helpers (module-mutable globals, like the Python QUIET/PROGRESS_NDJSON) ---
443
+
444
+ const state = {
445
+ QUIET: false,
446
+ PROGRESS_NDJSON: false,
447
+ };
448
+
449
+ function _emit_progress(obj: Record<string, unknown>): void {
450
+ if (!state.PROGRESS_NDJSON) return;
451
+ process.stdout.write(jsonDumpsCompact(obj) + '\n');
452
+ }
453
+
454
+ function _emit_progress_terminal(rc: number): void {
455
+ if (!state.PROGRESS_NDJSON) return;
456
+ if (rc === 0) {
457
+ _emit_progress({ type: 'done' });
458
+ } else {
459
+ _emit_progress({ type: 'error', code: 'E_INSTALL', exitCode: rc });
460
+ }
461
+ }
462
+
463
+ function info(msg: string): void {
464
+ if (!state.QUIET) process.stdout.write(` ${msg}\n`);
465
+ }
466
+
467
+ function success(msg: string): void {
468
+ if (!state.QUIET) process.stdout.write(` ✅ ${msg}\n`);
469
+ }
470
+
471
+ function skip(msg: string): void {
472
+ if (!state.QUIET) process.stdout.write(` ⏭️ ${msg}\n`);
473
+ }
474
+
475
+ function warn(msg: string): void {
476
+ process.stderr.write(` ⚠️ ${msg}\n`);
477
+ }
478
+
479
+ /** Mirror of Python `fail()`: prints the doctor hint, then `sys.exit(1)`. */
480
+ function fail(msg: string): never {
481
+ process.stderr.write(` ❌ ${msg}\n`);
482
+ process.stderr.write(
483
+ ' Diagnose: `./agent-config doctor` ' +
484
+ '(or `--check <id>` for a single category)\n',
485
+ );
486
+ throw new SystemExitError(1);
487
+ }
488
+
489
+ // --- Package detection ---
490
+
491
+ function detect_package_root(project_root: string): string {
492
+ const npm_path = path.join(project_root, 'node_modules', '@event4u', 'agent-config');
493
+ if (isDir(npm_path)) return resolvePath(npm_path);
494
+
495
+ if (pathExists(path.join(project_root, 'src', 'config', 'profiles', 'minimal.ini'))) {
496
+ return project_root;
497
+ }
498
+
499
+ fail(
500
+ 'Could not find agent-config package. Install via ' +
501
+ '`npx @event4u/agent-config init` or `npm install -g @event4u/agent-config`.',
502
+ );
503
+ }
504
+
505
+ function detect_package_type(package_root: string): string {
506
+ if (package_root.split(path.sep).includes('node_modules')) return 'npm';
507
+ return 'local';
508
+ }
509
+
510
+ function detect_package_type_for_project(project_root: string, package_root: string): string {
511
+ const npm_path = resolvePath(
512
+ path.join(project_root, 'node_modules', '@event4u', 'agent-config'),
513
+ );
514
+ const package_resolved = resolvePath(package_root);
515
+ if (package_resolved === npm_path) return 'npm';
516
+ return detect_package_type(package_root);
517
+ }
518
+
519
+ // --- Conflict resolution ---
520
+
521
+ function _is_interactive(): boolean {
522
+ try {
523
+ return Boolean(process.stdin.isTTY) && Boolean(process.stdout.isTTY);
524
+ } catch {
525
+ return false;
526
+ }
527
+ }
528
+
529
+ function _resolve_file_conflict(_target: string, _force_hint: boolean): string {
530
+ // del force_hint / del target — deploys always overwrite our own content.
531
+ return 'write';
532
+ }
533
+
534
+ // --- File utilities ---
535
+
536
+ function ensure_directory(p: string): void {
537
+ mkdirp(p);
538
+ }
539
+
540
+ function write_file(p: string, content: string): void {
541
+ ensure_directory(path.dirname(p));
542
+ writeText(p, content);
543
+ }
544
+
545
+ function read_json_file(p: string): Record<string, unknown> {
546
+ let data: unknown;
547
+ try {
548
+ data = JSON.parse(readText(p));
549
+ } catch {
550
+ warn(`Invalid JSON in ${p}, treating as empty`);
551
+ return {};
552
+ }
553
+ if (typeof data !== 'object' || data === null || Array.isArray(data)) {
554
+ warn(`Unexpected JSON shape in ${p}, treating as empty`);
555
+ return {};
556
+ }
557
+ return data as Record<string, unknown>;
558
+ }
559
+
560
+ function write_json_file(p: string, data: unknown): void {
561
+ const content = jsonDumpsIndent(data, 4) + '\n';
562
+ write_file(p, content);
563
+ }
564
+
565
+ function _isPlainObject(v: unknown): v is Record<string, unknown> {
566
+ return typeof v === 'object' && v !== null && !Array.isArray(v);
567
+ }
568
+
569
+ /** `copy.deepcopy` for JSON-shaped values. */
570
+ function deepcopy<T>(v: T): T {
571
+ if (v === null || typeof v !== 'object') return v;
572
+ if (Array.isArray(v)) return v.map((x) => deepcopy(x)) as unknown as T;
573
+ const out: Record<string, unknown> = {};
574
+ for (const k of Object.keys(v as Record<string, unknown>)) {
575
+ out[k] = deepcopy((v as Record<string, unknown>)[k]);
576
+ }
577
+ return out as unknown as T;
578
+ }
579
+
580
+ function deep_merge(
581
+ base: Record<string, unknown>,
582
+ overlay: Record<string, unknown>,
583
+ ): Record<string, unknown> {
584
+ const result = deepcopy(base);
585
+ for (const key of Object.keys(overlay)) {
586
+ const value = overlay[key];
587
+ if (
588
+ Object.prototype.hasOwnProperty.call(result, key) &&
589
+ _isPlainObject(result[key]) &&
590
+ _isPlainObject(value)
591
+ ) {
592
+ result[key] = deep_merge(
593
+ result[key] as Record<string, unknown>,
594
+ value as Record<string, unknown>,
595
+ );
596
+ } else {
597
+ result[key] = deepcopy(value);
598
+ }
599
+ }
600
+ return result;
601
+ }
602
+
603
+ /** Deep structural equality for JSON-shaped values (Python dict `==`). */
604
+ function jsonEqual(a: unknown, b: unknown): boolean {
605
+ if (a === b) return true;
606
+ if (typeof a !== typeof b) return false;
607
+ if (Array.isArray(a) || Array.isArray(b)) {
608
+ if (!Array.isArray(a) || !Array.isArray(b) || a.length !== b.length) return false;
609
+ return a.every((v, i) => jsonEqual(v, b[i]));
610
+ }
611
+ if (_isPlainObject(a) && _isPlainObject(b)) {
612
+ const ka = Object.keys(a);
613
+ const kb = Object.keys(b);
614
+ if (ka.length !== kb.length) return false;
615
+ return ka.every(
616
+ (k) => Object.prototype.hasOwnProperty.call(b, k) && jsonEqual(a[k], (b as Record<string, unknown>)[k]),
617
+ );
618
+ }
619
+ return false;
620
+ }
621
+
622
+ function merge_json_file(
623
+ p: string,
624
+ new_data: Record<string, unknown>,
625
+ _force: boolean,
626
+ label: string,
627
+ ): Record<string, unknown>[] {
628
+ // del force — our keys are always applied; the flag never gates a write.
629
+ const new_entries = build_merge_entries(label, new_data) as unknown as Record<
630
+ string,
631
+ unknown
632
+ >[];
633
+
634
+ if (!pathExists(p)) {
635
+ write_json_file(p, new_data);
636
+ success(`${label} created`);
637
+ return new_entries;
638
+ }
639
+
640
+ const existing = read_json_file(p);
641
+ const merged = deep_merge(existing, new_data);
642
+
643
+ if (jsonEqual(merged, existing)) {
644
+ skip(`${label} already configured`);
645
+ return new_entries;
646
+ }
647
+
648
+ write_json_file(p, merged);
649
+ success(`${label} updated`);
650
+ return new_entries;
651
+ }
652
+
653
+ // ---------------------------------------------------------------------------
654
+ // Legacy settings migration
655
+ // ---------------------------------------------------------------------------
656
+
657
+ function _parse_legacy_settings(text: string): [Record<string, string>, string[]] {
658
+ const values: Record<string, string> = {};
659
+ const unknown: string[] = [];
660
+ for (const raw of text.split('\n')) {
661
+ const line = raw.trim();
662
+ if (!line || line.startsWith('#')) continue;
663
+ if (!line.includes('=')) continue;
664
+ const eq = line.indexOf('=');
665
+ const key = line.slice(0, eq).trim();
666
+ const value = line.slice(eq + 1).trim();
667
+ if (!key) continue;
668
+ values[key] = value;
669
+ if (!(key in LEGACY_RENAME_MAP)) unknown.push(key);
670
+ }
671
+ return [values, unknown];
672
+ }
673
+
674
+ const _BARE_ID_RE = /^[a-z][a-z0-9_]*$/;
675
+
676
+ function _yaml_scalar(value: string): string {
677
+ if (value === '') return '""';
678
+ if (value === 'true' || value === 'false') return value;
679
+ if (value.length > 0 && /^[0-9]+$/.test(value)) return value; // str.isdigit()
680
+ if (_BARE_ID_RE.test(value)) return value;
681
+ const escaped = value.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
682
+ return `"${escaped}"`;
683
+ }
684
+
685
+ function _replace_template_value(template: string, dotted_path: string, value: string): string {
686
+ return _replace_template_value_raw(template, dotted_path, _yaml_scalar(value));
687
+ }
688
+
689
+ function _replace_template_value_raw(template: string, dotted_path: string, raw_yaml: string): string {
690
+ const parts = dotted_path.split('.');
691
+ if (parts.length === 0) return template;
692
+
693
+ const sections = parts.slice(0, parts.length - 1);
694
+ const key = parts[parts.length - 1];
695
+ const target_indent = ' '.repeat(sections.length);
696
+
697
+ const header_re = /^(\s*)([A-Za-z_][A-Za-z0-9_]*):\s*$/;
698
+ const scalar_re = /^(\s*)([A-Za-z_][A-Za-z0-9_]*):\s*\S.*$/;
699
+
700
+ const current_path: (string | null)[] = new Array(sections.length).fill(null);
701
+
702
+ const endsNl = template.endsWith('\n');
703
+ const lines = template.split('\n');
704
+ // Python splitlines() drops a trailing empty produced by a final newline.
705
+ if (endsNl && lines.length > 0 && lines[lines.length - 1] === '') {
706
+ lines.pop();
707
+ }
708
+ for (let idx = 0; idx < lines.length; idx += 1) {
709
+ const line = lines[idx] as string;
710
+ const stripped = line.trim();
711
+ if (!stripped || stripped.startsWith('#')) continue;
712
+
713
+ const m_header = header_re.exec(line);
714
+ if (m_header) {
715
+ const indent = m_header[1] as string;
716
+ const name = m_header[2] as string;
717
+ const depth = Math.floor(indent.length / 2);
718
+ if (depth < sections.length) {
719
+ current_path[depth] = name;
720
+ for (let d = depth + 1; d < sections.length; d += 1) {
721
+ current_path[d] = null;
722
+ }
723
+ }
724
+ continue;
725
+ }
726
+
727
+ const m_scalar = scalar_re.exec(line);
728
+ if (!m_scalar) continue;
729
+ const indent = m_scalar[1] as string;
730
+ const name = m_scalar[2] as string;
731
+ if (name !== key || indent !== target_indent) continue;
732
+ if (!arrayEqual(current_path, sections)) continue;
733
+ lines[idx] = `${indent}${key}: ${raw_yaml}`;
734
+ return lines.join('\n') + (endsNl ? '\n' : '');
735
+ }
736
+ return template;
737
+ }
738
+
739
+ function arrayEqual(a: (string | null)[], b: readonly string[]): boolean {
740
+ if (a.length !== b.length) return false;
741
+ return a.every((v, i) => v === b[i]);
742
+ }
743
+
744
+ function _append_unknown_legacy(
745
+ rendered: string,
746
+ legacy_values: Record<string, string>,
747
+ unknown_keys: string[],
748
+ ): string {
749
+ if (unknown_keys.length === 0) return rendered;
750
+ const block = [
751
+ '',
752
+ '# Unknown keys from the legacy .agent-settings — review and drop.',
753
+ '_legacy:',
754
+ ];
755
+ for (const key of [...unknown_keys].sort()) {
756
+ block.push(` ${key}: ${_yaml_scalar(legacy_values[key] as string)}`);
757
+ }
758
+ const suffix = block.join('\n') + '\n';
759
+ if (rendered.endsWith('\n')) return rendered + suffix;
760
+ return rendered + '\n' + suffix;
761
+ }
762
+
763
+ function _migrate_legacy_if_present(project_root: string, template_body: string): string | null {
764
+ const legacy_target = path.join(project_root, LEGACY_SETTINGS_FILE);
765
+ if (!isFile(legacy_target)) return null;
766
+
767
+ const legacy_text = readText(legacy_target);
768
+ const [values, unknown] = _parse_legacy_settings(legacy_text);
769
+
770
+ let rendered = template_body;
771
+ for (const flat_key of Object.keys(values)) {
772
+ if (flat_key in LEGACY_RENAME_MAP) {
773
+ rendered = _replace_template_value(
774
+ rendered,
775
+ LEGACY_RENAME_MAP[flat_key] as string,
776
+ values[flat_key] as string,
777
+ );
778
+ }
779
+ }
780
+ rendered = _append_unknown_legacy(rendered, values, unknown);
781
+
782
+ const backup_target = path.join(project_root, LEGACY_BACKUP_FILE);
783
+ writeText(backup_target, legacy_text);
784
+ fs.unlinkSync(legacy_target);
785
+
786
+ info(`Migrated legacy ${LEGACY_SETTINGS_FILE} → ${SETTINGS_FILE}`);
787
+ info(`Backup saved to ${LEGACY_BACKUP_FILE}`);
788
+ if (unknown.length > 0) {
789
+ warn(`Legacy keys not in rename map preserved under _legacy: ${[...unknown].sort().join(', ')}`);
790
+ }
791
+ return rendered;
792
+ }
793
+
794
+ // --- Bridge generators ---
795
+
796
+ function _parse_profile_ini(p: string): Record<string, string> {
797
+ const values: Record<string, string> = {};
798
+ for (const raw of readText(p).split('\n')) {
799
+ const line = raw.trim();
800
+ if (!line || line.startsWith(';') || line.startsWith('#')) continue;
801
+ if (!line.includes('=')) continue;
802
+ const eq = line.indexOf('=');
803
+ values[line.slice(0, eq).trim()] = line.slice(eq + 1).trim();
804
+ }
805
+ return values;
806
+ }
807
+
808
+ const _PLACEHOLDER_RE = /__[A-Z][A-Z0-9_]*__/g;
809
+
810
+ function _render_template(template: string, profile_values: Record<string, string>): string {
811
+ let body = template;
812
+ for (const key of Object.keys(profile_values)) {
813
+ const placeholder = `__${key.toUpperCase()}__`;
814
+ if (body.includes(placeholder)) {
815
+ body = body.split(placeholder).join(profile_values[key]);
816
+ }
817
+ }
818
+ const leftover = [...new Set(body.match(_PLACEHOLDER_RE) ?? [])].sort();
819
+ if (leftover.length > 0) {
820
+ fail('Template has unfilled placeholders after profile render: ' + leftover.join(', '));
821
+ }
822
+ return body;
823
+ }
824
+
825
+ function _load_valid_user_types(package_root: string): string[] {
826
+ const directory = path.join(package_root, USER_TYPES_DIR);
827
+ if (!isDir(directory)) return [];
828
+ return sortedGlobStems(directory, '.yml');
829
+ }
830
+
831
+ function _validate_user_type(package_root: string, value: string): string {
832
+ const cleaned = (value || '').trim();
833
+ if (!cleaned) return '';
834
+ const valid = _load_valid_user_types(package_root);
835
+ if (valid.length === 0) {
836
+ fail(`--user-type=${cleaned} requested but no user-types/*.yml present under ${package_root}`);
837
+ }
838
+ if (!valid.includes(cleaned)) {
839
+ fail(
840
+ `Unknown --user-type=${cleaned}. Valid: ${valid.join(', ')} ` +
841
+ '(empty string disables the filter).',
842
+ );
843
+ }
844
+ return cleaned;
845
+ }
846
+
847
+ function _inject_packs(body: string, packs: string[]): string {
848
+ if (packs.length === 0) return body;
849
+ const block = 'packs:\n' + packs.map((p) => ` - ${p}\n`).join('');
850
+ // splitlines(keepends=True)
851
+ const lines = splitlinesKeepends(body);
852
+ const out: string[] = [];
853
+ let inserted = false;
854
+ for (const line of lines) {
855
+ out.push(line);
856
+ if (!inserted && line.startsWith('rule_loading_tier:')) {
857
+ if (!line.endsWith('\n')) {
858
+ out[out.length - 1] = line + '\n';
859
+ }
860
+ out.push(block);
861
+ inserted = true;
862
+ }
863
+ }
864
+ if (!inserted) {
865
+ if (out.length > 0 && !(out[out.length - 1] as string).endsWith('\n')) {
866
+ out[out.length - 1] = (out[out.length - 1] as string) + '\n';
867
+ }
868
+ out.push(block);
869
+ }
870
+ return out.join('');
871
+ }
872
+
873
+ /** Python str.splitlines(keepends=True) for `\n`-terminated text. */
874
+ function splitlinesKeepends(text: string): string[] {
875
+ const out: string[] = [];
876
+ let start = 0;
877
+ for (let i = 0; i < text.length; i += 1) {
878
+ if (text[i] === '\n') {
879
+ out.push(text.slice(start, i + 1));
880
+ start = i + 1;
881
+ }
882
+ }
883
+ if (start < text.length) out.push(text.slice(start));
884
+ return out;
885
+ }
886
+
887
+ function ensure_agent_settings(
888
+ project_root: string,
889
+ package_root: string,
890
+ profile: string,
891
+ force: boolean,
892
+ user_type: string = '',
893
+ packs: string[] | null = null,
894
+ ): void {
895
+ const target = _canonical_settings_target(project_root);
896
+ const profile_source = path.join(package_root, 'src', 'config', 'profiles', `${profile}.ini`);
897
+ const template_source = path.join(package_root, 'src', 'config', 'agent-settings.template.yml');
898
+
899
+ if (!pathExists(profile_source)) fail(`Missing profile preset: ${profile_source}`);
900
+ if (!pathExists(template_source)) fail(`Missing settings template: ${template_source}`);
901
+
902
+ const template = readText(template_source);
903
+ if (!template.includes(RULE_LOADING_TIER_PLACEHOLDER)) {
904
+ fail(`Template is missing placeholder ${RULE_LOADING_TIER_PLACEHOLDER}`);
905
+ }
906
+ if (!template.includes(USER_TYPE_PLACEHOLDER)) {
907
+ fail(`Template is missing placeholder ${USER_TYPE_PLACEHOLDER}`);
908
+ }
909
+ const profile_values = _parse_profile_ini(profile_source);
910
+ if (profile_values['rule_loading_tier'] !== profile) {
911
+ // {v!r} → Python repr of a str (single-quoted) or None.
912
+ const got = 'rule_loading_tier' in profile_values
913
+ ? `'${profile_values['rule_loading_tier']}'`
914
+ : 'None';
915
+ fail(
916
+ `Profile preset ${path.basename(profile_source)} has rule_loading_tier=` +
917
+ `${got} but --profile=${profile}`,
918
+ );
919
+ }
920
+ profile_values['user_type'] = _validate_user_type(package_root, user_type);
921
+ let template_body = _render_template(template, profile_values);
922
+ template_body = _inject_packs(template_body, packs ?? []);
923
+
924
+ // ADR-038: relocate an existing repo-root .agent-settings.yml.
925
+ const legacy_root = path.join(project_root, SETTINGS_FILE);
926
+ if (isFile(legacy_root) && !pathExists(target)) {
927
+ mkdirp(path.dirname(target));
928
+ writeText(target, readText(legacy_root));
929
+ fs.unlinkSync(legacy_root);
930
+ success(`Migrated ${SETTINGS_FILE} → agents/settings/${SETTINGS_FILE} (ADR-038)`);
931
+ return;
932
+ }
933
+
934
+ const legacy_target = path.join(project_root, LEGACY_SETTINGS_FILE);
935
+ if (isFile(legacy_target) && pathExists(target)) {
936
+ warn(
937
+ `Both ${SETTINGS_FILE} and legacy ${LEGACY_SETTINGS_FILE} exist. ` +
938
+ `Skipping migration to avoid overwriting ${SETTINGS_FILE}. ` +
939
+ 'Delete one of them manually and re-run.',
940
+ );
941
+ return;
942
+ }
943
+
944
+ const migrated = _migrate_legacy_if_present(project_root, template_body);
945
+ if (migrated !== null) {
946
+ write_file(target, migrated);
947
+ success(`${SETTINGS_FILE} migrated from legacy key=value`);
948
+ return;
949
+ }
950
+
951
+ if (pathExists(target) && !force) {
952
+ skip(`${SETTINGS_FILE} already exists`);
953
+ return;
954
+ }
955
+
956
+ mkdirp(path.dirname(target));
957
+ write_file(target, template_body);
958
+ const user_type_value = profile_values['user_type'] ?? '';
959
+ const suffix = user_type_value ? `, user_type=${user_type_value}` : '';
960
+ success(`${SETTINGS_FILE} created (rule_loading_tier=${profile}${suffix})`);
961
+ }
962
+
963
+ function ensure_vscode_bridge(project_root: string, package_type: string, force: boolean): void {
964
+ const plugin_paths: Record<string, string> = {
965
+ npm: './node_modules/@event4u/agent-config/plugin/agent-config',
966
+ };
967
+ const plugin_path = plugin_paths[package_type] ?? './plugin/agent-config';
968
+ const bridge = { 'chat.pluginLocations': { [plugin_path]: true } };
969
+ merge_json_file(
970
+ path.join(project_root, '.vscode', 'settings.json'),
971
+ bridge,
972
+ force,
973
+ '.vscode/settings.json',
974
+ );
975
+ }
976
+
977
+ function ensure_augment_bridge(project_root: string, force: boolean): Record<string, unknown>[] {
978
+ const bridge = { enabledPlugins: { 'agent-config@event4u': true } };
979
+ return merge_json_file(
980
+ path.join(project_root, '.augment', 'settings.json'),
981
+ bridge,
982
+ force,
983
+ '.augment/settings.json',
984
+ );
985
+ }
986
+
987
+ const AUGMENT_USER_DIR = path.join(os.homedir(), '.augment');
988
+ const AUGMENT_USER_HOOKS_DIR = path.join(AUGMENT_USER_DIR, 'hooks');
989
+ const AUGMENT_DISPATCHER_TRAMPOLINE = 'augment-dispatcher.sh';
990
+ const AUGMENT_LEGACY_TRAMPOLINES: readonly string[] = [
991
+ 'augment-chat-history.sh',
992
+ 'augment-roadmap-progress.sh',
993
+ 'augment-onboarding-gate.sh',
994
+ 'augment-context-hygiene.sh',
995
+ ];
996
+ const AUGMENT_DISPATCHER_BINDINGS: ReadonlyArray<readonly [string, string]> = [
997
+ ['session_start', 'SessionStart'],
998
+ ['session_end', 'SessionEnd'],
999
+ ['stop', 'Stop'],
1000
+ ['pre_tool_use', 'PreToolUse'],
1001
+ ['post_tool_use', 'PostToolUse'],
1002
+ ];
1003
+
1004
+ function _deploy_augment_trampoline(package_root: string, name: string, force: boolean): string | null {
1005
+ const src = path.join(package_root, 'scripts', 'hooks', name);
1006
+ if (!pathExists(src)) {
1007
+ skip(`augment trampoline missing in package: ${src}`);
1008
+ return null;
1009
+ }
1010
+ mkdirp(AUGMENT_USER_HOOKS_DIR);
1011
+ const dst = path.join(AUGMENT_USER_HOOKS_DIR, name);
1012
+ const src_text = readText(src);
1013
+ if (pathExists(dst) && readText(dst) === src_text && !force) {
1014
+ skip(`~/.augment/hooks/${name} already up to date`);
1015
+ } else {
1016
+ writeText(dst, src_text);
1017
+ fs.chmodSync(dst, 0o755);
1018
+ success(`~/.augment/hooks/${name} installed`);
1019
+ }
1020
+ return dst;
1021
+ }
1022
+
1023
+ function _remove_legacy_augment_trampolines(): void {
1024
+ for (const name of AUGMENT_LEGACY_TRAMPOLINES) {
1025
+ const legacy = path.join(AUGMENT_USER_HOOKS_DIR, name);
1026
+ try {
1027
+ if (isFile(legacy)) {
1028
+ fs.unlinkSync(legacy);
1029
+ skip(`removed legacy ~/.augment/hooks/${name}`);
1030
+ }
1031
+ } catch {
1032
+ /* OSError → ignore */
1033
+ }
1034
+ }
1035
+ }
1036
+
1037
+ function ensure_augment_user_hooks(package_root: string, force: boolean): Record<string, unknown>[] {
1038
+ const dst = _deploy_augment_trampoline(package_root, AUGMENT_DISPATCHER_TRAMPOLINE, force);
1039
+ if (dst === null) return [];
1040
+
1041
+ _remove_legacy_augment_trampolines();
1042
+
1043
+ const per_event: Record<string, unknown[]> = {};
1044
+ for (const [ac_event, native] of AUGMENT_DISPATCHER_BINDINGS) {
1045
+ const cmd = `${dst} ${ac_event} ${native}`;
1046
+ const entry = { hooks: [{ type: 'command', command: cmd }] };
1047
+ (per_event[native] ??= []).push(entry);
1048
+ }
1049
+
1050
+ const settings_patch = { hooks: per_event };
1051
+ return merge_json_file(
1052
+ path.join(AUGMENT_USER_DIR, 'settings.json'),
1053
+ settings_patch,
1054
+ force,
1055
+ '~/.augment/settings.json',
1056
+ );
1057
+ }
1058
+
1059
+ const CLAUDE_PLUGIN_ID = 'agent-config@event4u-agent-config';
1060
+ const CLAUDE_LEGACY_PLUGIN_IDS: readonly string[] = [
1061
+ 'agent-conf@event4u',
1062
+ 'agent-config@event4u',
1063
+ ];
1064
+
1065
+ function _heal_legacy_claude_plugin_ids(p: string): string[] {
1066
+ if (!pathExists(p)) return [];
1067
+ const data = read_json_file(p);
1068
+ const enabled = data['enabledPlugins'];
1069
+ if (!_isPlainObject(enabled)) return [];
1070
+ const removed = CLAUDE_LEGACY_PLUGIN_IDS.filter((pid) => pid in enabled);
1071
+ if (removed.length === 0) return [];
1072
+ for (const pid of removed) {
1073
+ delete (enabled as Record<string, unknown>)[pid];
1074
+ }
1075
+ write_json_file(p, data);
1076
+ return removed;
1077
+ }
1078
+
1079
+ function ensure_claude_bridge(project_root: string, force: boolean): Record<string, unknown>[] {
1080
+ const target = path.join(project_root, '.claude', 'settings.json');
1081
+ const healed = _heal_legacy_claude_plugin_ids(target);
1082
+ for (const pid of healed) {
1083
+ success(`.claude/settings.json: removed stale plugin id \`${pid}\``);
1084
+ }
1085
+ const bridge = { enabledPlugins: { [CLAUDE_PLUGIN_ID]: true } };
1086
+ return merge_json_file(target, bridge, force || healed.length > 0, '.claude/settings.json');
1087
+ }
1088
+
1089
+ const CURSOR_DISPATCHER_BINDINGS: ReadonlyArray<readonly [string, string]> = [
1090
+ ['session_start', 'sessionStart'],
1091
+ ['session_end', 'sessionEnd'],
1092
+ ['stop', 'stop'],
1093
+ ['user_prompt_submit', 'beforeSubmitPrompt'],
1094
+ ['post_tool_use', 'postToolUse'],
1095
+ ];
1096
+
1097
+ function _cursor_dispatch_command(ac_event: string, native: string): string {
1098
+ return (
1099
+ `./agent-config dispatch:hook ` +
1100
+ `--platform cursor --event ${ac_event} ` +
1101
+ `--native-event ${native}`
1102
+ );
1103
+ }
1104
+
1105
+ function ensure_cursor_bridge(project_root: string, force: boolean): Record<string, unknown>[] {
1106
+ const hooks: Record<string, unknown[]> = {};
1107
+ for (const [ac_event, native] of CURSOR_DISPATCHER_BINDINGS) {
1108
+ (hooks[native] ??= []).push({ command: _cursor_dispatch_command(ac_event, native) });
1109
+ }
1110
+ const bridge = { version: 1, hooks };
1111
+ return merge_json_file(
1112
+ path.join(project_root, '.cursor', 'hooks.json'),
1113
+ bridge,
1114
+ force,
1115
+ '.cursor/hooks.json',
1116
+ );
1117
+ }
1118
+
1119
+ const CURSOR_USER_DIR = path.join(os.homedir(), '.cursor');
1120
+ const CURSOR_USER_HOOKS_DIR = path.join(CURSOR_USER_DIR, 'hooks');
1121
+ const CURSOR_DISPATCHER_TRAMPOLINE = 'cursor-dispatcher.sh';
1122
+
1123
+ function ensure_cursor_user_hooks(package_root: string, force: boolean): Record<string, unknown>[] {
1124
+ const src = path.join(package_root, 'scripts', 'hooks', CURSOR_DISPATCHER_TRAMPOLINE);
1125
+ if (!pathExists(src)) {
1126
+ skip(`cursor trampoline missing in package: ${src}`);
1127
+ return [];
1128
+ }
1129
+
1130
+ mkdirp(CURSOR_USER_HOOKS_DIR);
1131
+ const dst = path.join(CURSOR_USER_HOOKS_DIR, CURSOR_DISPATCHER_TRAMPOLINE);
1132
+ const src_text = readText(src);
1133
+ if (pathExists(dst) && readText(dst) === src_text && !force) {
1134
+ skip(`~/.cursor/hooks/${CURSOR_DISPATCHER_TRAMPOLINE} already up to date`);
1135
+ } else {
1136
+ writeText(dst, src_text);
1137
+ fs.chmodSync(dst, 0o755);
1138
+ success(`~/.cursor/hooks/${CURSOR_DISPATCHER_TRAMPOLINE} installed`);
1139
+ }
1140
+
1141
+ const hooks: Record<string, unknown[]> = {};
1142
+ for (const [ac_event, native] of CURSOR_DISPATCHER_BINDINGS) {
1143
+ (hooks[native] ??= []).push({ command: `${dst} ${ac_event} ${native}` });
1144
+ }
1145
+
1146
+ const settings_patch = { version: 1, hooks };
1147
+ return merge_json_file(
1148
+ path.join(CURSOR_USER_DIR, 'hooks.json'),
1149
+ settings_patch,
1150
+ force,
1151
+ '~/.cursor/hooks.json',
1152
+ );
1153
+ }
1154
+
1155
+ const CLINE_DISPATCHER_BINDINGS: ReadonlyArray<readonly [string, string]> = [
1156
+ ['session_start', 'TaskStart'],
1157
+ ['session_start', 'TaskResume'],
1158
+ ['session_end', 'TaskComplete'],
1159
+ ['stop', 'TaskCancel'],
1160
+ ['user_prompt_submit', 'UserPromptSubmit'],
1161
+ ['post_tool_use', 'PostToolUse'],
1162
+ ];
1163
+
1164
+ /** `shlex.quote` — POSIX shell single-quote escaping. */
1165
+ function shlexQuote(s: string): string {
1166
+ if (s === '') return "''";
1167
+ if (/^[A-Za-z0-9_@%+=:,./-]+$/.test(s)) return s;
1168
+ return "'" + s.replace(/'/g, "'\"'\"'") + "'";
1169
+ }
1170
+
1171
+ function clineProjectHookBody(native_event: string, ac_event: string, workspace_quoted: string): string {
1172
+ // Faithful render of CLINE_PROJECT_HOOK_TEMPLATE.format(...). The Python
1173
+ // template uses doubled braces ({{ }}) that .format() collapses to single.
1174
+ return (
1175
+ '#!/usr/bin/env bash\n' +
1176
+ '# Generated by event4u/agent-config install.py — DO NOT EDIT.\n' +
1177
+ `# Project-scope Cline hook for ${native_event} → agent-config ${ac_event}.\n` +
1178
+ '# Phase 7.6 (docs/contracts/hook-architecture-v1.md).\n' +
1179
+ 'set -u\n' +
1180
+ 'EVENT_DATA="$(cat)"\n' +
1181
+ `WORKSPACE_ROOT=${workspace_quoted}\n` +
1182
+ 'cd "$WORKSPACE_ROOT" 2>/dev/null || { printf \'%s\\n\' \'{}\'; exit 0; }\n' +
1183
+ 'if [ ! -x ./agent-config ]; then\n' +
1184
+ ' printf \'%s\\n\' \'{}\'\n' +
1185
+ ' exit 0\n' +
1186
+ 'fi\n' +
1187
+ 'printf \'%s\' "$EVENT_DATA" \\\n' +
1188
+ ' | ./agent-config dispatch:hook \\\n' +
1189
+ ' --platform cline \\\n' +
1190
+ ` --event ${ac_event} \\\n` +
1191
+ ` --native-event ${native_event} \\\n` +
1192
+ ' >/dev/null 2>&1 || true\n' +
1193
+ 'printf \'%s\\n\' \'{}\'\n' +
1194
+ 'exit 0\n'
1195
+ );
1196
+ }
1197
+
1198
+ function ensure_cline_bridge(project_root: string, force: boolean): void {
1199
+ const hooks_dir = path.join(project_root, '.clinerules', 'hooks');
1200
+ mkdirp(hooks_dir);
1201
+
1202
+ const workspace_quoted = shlexQuote(resolvePath(project_root));
1203
+ let written = 0;
1204
+ for (const [ac_event, native_event] of CLINE_DISPATCHER_BINDINGS) {
1205
+ const target = path.join(hooks_dir, native_event);
1206
+ const body = clineProjectHookBody(native_event, ac_event, workspace_quoted);
1207
+ if (pathExists(target) && readText(target) === body && !force) {
1208
+ continue;
1209
+ }
1210
+ if (pathExists(target) && !force) {
1211
+ skip(`.clinerules/hooks/${native_event} exists, needs update (use --force)`);
1212
+ continue;
1213
+ }
1214
+ writeText(target, body);
1215
+ fs.chmodSync(target, 0o755);
1216
+ written += 1;
1217
+ }
1218
+ if (written) {
1219
+ success(`.clinerules/hooks/ — ${written} script(s) installed`);
1220
+ } else {
1221
+ skip('.clinerules/hooks/ already up to date');
1222
+ }
1223
+ }
1224
+
1225
+ const CLINE_USER_DIR = path.join(os.homedir(), 'Documents', 'Cline', 'Hooks');
1226
+ const CLINE_DISPATCHER_TRAMPOLINE = 'cline-dispatcher.sh';
1227
+
1228
+ function ensure_cline_user_hooks(package_root: string, force: boolean): void {
1229
+ const src = path.join(package_root, 'scripts', 'hooks', CLINE_DISPATCHER_TRAMPOLINE);
1230
+ if (!pathExists(src)) {
1231
+ skip(`cline trampoline missing in package: ${src}`);
1232
+ return;
1233
+ }
1234
+
1235
+ mkdirp(CLINE_USER_DIR);
1236
+ const trampoline = path.join(CLINE_USER_DIR, CLINE_DISPATCHER_TRAMPOLINE);
1237
+ const src_text = readText(src);
1238
+ if (pathExists(trampoline) && readText(trampoline) === src_text && !force) {
1239
+ skip(`~/Documents/Cline/Hooks/${CLINE_DISPATCHER_TRAMPOLINE} already up to date`);
1240
+ } else {
1241
+ writeText(trampoline, src_text);
1242
+ fs.chmodSync(trampoline, 0o755);
1243
+ success(`~/Documents/Cline/Hooks/${CLINE_DISPATCHER_TRAMPOLINE} installed`);
1244
+ }
1245
+
1246
+ const trampoline_quoted = shlexQuote(trampoline);
1247
+ for (const [ac_event, native_event] of CLINE_DISPATCHER_BINDINGS) {
1248
+ const wrapper = path.join(CLINE_USER_DIR, native_event);
1249
+ const body =
1250
+ '#!/usr/bin/env bash\n' +
1251
+ '# Generated by event4u/agent-config install.py — DO NOT EDIT.\n' +
1252
+ `# User-scope Cline hook for ${native_event} → agent-config ${ac_event}.\n` +
1253
+ `exec ${trampoline_quoted} ${ac_event} ${native_event}\n`;
1254
+ if (pathExists(wrapper) && readText(wrapper) === body && !force) {
1255
+ continue;
1256
+ }
1257
+ writeText(wrapper, body);
1258
+ fs.chmodSync(wrapper, 0o755);
1259
+ }
1260
+ }
1261
+
1262
+ const WINDSURF_DISPATCHER_BINDINGS: ReadonlyArray<readonly [string, string]> = [
1263
+ ['session_start', 'post_setup_worktree'],
1264
+ ['user_prompt_submit', 'pre_user_prompt'],
1265
+ ['stop', 'post_cascade_response'],
1266
+ ];
1267
+
1268
+ function _windsurf_dispatch_command(ac_event: string, native: string): string {
1269
+ return (
1270
+ `./agent-config dispatch:hook ` +
1271
+ `--platform windsurf --event ${ac_event} ` +
1272
+ `--native-event ${native}`
1273
+ );
1274
+ }
1275
+
1276
+ function ensure_windsurf_bridge(project_root: string, force: boolean): Record<string, unknown>[] {
1277
+ const hooks: Record<string, unknown[]> = {};
1278
+ for (const [ac_event, native] of WINDSURF_DISPATCHER_BINDINGS) {
1279
+ (hooks[native] ??= []).push({
1280
+ command: _windsurf_dispatch_command(ac_event, native),
1281
+ show_output: false,
1282
+ });
1283
+ }
1284
+ const bridge = { hooks };
1285
+ return merge_json_file(
1286
+ path.join(project_root, '.windsurf', 'hooks.json'),
1287
+ bridge,
1288
+ force,
1289
+ '.windsurf/hooks.json',
1290
+ );
1291
+ }
1292
+
1293
+ const WINDSURF_USER_DIR = path.join(os.homedir(), '.codeium', 'windsurf');
1294
+ const WINDSURF_USER_HOOKS_DIR = path.join(WINDSURF_USER_DIR, 'hooks');
1295
+ const WINDSURF_DISPATCHER_TRAMPOLINE = 'windsurf-dispatcher.sh';
1296
+
1297
+ function ensure_windsurf_user_hooks(package_root: string, force: boolean): Record<string, unknown>[] {
1298
+ const src = path.join(package_root, 'scripts', 'hooks', WINDSURF_DISPATCHER_TRAMPOLINE);
1299
+ if (!pathExists(src)) {
1300
+ skip(`windsurf trampoline missing in package: ${src}`);
1301
+ return [];
1302
+ }
1303
+
1304
+ mkdirp(WINDSURF_USER_HOOKS_DIR);
1305
+ const dst = path.join(WINDSURF_USER_HOOKS_DIR, WINDSURF_DISPATCHER_TRAMPOLINE);
1306
+ const src_text = readText(src);
1307
+ if (pathExists(dst) && readText(dst) === src_text && !force) {
1308
+ skip(`~/.codeium/windsurf/hooks/${WINDSURF_DISPATCHER_TRAMPOLINE} already up to date`);
1309
+ } else {
1310
+ writeText(dst, src_text);
1311
+ fs.chmodSync(dst, 0o755);
1312
+ success(`~/.codeium/windsurf/hooks/${WINDSURF_DISPATCHER_TRAMPOLINE} installed`);
1313
+ }
1314
+
1315
+ const hooks: Record<string, unknown[]> = {};
1316
+ for (const [ac_event, native] of WINDSURF_DISPATCHER_BINDINGS) {
1317
+ (hooks[native] ??= []).push({
1318
+ command: `${dst} ${ac_event} ${native}`,
1319
+ show_output: false,
1320
+ });
1321
+ }
1322
+
1323
+ const settings_patch = { hooks };
1324
+ return merge_json_file(
1325
+ path.join(WINDSURF_USER_DIR, 'hooks.json'),
1326
+ settings_patch,
1327
+ force,
1328
+ '~/.codeium/windsurf/hooks.json',
1329
+ );
1330
+ }
1331
+
1332
+ const GEMINI_DISPATCHER_BINDINGS: ReadonlyArray<readonly [string, string, string]> = [
1333
+ ['session_start', 'SessionStart', ''],
1334
+ ['session_end', 'SessionEnd', ''],
1335
+ ['stop', 'AfterAgent', ''],
1336
+ ['user_prompt_submit', 'BeforeAgent', ''],
1337
+ ['post_tool_use', 'AfterTool', '.*'],
1338
+ ];
1339
+
1340
+ function _gemini_dispatch_command(ac_event: string, native: string): string {
1341
+ return (
1342
+ `./agent-config dispatch:hook ` +
1343
+ `--platform gemini --event ${ac_event} ` +
1344
+ `--native-event ${native}`
1345
+ );
1346
+ }
1347
+
1348
+ function _gemini_hooks_dict(
1349
+ command_factory: (ac_event: string, native: string) => string,
1350
+ ): Record<string, unknown[]> {
1351
+ const out: Record<string, unknown[]> = {};
1352
+ for (const [ac_event, native, matcher] of GEMINI_DISPATCHER_BINDINGS) {
1353
+ (out[native] ??= []).push({
1354
+ matcher,
1355
+ hooks: [{ type: 'command', command: command_factory(ac_event, native) }],
1356
+ });
1357
+ }
1358
+ return out;
1359
+ }
1360
+
1361
+ function ensure_gemini_bridge(project_root: string, force: boolean): Record<string, unknown>[] {
1362
+ const bridge = { hooks: _gemini_hooks_dict(_gemini_dispatch_command) };
1363
+ return merge_json_file(
1364
+ path.join(project_root, '.gemini', 'settings.json'),
1365
+ bridge,
1366
+ force,
1367
+ '.gemini/settings.json',
1368
+ );
1369
+ }
1370
+
1371
+ const GEMINI_USER_DIR = path.join(os.homedir(), '.gemini');
1372
+ const GEMINI_USER_HOOKS_DIR = path.join(GEMINI_USER_DIR, 'hooks');
1373
+ const GEMINI_DISPATCHER_TRAMPOLINE = 'gemini-dispatcher.sh';
1374
+
1375
+ function ensure_gemini_user_hooks(package_root: string, force: boolean): Record<string, unknown>[] {
1376
+ const src = path.join(package_root, 'scripts', 'hooks', GEMINI_DISPATCHER_TRAMPOLINE);
1377
+ if (!pathExists(src)) {
1378
+ skip(`gemini trampoline missing in package: ${src}`);
1379
+ return [];
1380
+ }
1381
+
1382
+ mkdirp(GEMINI_USER_HOOKS_DIR);
1383
+ const dst = path.join(GEMINI_USER_HOOKS_DIR, GEMINI_DISPATCHER_TRAMPOLINE);
1384
+ const src_text = readText(src);
1385
+ if (pathExists(dst) && readText(dst) === src_text && !force) {
1386
+ skip(`~/.gemini/hooks/${GEMINI_DISPATCHER_TRAMPOLINE} already up to date`);
1387
+ } else {
1388
+ writeText(dst, src_text);
1389
+ fs.chmodSync(dst, 0o755);
1390
+ success(`~/.gemini/hooks/${GEMINI_DISPATCHER_TRAMPOLINE} installed`);
1391
+ }
1392
+
1393
+ const settings_patch = {
1394
+ hooks: _gemini_hooks_dict((ac_event, native) => `${dst} ${ac_event} ${native}`),
1395
+ };
1396
+ return merge_json_file(
1397
+ path.join(GEMINI_USER_DIR, 'settings.json'),
1398
+ settings_patch,
1399
+ force,
1400
+ '~/.gemini/settings.json',
1401
+ );
1402
+ }
1403
+
1404
+ function ensure_copilot_bridge(project_root: string, force: boolean): void {
1405
+ const target = path.join(project_root, '.github', 'plugin', 'marketplace.json');
1406
+ const bridge = {
1407
+ marketplace: {
1408
+ name: 'event4u-agent-marketplace',
1409
+ plugins: [
1410
+ {
1411
+ id: 'agent-config@event4u',
1412
+ repository: 'https://github.com/event4u-app/agent-config',
1413
+ },
1414
+ ],
1415
+ },
1416
+ };
1417
+ if (pathExists(target) && !force) {
1418
+ skip('.github/plugin/marketplace.json already exists');
1419
+ return;
1420
+ }
1421
+ write_json_file(target, bridge);
1422
+ success('.github/plugin/marketplace.json created');
1423
+ }
1424
+
1425
+ const ROOCODE_MARKER = `# Agent Config bridge
1426
+
1427
+ This file marks the project as an \`event4u/agent-config\` consumer.
1428
+
1429
+ Roo Code reads \`.roo/rules/*.md\` as system-level instructions. The
1430
+ canonical rule and skill source lives under \`.augment/\` (Augment
1431
+ portability mirror — see \`AGENTS.md\` for orientation).
1432
+
1433
+ ## How to use
1434
+
1435
+ - These rules load automatically on every Roo Code session — no
1436
+ manual action required.
1437
+ - Switch Roo Code modes (Architect / Code / Ask / Debug / Custom)
1438
+ via the mode switcher to invoke different cognition profiles;
1439
+ every mode still sees these rules.
1440
+ - Slash commands and skills live under \`.augment/commands/\` and
1441
+ \`.augment/skills/\`. Roo Code does not register them natively —
1442
+ invoke them by name in chat (e.g. *"run the create-pr command"*).
1443
+
1444
+ See \`docs/setup/per-ide/roocode.md\` for the full activation guide.
1445
+
1446
+ Run \`./agent-config --help\` for available commands.
1447
+ `;
1448
+
1449
+ function ensure_roocode_bridge(project_root: string, force: boolean): void {
1450
+ const target = path.join(project_root, '.roo', 'rules', 'agent-config.md');
1451
+ if (pathExists(target) && !force) {
1452
+ skip('.roo/rules/agent-config.md already exists');
1453
+ return;
1454
+ }
1455
+ write_file(target, ROOCODE_MARKER);
1456
+ success('.roo/rules/agent-config.md created');
1457
+ }
1458
+
1459
+ const CLAUDE_DESKTOP_MARKER = `# Agent Config bridge — Claude Desktop
1460
+
1461
+ This file marks the project as an \`event4u/agent-config\` consumer.
1462
+
1463
+ Claude Desktop is a **global-scope** tool — it reads config from
1464
+ \`~/Library/Application Support/Claude/\` (macOS) and does not
1465
+ auto-discover project files. This marker is informational only.
1466
+
1467
+ To wire Claude Desktop to this project's rules, run:
1468
+ \`npx @event4u/agent-config init --ai claude-desktop --global\`
1469
+
1470
+ Canonical rule and skill source: \`.augment/\` (see \`AGENTS.md\`).
1471
+ `;
1472
+
1473
+ function ensure_claude_desktop_bridge(project_root: string, force: boolean): void {
1474
+ const target = path.join(project_root, '.claude-desktop', 'agent-config.md');
1475
+ if (pathExists(target) && !force) {
1476
+ skip('.claude-desktop/agent-config.md already exists');
1477
+ return;
1478
+ }
1479
+ write_file(target, CLAUDE_DESKTOP_MARKER);
1480
+ success('.claude-desktop/agent-config.md created');
1481
+ }
1482
+
1483
+ const AIDER_MARKER = `# Agent Config bridge — Aider
1484
+
1485
+ This file marks the project as an \`event4u/agent-config\` consumer.
1486
+
1487
+ Aider does not auto-discover this file. To activate it, add the
1488
+ following to \`.aider.conf.yml\` (create if missing):
1489
+
1490
+ \`\`\`yaml
1491
+ read:
1492
+ - .aider/agent-config.md
1493
+ \`\`\`
1494
+
1495
+ Or pass \`--read .aider/agent-config.md\` on the command line.
1496
+
1497
+ Canonical rule and skill source: \`.augment/\` (see \`AGENTS.md\`).
1498
+ `;
1499
+
1500
+ function ensure_aider_bridge(project_root: string, force: boolean): void {
1501
+ const target = path.join(project_root, '.aider', 'agent-config.md');
1502
+ if (pathExists(target) && !force) {
1503
+ skip('.aider/agent-config.md already exists');
1504
+ return;
1505
+ }
1506
+ write_file(target, AIDER_MARKER);
1507
+ success('.aider/agent-config.md created');
1508
+ }
1509
+
1510
+ const CODEX_MARKER = `# Agent Config bridge — Codex CLI
1511
+
1512
+ This file marks the project as an \`event4u/agent-config\` consumer.
1513
+
1514
+ Codex CLI auto-discovers \`AGENTS.md\` at the project root — that file
1515
+ is the canonical entry point. This marker is informational and tells
1516
+ developers where the rules and skills live.
1517
+
1518
+ Canonical rule and skill source: \`.augment/\` (see project \`AGENTS.md\`).
1519
+ `;
1520
+
1521
+ function ensure_codex_bridge(project_root: string, force: boolean): void {
1522
+ const target = path.join(project_root, '.codex', 'agent-config.md');
1523
+ if (pathExists(target) && !force) {
1524
+ skip('.codex/agent-config.md already exists');
1525
+ return;
1526
+ }
1527
+ write_file(target, CODEX_MARKER);
1528
+ success('.codex/agent-config.md created');
1529
+ }
1530
+
1531
+ const CONTINUE_MARKER = `# Agent Config bridge — Continue.dev
1532
+
1533
+ This file marks the project as an \`event4u/agent-config\` consumer.
1534
+
1535
+ Continue.dev auto-discovers \`.continue/rules/*.md\` as system-level
1536
+ rules per session. The canonical rule and skill source lives under
1537
+ \`.augment/\` (Augment portability mirror — see \`AGENTS.md\` for
1538
+ orientation).
1539
+ `;
1540
+
1541
+ function ensure_continue_bridge(project_root: string, force: boolean): void {
1542
+ const target = path.join(project_root, '.continue', 'rules', 'agent-config.md');
1543
+ if (pathExists(target) && !force) {
1544
+ skip('.continue/rules/agent-config.md already exists');
1545
+ return;
1546
+ }
1547
+ write_file(target, CONTINUE_MARKER);
1548
+ success('.continue/rules/agent-config.md created');
1549
+ }
1550
+
1551
+ const KILOCODE_MARKER = `# Agent Config bridge — Kilo Code
1552
+
1553
+ This file marks the project as an \`event4u/agent-config\` consumer.
1554
+
1555
+ Kilo Code auto-discovers \`.kilocode/rules/*.md\` as system-level rules
1556
+ per session. The canonical rule and skill source lives under
1557
+ \`.augment/\` (Augment portability mirror — see \`AGENTS.md\` for
1558
+ orientation).
1559
+
1560
+ ## How to use
1561
+
1562
+ - These rules load automatically on every Kilo Code session — no
1563
+ manual action required.
1564
+ - Switch Kilo Code modes (Architect / Code / Ask / Debug /
1565
+ Orchestrator) via the mode switcher to invoke different
1566
+ cognition profiles; every mode still sees these rules.
1567
+ - Slash commands and skills live under \`.augment/commands/\` and
1568
+ \`.augment/skills/\`. Kilo Code does not register them natively —
1569
+ invoke them by name in chat (e.g. *"run the create-pr command"*).
1570
+
1571
+ See \`docs/setup/per-ide/kilocode.md\` for the full activation guide.
1572
+ `;
1573
+
1574
+ function ensure_kilocode_bridge(project_root: string, force: boolean): void {
1575
+ const target = path.join(project_root, '.kilocode', 'rules', 'agent-config.md');
1576
+ if (pathExists(target) && !force) {
1577
+ skip('.kilocode/rules/agent-config.md already exists');
1578
+ return;
1579
+ }
1580
+ write_file(target, KILOCODE_MARKER);
1581
+ success('.kilocode/rules/agent-config.md created');
1582
+ }
1583
+
1584
+ const ZED_MARKER = `# Agent Config bridge — Zed
1585
+
1586
+ This file marks the project as an \`event4u/agent-config\` consumer.
1587
+
1588
+ Zed reads \`.rules\` at the project root as system-level instructions —
1589
+ that file is the canonical entry point. This marker is informational
1590
+ and tells developers where the rules and skills live.
1591
+
1592
+ To activate agent-config under Zed, point Zed's \`.rules\` at the
1593
+ canonical source (or symlink it):
1594
+
1595
+ \`\`\`
1596
+ # Append to .rules at project root
1597
+ @.augment/AGENTS.md
1598
+ \`\`\`
1599
+
1600
+ Canonical rule and skill source: \`.augment/\` (see \`AGENTS.md\`).
1601
+ `;
1602
+
1603
+ function ensure_zed_bridge(project_root: string, force: boolean): void {
1604
+ const target = path.join(project_root, '.zed', 'agent-config.md');
1605
+ if (pathExists(target) && !force) {
1606
+ skip('.zed/agent-config.md already exists');
1607
+ return;
1608
+ }
1609
+ write_file(target, ZED_MARKER);
1610
+ success('.zed/agent-config.md created');
1611
+ }
1612
+
1613
+ const JETBRAINS_MARKER = `# Agent Config bridge — JetBrains AI Assistant
1614
+
1615
+ This file marks the project as an \`event4u/agent-config\` consumer.
1616
+
1617
+ JetBrains AI Assistant reads custom prompts and guidelines from
1618
+ project-level config (\`.idea/\`) and user-scope settings. This marker
1619
+ is informational — to wire agent-config into JetBrains AI, point the
1620
+ assistant's custom-prompts path at \`.augment/\` or copy the relevant
1621
+ rules into your JetBrains profile.
1622
+
1623
+ Canonical rule and skill source: \`.augment/\` (see \`AGENTS.md\`).
1624
+ `;
1625
+
1626
+ function ensure_jetbrains_bridge(project_root: string, force: boolean): void {
1627
+ const target = path.join(project_root, '.jetbrains', 'agent-config.md');
1628
+ if (pathExists(target) && !force) {
1629
+ skip('.jetbrains/agent-config.md already exists');
1630
+ return;
1631
+ }
1632
+ write_file(target, JETBRAINS_MARKER);
1633
+ success('.jetbrains/agent-config.md created');
1634
+ }
1635
+
1636
+ const KIRO_MARKER = `# Agent Config bridge — Kiro
1637
+
1638
+ This file marks the project as an \`event4u/agent-config\` consumer.
1639
+
1640
+ Kiro auto-discovers \`.kiro/steering/*.md\` as steering documents per
1641
+ session. The canonical rule and skill source lives under \`.augment/\`
1642
+ (Augment portability mirror — see \`AGENTS.md\` for orientation).
1643
+
1644
+ ## How to use
1645
+
1646
+ - Steering documents load automatically on every Kiro session — no
1647
+ manual action required.
1648
+ - For structured, plan-first work, use Kiro's **Spec** workflow
1649
+ (the agent produces a spec → tasks → implementation under your
1650
+ review). For free-form work, use **Vibe**. Both honor these
1651
+ steering documents.
1652
+ - Slash commands and skills live under \`.augment/commands/\` and
1653
+ \`.augment/skills/\`. Kiro does not register them natively —
1654
+ invoke them by name in chat (e.g. *"run the create-pr command"*).
1655
+
1656
+ See \`docs/setup/per-ide/kiro.md\` for the full activation guide.
1657
+ `;
1658
+
1659
+ function ensure_kiro_bridge(project_root: string, force: boolean): void {
1660
+ const target = path.join(project_root, '.kiro', 'steering', 'agent-config.md');
1661
+ if (pathExists(target) && !force) {
1662
+ skip('.kiro/steering/agent-config.md already exists');
1663
+ return;
1664
+ }
1665
+ write_file(target, KIRO_MARKER);
1666
+ success('.kiro/steering/agent-config.md created');
1667
+ }
1668
+
1669
+ // --- Post-install smoke test ---
1670
+
1671
+ const SMOKE_PROBE_EVENTS: ReadonlyArray<readonly [string, string]> = [
1672
+ ['augment', 'session_start'],
1673
+ ['claude', 'SessionStart'],
1674
+ ['cursor', 'beforeShellExecution'],
1675
+ ['cline', 'session_start'],
1676
+ ['windsurf', 'post_setup_worktree'],
1677
+ ['gemini', 'SessionStart'],
1678
+ ];
1679
+
1680
+ const SMOKE_BRIDGE_PATHS: Record<string, string> = {
1681
+ augment: '.augment/settings.json',
1682
+ claude: '.claude/settings.json',
1683
+ cursor: '.cursor/hooks.json',
1684
+ cline: '.clinerules/hooks',
1685
+ windsurf: '.windsurf/hooks.json',
1686
+ gemini: '.gemini/settings.json',
1687
+ };
1688
+
1689
+ function dirHasEntries(p: string): boolean {
1690
+ try {
1691
+ return fs.readdirSync(p).length > 0;
1692
+ } catch {
1693
+ return false;
1694
+ }
1695
+ }
1696
+
1697
+ /**
1698
+ * Resolve how to run a `.ts` script: prefer the `tsx` binary from a
1699
+ * `node_modules/.bin` directory found by walking up from the script's
1700
+ * directory; fall back to `npx tsx`. Mirrors
1701
+ * `dispatch_hook.ts::_resolve_tsx_invocation` so the smoke probe runs the
1702
+ * dispatcher as TypeScript with no python3 dependency.
1703
+ */
1704
+ function _resolve_tsx_invocation(
1705
+ scriptPath: string,
1706
+ scriptArgs: string[],
1707
+ ): { command: string; args: string[] } {
1708
+ const binName = process.platform === 'win32' ? 'tsx.cmd' : 'tsx';
1709
+ let dir = path.dirname(scriptPath);
1710
+ for (;;) {
1711
+ const candidate = path.join(dir, 'node_modules', '.bin', binName);
1712
+ if (isFile(candidate)) {
1713
+ return { command: candidate, args: [scriptPath, ...scriptArgs] };
1714
+ }
1715
+ const parent = path.dirname(dir);
1716
+ if (parent === dir) break;
1717
+ dir = parent;
1718
+ }
1719
+ return { command: 'npx', args: ['tsx', scriptPath, ...scriptArgs] };
1720
+ }
1721
+
1722
+ function _smoke_test_hooks(project_root: string, package_root: string): number {
1723
+ // The dispatcher is a `.ts` concern run via tsx; in an installed package
1724
+ // layout it ships under `scripts/hooks/dispatch_hook.ts` (the package's
1725
+ // projection scripts dir, not `src/scripts`).
1726
+ const dispatcher = path.join(package_root, 'scripts', 'hooks', 'dispatch_hook.ts');
1727
+ const manifest = path.join(package_root, 'scripts', 'hook_manifest.yaml');
1728
+ if (!isFile(dispatcher) || !isFile(manifest)) return 0;
1729
+
1730
+ const failed: string[] = [];
1731
+ const skipped: string[] = [];
1732
+ const passed: string[] = [];
1733
+
1734
+ for (const [platform, native] of SMOKE_PROBE_EVENTS) {
1735
+ const rel_bridge = SMOKE_BRIDGE_PATHS[platform] ?? '';
1736
+ const bridge_path = rel_bridge ? path.join(project_root, rel_bridge) : null;
1737
+ const bridge_present = Boolean(
1738
+ bridge_path && (isFile(bridge_path) || (isDir(bridge_path) && dirHasEntries(bridge_path))),
1739
+ );
1740
+ if (!bridge_present) {
1741
+ skipped.push(platform);
1742
+ continue;
1743
+ }
1744
+ const dispatcherArgs = [
1745
+ '--manifest',
1746
+ manifest,
1747
+ '--platform',
1748
+ platform,
1749
+ '--event',
1750
+ 'session_start',
1751
+ '--native-event',
1752
+ native,
1753
+ '--dry-run',
1754
+ ];
1755
+ const inv = _resolve_tsx_invocation(dispatcher, dispatcherArgs);
1756
+ let res;
1757
+ try {
1758
+ res = spawnSync(inv.command, inv.args, {
1759
+ input: '{}',
1760
+ encoding: 'utf-8',
1761
+ cwd: project_root,
1762
+ timeout: 10000,
1763
+ });
1764
+ } catch (exc) {
1765
+ failed.push(`${platform}: ${String(exc)}`);
1766
+ continue;
1767
+ }
1768
+ // spawnSync surfaces ENOENT / timeout via res.error rather than throwing.
1769
+ if (res.error) {
1770
+ failed.push(`${platform}: ${String(res.error)}`);
1771
+ continue;
1772
+ }
1773
+ const returncode = res.status ?? 1;
1774
+ if (returncode !== 0) {
1775
+ const errTail = (res.stderr || '').trim().slice(0, 120);
1776
+ failed.push(`${platform}: exit=${returncode} ${errTail}`);
1777
+ continue;
1778
+ }
1779
+ let plan: unknown;
1780
+ try {
1781
+ plan = JSON.parse(res.stdout || '{}');
1782
+ } catch {
1783
+ failed.push(`${platform}: dispatcher did not emit JSON plan`);
1784
+ continue;
1785
+ }
1786
+ const concerns = _isPlainObject(plan) ? (plan as Record<string, unknown>)['concerns'] : undefined;
1787
+ if (!Array.isArray(concerns)) {
1788
+ failed.push(`${platform}: plan.concerns missing or not a list`);
1789
+ continue;
1790
+ }
1791
+ passed.push(platform);
1792
+ }
1793
+
1794
+ if (!state.QUIET) {
1795
+ if (passed.length) success(`hook smoke passed: ${passed.join(', ')}`);
1796
+ if (skipped.length) skip(`hook smoke skipped (bridge not installed): ${skipped.join(', ')}`);
1797
+ for (const line of failed) warn(`hook smoke failed — ${line}`);
1798
+ }
1799
+ return failed.length ? 1 : 0;
1800
+ }
1801
+
1802
+ // --- Global user-level install (ADR-007) ---
1803
+
1804
+ export const USER_SCOPE_PATHS: Record<string, string> = {
1805
+ 'claude-code': '~/.claude/',
1806
+ 'claude-desktop': '~/Library/Application Support/Claude/',
1807
+ cursor: '~/.cursor/',
1808
+ windsurf: '~/.codeium/windsurf/',
1809
+ cline: '~/Documents/Cline/Rules/',
1810
+ 'gemini-cli': '~/.gemini/',
1811
+ copilot: '~/.copilot/',
1812
+ augment: '~/.augment/',
1813
+ aider: '~/.aider.conf.yml',
1814
+ codex: '~/.codex/',
1815
+ roocode: '~/.roo/',
1816
+ continue: '~/.continue/',
1817
+ kilocode: '~/.kilocode/',
1818
+ zed: '~/.config/zed/',
1819
+ jetbrains: '~/.config/JetBrains/',
1820
+ kiro: '~/.kiro/',
1821
+ qoder: '~/.qoder/',
1822
+ opencode: '~/.opencode/',
1823
+ trae: '~/.trae/',
1824
+ antigravity: '~/.agents/',
1825
+ codebuddy: '~/.codebuddy/',
1826
+ droid: '~/.factory/',
1827
+ warp: '~/.warp/',
1828
+ };
1829
+
1830
+ const SCOPE_SUPPORT: Record<string, string> = {
1831
+ 'claude-code': 'global',
1832
+ 'claude-desktop': 'global',
1833
+ cursor: 'global',
1834
+ windsurf: 'global',
1835
+ cline: 'global',
1836
+ 'gemini-cli': 'global',
1837
+ copilot: 'both',
1838
+ augment: 'global',
1839
+ aider: 'global',
1840
+ codex: 'global',
1841
+ roocode: 'global',
1842
+ continue: 'global',
1843
+ kilocode: 'global',
1844
+ zed: 'global',
1845
+ jetbrains: 'global',
1846
+ kiro: 'global',
1847
+ qoder: 'global',
1848
+ opencode: 'global',
1849
+ trae: 'global',
1850
+ antigravity: 'global',
1851
+ codebuddy: 'global',
1852
+ droid: 'global',
1853
+ warp: 'global',
1854
+ };
1855
+
1856
+ export const PROJECT_BRIDGE_MARKERS: Record<string, string> = {
1857
+ 'claude-code': '.claude/settings.json',
1858
+ 'claude-desktop': '.claude-desktop/agent-config.md',
1859
+ cursor: '.cursor/hooks.json',
1860
+ windsurf: '.windsurf/hooks.json',
1861
+ cline: '.clinerules/hooks',
1862
+ 'gemini-cli': '.gemini/settings.json',
1863
+ copilot: '.github/plugin/marketplace.json',
1864
+ augment: '.augment/settings.json',
1865
+ aider: '.aider/agent-config.md',
1866
+ codex: '.codex/agent-config.md',
1867
+ roocode: '.roo/rules/agent-config.md',
1868
+ continue: '.continue/rules/agent-config.md',
1869
+ kilocode: '.kilocode/rules/agent-config.md',
1870
+ zed: '.zed/agent-config.md',
1871
+ jetbrains: '.jetbrains/agent-config.md',
1872
+ kiro: '.kiro/steering/agent-config.md',
1873
+ };
1874
+
1875
+ const _CLAUDE_SKILL_BUNDLE: ReadonlyArray<readonly [string, string]> = [
1876
+ ['dist/agent-src/rules', 'rules'],
1877
+ ['dist/agent-src/skills', 'skills'],
1878
+ ['dist/agent-src/commands', 'commands'],
1879
+ ['dist/agent-src/personas', 'personas'],
1880
+ ];
1881
+
1882
+ const GLOBAL_DEPLOY_SOURCES: Record<string, ReadonlyArray<readonly [string, string]>> = {
1883
+ 'claude-code': _CLAUDE_SKILL_BUNDLE,
1884
+ augment: [
1885
+ ['dist/agent-src/rules', 'rules'],
1886
+ ['dist/agent-src/skills', 'skills'],
1887
+ ['dist/agent-src/commands', 'commands'],
1888
+ ['dist/agent-src/contexts', 'contexts'],
1889
+ ['dist/agent-src/personas', 'personas'],
1890
+ ['dist/agent-src/templates', 'templates'],
1891
+ ],
1892
+ cursor: [
1893
+ ['dist/agent-src/rules', 'rules'],
1894
+ ['dist/agent-src/commands', 'commands'],
1895
+ ['dist/agent-src/personas', 'personas'],
1896
+ ],
1897
+ windsurf: [['dist/agent-src/rules', 'rules']],
1898
+ cline: [['dist/agent-src/rules', '']],
1899
+ 'gemini-cli': _CLAUDE_SKILL_BUNDLE,
1900
+ codex: _CLAUDE_SKILL_BUNDLE,
1901
+ continue: _CLAUDE_SKILL_BUNDLE,
1902
+ roocode: _CLAUDE_SKILL_BUNDLE,
1903
+ kilocode: _CLAUDE_SKILL_BUNDLE,
1904
+ qoder: _CLAUDE_SKILL_BUNDLE,
1905
+ opencode: _CLAUDE_SKILL_BUNDLE,
1906
+ trae: _CLAUDE_SKILL_BUNDLE,
1907
+ antigravity: _CLAUDE_SKILL_BUNDLE,
1908
+ codebuddy: _CLAUDE_SKILL_BUNDLE,
1909
+ droid: _CLAUDE_SKILL_BUNDLE,
1910
+ warp: _CLAUDE_SKILL_BUNDLE,
1911
+ kiro: [
1912
+ ['dist/agent-src/rules', 'rules'],
1913
+ ['dist/agent-src/skills', 'steering'],
1914
+ ['dist/agent-src/personas', 'personas'],
1915
+ ],
1916
+ };
1917
+
1918
+ const _CLAUDE_DESKTOP_MARKER_TEMPLATE_HEAD = `# agent-config — Claude Desktop marker
1919
+
1920
+ Installed by \`@event4u/agent-config\` (user scope, ADR-007).
1921
+
1922
+ `;
1923
+
1924
+ /** Render of _CLAUDE_DESKTOP_MARKER_TEMPLATE.format(...) — byte-faithful. */
1925
+ function claudeDesktopMarkerBody(
1926
+ lockfile: string,
1927
+ anchor: string,
1928
+ bundles_dir: string,
1929
+ bundle_count: number,
1930
+ ): string {
1931
+ return (
1932
+ '# agent-config — Claude Desktop marker\n' +
1933
+ '\n' +
1934
+ 'Installed by `@event4u/agent-config` (user scope, ADR-007).\n' +
1935
+ '\n' +
1936
+ `- Lockfile: \`${lockfile}\`\n` +
1937
+ `- Anchor: \`${anchor}\`\n` +
1938
+ `- Skill bundles: \`${bundles_dir}\` (${bundle_count} ZIPs)\n` +
1939
+ '\n' +
1940
+ '## Import skills into Claude Desktop\n' +
1941
+ '\n' +
1942
+ 'Claude Desktop has no filesystem skill-discovery convention — skills are\n' +
1943
+ 'imported manually via the Customize → Skills UI.\n' +
1944
+ '\n' +
1945
+ '1. Open Claude Desktop → **Settings → Customize → Skills**.\n' +
1946
+ '2. Click the **Upload skill** button.\n' +
1947
+ `3. Browse to \`${bundles_dir}\` and pick the \`<skill-name>.zip\` files you\n` +
1948
+ ' want to install. One ZIP = one skill.\n' +
1949
+ '4. Repeat per skill. Claude Desktop keeps each upload until you remove it.\n' +
1950
+ '\n' +
1951
+ 'The bundle directory is regenerated on every\n' +
1952
+ '`npx @event4u/agent-config init --tools=claude-desktop` run (only\n' +
1953
+ 'changed skills are rewritten — content-hash idempotency).\n' +
1954
+ '\n' +
1955
+ 'To remove this marker, delete this file.\n'
1956
+ );
1957
+ }
1958
+ void _CLAUDE_DESKTOP_MARKER_TEMPLATE_HEAD;
1959
+
1960
+ const _CLAUDE_DESKTOP_BUNDLES_SUBPATH = 'claude-desktop/bundles';
1961
+
1962
+ const GLOBAL_ROOT = path.join(os.homedir(), '.event4u', 'agent-config');
1963
+ const GLOBAL_USER_SETTINGS_PATH = path.join(GLOBAL_ROOT, '.agent-user.yml');
1964
+ const GLOBAL_AGENT_SETTINGS_PATH = path.join(GLOBAL_ROOT, '.agent-settings.yml');
1965
+ void GLOBAL_USER_SETTINGS_PATH;
1966
+
1967
+ function _bridge_marker(tool_id: string, scope: string): string {
1968
+ if (scope === 'global') return USER_SCOPE_PATHS[tool_id] ?? '';
1969
+ return PROJECT_BRIDGE_MARKERS[tool_id] ?? '';
1970
+ }
1971
+
1972
+ function _validate_scope(tools: Set<string>, scope: string, was_all: boolean): Set<string> {
1973
+ if (scope !== 'project' && scope !== 'global') {
1974
+ fail(`_validate_scope: unknown scope '${scope}'`);
1975
+ }
1976
+ if (process.env['AGENT_CONFIG_DEV_MODE'] === '1') return tools;
1977
+ const incompatible = [...tools]
1978
+ .filter((t) => {
1979
+ const sup = SCOPE_SUPPORT[t] ?? 'both';
1980
+ return sup !== 'both' && sup !== scope;
1981
+ })
1982
+ .sort();
1983
+ if (incompatible.length === 0) return tools;
1984
+ if (was_all) {
1985
+ return new Set([...tools].filter((t) => !incompatible.includes(t)));
1986
+ }
1987
+ const hint =
1988
+ scope === 'global' ? 'drop --global (project is the default scope)' : 'use --global';
1989
+ fail(`--tools: ${incompatible.join(', ')} does not support --${scope} scope (${hint})`);
1990
+ }
1991
+
1992
+ function _enforce_consumer_global_only(scope: string): void {
1993
+ if (scope !== 'project') return;
1994
+ if (process.env['AGENT_CONFIG_DEV_MODE'] === '1') return;
1995
+ fail(
1996
+ '--scope=project is reserved for maintainers (ADR-020 — consumer ' +
1997
+ 'installs are global-only). Set AGENT_CONFIG_DEV_MODE=1 to opt in. ' +
1998
+ 'See docs/maintainers/dev-mode.md.',
1999
+ );
2000
+ }
2001
+
2002
+ function _enforce_not_source_repo(scope: string, project_root: string): void {
2003
+ if (scope === 'global') return;
2004
+ if (process.env['AGENT_CONFIG_ALLOW_SELF_INSTALL'] === '1') return;
2005
+ const [is_source, signature] = _is_agent_config_source_repo(project_root);
2006
+ if (!is_source) return;
2007
+ fail(
2008
+ 'Refusing to install agent-config into its own source checkout ' +
2009
+ `(detected: ${signature}). The source repo is global-only — a ` +
2010
+ 'project-scope install would recreate the .augment/ .claude/ .cursor/ ' +
2011
+ 'projection trees in the repo (double token cost). Run `task sync` to ' +
2012
+ 'regenerate them from .agent-src.uncondensed/ instead, or set ' +
2013
+ 'AGENT_CONFIG_ALLOW_SELF_INSTALL=1 to force.',
2014
+ );
2015
+ }
2016
+
2017
+ // --- Three-layer settings reader ---
2018
+
2019
+ function _load_yaml_doc(p: string): Record<string, unknown> {
2020
+ if (!pathExists(p) || !isFile(p)) return {};
2021
+ let text: string;
2022
+ try {
2023
+ text = readText(p);
2024
+ } catch {
2025
+ return {};
2026
+ }
2027
+ const data = yamlSafeLoad(text);
2028
+ return _isPlainObject(data) ? (data as Record<string, unknown>) : {};
2029
+ }
2030
+
2031
+ function _load_default_settings(package_root: string): Record<string, unknown> {
2032
+ const template_source = path.join(package_root, 'src', 'config', 'agent-settings.template.yml');
2033
+ if (!pathExists(template_source)) return {};
2034
+ let text: string;
2035
+ try {
2036
+ text = readText(template_source);
2037
+ } catch {
2038
+ return {};
2039
+ }
2040
+ const rendered = text
2041
+ .split(RULE_LOADING_TIER_PLACEHOLDER)
2042
+ .join(DEFAULT_PROFILE)
2043
+ .split(USER_TYPE_PLACEHOLDER)
2044
+ .join('');
2045
+ const data = yamlSafeLoad(rendered);
2046
+ return _isPlainObject(data) ? (data as Record<string, unknown>) : {};
2047
+ }
2048
+
2049
+ function read_layered_settings(
2050
+ package_root: string,
2051
+ project_root: string | null = null,
2052
+ ): Record<string, unknown> {
2053
+ let merged = _load_default_settings(package_root);
2054
+ merged = deep_merge(merged, _load_yaml_doc(GLOBAL_AGENT_SETTINGS_PATH));
2055
+ if (project_root !== null) {
2056
+ const project_file = _resolve_settings_read(project_root);
2057
+ merged = deep_merge(merged, _load_yaml_doc(project_file));
2058
+ }
2059
+ return merged;
2060
+ }
2061
+ void read_layered_settings;
2062
+
2063
+ interface Options {
2064
+ profile: string;
2065
+ user_type: string;
2066
+ force: boolean;
2067
+ skip_bridges: boolean;
2068
+ augment_user_hooks: boolean;
2069
+ cursor_user_hooks: boolean;
2070
+ cline_user_hooks: boolean;
2071
+ windsurf_user_hooks: boolean;
2072
+ gemini_user_hooks: boolean;
2073
+ project: string | null;
2074
+ package: string | null;
2075
+ quiet: boolean;
2076
+ tools: string; // post-merge it's always a string
2077
+ ai: string | null;
2078
+ packs: string[]; // normalized to list post-parse
2079
+ core_only: boolean;
2080
+ no_smoke: boolean;
2081
+ global_install: boolean;
2082
+ scope: string | null;
2083
+ custom_path: string | null;
2084
+ offline: boolean;
2085
+ minimal: boolean;
2086
+ interactive: boolean;
2087
+ no_ui: boolean;
2088
+ dry_run: boolean;
2089
+ apply_payload: string | null;
2090
+ }
2091
+
2092
+ function _resolve_scope(
2093
+ opts: Options,
2094
+ detected: string,
2095
+ detect_reason: string,
2096
+ custom_path: string | null,
2097
+ ): string {
2098
+ if (opts.scope === 'project') return 'project';
2099
+ if (opts.scope === 'global') return 'global';
2100
+ if (opts.scope === 'prompt') {
2101
+ return _run_scope_prompt(opts, detect_reason || 'forced by --scope=prompt', custom_path);
2102
+ }
2103
+ if (opts.scope === 'auto') {
2104
+ if (detected === 'prompt') return _run_scope_prompt(opts, detect_reason, custom_path);
2105
+ if (!state.QUIET) info(`Scope: ${detected} (auto-detected; ${detect_reason})`);
2106
+ return detected;
2107
+ }
2108
+
2109
+ if (opts.global_install) return 'global';
2110
+
2111
+ if (detected === 'prompt') return _run_scope_prompt(opts, detect_reason, custom_path);
2112
+ if (!state.QUIET) {
2113
+ info(
2114
+ `Scope detection: ${detected} (${detect_reason}). Using project default for ` +
2115
+ 'backward compatibility; pass --scope=auto to honor detection.',
2116
+ );
2117
+ }
2118
+ return 'project';
2119
+ }
2120
+
2121
+ function _run_scope_prompt(opts: Options, reason: string, custom_path: string | null): string {
2122
+ if (!process.stdin.isTTY && custom_path === null) {
2123
+ fail(
2124
+ 'Ambiguous install scope detected and stdin is not a TTY. ' +
2125
+ 'Pass --scope=project|global (or --custom-path=<dir>) to override.',
2126
+ );
2127
+ }
2128
+ const choice = prompt_scope_choice(reason);
2129
+ if (choice === 'project') return 'project';
2130
+ if (choice === 'global') return 'global';
2131
+ let cp = custom_path;
2132
+ if (cp === null) {
2133
+ let raw: string | null;
2134
+ raw = _read_line('Custom destination path: ');
2135
+ if (raw === null) {
2136
+ fail('Custom-path prompt aborted (EOF on stdin)');
2137
+ }
2138
+ if (!raw) fail('Custom-path prompt requires a non-empty path');
2139
+ cp = resolvePath(expanduser(raw));
2140
+ opts.custom_path = cp;
2141
+ }
2142
+ if (!state.QUIET) info(`Custom destination: ${cp}`);
2143
+ return 'project';
2144
+ }
2145
+
2146
+ const SCOPE_DETECT_MANIFESTS: readonly string[] = [
2147
+ 'package.json',
2148
+ 'composer.json',
2149
+ 'pyproject.toml',
2150
+ 'Cargo.toml',
2151
+ 'go.mod',
2152
+ 'Gemfile',
2153
+ ];
2154
+ const SCOPE_DETECT_AI_DIRS: readonly string[] = [
2155
+ '.claude',
2156
+ '.cursor',
2157
+ '.windsurf',
2158
+ '.augment',
2159
+ '.clinerules',
2160
+ '.copilot',
2161
+ '.gemini',
2162
+ '.codex',
2163
+ '.aider',
2164
+ '.continue',
2165
+ '.roo',
2166
+ '.kilocode',
2167
+ ];
2168
+ const SCOPE_DETECT_AI_FILES: readonly string[] = [
2169
+ 'CLAUDE.md',
2170
+ 'AGENTS.md',
2171
+ 'GEMINI.md',
2172
+ '.windsurfrules',
2173
+ '.aider.conf.yml',
2174
+ ];
2175
+
2176
+ function detect_scope(cwd: string): [string, string] {
2177
+ if (pathExists(_resolve_settings_read(cwd))) {
2178
+ return ['project', `existing ${SETTINGS_FILE}`];
2179
+ }
2180
+
2181
+ const has_manifest = SCOPE_DETECT_MANIFESTS.find((m) => pathExists(path.join(cwd, m))) ?? null;
2182
+ const has_ai_dir = SCOPE_DETECT_AI_DIRS.find((d) => isDir(path.join(cwd, d))) ?? null;
2183
+ const has_ai_file = SCOPE_DETECT_AI_FILES.find((f) => pathExists(path.join(cwd, f))) ?? null;
2184
+
2185
+ if (has_manifest && (has_ai_dir || has_ai_file)) {
2186
+ const marker = has_ai_dir || has_ai_file;
2187
+ return ['prompt', `manifest (${has_manifest}) + AI-tool config (${marker})`];
2188
+ }
2189
+
2190
+ return ['global', 'no project-scope signals'];
2191
+ }
2192
+
2193
+ // --- Interactive prompts ---
2194
+
2195
+ const SCOPE_CUSTOM = 'custom';
2196
+
2197
+ /** `input(prompt).strip()` — returns null on EOF (Python raises EOFError). */
2198
+ function _read_line(prompt_text: string): string | null {
2199
+ const line = readLineSyncRaw(prompt_text);
2200
+ if (line === null) return null;
2201
+ return line.trim();
2202
+ }
2203
+
2204
+ /**
2205
+ * Synchronous single-line read from stdin with a prompt on stdout (no newline),
2206
+ * mirroring CPython `input()`. Returns null at EOF. Implemented via a blocking
2207
+ * fd read because Node has no built-in sync stdin line read.
2208
+ */
2209
+ function readLineSyncRaw(promptText: string): string | null {
2210
+ process.stdout.write(promptText);
2211
+ const buf = Buffer.alloc(1);
2212
+ const bytes: number[] = [];
2213
+ let sawAny = false;
2214
+ for (;;) {
2215
+ let n: number;
2216
+ try {
2217
+ n = fs.readSync(0, buf, 0, 1, null);
2218
+ } catch (err) {
2219
+ const code = (err as NodeJS.ErrnoException).code;
2220
+ if (code === 'EAGAIN') {
2221
+ continue;
2222
+ }
2223
+ if (code === 'EOF') {
2224
+ break;
2225
+ }
2226
+ throw err;
2227
+ }
2228
+ if (n === 0) break; // EOF
2229
+ sawAny = true;
2230
+ const ch = buf[0] as number;
2231
+ if (ch === 0x0a) {
2232
+ // strip a trailing \r (CRLF)
2233
+ if (bytes.length > 0 && bytes[bytes.length - 1] === 0x0d) bytes.pop();
2234
+ return Buffer.from(bytes).toString('utf-8');
2235
+ }
2236
+ bytes.push(ch);
2237
+ }
2238
+ if (!sawAny && bytes.length === 0) return null; // EOFError
2239
+ return Buffer.from(bytes).toString('utf-8');
2240
+ }
2241
+
2242
+ function prompt_scope_choice(reason: string): string {
2243
+ process.stdout.write('\n');
2244
+ info(`Ambiguous install scope: ${reason}.`);
2245
+ info('Choose where to install:');
2246
+ process.stdout.write(' 1) Project — install into the current directory\n');
2247
+ process.stdout.write(' 2) User — install into ~/ (recommended; one install per machine)\n');
2248
+ process.stdout.write(' 3) Custom — specify an explicit destination path\n');
2249
+ process.stdout.write('\n');
2250
+ let attempts = 0;
2251
+ while (attempts < 3) {
2252
+ const reply = _read_line('Choose [1/2/3]: ');
2253
+ if (reply === null) {
2254
+ fail('Scope prompt aborted (EOF on stdin); pass --scope=project|global to override');
2255
+ }
2256
+ if (['1', 'project', 'p'].includes(reply)) return 'project';
2257
+ if (['2', 'global', 'user', 'u', 'g'].includes(reply)) return 'global';
2258
+ if (['3', 'custom', 'c'].includes(reply)) return SCOPE_CUSTOM;
2259
+ attempts += 1;
2260
+ warn(`Invalid choice '${reply}'. Enter 1, 2, or 3.`);
2261
+ }
2262
+ fail('Scope prompt aborted (3 invalid replies); pass --scope=project|global to override');
2263
+ }
2264
+
2265
+ function prompt_collision_choice(p: string): string {
2266
+ process.stdout.write('\n');
2267
+ warn(`Existing file at ${p}`);
2268
+ info('Choose how to handle the collision:');
2269
+ process.stdout.write(' 1) Merge — append our content, preserve theirs\n');
2270
+ process.stdout.write(' 2) Backup and replace — rename existing to .bak.<ts>, write fresh\n');
2271
+ process.stdout.write(' 3) Abort — leave the file untouched, exit non-zero\n');
2272
+ process.stdout.write('\n');
2273
+ let attempts = 0;
2274
+ while (attempts < 3) {
2275
+ const reply = _read_line('Choose [1/2/3]: ');
2276
+ if (reply === null) {
2277
+ fail(`Collision prompt aborted (EOF on stdin) for ${p}`);
2278
+ }
2279
+ if (['1', 'merge', 'm'].includes(reply)) return 'merge';
2280
+ if (['2', 'backup', 'b'].includes(reply)) return 'backup';
2281
+ if (['3', 'abort', 'a'].includes(reply)) return 'abort';
2282
+ attempts += 1;
2283
+ warn(`Invalid choice '${reply}'. Enter 1, 2, or 3.`);
2284
+ }
2285
+ fail(`Collision prompt aborted (3 invalid replies) for ${p}`);
2286
+ }
2287
+ void prompt_collision_choice;
2288
+
2289
+ // --- Manifest / inventory helpers (lazy-import twins are eager static here) ---
2290
+
2291
+ function _sha256_of_file(p: string): string | null {
2292
+ return sha256OfFile(p);
2293
+ }
2294
+
2295
+ function _file_entry(p: string, kind: string, hash_content: boolean): Record<string, unknown> {
2296
+ return {
2297
+ path: p,
2298
+ kind,
2299
+ sha256: hash_content ? _sha256_of_file(p) : null,
2300
+ };
2301
+ }
2302
+
2303
+ type DeployResult = [number, number, string, string[]];
2304
+
2305
+ function _files_by_tool_from_deploy(
2306
+ deploy_results: Record<string, DeployResult>,
2307
+ ): Record<string, Record<string, unknown>[]> {
2308
+ const out: Record<string, Record<string, unknown>[]> = {};
2309
+ for (const tool_id of Object.keys(deploy_results)) {
2310
+ const [, , status, paths] = deploy_results[tool_id] as DeployResult;
2311
+ if (status === 'deployed') {
2312
+ out[tool_id] = paths.map((p) => _file_entry(p, 'deployed', true));
2313
+ } else if (status === 'marker') {
2314
+ out[tool_id] = paths.map((p) => _file_entry(p, 'marker', true));
2315
+ } else {
2316
+ out[tool_id] = [];
2317
+ }
2318
+ }
2319
+ return out;
2320
+ }
2321
+
2322
+ function _files_by_tool_from_bridges(
2323
+ tools: Set<string>,
2324
+ project_root: string,
2325
+ scope: string,
2326
+ ): Record<string, Record<string, unknown>[]> {
2327
+ const out: Record<string, Record<string, unknown>[]> = {};
2328
+ for (const tool_id of [...tools].sort()) {
2329
+ const marker = _bridge_marker(tool_id, scope);
2330
+ if (!marker) continue;
2331
+ let marker_path = marker;
2332
+ if (!path.isAbsolute(marker_path)) {
2333
+ marker_path = path.join(project_root, marker_path);
2334
+ }
2335
+ out[tool_id] = [_file_entry(marker_path, 'bridge', false)];
2336
+ }
2337
+ return out;
2338
+ }
2339
+
2340
+ function _update_installed_tools_manifest(
2341
+ project_root: string,
2342
+ tools: Set<string>,
2343
+ scope: string,
2344
+ force: boolean,
2345
+ files_by_tool: Record<string, Record<string, unknown>[]> | null = null,
2346
+ merged_keys_by_tool: Record<string, Record<string, unknown>[]> | null = null,
2347
+ ): number {
2348
+ const target = installed_tools.manifest_path(project_root);
2349
+ const existing = (installed_tools.read_manifest(target) ?? {}) as Record<string, unknown>;
2350
+ let entries = Array.isArray(existing['tools'])
2351
+ ? ([...(existing['tools'] as unknown[])] as Record<string, unknown>[])
2352
+ : [];
2353
+
2354
+ const version = installed_lock.current_package_version();
2355
+
2356
+ for (const tool_id of [...tools].sort()) {
2357
+ const marker = _bridge_marker(tool_id, scope);
2358
+ if (!marker) continue;
2359
+ const files = files_by_tool ? (files_by_tool[tool_id] ?? null) : null;
2360
+ const merged_keys = merged_keys_by_tool ? (merged_keys_by_tool[tool_id] ?? null) : null;
2361
+ try {
2362
+ entries = installed_tools.upsert_tool(entries, {
2363
+ name: tool_id,
2364
+ scope,
2365
+ bridge_marker: marker,
2366
+ force,
2367
+ files,
2368
+ merged_keys,
2369
+ });
2370
+ } catch (exc) {
2371
+ if (exc instanceof installed_tools.ScopeMismatchError) {
2372
+ if (!state.QUIET) {
2373
+ warn(String(exc.message));
2374
+ info(` Manifest: ${target}`);
2375
+ info(' Override: re-run with `--force` to rewrite the entry');
2376
+ }
2377
+ return 1;
2378
+ }
2379
+ throw exc;
2380
+ }
2381
+ }
2382
+
2383
+ installed_tools.write_manifest(target, version, entries);
2384
+ if (!state.QUIET) {
2385
+ const rel = isRelativeTo(target, project_root) ? path.relative(project_root, target) : target;
2386
+ info(`Manifest updated: ${rel}`);
2387
+ }
2388
+ return 0;
2389
+ }
2390
+
2391
+ /** `Path.is_relative_to`. */
2392
+ function isRelativeTo(child: string, parent: string): boolean {
2393
+ const rel = path.relative(parent, child);
2394
+ return rel === '' || (!rel.startsWith('..') && !path.isAbsolute(rel));
2395
+ }
2396
+
2397
+ // --- Global content deployment ---
2398
+
2399
+ function _resolve_package_root_for_global(): string {
2400
+ const here = resolvePath(_HERE);
2401
+ const candidate = path.dirname(path.dirname(path.dirname(here)));
2402
+ if (!pathExists(path.join(candidate, 'src', 'config', 'profiles', 'minimal.ini'))) {
2403
+ fail(
2404
+ `Could not locate agent-config package root from ${here}. ` +
2405
+ 'Expected src/config/profiles/minimal.ini at the parent directory.',
2406
+ );
2407
+ }
2408
+ return candidate;
2409
+ }
2410
+
2411
+ const CONSUMER_BRIDGE_MARKER_RELPATH = path.join('agents', '.event4u-bridge.yml');
2412
+
2413
+ const MIGRATE_LEGACY_YAML_FILES: readonly string[] = ['.agent-settings.yml', '.agent-user.yml'];
2414
+ const MIGRATE_LEGACY_TOOL_DIRS: readonly string[] = ['.augment', '.claude', '.cursor'];
2415
+ const AGENT_CONFIG_PACKAGE_NAME = '@event4u/agent-config';
2416
+
2417
+ // Return `[is_source_repo, signature]` for the maintainer auto-detect.
2418
+ // Signature #2: `.agent-src.uncondensed/` exists at `project_root` (legacy
2419
+ // layout) OR under `packages/<name>/` (current layout) — both unique to the
2420
+ // source repo. Hits skip the ADR-020 migration prompt automatically.
2421
+ function _is_agent_config_source_repo(project_root: string): [boolean, string] {
2422
+ if (process.env['AGENT_CONFIG_CONSUMER_MODE'] === '1') {
2423
+ return [false, 'consumer-mode-override'];
2424
+ }
2425
+
2426
+ const pkg_json = path.join(project_root, 'package.json');
2427
+ if (isFile(pkg_json)) {
2428
+ let data: unknown = {};
2429
+ try {
2430
+ data = JSON.parse(readText(pkg_json));
2431
+ } catch {
2432
+ data = {};
2433
+ }
2434
+ if (_isPlainObject(data) && (data as Record<string, unknown>)['name'] === AGENT_CONFIG_PACKAGE_NAME) {
2435
+ return [true, 'package.json:name'];
2436
+ }
2437
+ }
2438
+
2439
+ if (isDir(path.join(project_root, '.agent-src.uncondensed'))) {
2440
+ return [true, '.agent-src.uncondensed/'];
2441
+ }
2442
+ const packages_dir = path.join(project_root, 'packages');
2443
+ if (isDir(packages_dir)) {
2444
+ for (const child of fs.readdirSync(packages_dir)) {
2445
+ if (isDir(path.join(packages_dir, child, '.agent-src.uncondensed'))) {
2446
+ return [true, `packages/${child}/.agent-src.uncondensed/`];
2447
+ }
2448
+ }
2449
+ }
2450
+
2451
+ const installer_self = path.join(project_root, 'scripts', 'install.py');
2452
+ try {
2453
+ if (isFile(installer_self) && resolvePath(installer_self) === resolvePath(_HERE)) {
2454
+ return [true, 'src/scripts/install.py (self)'];
2455
+ }
2456
+ } catch {
2457
+ /* OSError → pass */
2458
+ }
2459
+
2460
+ return [false, ''];
2461
+ }
2462
+
2463
+ function _detect_legacy_for_migration(project_root: string): string[] {
2464
+ if (process.env['AGENT_CONFIG_DEV_MODE'] === '1') return [];
2465
+
2466
+ const [is_source, signature] = _is_agent_config_source_repo(project_root);
2467
+ if (is_source) {
2468
+ if (!state.QUIET) {
2469
+ warn(
2470
+ 'Maintainer mode auto-detected — agent-config source repo ' +
2471
+ `(signature: ${signature}). Skipping ADR-020 migration ` +
2472
+ 'prompt; the working tree stays intact. Set ' +
2473
+ 'AGENT_CONFIG_CONSUMER_MODE=1 to override for end-to-end ' +
2474
+ 'consumer-flow testing.',
2475
+ );
2476
+ }
2477
+ return [];
2478
+ }
2479
+
2480
+ if (isFile(path.join(project_root, CONSUMER_BRIDGE_MARKER_RELPATH))) return [];
2481
+
2482
+ const found: string[] = [];
2483
+ for (const name of MIGRATE_LEGACY_YAML_FILES) {
2484
+ if (isFile(path.join(project_root, name))) {
2485
+ found.push(name);
2486
+ } else if (isFile(path.join(project_root, 'settings', name))) {
2487
+ found.push(`settings/${name}`);
2488
+ }
2489
+ }
2490
+ for (const name of MIGRATE_LEGACY_TOOL_DIRS) {
2491
+ const p = path.join(project_root, name);
2492
+ if (isDir(p) && !isSymlink(p)) {
2493
+ found.push(`${name}/`);
2494
+ }
2495
+ }
2496
+ return found.sort();
2497
+ }
2498
+
2499
+ function _prompt_migrate_to_global(project_root: string, artefacts: string[]): boolean {
2500
+ if (!state.QUIET) {
2501
+ process.stdout.write('\n');
2502
+ warn('Legacy project-local artefacts detected — pre-ADR-020 layout:');
2503
+ for (const rel of artefacts) {
2504
+ info(` ${path.join(project_root, rel)}`);
2505
+ }
2506
+ info('The unified `agent-config migrate` sweeps these in one pass.');
2507
+ info('The wizard recreates fresh config afterwards.');
2508
+ }
2509
+
2510
+ if (!_is_interactive()) {
2511
+ if (!state.QUIET) info('Non-interactive mode → defaulting to YES (run migration).');
2512
+ return true;
2513
+ }
2514
+
2515
+ let attempts = 0;
2516
+ while (attempts < 3) {
2517
+ const reply = _read_line('Run `agent-config migrate` now? [Y/n]: ');
2518
+ if (reply === null) return false;
2519
+ if (reply === '' || ['y', 'yes'].includes(reply.toLowerCase())) return true;
2520
+ if (['n', 'no'].includes(reply.toLowerCase())) return false;
2521
+ attempts += 1;
2522
+ warn(`Invalid choice '${reply}'. Enter Y or n.`);
2523
+ }
2524
+ return false;
2525
+ }
2526
+
2527
+ function _run_migrate_to_global(project_root: string): number {
2528
+ // The .py runs `scripts._cli.cmd_migrate.main([], cwd, out=sys.stdout)`
2529
+ // in-process. The `.ts` twin (`./_cli/cmd_migrate.ts`) is statically
2530
+ // imported above, so this is a direct in-process call — no subprocess.
2531
+ // cmd_migrate.main defaults its `out` sink to `process.stdout` and its
2532
+ // `err` sink to `process.stderr`, reproducing the old
2533
+ // `stdio:['ignore','inherit','pipe']` semantics (child stdout → parent
2534
+ // stdout; migrator stderr → parent stderr). It returns the int exit code.
2535
+ //
2536
+ // The old python3-spawn path returned 1 on a MIGRATE_UNAVAILABLE (exit 99)
2537
+ // sentinel or a spawn error. With a static import the migrator can never be
2538
+ // "unavailable", so that arm is dead — but the defensive try/catch is kept:
2539
+ // any unexpected throw maps to the same exit-1 contract.
2540
+ try {
2541
+ return cmdMigrateMain([], { cwd: project_root });
2542
+ } catch (exc) {
2543
+ warn(`migrate unavailable: ${String(exc)}`);
2544
+ return 1;
2545
+ }
2546
+ }
2547
+
2548
+ function _format_global_root_for_marker(global_root: string): string {
2549
+ const home = resolvePath(os.homedir());
2550
+ const resolved = resolvePath(global_root);
2551
+ const rel = path.relative(home, resolved);
2552
+ if (rel === '' || rel.startsWith('..') || path.isAbsolute(rel)) {
2553
+ return global_root;
2554
+ }
2555
+ return `~/${rel.split(path.sep).join('/')}`;
2556
+ }
2557
+
2558
+ /**
2559
+ * Write `agents/.event4u-bridge.yml`. Skipped under `AGENT_CONFIG_DEV_MODE=1`
2560
+ * and when the project root is the agent-config source repo
2561
+ * (`.agent-src.uncondensed/` present) — same rationale. Atomic write.
2562
+ */
2563
+ function _write_consumer_bridge_marker(
2564
+ project_root: string,
2565
+ installer_version: string,
2566
+ env: NodeJS.ProcessEnv | null = null,
2567
+ now: Date | null = null,
2568
+ ): string | null {
2569
+ const env_map = env ?? process.env;
2570
+ if (env_map['AGENT_CONFIG_DEV_MODE'] === '1') return null;
2571
+ if (isDir(path.join(project_root, '.agent-src.uncondensed'))) return null;
2572
+
2573
+ const global_root_str = _format_global_root_for_marker(
2574
+ user_global_paths.event4u_root(env_map),
2575
+ );
2576
+ const stamp = utcStamp(now ?? undefined);
2577
+
2578
+ const body =
2579
+ '# event4u/agent-config — consumer bridge marker (auto-written).\n' +
2580
+ '# Spec: docs/contracts/consumer-bridge.md (event4u-bridge/v1).\n' +
2581
+ '# Reader contract: expand ~ against the current $HOME; fail closed\n' +
2582
+ '# when global_root is missing on disk; never write back through it.\n' +
2583
+ 'schema: event4u-bridge/v1\n' +
2584
+ `global_root: ${global_root_str}\n` +
2585
+ `installed_at: ${stamp}\n` +
2586
+ `installer_version: ${installer_version}\n`;
2587
+
2588
+ const target = path.join(project_root, CONSUMER_BRIDGE_MARKER_RELPATH);
2589
+ mkdirp(path.dirname(target));
2590
+ atomicWrite0644(target, body, '.event4u-bridge.');
2591
+ return target;
2592
+ }
2593
+
2594
+ const PROJECT_ANCHOR_TOOLS: Record<string, string> = {
2595
+ windsurf: '.windsurf/agent-config.bridge.yml',
2596
+ cline: '.clinerules/agent-config.bridge.yml',
2597
+ 'gemini-cli': '.gemini/agent-config.bridge.yml',
2598
+ };
2599
+
2600
+ /**
2601
+ * Plant thin pointer files for PROJECT_ANCHOR_TOOLS. Same gate as the bridge
2602
+ * marker: skipped under dev mode and inside the agent-config source repo
2603
+ * (`.agent-src.uncondensed/` present). Atomic write per file.
2604
+ */
2605
+ function _write_per_tool_project_anchors(
2606
+ project_root: string,
2607
+ tools: Set<string>,
2608
+ env: NodeJS.ProcessEnv | null = null,
2609
+ now: Date | null = null,
2610
+ ): string[] {
2611
+ const env_map = env ?? process.env;
2612
+ if (env_map['AGENT_CONFIG_DEV_MODE'] === '1') return [];
2613
+ if (isDir(path.join(project_root, '.agent-src.uncondensed'))) return [];
2614
+
2615
+ const global_root_str = _format_global_root_for_marker(
2616
+ user_global_paths.event4u_root(env_map),
2617
+ );
2618
+ const stamp = utcStamp(now ?? undefined);
2619
+ const written: string[] = [];
2620
+
2621
+ for (const tool_id of Object.keys(PROJECT_ANCHOR_TOOLS).sort()) {
2622
+ const rel_path = PROJECT_ANCHOR_TOOLS[tool_id] as string;
2623
+ if (!tools.has(tool_id)) continue;
2624
+ const target = path.join(project_root, rel_path);
2625
+ mkdirp(path.dirname(target));
2626
+
2627
+ const bridge_abs = path.join(project_root, CONSUMER_BRIDGE_MARKER_RELPATH);
2628
+ const bridge_rel = path.relative(path.dirname(target), bridge_abs);
2629
+
2630
+ const body =
2631
+ '# event4u/agent-config — per-tool project anchor (auto-written).\n' +
2632
+ '# Spec: docs/contracts/consumer-bridge.md § Per-tool anchor strategy.\n' +
2633
+ `# Tool: ${tool_id}. Bridge marker: agents/.event4u-bridge.yml.\n` +
2634
+ 'schema: event4u-bridge/v1\n' +
2635
+ `tool: ${tool_id}\n` +
2636
+ `bridge: ${bridge_rel}\n` +
2637
+ `global_root: ${global_root_str}\n` +
2638
+ `installed_at: ${stamp}\n`;
2639
+
2640
+ atomicWrite0644(target, body, '.agent-config.bridge.');
2641
+ written.push(target);
2642
+ }
2643
+
2644
+ return written;
2645
+ }
2646
+
2647
+ const PACKAGE_TAG_ID = 'event4u/agent-config';
2648
+
2649
+ function _inject_package_tag(
2650
+ target: string,
2651
+ source: string | null,
2652
+ package_root: string | null,
2653
+ ): void {
2654
+ if (path.extname(target) !== '.md') return;
2655
+ let text: string;
2656
+ try {
2657
+ text = readText(target);
2658
+ } catch {
2659
+ return;
2660
+ }
2661
+ if (!text.startsWith('---\n') && !text.startsWith('---\r\n')) return;
2662
+ const lines = splitlinesKeepends(text);
2663
+ let close_idx: number | null = null;
2664
+ for (let i = 1; i < lines.length; i += 1) {
2665
+ if ((lines[i] as string).replace(/[\r\n]+$/, '') === '---') {
2666
+ close_idx = i;
2667
+ break;
2668
+ }
2669
+ }
2670
+ if (close_idx === null) return;
2671
+ let fm_lines = lines.slice(1, close_idx);
2672
+ const body_lines = lines.slice(close_idx);
2673
+
2674
+ let source_value: string | null = null;
2675
+ if (source !== null) {
2676
+ let resolved_src: string;
2677
+ try {
2678
+ resolved_src = resolvePath(source);
2679
+ } catch {
2680
+ resolved_src = source;
2681
+ }
2682
+ if (package_root !== null) {
2683
+ const rel = path.relative(resolvePath(package_root), resolved_src);
2684
+ if (rel !== '' && !rel.startsWith('..') && !path.isAbsolute(rel)) {
2685
+ source_value = rel;
2686
+ } else {
2687
+ source_value = resolved_src;
2688
+ }
2689
+ } else {
2690
+ source_value = resolved_src;
2691
+ }
2692
+ }
2693
+
2694
+ const _set_key = (block: string[], key: string, value: string): string[] => {
2695
+ const prefix = `${key}:`;
2696
+ const rendered = `${key}: ${value}\n`;
2697
+ for (let idx = 0; idx < block.length; idx += 1) {
2698
+ if ((block[idx] as string).startsWith(prefix)) {
2699
+ block[idx] = rendered;
2700
+ return block;
2701
+ }
2702
+ }
2703
+ block.push(rendered);
2704
+ return block;
2705
+ };
2706
+
2707
+ fm_lines = _set_key(fm_lines, 'package', PACKAGE_TAG_ID);
2708
+ if (source_value !== null) {
2709
+ fm_lines = _set_key(fm_lines, 'source_path', source_value);
2710
+ }
2711
+ const new_text = [lines[0], ...fm_lines, ...body_lines].join('');
2712
+ if (new_text !== text) {
2713
+ writeText(target, new_text);
2714
+ }
2715
+ }
2716
+
2717
+ function _copy_dir_dereferencing_symlinks(
2718
+ src: string,
2719
+ dest: string,
2720
+ force: boolean,
2721
+ package_root: string | null = null,
2722
+ ): [number, number, string[]] {
2723
+ let written = 0;
2724
+ let skipped = 0;
2725
+ const written_paths: string[] = [];
2726
+ if (!pathExists(src)) return [0, 0, written_paths];
2727
+ if (!isDir(src)) {
2728
+ mkdirp(path.dirname(dest));
2729
+ const decision = _resolve_file_conflict(dest, force);
2730
+ if (decision === 'skip') return [0, 1, written_paths];
2731
+ fs.copyFileSync(src, dest); // follow_symlinks=True is fs.copyFileSync default
2732
+ _inject_package_tag(dest, src, package_root);
2733
+ written_paths.push(dest);
2734
+ return [1, 0, written_paths];
2735
+ }
2736
+ mkdirp(dest);
2737
+ // Python uses src.rglob("*") (os.scandir order, non-deterministic). We use
2738
+ // a sorted depth-first walk to match the sibling inventory twin
2739
+ // (expected_deploy_files) and yield a deterministic manifest files[] order.
2740
+ const walk = (node: string): string[] => {
2741
+ const acc: string[] = [];
2742
+ const names = fs
2743
+ .readdirSync(node)
2744
+ .sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));
2745
+ for (const name of names) {
2746
+ const entry = path.join(node, name);
2747
+ acc.push(entry);
2748
+ const lst = fs.lstatSync(entry);
2749
+ if (lst.isDirectory() && !lst.isSymbolicLink()) {
2750
+ acc.push(...walk(entry));
2751
+ }
2752
+ }
2753
+ return acc;
2754
+ };
2755
+ for (const entry of walk(src)) {
2756
+ const rel = path.relative(src, entry);
2757
+ const target = path.join(dest, rel);
2758
+ const lst = fs.lstatSync(entry);
2759
+ if (lst.isDirectory() && !lst.isSymbolicLink()) {
2760
+ mkdirp(target);
2761
+ continue;
2762
+ }
2763
+ let resolvedIsDir = false;
2764
+ let resolved = entry;
2765
+ try {
2766
+ resolved = fs.realpathSync(entry);
2767
+ resolvedIsDir = fs.statSync(entry).isDirectory();
2768
+ } catch {
2769
+ resolvedIsDir = false;
2770
+ }
2771
+ if (resolvedIsDir) {
2772
+ mkdirp(target);
2773
+ const [sub_w, sub_s, sub_p] = _copy_dir_dereferencing_symlinks(
2774
+ resolved,
2775
+ target,
2776
+ force,
2777
+ package_root,
2778
+ );
2779
+ written += sub_w;
2780
+ skipped += sub_s;
2781
+ written_paths.push(...sub_p);
2782
+ continue;
2783
+ }
2784
+ const decision = _resolve_file_conflict(target, force);
2785
+ if (decision === 'skip') {
2786
+ skipped += 1;
2787
+ continue;
2788
+ }
2789
+ mkdirp(path.dirname(target));
2790
+ fs.copyFileSync(resolved, target);
2791
+ _inject_package_tag(target, resolved, package_root);
2792
+ written += 1;
2793
+ written_paths.push(target);
2794
+ }
2795
+ return [written, skipped, written_paths];
2796
+ }
2797
+
2798
+ function _claude_desktop_bundles_dir(): string {
2799
+ return user_global_paths.write_target(_CLAUDE_DESKTOP_BUNDLES_SUBPATH);
2800
+ }
2801
+
2802
+ function _write_claude_desktop_marker(
2803
+ _force: boolean,
2804
+ lockfile_path: string,
2805
+ bundles_dir: string,
2806
+ bundle_count: number,
2807
+ ): [number, number, string[]] {
2808
+ const anchor = expanduser(USER_SCOPE_PATHS['claude-desktop'] as string);
2809
+ const target = path.join(anchor, 'agent-config.md');
2810
+ mkdirp(anchor);
2811
+ const body = claudeDesktopMarkerBody(lockfile_path, anchor, bundles_dir, bundle_count);
2812
+ writeText(target, body);
2813
+ return [1, 0, [target]];
2814
+ }
2815
+
2816
+ function _deploy_claude_desktop(
2817
+ force: boolean,
2818
+ package_root: string,
2819
+ lockfile_path: string,
2820
+ ): DeployResult {
2821
+ const bundles_dir = _claude_desktop_bundles_dir();
2822
+ claude_desktop_bundler.build_skill_bundles(package_root, bundles_dir, force);
2823
+ claude_desktop_bundler.build_command_bundles(package_root, bundles_dir, force);
2824
+ const bundle_count = countZips(bundles_dir);
2825
+ const [, , marker_paths] = _write_claude_desktop_marker(
2826
+ force,
2827
+ lockfile_path,
2828
+ bundles_dir,
2829
+ bundle_count,
2830
+ );
2831
+ return [bundle_count, 0, 'deployed', [bundles_dir, ...marker_paths]];
2832
+ }
2833
+
2834
+ function _deploy_global_content(
2835
+ tools: Set<string>,
2836
+ force: boolean,
2837
+ package_root: string,
2838
+ lockfile_path: string,
2839
+ ): Record<string, DeployResult> {
2840
+ const results: Record<string, DeployResult> = {};
2841
+ for (const tool_id of [...tools].sort()) {
2842
+ if (tool_id === 'claude-desktop') {
2843
+ results[tool_id] = _deploy_claude_desktop(force, package_root, lockfile_path);
2844
+ continue;
2845
+ }
2846
+ const plan = GLOBAL_DEPLOY_SOURCES[tool_id];
2847
+ if (plan === undefined) {
2848
+ const status = ['copilot', 'aider', 'zed', 'jetbrains'].includes(tool_id)
2849
+ ? 'hint'
2850
+ : 'unsupported';
2851
+ results[tool_id] = [0, 0, status, []];
2852
+ continue;
2853
+ }
2854
+ const anchor_raw = USER_SCOPE_PATHS[tool_id];
2855
+ if (!anchor_raw) {
2856
+ results[tool_id] = [0, 0, 'unsupported', []];
2857
+ continue;
2858
+ }
2859
+ const anchor = expanduser(anchor_raw);
2860
+ let written_total = 0;
2861
+ let skipped_total = 0;
2862
+ const written_paths: string[] = [];
2863
+ let current_files = new Set<string>();
2864
+ for (const [src_rel, dest_sub] of plan) {
2865
+ const src = path.join(package_root, src_rel);
2866
+ const dest = dest_sub ? path.join(anchor, dest_sub) : anchor;
2867
+ const [w, s, paths] = _copy_dir_dereferencing_symlinks(src, dest, force, package_root);
2868
+ written_total += w;
2869
+ skipped_total += s;
2870
+ written_paths.push(...paths);
2871
+ current_files = setUnion(
2872
+ current_files,
2873
+ global_deploy_inventory.expected_deploy_files(src, dest_sub ? dest_sub : ''),
2874
+ );
2875
+ }
2876
+ const missing_targets = _verify_deploy_targets(anchor, plan);
2877
+ if (missing_targets.length > 0) {
2878
+ if (!state.QUIET) {
2879
+ warn(
2880
+ `${tool_id}: deploy postcheck failed — ` +
2881
+ `missing/empty: ${missing_targets.join(', ')}`,
2882
+ );
2883
+ }
2884
+ _emit_progress({ type: 'verify_failed', tool: tool_id, missing: missing_targets });
2885
+ results[tool_id] = [written_total, skipped_total, 'deploy_failed', written_paths];
2886
+ continue;
2887
+ }
2888
+ _emit_progress({ type: 'verified', tool: tool_id });
2889
+
2890
+ const inventory = global_deploy_inventory.load_inventory();
2891
+ let reaped: string[] = [];
2892
+ const inv_tools = (inventory['tools'] as Record<string, unknown> | undefined) ?? {};
2893
+ if (tool_id in inv_tools) {
2894
+ reaped = reaped.concat(
2895
+ global_deploy_inventory.reap_stale(tool_id, anchor, current_files, inventory),
2896
+ );
2897
+ }
2898
+ reaped = reaped.concat(
2899
+ global_deploy_inventory.reap_tagged_orphans(
2900
+ anchor,
2901
+ plan.map(([, dest_sub]) => dest_sub),
2902
+ current_files,
2903
+ PACKAGE_TAG_ID,
2904
+ ),
2905
+ );
2906
+ reaped = [...new Set(reaped)].sort();
2907
+ global_deploy_inventory.record_deploy(tool_id, anchor_raw, current_files, inventory);
2908
+ global_deploy_inventory.save_inventory(inventory);
2909
+ if (reaped.length > 0 && !state.QUIET) {
2910
+ info(
2911
+ ` ${tool_id}: reaped ${reaped.length} stale deployed file(s) ` +
2912
+ 'from a previous install',
2913
+ );
2914
+ }
2915
+ _emit_progress({ type: 'reaped', tool: tool_id, count: reaped.length });
2916
+ results[tool_id] = [written_total, skipped_total, 'deployed', written_paths];
2917
+ }
2918
+ return results;
2919
+ }
2920
+
2921
+ function setUnion(a: Set<string>, b: Set<string>): Set<string> {
2922
+ const out = new Set(a);
2923
+ for (const v of b) out.add(v);
2924
+ return out;
2925
+ }
2926
+
2927
+ function _preview_global_reap(
2928
+ tools: Set<string>,
2929
+ package_root: string,
2930
+ ): Record<string, string[]> {
2931
+ const inventory = global_deploy_inventory.load_inventory();
2932
+ const preview: Record<string, string[]> = {};
2933
+ for (const tool_id of [...tools].sort()) {
2934
+ const plan = GLOBAL_DEPLOY_SOURCES[tool_id];
2935
+ if (plan === undefined) continue;
2936
+ const anchor_raw = USER_SCOPE_PATHS[tool_id];
2937
+ if (!anchor_raw) continue;
2938
+ const anchor = expanduser(anchor_raw);
2939
+ let current_files = new Set<string>();
2940
+ for (const [src_rel, dest_sub] of plan) {
2941
+ const src = path.join(package_root, src_rel);
2942
+ current_files = setUnion(
2943
+ current_files,
2944
+ global_deploy_inventory.expected_deploy_files(src, dest_sub ? dest_sub : ''),
2945
+ );
2946
+ }
2947
+ let would_reap: string[] = [];
2948
+ const inv_tools = (inventory['tools'] as Record<string, unknown> | undefined) ?? {};
2949
+ if (tool_id in inv_tools) {
2950
+ would_reap = would_reap.concat(
2951
+ global_deploy_inventory.reap_stale(tool_id, anchor, current_files, inventory, true),
2952
+ );
2953
+ }
2954
+ would_reap = would_reap.concat(
2955
+ global_deploy_inventory.reap_tagged_orphans(
2956
+ anchor,
2957
+ plan.map(([, dest_sub]) => dest_sub),
2958
+ current_files,
2959
+ PACKAGE_TAG_ID,
2960
+ true,
2961
+ ),
2962
+ );
2963
+ const paths = [...new Set(would_reap.map((p) => String(p)))].sort();
2964
+ if (paths.length > 0) preview[tool_id] = paths;
2965
+ }
2966
+ return preview;
2967
+ }
2968
+
2969
+ function _verify_deploy_targets(anchor: string, plan: ReadonlyArray<readonly [string, string]>): string[] {
2970
+ const missing: string[] = [];
2971
+ for (const [, dest_sub] of plan) {
2972
+ const target = dest_sub ? path.join(anchor, dest_sub) : anchor;
2973
+ const label = dest_sub || '.';
2974
+ if (!isDir(target)) {
2975
+ missing.push(label);
2976
+ continue;
2977
+ }
2978
+ try {
2979
+ const entries = fs.readdirSync(target);
2980
+ if (entries.length === 0) missing.push(label);
2981
+ } catch {
2982
+ missing.push(label);
2983
+ }
2984
+ }
2985
+ return missing;
2986
+ }
2987
+
2988
+ /**
2989
+ * Remove lab-tier skills/commands from a completed deploy (core-only).
2990
+ *
2991
+ * road-to-install-contract-stability Phase 2 Step 2. Skills are pruned by
2992
+ * whole directory (tier decided by the skill's `SKILL.md` frontmatter);
2993
+ * commands by file (own frontmatter). Rules / personas / contexts / templates
2994
+ * are core/shared and left intact. Returns `[pruned_count, adjusted_results]`
2995
+ * with the lab paths removed from each tool's `written_paths` so the manifest
2996
+ * never records them.
2997
+ */
2998
+ function _prune_lab_modules(
2999
+ deploy_results: Record<string, DeployResult>,
3000
+ lab_ids: Set<string>,
3001
+ ): [number, Record<string, DeployResult>] {
3002
+ let pruned = 0;
3003
+ const adjusted: Record<string, DeployResult> = {};
3004
+ for (const tool_id of Object.keys(deploy_results)) {
3005
+ const [written, skipped, status, paths] = deploy_results[tool_id] as DeployResult;
3006
+ const lab_skill_dirs = new Set<string>();
3007
+ for (const p of paths) {
3008
+ const parts = p.split(path.sep);
3009
+ if (parts.includes('skills')) {
3010
+ const i = parts.indexOf('skills');
3011
+ if (i + 1 < parts.length) {
3012
+ const skill_root = parts.slice(0, i + 2).join(path.sep);
3013
+ if (!lab_skill_dirs.has(skill_root)) {
3014
+ const skillmd = path.join(skill_root, 'SKILL.md');
3015
+ if (pathExists(skillmd) && surface_tiers.is_lab_artefact(skillmd, lab_ids)) {
3016
+ lab_skill_dirs.add(skill_root);
3017
+ }
3018
+ }
3019
+ }
3020
+ }
3021
+ }
3022
+ const keep: string[] = [];
3023
+ const delete_files: string[] = [];
3024
+ for (const p of paths) {
3025
+ const parts = p.split(path.sep);
3026
+ let is_lab = false;
3027
+ if (parts.includes('skills')) {
3028
+ const i = parts.indexOf('skills');
3029
+ if (i + 1 < parts.length && lab_skill_dirs.has(parts.slice(0, i + 2).join(path.sep))) {
3030
+ is_lab = true;
3031
+ }
3032
+ } else if (
3033
+ parts.includes('commands') &&
3034
+ path.extname(p) === '.md' &&
3035
+ surface_tiers.is_lab_artefact(p, lab_ids)
3036
+ ) {
3037
+ is_lab = true;
3038
+ }
3039
+ (is_lab ? delete_files : keep).push(p);
3040
+ }
3041
+ for (const d of lab_skill_dirs) {
3042
+ fs.rmSync(d, { recursive: true, force: true });
3043
+ }
3044
+ for (const p of delete_files) {
3045
+ if (p.split(path.sep).includes('commands') && pathExists(p)) {
3046
+ try {
3047
+ fs.unlinkSync(p);
3048
+ } catch {
3049
+ // OSError → swallow, mirroring the .py.
3050
+ }
3051
+ }
3052
+ }
3053
+ pruned += delete_files.length;
3054
+ adjusted[tool_id] = [Math.max(0, written - delete_files.length), skipped, status, keep];
3055
+ }
3056
+ return [pruned, adjusted];
3057
+ }
3058
+
3059
+ function install_global(
3060
+ tools: Set<string>,
3061
+ force: boolean,
3062
+ project_root: string | null = null,
3063
+ core_only = false,
3064
+ ): number {
3065
+ const migrated = user_global_paths.migrate_legacy_namespace();
3066
+ if (migrated && !state.QUIET) {
3067
+ info(
3068
+ '🔁 Migrated user-global config to ' +
3069
+ `${user_global_paths.event4u_root()} (legacy ` +
3070
+ `${user_global_paths.legacy_xdg_root()} preserved as fallback)`,
3071
+ );
3072
+ }
3073
+
3074
+ const installed_version = installed_lock.current_package_version();
3075
+ const read_path = installed_lock.lockfile_path();
3076
+ const write_path = installed_lock.lockfile_write_path();
3077
+ const [, recorded] = installed_lock.check_version(installed_version, { path: read_path });
3078
+ const classification = installed_lock.classify_mismatch(installed_version, recorded);
3079
+
3080
+ if (classification === 'downgrade' && !force) {
3081
+ if (!state.QUIET) {
3082
+ process.stdout.write('\n');
3083
+ warn('Refusing global install: lockfile records a newer version.');
3084
+ info(` Lockfile: ${read_path}`);
3085
+ info(` Recorded version: ${recorded}`);
3086
+ info(` Current package: ${installed_version}`);
3087
+ info(' Fix: upgrade the package, or re-run with `--force`');
3088
+ process.stdout.write('\n');
3089
+ }
3090
+ return 1;
3091
+ }
3092
+
3093
+ if (['upgrade', 'unparseable'].includes(classification) && !state.QUIET) {
3094
+ info(`🔄 Upgrading lockfile from ${recorded} to ${installed_version}, redeploying tools`);
3095
+ }
3096
+
3097
+ // Install-ABI migration (road-to-install-contract-stability Phase 1.5):
3098
+ // an installed tree at install_layout_version < current (absent =
3099
+ // pre-freeze v0) is migrated in place before the deploy, surgical-uninstall
3100
+ // pointers preserved. At the freeze baseline this only stamps the version;
3101
+ // future layout versions add concrete shape transforms in migrate_layout.
3102
+ const migration = installed_lock.migrate_layout({ path: write_path });
3103
+ if (migration && migration.changed.length > 0 && !state.QUIET) {
3104
+ info(
3105
+ '🔧 Migrated install layout ' +
3106
+ `v${migration.from} → v${migration.to}: ` +
3107
+ migration.changed.join('; '),
3108
+ );
3109
+ }
3110
+
3111
+ if (!state.QUIET) {
3112
+ process.stdout.write('\n');
3113
+ info('Agent Config — Global (user-scope) install [ADR-007]');
3114
+ info('Per-tool anchor paths:');
3115
+ for (const tool_id of [...tools].sort()) {
3116
+ const anchor = USER_SCOPE_PATHS[tool_id];
3117
+ if (anchor === undefined) continue;
3118
+ process.stdout.write(` ${tool_id.padEnd(15)} → ${anchor}\n`);
3119
+ }
3120
+ }
3121
+
3122
+ const existing = installed_lock.read_lockfile(read_path) ?? {};
3123
+ const existing_tools = Array.isArray((existing as Record<string, unknown>)['tools'])
3124
+ ? ((existing as Record<string, unknown>)['tools'] as string[])
3125
+ : [];
3126
+ const merged_tools = [...new Set([...existing_tools, ...tools])].sort();
3127
+ const written = installed_lock.write_lockfile(installed_version, merged_tools, { path: write_path });
3128
+
3129
+ if (!state.QUIET) {
3130
+ process.stdout.write('\n');
3131
+ info(`Lockfile written: ${written}`);
3132
+ info(` schema_version=1, agent_config_version=${installed_version}`);
3133
+ info(` tools=${merged_tools.join(',')}`);
3134
+ }
3135
+
3136
+ const package_root = _resolve_package_root_for_global();
3137
+ let deploy_results = _deploy_global_content(tools, force, package_root, written);
3138
+
3139
+ // Core-only mode (road-to-install-contract-stability Phase 2 Step 2): prune
3140
+ // lab-tier skills/commands from the just-deployed tree so the installed
3141
+ // surface carries zero lab modules. Runs after the deploy postcheck so the
3142
+ // prune operates on a verified-complete tree; the adjusted results feed the
3143
+ // manifest so lab paths are never recorded.
3144
+ if (core_only) {
3145
+ const lab_ids = surface_tiers.load_lab_pack_ids(package_root);
3146
+ let pruned: number;
3147
+ [pruned, deploy_results] = _prune_lab_modules(deploy_results, lab_ids);
3148
+ if (!state.QUIET) {
3149
+ info(
3150
+ `🧹 Core-only install: pruned ${pruned} lab-tier artefact(s) ` +
3151
+ `(packs: ${[...lab_ids].sort().join(', ')}).`,
3152
+ );
3153
+ }
3154
+ }
3155
+
3156
+ const failed_tools = new Set<string>(
3157
+ Object.keys(deploy_results).filter(
3158
+ (tool_id) => (deploy_results[tool_id] as DeployResult)[2] === 'deploy_failed',
3159
+ ),
3160
+ );
3161
+ if (failed_tools.size > 0) {
3162
+ const corrected_tools = merged_tools.filter((t) => !failed_tools.has(t));
3163
+ if (!arrayStrEqual(corrected_tools, merged_tools)) {
3164
+ installed_lock.write_lockfile(installed_version, corrected_tools, { path: write_path });
3165
+ if (!state.QUIET) {
3166
+ warn(
3167
+ 'Lockfile corrected after deploy postcheck — dropped ' +
3168
+ `${[...failed_tools].sort().join(', ')} (verification failed).`,
3169
+ );
3170
+ }
3171
+ }
3172
+ }
3173
+
3174
+ if (state.PROGRESS_NDJSON) {
3175
+ const ordered = Object.keys(deploy_results).sort();
3176
+ const total = ordered.length;
3177
+ ordered.forEach((tool_id, i) => {
3178
+ const [, , status] = deploy_results[tool_id] as DeployResult;
3179
+ _emit_progress({
3180
+ type: 'file',
3181
+ file: tool_id,
3182
+ status,
3183
+ written: i + 1,
3184
+ total,
3185
+ });
3186
+ });
3187
+ }
3188
+
3189
+ if (!state.QUIET) {
3190
+ process.stdout.write('\n');
3191
+ info('Deployed per-tool content:');
3192
+ for (const tool_id of Object.keys(deploy_results).sort()) {
3193
+ const [w, s, status] = deploy_results[tool_id] as DeployResult;
3194
+ const anchor = USER_SCOPE_PATHS[tool_id] ?? '';
3195
+ if (status === 'deployed' && tool_id === 'claude-desktop') {
3196
+ const bundles_dir = _claude_desktop_bundles_dir();
3197
+ process.stdout.write(` ${tool_id.padEnd(15)} → ${bundles_dir} (${w} bundles)\n`);
3198
+ } else if (status === 'deployed') {
3199
+ process.stdout.write(` ${tool_id.padEnd(15)} → ${anchor} (${w} files, ${s} skipped)\n`);
3200
+ } else if (status === 'marker') {
3201
+ process.stdout.write(
3202
+ ` ${tool_id.padEnd(15)} → ${anchor}agent-config.md (${w ? 'written' : 'skipped'})\n`,
3203
+ );
3204
+ } else if (status === 'hint') {
3205
+ process.stdout.write(
3206
+ ` ${tool_id.padEnd(15)} → no user-scope convention; use \`agent-config export --tool=${tool_id}\`\n`,
3207
+ );
3208
+ } else {
3209
+ process.stdout.write(
3210
+ ` ${tool_id.padEnd(15)} → no global-scope content yet (project-scope install supported)\n`,
3211
+ );
3212
+ }
3213
+ }
3214
+ }
3215
+
3216
+ // Skipped inside the agent-config source repo (detected by
3217
+ // `.agent-src.uncondensed/`) — maintainers dogfood with their own
3218
+ // `.agent-settings.yml` and the manifest would be untracked noise.
3219
+ if (
3220
+ project_root !== null &&
3221
+ pathExists(_resolve_settings_read(project_root)) &&
3222
+ !isDir(path.join(project_root, '.agent-src.uncondensed'))
3223
+ ) {
3224
+ const files_by_tool = _files_by_tool_from_deploy(deploy_results);
3225
+ const rc = _update_installed_tools_manifest(project_root, tools, 'global', force, files_by_tool);
3226
+ if (rc !== 0) return rc;
3227
+
3228
+ // Consumer bridge marker (Phase 4.2). The surrounding
3229
+ // `.agent-src.uncondensed` guard already covers the source-repo case;
3230
+ // the dev-mode skip is enforced inside the writer.
3231
+ const marker_path = _write_consumer_bridge_marker(project_root, installed_version);
3232
+ if (marker_path !== null && !state.QUIET) {
3233
+ const rel = isRelativeTo(marker_path, project_root)
3234
+ ? path.relative(project_root, marker_path)
3235
+ : marker_path;
3236
+ info(`Bridge marker written: ${rel}`);
3237
+ }
3238
+
3239
+ const anchor_paths = _write_per_tool_project_anchors(project_root, tools);
3240
+ if (anchor_paths.length > 0 && !state.QUIET) {
3241
+ for (const p of anchor_paths) {
3242
+ const rel = isRelativeTo(p, project_root) ? path.relative(project_root, p) : p;
3243
+ info(`Project anchor written: ${rel}`);
3244
+ }
3245
+ }
3246
+ }
3247
+
3248
+ if (!state.QUIET) {
3249
+ process.stdout.write('\n');
3250
+ success('Global install completed.');
3251
+ process.stdout.write('\n');
3252
+ }
3253
+ return 0;
3254
+ }
3255
+
3256
+ function arrayStrEqual(a: string[], b: string[]): boolean {
3257
+ return a.length === b.length && a.every((v, i) => v === b[i]);
3258
+ }
3259
+
3260
+ // --- Argument parsing ---
3261
+
3262
+ function _merge_tools_aliases(tools: string | null, ai: string | null): string {
3263
+ const items: string[] = [];
3264
+ for (const raw of [tools, ai]) {
3265
+ if (!raw) continue;
3266
+ for (const piece of raw.split(',')) {
3267
+ const stripped = piece.trim();
3268
+ if (stripped && !items.includes(stripped)) items.push(stripped);
3269
+ }
3270
+ }
3271
+ return items.length > 0 ? items.join(',') : 'all';
3272
+ }
3273
+
3274
+ const PROG = 'install.py';
3275
+ // Verbatim argparse usage block at COLUMNS=80 (captured from the .py). The
3276
+ // `--help` BODY (per-flag descriptions) is a documented divergence — argparse
3277
+ // re-wraps it to the live terminal width, which is not worth reproducing; the
3278
+ // golden tests assert the `usage:` token + exit code, not the body prose.
3279
+ const USAGE =
3280
+ `usage: ${PROG} [-h] [--profile PROFILE] [--user-type USER_TYPE] [--force]\n` +
3281
+ ' [--skip-bridges] [--augment-user-hooks]\n' +
3282
+ ' [--cursor-user-hooks] [--cline-user-hooks]\n' +
3283
+ ' [--windsurf-user-hooks] [--gemini-user-hooks]\n' +
3284
+ ' [--project PROJECT] [--package PACKAGE] [--quiet]\n' +
3285
+ ' [--tools TOOLS] [--ai AI] [--packs PACKS] [--core-only]\n' +
3286
+ ' [--no-smoke] [--global]\n' +
3287
+ ' [--scope {project,global,prompt,auto}]\n' +
3288
+ ' [--custom-path CUSTOM_PATH] [--offline] [--minimal]\n' +
3289
+ ' [--interactive] [--no-ui] [--dry-run]\n' +
3290
+ ' [--apply-payload APPLY_PAYLOAD]\n';
3291
+
3292
+ const _STORE_TRUE_FLAGS: Record<string, keyof Options> = {
3293
+ '--force': 'force',
3294
+ '--skip-bridges': 'skip_bridges',
3295
+ '--augment-user-hooks': 'augment_user_hooks',
3296
+ '--cursor-user-hooks': 'cursor_user_hooks',
3297
+ '--cline-user-hooks': 'cline_user_hooks',
3298
+ '--windsurf-user-hooks': 'windsurf_user_hooks',
3299
+ '--gemini-user-hooks': 'gemini_user_hooks',
3300
+ '--quiet': 'quiet',
3301
+ '--core-only': 'core_only',
3302
+ '--no-smoke': 'no_smoke',
3303
+ '--global': 'global_install',
3304
+ '--offline': 'offline',
3305
+ '--minimal': 'minimal',
3306
+ '--settings-only': 'minimal',
3307
+ '--interactive': 'interactive',
3308
+ '--no-ui': 'no_ui',
3309
+ '--dry-run': 'dry_run',
3310
+ };
3311
+
3312
+ const _VALUE_FLAGS: Record<string, keyof Options> = {
3313
+ '--profile': 'profile',
3314
+ '--user-type': 'user_type',
3315
+ '--project': 'project',
3316
+ '--package': 'package',
3317
+ '--tools': 'tools',
3318
+ '--ai': 'ai',
3319
+ '--packs': 'packs',
3320
+ '--scope': 'scope',
3321
+ '--custom-path': 'custom_path',
3322
+ '--apply-payload': 'apply_payload',
3323
+ };
3324
+
3325
+ function _argError(msg: string): never {
3326
+ process.stderr.write(USAGE);
3327
+ process.stderr.write(`${PROG}: error: ${msg}\n`);
3328
+ throw new ArgparseExit(2);
3329
+ }
3330
+
3331
+ function parse_options(argv: string[]): Options {
3332
+ const opts: Record<string, unknown> = {
3333
+ profile: DEFAULT_PROFILE,
3334
+ user_type: '',
3335
+ force: false,
3336
+ skip_bridges: false,
3337
+ augment_user_hooks: false,
3338
+ cursor_user_hooks: false,
3339
+ cline_user_hooks: false,
3340
+ windsurf_user_hooks: false,
3341
+ gemini_user_hooks: false,
3342
+ project: null,
3343
+ package: null,
3344
+ quiet: false,
3345
+ tools: null,
3346
+ ai: null,
3347
+ packs: null,
3348
+ core_only: false,
3349
+ no_smoke: false,
3350
+ global_install: false,
3351
+ scope: null,
3352
+ custom_path: null,
3353
+ offline: false,
3354
+ minimal: false,
3355
+ interactive: false,
3356
+ no_ui: false,
3357
+ dry_run: false,
3358
+ apply_payload: null,
3359
+ };
3360
+
3361
+ const positionals: string[] = [];
3362
+ let i = 0;
3363
+ while (i < argv.length) {
3364
+ const a = argv[i] as string;
3365
+ if (a === '-h' || a === '--help') {
3366
+ process.stdout.write(USAGE);
3367
+ throw new ArgparseExit(0);
3368
+ }
3369
+ // --flag=value form
3370
+ const eq = a.startsWith('--') ? a.indexOf('=') : -1;
3371
+ const flag = eq >= 0 ? a.slice(0, eq) : a;
3372
+ const inlineVal = eq >= 0 ? a.slice(eq + 1) : null;
3373
+
3374
+ const storeTrueDest = _STORE_TRUE_FLAGS[flag];
3375
+ if (storeTrueDest !== undefined) {
3376
+ if (inlineVal !== null) {
3377
+ _argError(`argument ${flag}: ignored explicit argument '${inlineVal}'`);
3378
+ }
3379
+ opts[storeTrueDest] = true;
3380
+ i += 1;
3381
+ continue;
3382
+ }
3383
+ const valueDest = _VALUE_FLAGS[flag];
3384
+ if (valueDest !== undefined) {
3385
+ let value: string;
3386
+ if (inlineVal !== null) {
3387
+ value = inlineVal;
3388
+ } else {
3389
+ if (i + 1 >= argv.length) _argError(`argument ${flag}: expected one argument`);
3390
+ value = argv[i + 1] as string;
3391
+ i += 1;
3392
+ }
3393
+ if (flag === '--scope' && !['project', 'global', 'prompt', 'auto'].includes(value)) {
3394
+ _argError(
3395
+ `argument --scope: invalid choice: '${value}' ` +
3396
+ "(choose from 'project', 'global', 'prompt', 'auto')",
3397
+ );
3398
+ }
3399
+ opts[valueDest] = value;
3400
+ i += 1;
3401
+ continue;
3402
+ }
3403
+ if (a.startsWith('-') && a !== '-') {
3404
+ _argError(`unrecognized arguments: ${a}`);
3405
+ }
3406
+ positionals.push(a);
3407
+ i += 1;
3408
+ }
3409
+ if (positionals.length > 0) {
3410
+ _argError(`unrecognized arguments: ${positionals.join(' ')}`);
3411
+ }
3412
+
3413
+ opts.tools = _merge_tools_aliases(opts.tools as string | null, opts.ai as string | null);
3414
+ const rawPacks = opts.packs;
3415
+ opts.packs =
3416
+ typeof rawPacks === 'string'
3417
+ ? rawPacks
3418
+ .split(',')
3419
+ .map((p) => p.trim())
3420
+ .filter((p) => p)
3421
+ : [];
3422
+ if (opts.scope === 'global' && opts.custom_path) {
3423
+ fail('--custom-path is incompatible with --scope=global');
3424
+ }
3425
+ if (opts.global_install && opts.custom_path) {
3426
+ fail('--custom-path is incompatible with --global');
3427
+ }
3428
+ if (opts.scope !== null && opts.global_install && opts.scope !== 'global') {
3429
+ fail(`--scope=${opts.scope} conflicts with --global; pick one`);
3430
+ }
3431
+ return opts as unknown as Options;
3432
+ }
3433
+
3434
+ const _VALID_TOOLS: ReadonlySet<string> = new Set([
3435
+ 'claude-code',
3436
+ 'claude-desktop',
3437
+ 'cursor',
3438
+ 'windsurf',
3439
+ 'cline',
3440
+ 'gemini-cli',
3441
+ 'copilot',
3442
+ 'augment',
3443
+ 'aider',
3444
+ 'codex',
3445
+ 'roocode',
3446
+ 'continue',
3447
+ 'kilocode',
3448
+ 'zed',
3449
+ 'jetbrains',
3450
+ 'kiro',
3451
+ 'qoder',
3452
+ 'opencode',
3453
+ 'trae',
3454
+ 'antigravity',
3455
+ 'codebuddy',
3456
+ 'droid',
3457
+ 'warp',
3458
+ 'all',
3459
+ ]);
3460
+
3461
+ function _parse_tools(raw: string): Set<string> {
3462
+ if (!raw || !raw.trim()) fail('--tools requires a non-empty value');
3463
+ const items = raw.split(',').map((s) => s.trim()).filter((s) => s);
3464
+ if (items.length === 0) fail('--tools requires at least one ID');
3465
+ const unknown = items.filter((s) => !_VALID_TOOLS.has(s));
3466
+ if (unknown.length > 0) {
3467
+ fail(
3468
+ `--tools: unknown ID(s): ${unknown.join(', ')} ` +
3469
+ `(valid: ${[..._VALID_TOOLS].sort().join(', ')})`,
3470
+ );
3471
+ }
3472
+ if (items.includes('all')) {
3473
+ return new Set([..._VALID_TOOLS].filter((t) => t !== 'all'));
3474
+ }
3475
+ return new Set(items);
3476
+ }
3477
+
3478
+ function _tools_was_all(raw: string): boolean {
3479
+ if (!raw || !raw.trim()) return false;
3480
+ const items = raw.split(',').map((s) => s.trim()).filter((s) => s);
3481
+ return items.includes('all');
3482
+ }
3483
+
3484
+ function _is_tool_enabled(tools: Set<string>, tool_id: string): boolean {
3485
+ return tools.has(tool_id);
3486
+ }
3487
+
3488
+ // --- Minimal init ---
3489
+
3490
+ function _minimal_templates_root(): string {
3491
+ const start = resolvePath(_HERE);
3492
+ const chain = [start];
3493
+ let cur = start;
3494
+ for (;;) {
3495
+ const parent = path.dirname(cur);
3496
+ if (parent === cur) break;
3497
+ chain.push(parent);
3498
+ cur = parent;
3499
+ }
3500
+ for (const ancestor of chain) {
3501
+ const candidate = path.join(ancestor, 'src', 'templates', 'minimal');
3502
+ if (isDir(candidate)) return candidate;
3503
+ }
3504
+ fail('Could not locate src/templates/minimal/ — package install is corrupt.');
3505
+ }
3506
+
3507
+ const INSTALL_MODE_MARKER_REL = 'agents/.agent-state/install-mode.txt';
3508
+
3509
+ function _write_install_mode_marker(project_root: string, mode: string): void {
3510
+ if (mode !== 'minimal' && mode !== 'full') return;
3511
+ const marker = path.join(project_root, INSTALL_MODE_MARKER_REL);
3512
+ try {
3513
+ mkdirp(path.dirname(marker));
3514
+ writeText(marker, `${mode}\n`);
3515
+ } catch {
3516
+ /* advisory marker; never abort */
3517
+ }
3518
+ }
3519
+
3520
+ function install_minimal(target_root_in: string, force: boolean, user_type: string = ''): number {
3521
+ let target_root = resolvePath(target_root_in);
3522
+ mkdirp(target_root);
3523
+
3524
+ const parent = path.dirname(target_root);
3525
+ if (parent !== target_root) {
3526
+ const existing = find_project_root_with_anchor(parent);
3527
+ if (existing !== null && existing[0] !== target_root) {
3528
+ const [root, anchor] = existing;
3529
+ fail(
3530
+ 'Refusing to nest an agent-config layer inside an existing ' +
3531
+ `project (anchor: ${anchor}). Existing root: ${root}. ` +
3532
+ 'Remove the parent layer first or run `--minimal` outside it.',
3533
+ );
3534
+ }
3535
+ }
3536
+
3537
+ const templates = _minimal_templates_root();
3538
+ const settings_src = path.join(templates, SETTINGS_FILE);
3539
+ const overrides_gitkeep_src = path.join(templates, 'overrides-gitkeep');
3540
+ const overrides_readme_src = path.join(templates, 'agents-overrides-readme.md');
3541
+
3542
+ if (!isFile(settings_src)) fail(`Bundled minimal settings template missing under ${templates}`);
3543
+ if (!isFile(overrides_gitkeep_src) || !isFile(overrides_readme_src)) {
3544
+ fail(`Bundled overrides scaffold templates missing under ${templates}`);
3545
+ }
3546
+
3547
+ info(`Minimal init → ${target_root}`);
3548
+
3549
+ const overrides_root = path.join(target_root, 'agents', 'overrides');
3550
+ mkdirp(overrides_root);
3551
+ const gitkeep_body = readText(overrides_gitkeep_src);
3552
+ for (const sub of ['rules', 'skills', 'commands']) {
3553
+ const sub_dir = path.join(overrides_root, sub);
3554
+ mkdirp(sub_dir);
3555
+ const gitkeep_dst = path.join(sub_dir, '.gitkeep');
3556
+ if (pathExists(gitkeep_dst) && !force) {
3557
+ skip(`agents/overrides/${sub}/.gitkeep already exists (use --force to overwrite)`);
3558
+ } else {
3559
+ writeText(gitkeep_dst, gitkeep_body);
3560
+ success(`Wrote agents/overrides/${sub}/.gitkeep`);
3561
+ }
3562
+ }
3563
+
3564
+ const readme_dst = path.join(overrides_root, 'README.md');
3565
+ if (pathExists(readme_dst) && !force) {
3566
+ skip('agents/overrides/README.md already exists (use --force to overwrite)');
3567
+ } else {
3568
+ writeText(readme_dst, readText(overrides_readme_src));
3569
+ success('Wrote agents/overrides/README.md');
3570
+ }
3571
+
3572
+ if (user_type) {
3573
+ const settings_dst = _canonical_settings_target(target_root);
3574
+ if (pathExists(settings_dst) && !force) {
3575
+ skip(`${SETTINGS_FILE} already exists (use --force to overwrite)`);
3576
+ } else {
3577
+ const body =
3578
+ readText(settings_src).replace(/\s+$/, '') +
3579
+ '\n\n# --- Personal (step-9 user-type axis) ---\n' +
3580
+ 'personal:\n' +
3581
+ ` user_type: ${user_type}\n`;
3582
+ mkdirp(path.dirname(settings_dst));
3583
+ writeText(settings_dst, body);
3584
+ success(`Wrote ${SETTINGS_FILE} (user_type=${user_type})`);
3585
+ }
3586
+ }
3587
+
3588
+ const installed_version = installed_lock.current_package_version();
3589
+ const marker_path = _write_consumer_bridge_marker(target_root, installed_version);
3590
+ if (marker_path !== null) {
3591
+ const rel = isRelativeTo(marker_path, target_root)
3592
+ ? path.relative(target_root, marker_path)
3593
+ : marker_path;
3594
+ success(`Wrote ${rel}`);
3595
+ }
3596
+
3597
+ _write_install_mode_marker(target_root, 'minimal');
3598
+
3599
+ if (!state.QUIET) {
3600
+ process.stderr.write(
3601
+ 'ℹ️ Minimal install — run `agent-config install --force` ' +
3602
+ 'to add AGENTS.md, bridges, and tool integrations.\n',
3603
+ );
3604
+ }
3605
+
3606
+ if (!state.QUIET) {
3607
+ process.stdout.write('\n');
3608
+ info('Next steps:');
3609
+ info(' • Ensure `agent-config` is on $PATH: npm install -g @event4u/agent-config');
3610
+ info(' • Drop project-scoped overrides under `agents/overrides/{rules,skills,commands}/`.');
3611
+ info(' • Run `agent-config doctor` to verify the layer is picked up.');
3612
+ }
3613
+ return 0;
3614
+ }
3615
+
3616
+ // --- Interactive init ---
3617
+
3618
+ const _INTERACTIVE_USER_TYPES: ReadonlyArray<readonly [string, string]> = [
3619
+ ['creator', 'Content / writing / publishing'],
3620
+ ['founder', 'Early-stage company building'],
3621
+ ['consultant', 'Advisory / strategy / discovery'],
3622
+ ['gtm', 'Sales / marketing / revenue ops'],
3623
+ ['finance', 'Finance / FP&A / unit economics'],
3624
+ ['ops', 'Operations / incident / compliance'],
3625
+ ['developer', 'Engineering / code-heavy work'],
3626
+ ];
3627
+ const _INTERACTIVE_STACKS: ReadonlyArray<readonly [string, string]> = [
3628
+ ['none', 'No code project / pure content'],
3629
+ ['laravel', 'PHP / Laravel'],
3630
+ ['nextjs', 'TypeScript / Next.js / React'],
3631
+ ['python', 'Python / FastAPI / Django'],
3632
+ ['symfony', 'PHP / Symfony'],
3633
+ ['generic', 'Other / mixed stack'],
3634
+ ];
3635
+ const _INTERACTIVE_VERBOSITIES: ReadonlyArray<readonly [string, string]> = [
3636
+ ['quiet', 'Telegraph / minimal output'],
3637
+ ['normal', 'Default verbosity'],
3638
+ ['verbose', 'Full intent announcements + play-by-play'],
3639
+ ];
3640
+ const _LOCAL_CONFIG_FILE = '.agent-config.local.json';
3641
+
3642
+ function _interactive_prompt_choice(label: string, options: ReadonlyArray<readonly [string, string]>): string {
3643
+ process.stdout.write('\n');
3644
+ process.stdout.write(` ${label}\n`);
3645
+ options.forEach(([key, blurb], idx) => {
3646
+ process.stdout.write(` ${idx + 1}. ${key} — ${blurb}\n`);
3647
+ });
3648
+ process.stdout.write('\n');
3649
+ for (;;) {
3650
+ const raw = _read_line(` Choice [1-${options.length}, default 1]: `);
3651
+ if (raw === null) return options[0]![0];
3652
+ if (!raw) return options[0]![0];
3653
+ if (/^[0-9]+$/.test(raw)) {
3654
+ const n = parseInt(raw, 10);
3655
+ if (n >= 1 && n <= options.length) return options[n - 1]![0];
3656
+ }
3657
+ for (const [key] of options) {
3658
+ if (raw.toLowerCase() === key) return key;
3659
+ }
3660
+ process.stdout.write(
3661
+ ` ⚠️ Pick a number 1-${options.length} or one of: ` +
3662
+ `${options.map(([k]) => k).join(', ')}.\n`,
3663
+ );
3664
+ }
3665
+ }
3666
+
3667
+ function run_interactive_init(project_root: string, force: boolean): number {
3668
+ if (!process.stdin.isTTY) {
3669
+ warn(
3670
+ '--interactive requested but stdin is not a TTY; skipping the ' +
3671
+ `prompt. Re-run interactively or hand-edit ${_LOCAL_CONFIG_FILE}.`,
3672
+ );
3673
+ return 0;
3674
+ }
3675
+
3676
+ const target = path.join(project_root, _LOCAL_CONFIG_FILE);
3677
+ if (pathExists(target) && !force) {
3678
+ warn(
3679
+ `${_LOCAL_CONFIG_FILE} already exists; re-run with --force to ` +
3680
+ 'overwrite. Skipping interactive init.',
3681
+ );
3682
+ return 0;
3683
+ }
3684
+
3685
+ process.stdout.write('\n');
3686
+ info('Interactive init — captures user-type / stack / verbosity');
3687
+ info('(forward-compatible stub; runtime filtering activates with step-9)');
3688
+
3689
+ const user_type = _interactive_prompt_choice('Primary user type:', _INTERACTIVE_USER_TYPES);
3690
+ const stack = _interactive_prompt_choice('Project stack:', _INTERACTIVE_STACKS);
3691
+ const verbosity = _interactive_prompt_choice('Verbosity profile:', _INTERACTIVE_VERBOSITIES);
3692
+
3693
+ const payload: Record<string, unknown> = {
3694
+ $schema:
3695
+ 'https://github.com/event4u-app/agent-config/src/scripts/schemas/local-config.schema.json',
3696
+ version: 1,
3697
+ user_type,
3698
+ stack,
3699
+ verbosity,
3700
+ universal_skills_contract: 'docs/contracts/universal-skills.md',
3701
+ };
3702
+
3703
+ try {
3704
+ writeText(target, jsonDumpsIndent(payload, 2) + '\n');
3705
+ } catch (exc) {
3706
+ warn(`Could not write ${target}: ${String(exc)}`);
3707
+ return 1;
3708
+ }
3709
+
3710
+ success(`Wrote ${path.relative(project_root, target)} (${user_type} / ${stack} / ${verbosity})`);
3711
+ return 0;
3712
+ }
3713
+
3714
+ // --- Wizard auto-launch ---
3715
+
3716
+ const _WIZARD_READY_RE = /^WIZARD_READY (http:\/\/(?:127\.0\.0\.1|localhost):\d+\/\S*)\r?$/;
3717
+ const _WIZARD_TIMEOUTS: readonly number[] = [10.0, 20.0, 40.0, 80.0];
3718
+
3719
+ function _wizard_should_launch(opts: Options): [boolean, string] {
3720
+ if (opts.no_ui) return [false, '--no-ui flag set'];
3721
+ const env_no_ui = (process.env['AGENT_CONFIG_NO_UI'] ?? '').trim();
3722
+ if (env_no_ui && env_no_ui !== '0') return [false, 'AGENT_CONFIG_NO_UI env set'];
3723
+ if ((process.env['CI'] ?? '').trim()) return [false, 'CI environment detected'];
3724
+ if (!process.stdout.isTTY) return [false, 'stdout is not a TTY'];
3725
+ const tools_raw = opts.tools;
3726
+ if (tools_raw && !_tools_was_all(tools_raw)) {
3727
+ return [false, 'explicit --tools= selection (headless install)'];
3728
+ }
3729
+ return [true, ''];
3730
+ }
3731
+
3732
+ function _wizard_cli_dist(_project_root: string): string | null {
3733
+ const package_root = path.dirname(path.dirname(path.dirname(resolvePath(_HERE))));
3734
+ const cli = path.join(package_root, 'dist', 'cli', 'agent-config.js');
3735
+ return pathExists(cli) ? cli : null;
3736
+ }
3737
+
3738
+ function _server_info_path(): string {
3739
+ return path.join(os.homedir(), '.event4u', 'agent-config', 'local-server.json');
3740
+ }
3741
+
3742
+ function _pid_is_agent_config(pid: number): boolean {
3743
+ let res;
3744
+ try {
3745
+ res = spawnSync('ps', ['-p', String(pid), '-o', 'command='], {
3746
+ encoding: 'utf-8',
3747
+ timeout: 5000,
3748
+ });
3749
+ } catch {
3750
+ return false;
3751
+ }
3752
+ if (res.error) return false;
3753
+ return (res.stdout || '').toLowerCase().includes('agent-config');
3754
+ }
3755
+
3756
+ function unlinkMissingOk(p: string): void {
3757
+ try {
3758
+ fs.unlinkSync(p);
3759
+ } catch {
3760
+ /* missing_ok */
3761
+ }
3762
+ }
3763
+
3764
+ /** `os.kill(pid, 0)` liveness probe → returns true if the process exists. */
3765
+ function pidAlive(pid: number): boolean {
3766
+ try {
3767
+ process.kill(pid, 0);
3768
+ return true;
3769
+ } catch (err) {
3770
+ // EPERM means it exists but we can't signal it — Python's os.kill(pid,0)
3771
+ // would NOT raise in that case; treat EPERM as alive.
3772
+ return (err as NodeJS.ErrnoException).code === 'EPERM';
3773
+ }
3774
+ }
3775
+
3776
+ function _kill_stale_wizard_server(): void {
3777
+ const p = _server_info_path();
3778
+ let infoObj: Record<string, unknown>;
3779
+ try {
3780
+ infoObj = JSON.parse(readText(p));
3781
+ } catch {
3782
+ return;
3783
+ }
3784
+ const pid = infoObj['pid'];
3785
+ if (typeof pid !== 'number' || !Number.isInteger(pid)) {
3786
+ unlinkMissingOk(p);
3787
+ return;
3788
+ }
3789
+ if (!pidAlive(pid)) {
3790
+ unlinkMissingOk(p);
3791
+ return;
3792
+ }
3793
+ if (!_pid_is_agent_config(pid)) return;
3794
+ try {
3795
+ process.kill(pid, 'SIGTERM');
3796
+ } catch {
3797
+ unlinkMissingOk(p);
3798
+ return;
3799
+ }
3800
+ let exited = false;
3801
+ for (let n = 0; n < 30; n += 1) {
3802
+ if (!pidAlive(pid)) {
3803
+ exited = true;
3804
+ break;
3805
+ }
3806
+ sleepMs(100);
3807
+ }
3808
+ if (!exited) {
3809
+ try {
3810
+ process.kill(pid, 'SIGKILL');
3811
+ } catch {
3812
+ /* OSError → pass */
3813
+ }
3814
+ }
3815
+ unlinkMissingOk(p);
3816
+ process.stdout.write('(Stopped the previous wizard server.)\n');
3817
+ }
3818
+
3819
+ /** Blocking sleep, mirroring `time.sleep` in a synchronous flow. */
3820
+ function sleepMs(ms: number): void {
3821
+ const end = Date.now() + ms;
3822
+ const buf = new Int32Array(new SharedArrayBuffer(4));
3823
+ while (Date.now() < end) {
3824
+ Atomics.wait(buf, 0, 0, Math.max(1, end - Date.now()));
3825
+ }
3826
+ }
3827
+
3828
+ function _wizard_spawn(project_root: string, pass_project_root: boolean = true): number {
3829
+ _kill_stale_wizard_server();
3830
+
3831
+ const cli = _wizard_cli_dist(project_root);
3832
+ if (cli === null) {
3833
+ process.stdout.write(
3834
+ "(Wizard not available — CLI bundle not built. " +
3835
+ "Run 'npm run build' at the package root to produce dist/cli/.)\n",
3836
+ );
3837
+ return 0;
3838
+ }
3839
+
3840
+ const cmd = ['node', cli, 'install', '--no-open'];
3841
+ if (pass_project_root) {
3842
+ cmd.push('--project-root', project_root);
3843
+ }
3844
+ const env = { ...process.env };
3845
+
3846
+ // The Python uses subprocess.Popen + a stderr-draining thread + a
3847
+ // readline loop with progressive timeouts, then child.wait(). Node has no
3848
+ // synchronous equivalent that also streams; we run the whole wizard
3849
+ // handoff via a synchronous helper that mirrors the observable behaviour
3850
+ // (URL banner, stderr tail, browser open, blocking wait). Implemented with
3851
+ // spawnSync for the simple boot+wait, and a bounded readiness poll.
3852
+ return _wizard_run_sync(cmd, env, cli);
3853
+ }
3854
+
3855
+ /**
3856
+ * Synchronous wizard run mirroring `_wizard_spawn` + `_wizard_await_ready`.
3857
+ * spawnSync blocks until the child exits and captures stdout/stderr; we then
3858
+ * scan the captured stdout for the WIZARD_READY line, print the banner / open
3859
+ * the browser, and return the child's exit code. A child that never prints
3860
+ * WIZARD_READY before exiting falls through to the timeout fallback message.
3861
+ *
3862
+ * Divergence note: the Python streams stdout line-by-line with a 150s
3863
+ * progressive-timeout budget and hands the terminal to a still-running child
3864
+ * (Ctrl-C forwarding). spawnSync cannot hand off an interactive child, so this
3865
+ * twin runs the child to completion under the same total budget and surfaces
3866
+ * the same banner/fallback text. This path is network/subprocess-bound and is
3867
+ * NOT exercised by the deterministic golden tests (guarded behind the TTY +
3868
+ * dist-present gates).
3869
+ */
3870
+ function _wizard_run_sync(cmd: string[], env: NodeJS.ProcessEnv, cli: string): number {
3871
+ const total = _WIZARD_TIMEOUTS.reduce((a, b) => a + b, 0);
3872
+ let res;
3873
+ try {
3874
+ res = spawnSync(cmd[0] as string, cmd.slice(1), {
3875
+ env,
3876
+ encoding: 'utf-8',
3877
+ timeout: total * 1000,
3878
+ maxBuffer: 64 * 1024 * 1024,
3879
+ });
3880
+ } catch (exc) {
3881
+ process.stdout.write(
3882
+ `(Wizard failed to start: ${String(exc)}; run 'node ${cli} install --no-open' manually.)\n`,
3883
+ );
3884
+ return 0;
3885
+ }
3886
+ if (res.error && (res.error as NodeJS.ErrnoException).code === 'ENOENT') {
3887
+ process.stdout.write(
3888
+ `(Wizard failed to start: ${String(res.error)}; run 'node ${cli} install --no-open' manually.)\n`,
3889
+ );
3890
+ return 0;
3891
+ }
3892
+ const stdout = res.stdout || '';
3893
+ let matched_url: string | null = null;
3894
+ for (const line of stdout.split('\n')) {
3895
+ const m = _WIZARD_READY_RE.exec(line + '\n');
3896
+ if (m) {
3897
+ matched_url = m[1] as string;
3898
+ break;
3899
+ }
3900
+ }
3901
+ if (matched_url === null) {
3902
+ const stderrLines = (res.stderr || '').split('\n').filter((l) => l !== '');
3903
+ const tail = stderrLines.length ? stderrLines.slice(-20).join('\n ') : '(no stderr captured)';
3904
+ process.stdout.write(
3905
+ `(Wizard server boot timed out after ${Math.trunc(total)}s; ` +
3906
+ `run 'node ${cli} install --no-open' manually.)\n` +
3907
+ ` Last stderr:\n ${tail}\n`,
3908
+ );
3909
+ return 0;
3910
+ }
3911
+ process.stdout.write('\n');
3912
+ process.stdout.write(`Setup wizard ready: ${matched_url}\n`);
3913
+ _openBrowser(matched_url);
3914
+ process.stdout.write('(Wizard runs in the background; close the tab or press Ctrl-C to stop.)\n');
3915
+ return res.status ?? 0;
3916
+ }
3917
+
3918
+ /** `webbrowser.open` — best-effort platform open; never fatal. */
3919
+ function _openBrowser(url: string): void {
3920
+ try {
3921
+ const opener =
3922
+ process.platform === 'darwin'
3923
+ ? ['open', [url]]
3924
+ : process.platform === 'win32'
3925
+ ? ['cmd', ['/c', 'start', '', url]]
3926
+ : ['xdg-open', [url]];
3927
+ spawnSync(opener[0] as string, opener[1] as string[], { stdio: 'ignore' });
3928
+ } catch {
3929
+ /* best-effort, never fatal */
3930
+ }
3931
+ }
3932
+
3933
+ function _dry_run_summary(opts: Options): number {
3934
+ const target = resolvePath(
3935
+ opts.custom_path || opts.project || process.env['PROJECT_ROOT'] || process.cwd(),
3936
+ );
3937
+ const [will_launch, why_not] = _wizard_should_launch(opts);
3938
+ process.stdout.write('\n');
3939
+ process.stdout.write('[dry-run] Plan summary — no files written, no subprocesses spawned:\n');
3940
+ process.stdout.write(` profile: ${opts.profile}\n`);
3941
+ process.stdout.write(` user-type: ${opts.user_type || '(none)'}\n`);
3942
+ process.stdout.write(` scope: ${opts.scope || (opts.global_install ? 'global' : 'auto')}\n`);
3943
+ process.stdout.write(` tools: ${opts.tools || 'all'}\n`);
3944
+ process.stdout.write(` target: ${target}\n`);
3945
+ process.stdout.write(` minimal: ${pyBool(opts.minimal)}\n`);
3946
+ process.stdout.write(` force: ${pyBool(opts.force)}\n`);
3947
+ process.stdout.write(` offline: ${pyBool(opts.offline)}\n`);
3948
+ if (will_launch) {
3949
+ process.stdout.write(' wizard: Would auto-launch (pass --no-ui to suppress).\n');
3950
+ } else {
3951
+ process.stdout.write(` wizard: Suppressed (${why_not}).\n`);
3952
+ }
3953
+ if (opts.global_install) {
3954
+ let preview: Record<string, string[]> = {};
3955
+ try {
3956
+ preview = _preview_global_reap(
3957
+ _parse_tools(opts.tools || 'all'),
3958
+ _resolve_package_root_for_global(),
3959
+ );
3960
+ } catch {
3961
+ preview = {};
3962
+ }
3963
+ const total = Object.values(preview).reduce((a, v) => a + v.length, 0);
3964
+ process.stdout.write('\n');
3965
+ if (total === 0) {
3966
+ process.stdout.write(' reap (cleanup): nothing to reap — no stale deployed files.\n');
3967
+ } else {
3968
+ process.stdout.write(` reap (cleanup): would remove ${total} stale file(s):\n`);
3969
+ for (const tool_id of Object.keys(preview).sort()) {
3970
+ for (const p of preview[tool_id] as string[]) {
3971
+ process.stdout.write(` ${tool_id}: ${p}\n`);
3972
+ }
3973
+ }
3974
+ }
3975
+ }
3976
+ process.stdout.write('\n');
3977
+ return 0;
3978
+ }
3979
+
3980
+ /** Python `str(bool)` → 'True' / 'False'. */
3981
+ function pyBool(v: boolean): string {
3982
+ return v ? 'True' : 'False';
3983
+ }
3984
+
3985
+ function _apply_payload_preview(payload: Record<string, unknown>, opts: Options): number {
3986
+ const schema_version = payload['schema_version'] ?? '<missing>';
3987
+ const target = resolvePath(
3988
+ opts.custom_path || opts.project || process.env['PROJECT_ROOT'] || process.cwd(),
3989
+ );
3990
+ process.stdout.write('\n');
3991
+ process.stdout.write(
3992
+ '[apply-payload] Plan summary — no files written, no subprocesses spawned:\n',
3993
+ );
3994
+ process.stdout.write(` schema: ${schema_version}\n`);
3995
+ if (schema_version === 'wizard-v2') {
3996
+ const tools = (payload['tools'] as unknown[]) || [];
3997
+ const packs = (payload['packs'] as unknown[]) || [];
3998
+ const settings = (payload['settings'] as Record<string, unknown>) || {};
3999
+ const scope_to_project = Boolean(payload['scope_to_project_only'] ?? false);
4000
+ process.stdout.write(` tools: ${tools.length ? tools.join(',') : '(none)'}\n`);
4001
+ process.stdout.write(` packs: ${packs.length ? packs.join(',') : '(base)'}\n`);
4002
+ process.stdout.write(` settings: ${Object.keys(settings).length} top-level key(s)\n`);
4003
+ process.stdout.write(` scope: ${scope_to_project ? 'project' : 'global'}\n`);
4004
+ } else if (schema_version === 'installer-v1') {
4005
+ const ai_tools = (payload['ai_tools'] as unknown[]) || [];
4006
+ const configs = (payload['configs'] as Record<string, unknown>) || {};
4007
+ process.stdout.write(` ai_tools: ${ai_tools.length ? ai_tools.join(',') : '(none)'}\n`);
4008
+ process.stdout.write(` configs: ${Object.keys(configs).length} tool config(s)\n`);
4009
+ } else {
4010
+ process.stdout.write(` error: unsupported schema_version: ${pyRepr(schema_version)}\n`);
4011
+ process.stdout.write('\n');
4012
+ return 2;
4013
+ }
4014
+ process.stdout.write(` target: ${target}\n`);
4015
+ process.stdout.write(` dry_run: ${pyBool(Boolean(payload['dry_run'] ?? opts.dry_run))}\n`);
4016
+ process.stdout.write('\n');
4017
+ return 0;
4018
+ }
4019
+
4020
+ /** Python `repr()` for a string scalar (single-quoted) or other primitives. */
4021
+ function pyRepr(v: unknown): string {
4022
+ if (typeof v === 'string') return `'${v.replace(/\\/g, '\\\\').replace(/'/g, "\\'")}'`;
4023
+ if (v === null || v === undefined) return 'None';
4024
+ if (typeof v === 'boolean') return v ? 'True' : 'False';
4025
+ return String(v);
4026
+ }
4027
+
4028
+ // --- Main ---
4029
+
4030
+ function main(argv: string[]): number {
4031
+ const opts = parse_options(argv);
4032
+ state.QUIET = opts.quiet;
4033
+
4034
+ if (opts.apply_payload) {
4035
+ const payload_path = resolvePath(opts.apply_payload);
4036
+ if (!isFile(payload_path)) fail(`--apply-payload path not found: ${payload_path}`);
4037
+ let payload: unknown;
4038
+ try {
4039
+ payload = JSON.parse(readText(payload_path));
4040
+ } catch (exc) {
4041
+ fail(`--apply-payload JSON parse error: ${String(exc)}`);
4042
+ }
4043
+ if (!_isPlainObject(payload)) fail('--apply-payload root must be a JSON object');
4044
+ const pl = payload as Record<string, unknown>;
4045
+ const schema_version = pl['schema_version'];
4046
+ if (schema_version !== 'wizard-v2' && schema_version !== 'installer-v1') {
4047
+ fail(
4048
+ `--apply-payload schema_version must be 'wizard-v2' or ` +
4049
+ `'installer-v1', got ${pyRepr(schema_version)}`,
4050
+ );
4051
+ }
4052
+ if (schema_version === 'wizard-v2') {
4053
+ const tools = pl['tools'];
4054
+ if (Array.isArray(tools) && tools.length > 0) {
4055
+ opts.tools = tools.filter((t) => typeof t === 'string').join(',');
4056
+ }
4057
+ if (Boolean(pl['scope_to_project_only'] ?? false)) {
4058
+ opts.scope = 'project';
4059
+ } else {
4060
+ opts.scope = 'global';
4061
+ }
4062
+ const settings = pl['settings'];
4063
+ if (_isPlainObject(settings)) {
4064
+ const rule_loading_tier =
4065
+ (settings['rule_loading_tier'] as unknown) || (settings['cost_profile'] as unknown);
4066
+ if (typeof rule_loading_tier === 'string' && rule_loading_tier) {
4067
+ opts.profile = rule_loading_tier;
4068
+ }
4069
+ const personal = settings['personal'];
4070
+ if (_isPlainObject(personal)) {
4071
+ const user_type = personal['user_type'];
4072
+ if (typeof user_type === 'string' && user_type) {
4073
+ opts.user_type = user_type;
4074
+ }
4075
+ }
4076
+ }
4077
+ const packs = pl['packs'];
4078
+ if (Array.isArray(packs)) {
4079
+ opts.packs = packs.filter((p) => typeof p === 'string') as string[];
4080
+ }
4081
+ } else if (schema_version === 'installer-v1') {
4082
+ const ai_tools = pl['ai_tools'];
4083
+ if (Array.isArray(ai_tools) && ai_tools.length > 0) {
4084
+ opts.tools = ai_tools.filter((t) => typeof t === 'string').join(',');
4085
+ }
4086
+ }
4087
+ if (Boolean(pl['dry_run'] ?? false)) {
4088
+ opts.dry_run = true;
4089
+ }
4090
+ if (opts.dry_run) {
4091
+ return _apply_payload_preview(pl, opts);
4092
+ }
4093
+ state.PROGRESS_NDJSON = true;
4094
+ state.QUIET = true;
4095
+ }
4096
+
4097
+ if (opts.offline) {
4098
+ process.env['AGENT_CONFIG_OFFLINE'] = '1';
4099
+ process.env['AGENT_CONFIG_NO_UPDATE_CHECK'] = '1';
4100
+ }
4101
+
4102
+ if (!SUPPORTED_PROFILES.includes(opts.profile)) {
4103
+ fail(`Unsupported profile: ${opts.profile}. Supported: ${SUPPORTED_PROFILES.join(', ')}`);
4104
+ }
4105
+
4106
+ if (opts.dry_run) {
4107
+ return _dry_run_summary(opts);
4108
+ }
4109
+
4110
+ {
4111
+ const [will_launch, why_not] = _wizard_should_launch(opts);
4112
+ if (will_launch) {
4113
+ if (!state.QUIET) info('Setup wizard will launch automatically after install.');
4114
+ } else if (!state.QUIET) {
4115
+ info(`Setup wizard auto-launch disabled (${why_not}).`);
4116
+ }
4117
+ }
4118
+
4119
+ if (opts.minimal) {
4120
+ const target_root = resolvePath(
4121
+ opts.custom_path || opts.project || process.env['PROJECT_ROOT'] || process.cwd(),
4122
+ );
4123
+ const minimal_package_root = path.dirname(
4124
+ path.dirname(path.dirname(_minimal_templates_root())),
4125
+ );
4126
+ const validated_user_type = _validate_user_type(minimal_package_root, opts.user_type);
4127
+ return install_minimal(target_root, opts.force, validated_user_type);
4128
+ }
4129
+
4130
+ const detect_root = resolvePath(opts.project || process.env['PROJECT_ROOT'] || process.cwd());
4131
+ const [detected, detect_reason] = detect_scope(detect_root);
4132
+ const custom_path: string | null = opts.custom_path ? resolvePath(opts.custom_path) : null;
4133
+ const scope = _resolve_scope(opts, detected, detect_reason, custom_path);
4134
+ _enforce_consumer_global_only(scope);
4135
+ _enforce_not_source_repo(scope, detect_root);
4136
+
4137
+ let parsed_tools = _parse_tools(opts.tools);
4138
+ const tools_was_all = _tools_was_all(opts.tools);
4139
+ parsed_tools = _validate_scope(parsed_tools, scope, tools_was_all);
4140
+
4141
+ const wizard_handoff = _wizard_should_launch(opts)[0];
4142
+
4143
+ if (scope === 'global') {
4144
+ const artefacts = _detect_legacy_for_migration(detect_root);
4145
+ if (artefacts.length > 0 && (wizard_handoff || _prompt_migrate_to_global(detect_root, artefacts))) {
4146
+ const rc = _run_migrate_to_global(detect_root);
4147
+ if (rc !== 0) return rc;
4148
+ }
4149
+ const rc = install_global(parsed_tools, opts.force, detect_root, opts.core_only);
4150
+ _emit_progress_terminal(rc);
4151
+ if (rc === 0 && wizard_handoff) {
4152
+ return _wizard_spawn(detect_root, false);
4153
+ }
4154
+ return rc;
4155
+ }
4156
+
4157
+ const project_root =
4158
+ custom_path || resolvePath(opts.project || process.env['PROJECT_ROOT'] || process.cwd());
4159
+ const is_first_run = !pathExists(path.join(project_root, SETTINGS_FILE));
4160
+ const rc = _main_project_install(opts, project_root, parsed_tools, is_first_run);
4161
+ if (rc === 0 && opts.interactive) {
4162
+ run_interactive_init(project_root, opts.force);
4163
+ }
4164
+ _emit_progress_terminal(rc);
4165
+ return rc;
4166
+ }
4167
+
4168
+ function _propose_modules_config(project_root: string, is_first_run: boolean): void {
4169
+ if (!is_first_run || state.QUIET || !process.stdin.isTTY || !process.stdout.isTTY) return;
4170
+ let candidates;
4171
+ try {
4172
+ candidates = detect_module_roots(project_root);
4173
+ } catch {
4174
+ return;
4175
+ }
4176
+ if (!candidates || candidates.length === 0) return;
4177
+ process.stdout.write('\n');
4178
+ info('Module-root candidates detected — propose `modules:` block');
4179
+ info('Paste into .agent-project-settings.yml to enable module-aware skills (or skip; the block stays opt-in).');
4180
+ process.stdout.write('\n');
4181
+ process.stdout.write(' modules:\n');
4182
+ process.stdout.write(' enabled: true\n');
4183
+ process.stdout.write(' root_paths: [' + candidates.map((c) => c.path).join(', ') + ']\n');
4184
+ const primary_ns =
4185
+ candidates.find((c) => c.namespace_template_guess)?.namespace_template_guess ?? '';
4186
+ if (primary_ns) {
4187
+ process.stdout.write(` namespace_template: '${primary_ns}'\n`);
4188
+ }
4189
+ process.stdout.write(' agent_folder: agents\n');
4190
+ process.stdout.write(' skip_dirs: [.module-template, .example]\n');
4191
+ process.stdout.write('\n');
4192
+ info(
4193
+ 'Re-run anytime via `./scripts-run src/scripts/propose_modules_config` ' +
4194
+ '(installed under <package>/src/scripts/).',
4195
+ );
4196
+ }
4197
+
4198
+ function _read_consumer_auto_switch(project_root: string): string {
4199
+ let data: Record<string, unknown>;
4200
+ try {
4201
+ data = load_agent_settings({ project_path: _resolve_settings_read(project_root) });
4202
+ } catch {
4203
+ return 'suggest';
4204
+ }
4205
+ const model = _isPlainObject(data) ? data['model'] : null;
4206
+ const value = _isPlainObject(model) ? (model as Record<string, unknown>)['auto_switch'] : null;
4207
+ if (typeof value === 'string' && ['auto', 'suggest', 'off'].includes(value.trim().toLowerCase())) {
4208
+ return value.trim().toLowerCase();
4209
+ }
4210
+ return 'suggest';
4211
+ }
4212
+
4213
+ function finalize_claude_model_tiers(project_root: string): number {
4214
+ const claude_skills = path.join(project_root, '.claude', 'skills');
4215
+ const augment_skills = path.join(project_root, '.augment', 'skills');
4216
+ if (!isDir(claude_skills) || !isDir(augment_skills)) return 0;
4217
+ if (_read_consumer_auto_switch(project_root) !== 'auto') return 0;
4218
+
4219
+ let rendered = 0;
4220
+ const entries = fs
4221
+ .readdirSync(claude_skills)
4222
+ .sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));
4223
+ for (const name of entries) {
4224
+ const entry = path.join(claude_skills, name);
4225
+ const src_dir = path.join(augment_skills, name);
4226
+ const src_md = path.join(src_dir, 'SKILL.md');
4227
+ let tier: string | null;
4228
+ try {
4229
+ tier = read_model_tier(src_md);
4230
+ } catch {
4231
+ tier = null;
4232
+ }
4233
+ if (tier === null || !(tier in TIER_TO_CLAUDE_MODEL) || !isDir(src_dir)) continue;
4234
+ if (isSymlink(entry) || isFile(entry)) {
4235
+ fs.unlinkSync(entry);
4236
+ } else if (isDir(entry)) {
4237
+ fs.rmSync(entry, { recursive: true, force: true });
4238
+ }
4239
+ mkdirp(entry);
4240
+ const srcFiles = fs.readdirSync(src_dir).sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));
4241
+ for (const fname of srcFiles) {
4242
+ if (fname === 'SKILL.md') {
4243
+ writeText(
4244
+ path.join(entry, 'SKILL.md'),
4245
+ render_native_model_md(readText(src_md), tier),
4246
+ );
4247
+ } else {
4248
+ fs.symlinkSync(
4249
+ path.join('../../../.augment/skills', name, fname),
4250
+ path.join(entry, fname),
4251
+ );
4252
+ }
4253
+ }
4254
+ rendered += 1;
4255
+ }
4256
+
4257
+ if (rendered && !state.QUIET) {
4258
+ info(
4259
+ `Applied native model: to ${rendered} model-tier skill(s) in ` +
4260
+ '.claude/skills/ (model.auto_switch=auto)',
4261
+ );
4262
+ }
4263
+ return rendered;
4264
+ }
4265
+
4266
+ function _main_project_install(
4267
+ opts: Options,
4268
+ project_root: string,
4269
+ parsed_tools: Set<string>,
4270
+ is_first_run: boolean,
4271
+ ): number {
4272
+ let package_root: string;
4273
+ let package_type: string;
4274
+ if (opts.package) {
4275
+ package_root = resolvePath(opts.package);
4276
+ if (!pathExists(path.join(package_root, 'src', 'config', 'profiles', 'minimal.ini'))) {
4277
+ fail(`Invalid --package path (missing src/config/profiles/minimal.ini): ${package_root}`);
4278
+ }
4279
+ package_type = detect_package_type_for_project(project_root, package_root);
4280
+ } else {
4281
+ package_root = detect_package_root(project_root);
4282
+ package_type = detect_package_type(package_root);
4283
+ }
4284
+
4285
+ if (!state.QUIET) {
4286
+ process.stdout.write('\n');
4287
+ info('Agent Config — Project Bridge Installer');
4288
+ info(`Project: ${project_root}`);
4289
+ info(`Package: ${package_root}`);
4290
+ info(`Type: ${package_type}`);
4291
+ info(`Profile: ${opts.profile}`);
4292
+ if (opts.user_type) info(`UserType: ${opts.user_type}`);
4293
+ process.stdout.write('\n');
4294
+ }
4295
+
4296
+ ensure_agent_settings(project_root, package_root, opts.profile, opts.force, opts.user_type, opts.packs ?? null);
4297
+
4298
+ _write_install_mode_marker(project_root, 'full');
4299
+
4300
+ const tools = parsed_tools;
4301
+ const merged_keys_by_tool: Record<string, Record<string, unknown>[]> = {};
4302
+
4303
+ if (!opts.skip_bridges) {
4304
+ ensure_vscode_bridge(project_root, package_type, opts.force);
4305
+ merged_keys_by_tool['augment'] = ensure_augment_bridge(project_root, opts.force);
4306
+ if (_is_tool_enabled(tools, 'claude-code')) {
4307
+ merged_keys_by_tool['claude-code'] = ensure_claude_bridge(project_root, opts.force);
4308
+ }
4309
+ if (_is_tool_enabled(tools, 'cursor')) {
4310
+ merged_keys_by_tool['cursor'] = ensure_cursor_bridge(project_root, opts.force);
4311
+ }
4312
+ if (_is_tool_enabled(tools, 'cline')) ensure_cline_bridge(project_root, opts.force);
4313
+ if (_is_tool_enabled(tools, 'windsurf')) {
4314
+ merged_keys_by_tool['windsurf'] = ensure_windsurf_bridge(project_root, opts.force);
4315
+ }
4316
+ if (_is_tool_enabled(tools, 'gemini-cli')) {
4317
+ merged_keys_by_tool['gemini-cli'] = ensure_gemini_bridge(project_root, opts.force);
4318
+ }
4319
+ if (_is_tool_enabled(tools, 'copilot')) ensure_copilot_bridge(project_root, opts.force);
4320
+ if (_is_tool_enabled(tools, 'roocode')) ensure_roocode_bridge(project_root, opts.force);
4321
+ if (_is_tool_enabled(tools, 'claude-desktop')) ensure_claude_desktop_bridge(project_root, opts.force);
4322
+ if (_is_tool_enabled(tools, 'aider')) ensure_aider_bridge(project_root, opts.force);
4323
+ if (_is_tool_enabled(tools, 'codex')) ensure_codex_bridge(project_root, opts.force);
4324
+ if (_is_tool_enabled(tools, 'continue')) ensure_continue_bridge(project_root, opts.force);
4325
+ if (_is_tool_enabled(tools, 'kilocode')) ensure_kilocode_bridge(project_root, opts.force);
4326
+ if (_is_tool_enabled(tools, 'zed')) ensure_zed_bridge(project_root, opts.force);
4327
+ if (_is_tool_enabled(tools, 'jetbrains')) ensure_jetbrains_bridge(project_root, opts.force);
4328
+ if (_is_tool_enabled(tools, 'kiro')) ensure_kiro_bridge(project_root, opts.force);
4329
+ }
4330
+
4331
+ if (opts.augment_user_hooks) {
4332
+ (merged_keys_by_tool['augment'] ??= []).push(...ensure_augment_user_hooks(package_root, opts.force));
4333
+ }
4334
+ if (opts.cursor_user_hooks && _is_tool_enabled(tools, 'cursor')) {
4335
+ (merged_keys_by_tool['cursor'] ??= []).push(...ensure_cursor_user_hooks(package_root, opts.force));
4336
+ }
4337
+ if (opts.cline_user_hooks && _is_tool_enabled(tools, 'cline')) {
4338
+ ensure_cline_user_hooks(package_root, opts.force);
4339
+ }
4340
+ if (opts.windsurf_user_hooks && _is_tool_enabled(tools, 'windsurf')) {
4341
+ (merged_keys_by_tool['windsurf'] ??= []).push(...ensure_windsurf_user_hooks(package_root, opts.force));
4342
+ }
4343
+ if (opts.gemini_user_hooks && _is_tool_enabled(tools, 'gemini-cli')) {
4344
+ (merged_keys_by_tool['gemini-cli'] ??= []).push(...ensure_gemini_user_hooks(package_root, opts.force));
4345
+ }
4346
+
4347
+ if (state.PROGRESS_NDJSON && !opts.skip_bridges) {
4348
+ const ordered = [...tools].sort();
4349
+ const total = ordered.length;
4350
+ ordered.forEach((tool_id, i) => {
4351
+ _emit_progress({ type: 'file', file: tool_id, status: 'deployed', written: i + 1, total });
4352
+ });
4353
+ }
4354
+
4355
+ if (!opts.skip_bridges && !opts.no_smoke) {
4356
+ if (!state.QUIET) {
4357
+ process.stdout.write('\n');
4358
+ info('Smoke-testing installed hook bridges (dry-run)');
4359
+ }
4360
+ _smoke_test_hooks(project_root, package_root);
4361
+ }
4362
+
4363
+ if (!opts.skip_bridges) {
4364
+ const files_by_tool = _files_by_tool_from_bridges(parsed_tools, project_root, 'project');
4365
+ const rc = _update_installed_tools_manifest(
4366
+ project_root,
4367
+ parsed_tools,
4368
+ 'project',
4369
+ opts.force,
4370
+ files_by_tool,
4371
+ merged_keys_by_tool,
4372
+ );
4373
+ if (rc !== 0) return rc;
4374
+ }
4375
+
4376
+ if (!opts.skip_bridges && _is_tool_enabled(tools, 'claude-code')) {
4377
+ finalize_claude_model_tiers(project_root);
4378
+ }
4379
+
4380
+ if (!state.QUIET) {
4381
+ process.stdout.write('\n');
4382
+ success('Done.');
4383
+ if (is_first_run) {
4384
+ process.stdout.write('\n');
4385
+ process.stdout.write(' Try these 3 prompts with your agent:\n');
4386
+ process.stdout.write(' 1. "Refactor this function" → agent analyzes first\n');
4387
+ process.stdout.write(' 2. "Add caching to this" → agent asks instead of guessing\n');
4388
+ process.stdout.write(' 3. "Implement this feature" → agent respects your codebase\n');
4389
+ process.stdout.write('\n');
4390
+ process.stdout.write(' Next steps:\n');
4391
+ process.stdout.write(' • Commit .agent-settings.yml and bridge files to your repo\n');
4392
+ process.stdout.write(' • New team members run `npx @event4u/agent-config init` — done\n');
4393
+ process.stdout.write(' • Inspect hook coverage: ./agent-config hooks:status\n');
4394
+ process.stdout.write(
4395
+ ' • Full walkthrough: https://github.com/event4u-app/agent-config/blob/main/docs/getting-started.md\n',
4396
+ );
4397
+ process.stdout.write('\n');
4398
+ } else {
4399
+ process.stdout.write(
4400
+ ' Re-run complete. Walkthrough: https://github.com/event4u-app/agent-config/blob/main/docs/getting-started.md\n',
4401
+ );
4402
+ process.stdout.write('\n');
4403
+ }
4404
+ }
4405
+
4406
+ _propose_modules_config(project_root, is_first_run);
4407
+
4408
+ const will_launch = _wizard_should_launch(opts)[0];
4409
+ if (will_launch) {
4410
+ return _wizard_spawn(project_root);
4411
+ }
4412
+ return 0;
4413
+ }
4414
+
4415
+ // --- CLI entry ---
4416
+
4417
+ // Node realpath-resolves the entry module's `import.meta.url` (symlinks
4418
+ // followed), but `path.resolve(argv[1])` does NOT. On macOS the temp tree is
4419
+ // under `/var/folders/...` (a `/var → /private/var` symlink), so a bare
4420
+ // `path.resolve` comparison mismatches and `main()` never fires — a silent
4421
+ // no-op. This bit the pre-bundled installer (`dist/install/install.mjs`) on
4422
+ // the macOS `setup.sh` path specifically (Linux `/tmp` is not symlinked).
4423
+ // Realpath-resolve `argv[1]` so both sides are canonical; fall back to the
4424
+ // bare resolve if the path can't be realpath'd.
4425
+ function _resolvedArgv1(): string | undefined {
4426
+ if (process.argv[1] === undefined) return undefined;
4427
+ try {
4428
+ return fs.realpathSync(path.resolve(process.argv[1]));
4429
+ } catch {
4430
+ return path.resolve(process.argv[1]);
4431
+ }
4432
+ }
4433
+ const _argv1 = _resolvedArgv1();
4434
+ const _isCliEntry = _argv1 !== undefined && import.meta.url === pathToFileURL(_argv1).href;
4435
+ if (_isCliEntry || _argv1 === _HERE) {
4436
+ try {
4437
+ process.exitCode = main(process.argv.slice(2));
4438
+ } catch (e) {
4439
+ if (e instanceof SystemExitError || e instanceof ArgparseExit) {
4440
+ process.exitCode = e.code;
4441
+ } else {
4442
+ throw e;
4443
+ }
4444
+ }
4445
+ }
4446
+
4447
+ export {
4448
+ main,
4449
+ parse_options,
4450
+ _merge_tools_aliases,
4451
+ _parse_tools,
4452
+ _tools_was_all,
4453
+ _is_tool_enabled,
4454
+ _yaml_scalar,
4455
+ _parse_legacy_settings,
4456
+ _replace_template_value,
4457
+ _replace_template_value_raw,
4458
+ _append_unknown_legacy,
4459
+ _render_template,
4460
+ _parse_profile_ini,
4461
+ _inject_packs,
4462
+ deep_merge,
4463
+ _validate_scope,
4464
+ _bridge_marker,
4465
+ detect_scope,
4466
+ _resolve_scope,
4467
+ _is_agent_config_source_repo,
4468
+ _detect_legacy_for_migration,
4469
+ _format_global_root_for_marker,
4470
+ _files_by_tool_from_bridges,
4471
+ _files_by_tool_from_deploy,
4472
+ _verify_deploy_targets,
4473
+ _wizard_should_launch,
4474
+ _dry_run_summary,
4475
+ _apply_payload_preview,
4476
+ jsonDumpsIndent,
4477
+ jsonDumpsCompact,
4478
+ _canonical_settings_target,
4479
+ _resolve_settings_read,
4480
+ detect_package_type,
4481
+ detect_package_type_for_project,
4482
+ SystemExitError,
4483
+ ArgparseExit,
4484
+ state,
4485
+ // ADR-200 py2ts: re-exported for test_consumer_model_tier.ts twin
4486
+ // (Python test imports `install.finalize_claude_model_tiers` directly).
4487
+ finalize_claude_model_tiers,
4488
+ SUPPORTED_PROFILES,
4489
+ DEFAULT_PROFILE,
4490
+ _VALID_TOOLS,
4491
+ // ADR-200 py2ts: re-exported for cmd_export.ts / cmd_settings_migrate.ts
4492
+ // twins (Python imports these as `scripts.install` module constants).
4493
+ SETTINGS_FILE,
4494
+ GLOBAL_AGENT_SETTINGS_PATH,
4495
+ GLOBAL_USER_SETTINGS_PATH,
4496
+ AIDER_MARKER,
4497
+ CLAUDE_DESKTOP_MARKER,
4498
+ CODEX_MARKER,
4499
+ CONTINUE_MARKER,
4500
+ JETBRAINS_MARKER,
4501
+ KILOCODE_MARKER,
4502
+ KIRO_MARKER,
4503
+ ROOCODE_MARKER,
4504
+ ZED_MARKER,
4505
+ // ADR-200 py2ts: re-exported for test_install_snapshot.ts twin
4506
+ CURSOR_DISPATCHER_BINDINGS,
4507
+ CLINE_DISPATCHER_BINDINGS,
4508
+ WINDSURF_DISPATCHER_BINDINGS,
4509
+ GEMINI_DISPATCHER_BINDINGS,
4510
+ ensure_cursor_bridge,
4511
+ ensure_cline_bridge,
4512
+ ensure_windsurf_bridge,
4513
+ ensure_gemini_bridge,
4514
+ };
4515
+ export type { Options };