@monoes/monomindcli 1.14.6 → 1.15.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 (329) hide show
  1. package/.claude/agents/reengineer-squad/boss.md +113 -0
  2. package/.claude/agents/reengineer-squad/critic-architect.md +132 -0
  3. package/.claude/agents/reengineer-squad/git-manager.md +145 -0
  4. package/.claude/agents/reengineer-squad/idea-generator.md +95 -0
  5. package/.claude/agents/reengineer-squad/implementer.md +112 -0
  6. package/.claude/agents/reengineer-squad/integration-planner.md +112 -0
  7. package/.claude/agents/reengineer-squad/source-analyst.md +103 -0
  8. package/.claude/agents/reengineer-squad/target-analyst.md +118 -0
  9. package/.claude/agents/reengineer-squad/tester.md +105 -0
  10. package/.claude/commands/mastermind/master.md +35 -14
  11. package/.claude/helpers/handlers/capture-handler.cjs +155 -18
  12. package/.claude/helpers/monolean-activate.cjs +20 -0
  13. package/.claude/helpers/monolean-config.cjs +76 -0
  14. package/.claude/helpers/monolean-instructions.cjs +109 -0
  15. package/.claude/helpers/monolean-propagate.cjs +9 -0
  16. package/.claude/helpers/monolean-tracker.cjs +18 -0
  17. package/.claude/helpers/skill-registry.json +2 -2
  18. package/.claude/skills/agent-browser-testing/SKILL.md +301 -18
  19. package/.claude/skills/mastermind/runorg.md +69 -23
  20. package/.claude/skills/monodesign/SKILL.md +32 -1
  21. package/.claude/skills/monodesign/adapt.md +53 -0
  22. package/.claude/skills/monodesign/agents/monodesign-asset-producer.md +100 -0
  23. package/.claude/skills/monodesign/animate.md +65 -0
  24. package/.claude/skills/monodesign/audit.md +89 -0
  25. package/.claude/skills/monodesign/bolder.md +50 -0
  26. package/.claude/skills/monodesign/clarify.md +64 -0
  27. package/.claude/skills/monodesign/colorize.md +68 -0
  28. package/.claude/skills/monodesign/craft.md +51 -0
  29. package/.claude/skills/monodesign/critique.md +66 -0
  30. package/.claude/skills/monodesign/delight.md +47 -0
  31. package/.claude/skills/monodesign/distill.md +56 -0
  32. package/.claude/skills/monodesign/document.md +80 -0
  33. package/.claude/skills/monodesign/extract.md +74 -0
  34. package/.claude/skills/monodesign/harden.md +65 -0
  35. package/.claude/skills/monodesign/live.md +59 -0
  36. package/.claude/skills/monodesign/onboard.md +50 -0
  37. package/.claude/skills/monodesign/optimize.md +64 -0
  38. package/.claude/skills/monodesign/overdrive.md +56 -0
  39. package/.claude/skills/monodesign/polish.md +68 -0
  40. package/.claude/skills/monodesign/quieter.md +57 -0
  41. package/.claude/skills/monodesign/reference/antipatterns-catalog.md +248 -76
  42. package/.claude/skills/monodesign/reference/codex.md +107 -0
  43. package/.claude/skills/monodesign/reference/craft.md +3 -0
  44. package/.claude/skills/monodesign/reference/hooks.md +99 -0
  45. package/.claude/skills/monodesign/reference/image-prompts.md +12 -0
  46. package/.claude/skills/monodesign/shape.md +71 -0
  47. package/.claude/skills/monodesign/teach.md +69 -0
  48. package/.claude/skills/monodesign/typeset.md +59 -0
  49. package/.claude/skills/monolean/SKILL.md +118 -0
  50. package/.claude/skills/monolean-audit/SKILL.md +41 -0
  51. package/.claude/skills/monolean-debt/SKILL.md +46 -0
  52. package/.claude/skills/monolean-help/SKILL.md +60 -0
  53. package/.claude/skills/monolean-review/SKILL.md +57 -0
  54. package/bin/cli.js +3 -1
  55. package/dist/dashboard/server.js +137 -0
  56. package/dist/src/__tests__/browse-adapters.test.d.ts +2 -0
  57. package/dist/src/__tests__/browse-adapters.test.d.ts.map +1 -0
  58. package/dist/src/__tests__/browse-adapters.test.js +51 -0
  59. package/dist/src/__tests__/browse-adapters.test.js.map +1 -0
  60. package/dist/src/__tests__/browse-analyzer.test.d.ts +2 -0
  61. package/dist/src/__tests__/browse-analyzer.test.d.ts.map +1 -0
  62. package/dist/src/__tests__/browse-analyzer.test.js +68 -0
  63. package/dist/src/__tests__/browse-analyzer.test.js.map +1 -0
  64. package/dist/src/__tests__/browse-builtin-handlers.test.d.ts +2 -0
  65. package/dist/src/__tests__/browse-builtin-handlers.test.d.ts.map +1 -0
  66. package/dist/src/__tests__/browse-builtin-handlers.test.js +139 -0
  67. package/dist/src/__tests__/browse-builtin-handlers.test.js.map +1 -0
  68. package/dist/src/__tests__/browse-cdp.test.d.ts +2 -0
  69. package/dist/src/__tests__/browse-cdp.test.d.ts.map +1 -0
  70. package/dist/src/__tests__/browse-cdp.test.js +169 -0
  71. package/dist/src/__tests__/browse-cdp.test.js.map +1 -0
  72. package/dist/src/__tests__/browse-dashboard.test.d.ts +2 -0
  73. package/dist/src/__tests__/browse-dashboard.test.d.ts.map +1 -0
  74. package/dist/src/__tests__/browse-dashboard.test.js +179 -0
  75. package/dist/src/__tests__/browse-dashboard.test.js.map +1 -0
  76. package/dist/src/__tests__/browse-engine.test.d.ts +2 -0
  77. package/dist/src/__tests__/browse-engine.test.d.ts.map +1 -0
  78. package/dist/src/__tests__/browse-engine.test.js +122 -0
  79. package/dist/src/__tests__/browse-engine.test.js.map +1 -0
  80. package/dist/src/__tests__/browse-expression.test.d.ts +2 -0
  81. package/dist/src/__tests__/browse-expression.test.d.ts.map +1 -0
  82. package/dist/src/__tests__/browse-expression.test.js +54 -0
  83. package/dist/src/__tests__/browse-expression.test.js.map +1 -0
  84. package/dist/src/__tests__/browse-store.test.d.ts +2 -0
  85. package/dist/src/__tests__/browse-store.test.d.ts.map +1 -0
  86. package/dist/src/__tests__/browse-store.test.js +99 -0
  87. package/dist/src/__tests__/browse-store.test.js.map +1 -0
  88. package/dist/src/__tests__/browse-workflow-types.test.d.ts +2 -0
  89. package/dist/src/__tests__/browse-workflow-types.test.d.ts.map +1 -0
  90. package/dist/src/__tests__/browse-workflow-types.test.js +33 -0
  91. package/dist/src/__tests__/browse-workflow-types.test.js.map +1 -0
  92. package/dist/src/browser/action-builder/analyzer.d.ts +11 -0
  93. package/dist/src/browser/action-builder/analyzer.d.ts.map +1 -0
  94. package/dist/src/browser/action-builder/analyzer.js +71 -0
  95. package/dist/src/browser/action-builder/analyzer.js.map +1 -0
  96. package/dist/src/browser/action-builder/types.d.ts +47 -0
  97. package/dist/src/browser/action-builder/types.d.ts.map +1 -0
  98. package/dist/src/browser/action-builder/types.js +2 -0
  99. package/dist/src/browser/action-builder/types.js.map +1 -0
  100. package/dist/src/browser/adapters/gemini.d.ts +3 -0
  101. package/dist/src/browser/adapters/gemini.d.ts.map +1 -0
  102. package/dist/src/browser/adapters/gemini.js +16 -0
  103. package/dist/src/browser/adapters/gemini.js.map +1 -0
  104. package/dist/src/browser/adapters/google.d.ts +3 -0
  105. package/dist/src/browser/adapters/google.d.ts.map +1 -0
  106. package/dist/src/browser/adapters/google.js +17 -0
  107. package/dist/src/browser/adapters/google.js.map +1 -0
  108. package/dist/src/browser/adapters/index.d.ts +19 -0
  109. package/dist/src/browser/adapters/index.d.ts.map +1 -0
  110. package/dist/src/browser/adapters/index.js +23 -0
  111. package/dist/src/browser/adapters/index.js.map +1 -0
  112. package/dist/src/browser/adapters/instagram.d.ts +3 -0
  113. package/dist/src/browser/adapters/instagram.d.ts.map +1 -0
  114. package/dist/src/browser/adapters/instagram.js +17 -0
  115. package/dist/src/browser/adapters/instagram.js.map +1 -0
  116. package/dist/src/browser/adapters/linkedin.d.ts +3 -0
  117. package/dist/src/browser/adapters/linkedin.d.ts.map +1 -0
  118. package/dist/src/browser/adapters/linkedin.js +19 -0
  119. package/dist/src/browser/adapters/linkedin.js.map +1 -0
  120. package/dist/src/browser/adapters/microsoft.d.ts +3 -0
  121. package/dist/src/browser/adapters/microsoft.d.ts.map +1 -0
  122. package/dist/src/browser/adapters/microsoft.js +16 -0
  123. package/dist/src/browser/adapters/microsoft.js.map +1 -0
  124. package/dist/src/browser/adapters/x.d.ts +3 -0
  125. package/dist/src/browser/adapters/x.d.ts.map +1 -0
  126. package/dist/src/browser/adapters/x.js +19 -0
  127. package/dist/src/browser/adapters/x.js.map +1 -0
  128. package/dist/src/browser/dashboard/api-types.d.ts +50 -0
  129. package/dist/src/browser/dashboard/api-types.d.ts.map +1 -0
  130. package/dist/src/browser/dashboard/api-types.js +14 -0
  131. package/dist/src/browser/dashboard/api-types.js.map +1 -0
  132. package/dist/src/browser/dashboard/server.d.ts +9 -0
  133. package/dist/src/browser/dashboard/server.d.ts.map +1 -0
  134. package/dist/src/browser/dashboard/server.js +62 -0
  135. package/dist/src/browser/dashboard/server.js.map +1 -0
  136. package/dist/src/browser/dashboard/ui.html +1811 -0
  137. package/dist/src/browser/workflow/builtin-handlers.d.ts +3 -0
  138. package/dist/src/browser/workflow/builtin-handlers.d.ts.map +1 -0
  139. package/dist/src/browser/workflow/builtin-handlers.js +343 -0
  140. package/dist/src/browser/workflow/builtin-handlers.js.map +1 -0
  141. package/dist/src/browser/workflow/engine.d.ts +15 -0
  142. package/dist/src/browser/workflow/engine.d.ts.map +1 -0
  143. package/dist/src/browser/workflow/engine.js +127 -0
  144. package/dist/src/browser/workflow/engine.js.map +1 -0
  145. package/dist/src/browser/workflow/expression.d.ts +4 -0
  146. package/dist/src/browser/workflow/expression.d.ts.map +1 -0
  147. package/dist/src/browser/workflow/expression.js +64 -0
  148. package/dist/src/browser/workflow/expression.js.map +1 -0
  149. package/dist/src/browser/workflow/store.d.ts +24 -0
  150. package/dist/src/browser/workflow/store.d.ts.map +1 -0
  151. package/dist/src/browser/workflow/store.js +145 -0
  152. package/dist/src/browser/workflow/store.js.map +1 -0
  153. package/dist/src/browser/workflow/types.d.ts +48 -0
  154. package/dist/src/browser/workflow/types.d.ts.map +1 -0
  155. package/dist/src/browser/workflow/types.js +2 -0
  156. package/dist/src/browser/workflow/types.js.map +1 -0
  157. package/dist/src/commands/browse-action.d.ts +4 -0
  158. package/dist/src/commands/browse-action.d.ts.map +1 -0
  159. package/dist/src/commands/browse-action.js +151 -0
  160. package/dist/src/commands/browse-action.js.map +1 -0
  161. package/dist/src/commands/browse-platform.d.ts +4 -0
  162. package/dist/src/commands/browse-platform.d.ts.map +1 -0
  163. package/dist/src/commands/browse-platform.js +117 -0
  164. package/dist/src/commands/browse-platform.js.map +1 -0
  165. package/dist/src/commands/browse-workflow.d.ts +4 -0
  166. package/dist/src/commands/browse-workflow.d.ts.map +1 -0
  167. package/dist/src/commands/browse-workflow.js +153 -0
  168. package/dist/src/commands/browse-workflow.js.map +1 -0
  169. package/dist/src/commands/browse.d.ts +10 -6
  170. package/dist/src/commands/browse.d.ts.map +1 -1
  171. package/dist/src/commands/browse.js +11 -2154
  172. package/dist/src/commands/browse.js.map +1 -1
  173. package/dist/src/commands/design-detect.d.ts +21 -0
  174. package/dist/src/commands/design-detect.d.ts.map +1 -0
  175. package/dist/src/commands/design-detect.js +127 -0
  176. package/dist/src/commands/design-detect.js.map +1 -0
  177. package/dist/src/commands/design-palette.d.ts +22 -0
  178. package/dist/src/commands/design-palette.d.ts.map +1 -0
  179. package/dist/src/commands/design-palette.js +539 -0
  180. package/dist/src/commands/design-palette.js.map +1 -0
  181. package/dist/src/commands/hooks-core-commands.d.ts +10 -0
  182. package/dist/src/commands/hooks-core-commands.d.ts.map +1 -0
  183. package/dist/src/commands/hooks-core-commands.js +377 -0
  184. package/dist/src/commands/hooks-core-commands.js.map +1 -0
  185. package/dist/src/commands/hooks-coverage-commands.d.ts +12 -0
  186. package/dist/src/commands/hooks-coverage-commands.d.ts.map +1 -0
  187. package/dist/src/commands/hooks-coverage-commands.js +1217 -0
  188. package/dist/src/commands/hooks-coverage-commands.js.map +1 -0
  189. package/dist/src/commands/hooks-coverage-utils.d.ts +42 -0
  190. package/dist/src/commands/hooks-coverage-utils.d.ts.map +1 -0
  191. package/dist/src/commands/hooks-coverage-utils.js +220 -0
  192. package/dist/src/commands/hooks-coverage-utils.js.map +1 -0
  193. package/dist/src/commands/hooks-extended-commands.d.ts +14 -0
  194. package/dist/src/commands/hooks-extended-commands.d.ts.map +1 -0
  195. package/dist/src/commands/hooks-extended-commands.js +579 -0
  196. package/dist/src/commands/hooks-extended-commands.js.map +1 -0
  197. package/dist/src/commands/hooks-formatting.d.ts +13 -0
  198. package/dist/src/commands/hooks-formatting.d.ts.map +1 -0
  199. package/dist/src/commands/hooks-formatting.js +42 -0
  200. package/dist/src/commands/hooks-formatting.js.map +1 -0
  201. package/dist/src/commands/hooks-routing-commands.d.ts +15 -0
  202. package/dist/src/commands/hooks-routing-commands.d.ts.map +1 -0
  203. package/dist/src/commands/hooks-routing-commands.js +723 -0
  204. package/dist/src/commands/hooks-routing-commands.js.map +1 -0
  205. package/dist/src/commands/hooks-workers.d.ts +9 -0
  206. package/dist/src/commands/hooks-workers.d.ts.map +1 -0
  207. package/dist/src/commands/hooks-workers.js +782 -0
  208. package/dist/src/commands/hooks-workers.js.map +1 -0
  209. package/dist/src/commands/hooks.d.ts +8 -0
  210. package/dist/src/commands/hooks.d.ts.map +1 -1
  211. package/dist/src/commands/hooks.js +179 -4103
  212. package/dist/src/commands/hooks.js.map +1 -1
  213. package/dist/src/commands/index.d.ts +1 -0
  214. package/dist/src/commands/index.d.ts.map +1 -1
  215. package/dist/src/commands/index.js +6 -0
  216. package/dist/src/commands/index.js.map +1 -1
  217. package/dist/src/commands/org.d.ts.map +1 -1
  218. package/dist/src/commands/org.js +14 -15
  219. package/dist/src/commands/org.js.map +1 -1
  220. package/dist/src/commands/tokens.d.ts.map +1 -1
  221. package/dist/src/commands/tokens.js +77 -1
  222. package/dist/src/commands/tokens.js.map +1 -1
  223. package/dist/src/init/executor.d.ts.map +1 -1
  224. package/dist/src/init/executor.js +18 -8
  225. package/dist/src/init/executor.js.map +1 -1
  226. package/dist/src/init/settings-generator.d.ts.map +1 -1
  227. package/dist/src/init/settings-generator.js +39 -5
  228. package/dist/src/init/settings-generator.js.map +1 -1
  229. package/dist/src/init/statusline-generator.d.ts.map +1 -1
  230. package/dist/src/init/statusline-generator.js +25 -5
  231. package/dist/src/init/statusline-generator.js.map +1 -1
  232. package/dist/src/mcp-tools/browser-tools.d.ts +3 -5
  233. package/dist/src/mcp-tools/browser-tools.d.ts.map +1 -1
  234. package/dist/src/mcp-tools/browser-tools.js +619 -326
  235. package/dist/src/mcp-tools/browser-tools.js.map +1 -1
  236. package/dist/src/mcp-tools/hooks-embedding.d.ts +161 -0
  237. package/dist/src/mcp-tools/hooks-embedding.d.ts.map +1 -0
  238. package/dist/src/mcp-tools/hooks-embedding.js +506 -0
  239. package/dist/src/mcp-tools/hooks-embedding.js.map +1 -0
  240. package/dist/src/mcp-tools/hooks-intelligence.d.ts +26 -0
  241. package/dist/src/mcp-tools/hooks-intelligence.d.ts.map +1 -0
  242. package/dist/src/mcp-tools/hooks-intelligence.js +1328 -0
  243. package/dist/src/mcp-tools/hooks-intelligence.js.map +1 -0
  244. package/dist/src/mcp-tools/hooks-routing.d.ts +27 -0
  245. package/dist/src/mcp-tools/hooks-routing.d.ts.map +1 -0
  246. package/dist/src/mcp-tools/hooks-routing.js +1591 -0
  247. package/dist/src/mcp-tools/hooks-routing.js.map +1 -0
  248. package/dist/src/mcp-tools/hooks-tools.d.ts +3 -38
  249. package/dist/src/mcp-tools/hooks-tools.d.ts.map +1 -1
  250. package/dist/src/mcp-tools/hooks-tools.js +5 -3393
  251. package/dist/src/mcp-tools/hooks-tools.js.map +1 -1
  252. package/dist/src/mcp-tools/monograph-tools.d.ts.map +1 -1
  253. package/dist/src/mcp-tools/monograph-tools.js +24 -14
  254. package/dist/src/mcp-tools/monograph-tools.js.map +1 -1
  255. package/dist/src/mcp-tools/workflow-tools.d.ts.map +1 -1
  256. package/dist/src/mcp-tools/workflow-tools.js +54 -1
  257. package/dist/src/mcp-tools/workflow-tools.js.map +1 -1
  258. package/dist/src/memory/embedding-operations.d.ts +58 -0
  259. package/dist/src/memory/embedding-operations.d.ts.map +1 -0
  260. package/dist/src/memory/embedding-operations.js +299 -0
  261. package/dist/src/memory/embedding-operations.js.map +1 -0
  262. package/dist/src/memory/ewc-consolidation.d.ts.map +1 -1
  263. package/dist/src/memory/ewc-consolidation.js +37 -3
  264. package/dist/src/memory/ewc-consolidation.js.map +1 -1
  265. package/dist/src/memory/hnsw-operations.d.ts +130 -0
  266. package/dist/src/memory/hnsw-operations.d.ts.map +1 -0
  267. package/dist/src/memory/hnsw-operations.js +400 -0
  268. package/dist/src/memory/hnsw-operations.js.map +1 -0
  269. package/dist/src/memory/intelligence.d.ts.map +1 -1
  270. package/dist/src/memory/intelligence.js +42 -23
  271. package/dist/src/memory/intelligence.js.map +1 -1
  272. package/dist/src/memory/memory-bridge.d.ts.map +1 -1
  273. package/dist/src/memory/memory-bridge.js +52 -8
  274. package/dist/src/memory/memory-bridge.js.map +1 -1
  275. package/dist/src/memory/memory-crud.d.ts +67 -0
  276. package/dist/src/memory/memory-crud.d.ts.map +1 -0
  277. package/dist/src/memory/memory-crud.js +415 -0
  278. package/dist/src/memory/memory-crud.js.map +1 -0
  279. package/dist/src/memory/memory-initializer.d.ts +9 -322
  280. package/dist/src/memory/memory-initializer.d.ts.map +1 -1
  281. package/dist/src/memory/memory-initializer.js +17 -1794
  282. package/dist/src/memory/memory-initializer.js.map +1 -1
  283. package/dist/src/memory/memory-migrations.d.ts +30 -0
  284. package/dist/src/memory/memory-migrations.d.ts.map +1 -0
  285. package/dist/src/memory/memory-migrations.js +134 -0
  286. package/dist/src/memory/memory-migrations.js.map +1 -0
  287. package/dist/src/memory/memory-read.d.ts +78 -0
  288. package/dist/src/memory/memory-read.d.ts.map +1 -0
  289. package/dist/src/memory/memory-read.js +331 -0
  290. package/dist/src/memory/memory-read.js.map +1 -0
  291. package/dist/src/memory/memory-schema.d.ts +13 -0
  292. package/dist/src/memory/memory-schema.d.ts.map +1 -0
  293. package/dist/src/memory/memory-schema.js +167 -0
  294. package/dist/src/memory/memory-schema.js.map +1 -0
  295. package/dist/src/memory/sona-optimizer.d.ts.map +1 -1
  296. package/dist/src/memory/sona-optimizer.js +37 -4
  297. package/dist/src/memory/sona-optimizer.js.map +1 -1
  298. package/dist/src/monovector/route-outcomes.d.ts.map +1 -1
  299. package/dist/src/monovector/route-outcomes.js +16 -6
  300. package/dist/src/monovector/route-outcomes.js.map +1 -1
  301. package/dist/src/pricing/model-pricing.d.ts +41 -0
  302. package/dist/src/pricing/model-pricing.d.ts.map +1 -0
  303. package/dist/src/pricing/model-pricing.js +61 -0
  304. package/dist/src/pricing/model-pricing.js.map +1 -0
  305. package/dist/src/ui/.monomind/capture/active-run.json +1 -0
  306. package/dist/src/ui/.monomind/orgs/system-trial-qa/runs/real-events-1782290897.convs.jsonl +3 -0
  307. package/dist/src/ui/.monomind/orgs/system-trial-qa/runs/real-events-1782290897.jsonl +11 -0
  308. package/dist/src/ui/.monomind/orgs/system-trial-qa/runs/rigid-qa-restart-1782288201.jsonl +540 -0
  309. package/dist/src/ui/.monomind/orgs/system-trial-qa-threads.jsonl +3 -0
  310. package/dist/src/ui/.monomind/orgs/test-event-fix/runs/rigid-qa-restart-1782288201.jsonl +2 -0
  311. package/dist/src/ui/MODULARIZATION_PLAN.md +79 -0
  312. package/dist/src/ui/collector.mjs +23 -13
  313. package/dist/src/ui/dashboard.html +1652 -13
  314. package/dist/src/ui/data/known-projects.json +1 -0
  315. package/dist/src/ui/data/mastermind-events.jsonl +553 -0
  316. package/dist/src/ui/data/sessions/_index.json +1 -0
  317. package/dist/src/ui/data/sessions/final-sess-001.jsonl +542 -0
  318. package/dist/src/ui/data/unknown-events.jsonl +1 -0
  319. package/dist/src/ui/orgs.html +154 -10
  320. package/dist/src/ui/server.mjs +1131 -168
  321. package/dist/src/ui/sse-manager.mjs +119 -0
  322. package/dist/src/update/checker.js +1 -1
  323. package/dist/src/update/checker.js.map +1 -1
  324. package/dist/tsconfig.tsbuildinfo +1 -1
  325. package/dist/workflow/builtin-handlers.js +321 -0
  326. package/dist/workflow/engine.js +253 -0
  327. package/dist/workflow/expression.js +98 -0
  328. package/dist/workflow/types.js +2 -0
  329. package/package.json +8 -5
@@ -1,285 +1,66 @@
1
1
  /**
2
2
  * CLI Hooks Command
3
3
  * Self-learning hooks system for intelligent workflow automation
4
+ *
5
+ * This file is the main registration entry point.
6
+ * Commands are extracted to sub-modules (ARCH-1):
7
+ * - hooks-core-commands.ts — pre/post edit and command hooks
8
+ * - hooks-routing-commands.ts — route/explain/pretrain/build-agents/metrics/transfer/list
9
+ * - hooks-workers.ts — intelligence and worker commands
10
+ * - hooks-coverage-commands.ts — coverage-aware routing
11
+ * - hooks-extended-commands.ts — token optimize, model routing, agent teams
4
12
  */
5
13
  import { output } from '../output.js';
6
- import { confirm } from '../prompt.js';
7
14
  import { callMCPTool, MCPClientError } from '../mcp-client.js';
8
- import { storeCommand } from './transfer-store.js';
9
- import { existsSync, readFileSync, statSync } from 'node:fs';
10
- import { join } from 'node:path';
11
- /**
12
- * Read coverage data from disk. Checks these locations in order:
13
- * 1. coverage/coverage-summary.json (Jest/Istanbul)
14
- * 2. coverage/lcov.info (lcov format)
15
- * 3. .nyc_output/out.json (nyc)
16
- */
17
- function readCoverageFromDisk() {
18
- const cwd = process.cwd();
19
- const noData = {
20
- found: false,
21
- source: 'none',
22
- entries: [],
23
- summary: { totalFiles: 0, overallLineCoverage: 0, overallBranchCoverage: 0, overallFunctionCoverage: 0, overallStatementCoverage: 0 },
24
- };
25
- // 1. Try coverage-summary.json (Jest/Istanbul)
26
- for (const relPath of ['coverage/coverage-summary.json', 'coverage-summary.json']) {
27
- const summaryPath = join(cwd, relPath);
28
- if (existsSync(summaryPath)) {
29
- try {
30
- if (statSync(summaryPath).size <= 4_194_304) {
31
- const raw = JSON.parse(readFileSync(summaryPath, 'utf-8'));
32
- return parseCoverageSummaryJson(raw, relPath);
33
- }
34
- }
35
- catch {
36
- // malformed, try next
37
- }
38
- }
39
- }
40
- // 2. Try lcov.info
41
- for (const relPath of ['coverage/lcov.info', 'lcov.info']) {
42
- const lcovPath = join(cwd, relPath);
43
- if (existsSync(lcovPath)) {
44
- try {
45
- if (statSync(lcovPath).size <= 8_388_608) {
46
- const raw = readFileSync(lcovPath, 'utf-8');
47
- return parseLcovInfo(raw, relPath);
48
- }
49
- }
50
- catch {
51
- // malformed, try next
52
- }
53
- }
54
- }
55
- // 3. Try .nyc_output/out.json
56
- const nycPath = join(cwd, '.nyc_output', 'out.json');
57
- if (existsSync(nycPath)) {
58
- try {
59
- if (statSync(nycPath).size <= 4_194_304) {
60
- const raw = JSON.parse(readFileSync(nycPath, 'utf-8'));
61
- return parseCoverageSummaryJson(raw, '.nyc_output/out.json');
62
- }
63
- }
64
- catch {
65
- // malformed
66
- }
67
- }
68
- return noData;
69
- }
70
- function parseCoverageSummaryJson(data, source) {
71
- const entries = [];
72
- let totalLines = 0, coveredLines = 0;
73
- let totalBranches = 0, coveredBranches = 0;
74
- let totalFunctions = 0, coveredFunctions = 0;
75
- let totalStatements = 0, coveredStatements = 0;
76
- for (const [filePath, metrics] of Object.entries(data)) {
77
- if (filePath === 'total')
78
- continue;
79
- const m = metrics;
80
- if (!m || typeof m !== 'object')
81
- continue;
82
- const linePct = m.lines?.pct ?? (m.lines?.covered != null ? ((m.lines?.covered ?? 0) / Math.max(m.lines?.total ?? 1, 1)) * 100 : 0);
83
- const branchPct = m.branches?.pct ?? (m.branches?.total ? ((m.branches?.covered ?? 0) / m.branches.total) * 100 : 100);
84
- const funcPct = m.functions?.pct ?? (m.functions?.total ? ((m.functions?.covered ?? 0) / m.functions.total) * 100 : 100);
85
- const stmtPct = m.statements?.pct ?? (m.statements?.total ? ((m.statements?.covered ?? 0) / m.statements.total) * 100 : 100);
86
- entries.push({ filePath, lines: linePct, branches: branchPct, functions: funcPct, statements: stmtPct });
87
- totalLines += m.lines?.total ?? 0;
88
- coveredLines += m.lines?.covered ?? 0;
89
- totalBranches += m.branches?.total ?? 0;
90
- coveredBranches += m.branches?.covered ?? 0;
91
- totalFunctions += m.functions?.total ?? 0;
92
- coveredFunctions += m.functions?.covered ?? 0;
93
- totalStatements += m.statements?.total ?? 0;
94
- coveredStatements += m.statements?.covered ?? 0;
95
- }
96
- // Also read the total key if present
97
- const total = data['total'];
98
- const overallLine = total?.lines?.pct ?? (totalLines > 0 ? (coveredLines / totalLines) * 100 : 0);
99
- const overallBranch = total?.branches?.pct ?? (totalBranches > 0 ? (coveredBranches / totalBranches) * 100 : 0);
100
- const overallFunction = total?.functions?.pct ?? (totalFunctions > 0 ? (coveredFunctions / totalFunctions) * 100 : 0);
101
- const overallStatement = total?.statements?.pct ?? (totalStatements > 0 ? (coveredStatements / totalStatements) * 100 : 0);
102
- // Sort by lowest line coverage
103
- entries.sort((a, b) => a.lines - b.lines);
104
- return {
105
- found: true,
106
- source,
107
- entries,
108
- summary: {
109
- totalFiles: entries.length,
110
- overallLineCoverage: overallLine,
111
- overallBranchCoverage: overallBranch,
112
- overallFunctionCoverage: overallFunction,
113
- overallStatementCoverage: overallStatement,
114
- },
115
- };
116
- }
117
- function parseLcovInfo(raw, source) {
118
- const entries = [];
119
- let currentFile = '';
120
- let linesHit = 0, linesFound = 0;
121
- let branchesHit = 0, branchesFound = 0;
122
- let functionsHit = 0, functionsFound = 0;
123
- let totalLines = 0, coveredLines = 0;
124
- let totalBranches = 0, coveredBranches = 0;
125
- let totalFunctions = 0, coveredFunctions = 0;
126
- let totalStatements = 0, coveredStatements = 0;
127
- const flushRecord = () => {
128
- if (currentFile) {
129
- entries.push({
130
- filePath: currentFile,
131
- lines: linesFound > 0 ? (linesHit / linesFound) * 100 : 0,
132
- branches: branchesFound > 0 ? (branchesHit / branchesFound) * 100 : 100,
133
- functions: functionsFound > 0 ? (functionsHit / functionsFound) * 100 : 100,
134
- statements: linesFound > 0 ? (linesHit / linesFound) * 100 : 0,
135
- });
136
- totalLines += linesFound;
137
- coveredLines += linesHit;
138
- totalBranches += branchesFound;
139
- coveredBranches += branchesHit;
140
- totalFunctions += functionsFound;
141
- coveredFunctions += functionsHit;
142
- totalStatements += linesFound;
143
- coveredStatements += linesHit;
144
- }
145
- };
146
- for (const line of raw.split('\n')) {
147
- const trimmed = line.trim();
148
- if (trimmed.startsWith('SF:')) {
149
- currentFile = trimmed.slice(3);
150
- linesHit = 0;
151
- linesFound = 0;
152
- branchesHit = 0;
153
- branchesFound = 0;
154
- functionsHit = 0;
155
- functionsFound = 0;
156
- }
157
- else if (trimmed.startsWith('LH:')) {
158
- linesHit = parseInt(trimmed.slice(3), 10) || 0;
159
- }
160
- else if (trimmed.startsWith('LF:')) {
161
- linesFound = parseInt(trimmed.slice(3), 10) || 0;
162
- }
163
- else if (trimmed.startsWith('BRH:')) {
164
- branchesHit = parseInt(trimmed.slice(4), 10) || 0;
165
- }
166
- else if (trimmed.startsWith('BRF:')) {
167
- branchesFound = parseInt(trimmed.slice(4), 10) || 0;
168
- }
169
- else if (trimmed.startsWith('FNH:')) {
170
- functionsHit = parseInt(trimmed.slice(4), 10) || 0;
171
- }
172
- else if (trimmed.startsWith('FNF:')) {
173
- functionsFound = parseInt(trimmed.slice(4), 10) || 0;
174
- }
175
- else if (trimmed === 'end_of_record') {
176
- flushRecord();
177
- currentFile = '';
178
- }
179
- }
180
- flushRecord();
181
- entries.sort((a, b) => a.lines - b.lines);
182
- return {
183
- found: true,
184
- source,
185
- entries,
186
- summary: {
187
- totalFiles: entries.length,
188
- overallLineCoverage: totalLines > 0 ? (coveredLines / totalLines) * 100 : 0,
189
- overallBranchCoverage: totalBranches > 0 ? (coveredBranches / totalBranches) * 100 : 100,
190
- overallFunctionCoverage: totalFunctions > 0 ? (coveredFunctions / totalFunctions) * 100 : 0,
191
- overallStatementCoverage: totalStatements > 0 ? (coveredStatements / totalStatements) * 100 : 0,
192
- },
193
- };
194
- }
195
- /**
196
- * Classify a coverage gap by priority type based on coverage percentage and threshold
197
- */
198
- function classifyCoverageGap(coveragePct, threshold) {
199
- if (coveragePct < threshold * 0.25)
200
- return { gapType: 'critical', priority: 10 };
201
- if (coveragePct < threshold * 0.5)
202
- return { gapType: 'high', priority: 7 };
203
- if (coveragePct < threshold * 0.75)
204
- return { gapType: 'medium', priority: 5 };
205
- if (coveragePct < threshold)
206
- return { gapType: 'low', priority: 3 };
207
- return { gapType: 'ok', priority: 0 };
208
- }
209
- /**
210
- * Suggest agents for a file based on its path
211
- */
212
- function suggestAgentsForFile(filePath) {
213
- const lower = filePath.toLowerCase();
214
- if (lower.includes('test') || lower.includes('spec'))
215
- return ['tester'];
216
- if (lower.includes('security') || lower.includes('auth'))
217
- return ['security-auditor', 'tester'];
218
- if (lower.includes('api') || lower.includes('route') || lower.includes('controller'))
219
- return ['coder', 'tester'];
220
- if (lower.includes('model') || lower.includes('schema') || lower.includes('entity'))
221
- return ['coder', 'tester'];
222
- return ['tester', 'coder'];
223
- }
224
- // Hook types
225
- const HOOK_TYPES = [
226
- { value: 'pre-edit', label: 'Pre-Edit', hint: 'Get context before editing files' },
227
- { value: 'post-edit', label: 'Post-Edit', hint: 'Record editing outcomes' },
228
- { value: 'pre-command', label: 'Pre-Command', hint: 'Assess risk before commands' },
229
- { value: 'post-command', label: 'Post-Command', hint: 'Record command outcomes' },
230
- { value: 'route', label: 'Route', hint: 'Route tasks to optimal agents' },
231
- { value: 'explain', label: 'Explain', hint: 'Explain routing decisions' }
232
- ];
233
- // Agent routing options
234
- const AGENT_TYPES = [
235
- 'coder', 'researcher', 'tester', 'reviewer', 'architect',
236
- 'security-architect', 'security-auditor', 'memory-specialist',
237
- 'swarm-specialist', 'performance-engineer', 'core-architect',
238
- 'test-architect', 'coordinator', 'analyst', 'optimizer'
239
- ];
240
- // Pre-edit subcommand
241
- const preEditCommand = {
242
- name: 'pre-edit',
243
- description: 'Get context and agent suggestions before editing a file',
15
+ import { intelligenceCommand, workerCommand } from './hooks-workers.js';
16
+ import { coverageRouteCommand, coverageSuggestCommand, coverageGapsCommand, progressHookCommand, statuslineCommand, } from './hooks-coverage-commands.js';
17
+ import { tokenOptimizeCommand, modelRouteCommand, modelOutcomeCommand, modelStatsCommand, teammateIdleCommand, taskCompletedCommand, notifyCommand, } from './hooks-extended-commands.js';
18
+ import { preEditCommand, postEditCommand, preCommandCommand, postCommandCommand, } from './hooks-core-commands.js';
19
+ import { routeCommand, explainCommand, pretrainCommand, buildAgentsCommand, metricsCommand, transferCommand, listCommand, } from './hooks-routing-commands.js';
20
+ // Pre-task subcommand
21
+ const preTaskCommand = {
22
+ name: 'pre-task',
23
+ description: 'Record task start and get agent suggestions',
244
24
  options: [
245
25
  {
246
- name: 'file',
247
- short: 'f',
248
- description: 'File path to edit',
249
- type: 'string',
250
- required: false
26
+ name: 'task-id',
27
+ short: 'i',
28
+ description: 'Unique task identifier (auto-generated if omitted)',
29
+ type: 'string'
251
30
  },
252
31
  {
253
- name: 'operation',
254
- short: 'o',
255
- description: 'Type of edit operation (create, update, delete, refactor)',
32
+ name: 'description',
33
+ short: 'd',
34
+ description: 'Task description',
256
35
  type: 'string',
257
- default: 'update'
36
+ required: true
258
37
  },
259
38
  {
260
- name: 'context',
261
- short: 'c',
262
- description: 'Additional context about the edit',
263
- type: 'string'
39
+ name: 'auto-spawn',
40
+ short: 'a',
41
+ description: 'Auto-spawn suggested agents',
42
+ type: 'boolean',
43
+ default: false
264
44
  }
265
45
  ],
266
46
  examples: [
267
- { command: 'monomind hooks pre-edit -f src/utils.ts', description: 'Get context before editing' },
268
- { command: 'monomind hooks pre-edit -f src/api.ts -o refactor', description: 'Pre-edit with operation type' }
47
+ { command: 'monomind hooks pre-task -i task-123 -d "Fix auth bug"', description: 'Record task start' },
48
+ { command: 'monomind hooks pre-task -i task-456 -d "Implement feature" --auto-spawn', description: 'With auto-spawn' }
269
49
  ],
270
50
  action: async (ctx) => {
271
- // Default file to 'unknown' for backward compatibility (env var may be empty)
272
- const filePath = ctx.args[0] || ctx.flags.file || 'unknown';
273
- const operation = ctx.flags.operation || 'update';
274
- output.printInfo(`Analyzing context for: ${output.highlight(filePath)}`);
51
+ const taskId = ctx.flags['task-id'] || `task-${Date.now().toString(36)}`;
52
+ const description = ctx.args[0] || ctx.flags.description;
53
+ if (!description) {
54
+ output.printError('Description is required: --description "your task"');
55
+ return { success: false, exitCode: 1 };
56
+ }
57
+ output.printInfo(`Starting task: ${output.highlight(taskId)}`);
275
58
  try {
276
- // Call MCP tool for pre-edit hook
277
- const result = await callMCPTool('hooks_pre-edit', {
278
- filePath,
279
- operation,
280
- context: ctx.flags.context,
281
- includePatterns: true,
282
- includeRisks: true,
59
+ const result = await callMCPTool('hooks_pre-task', {
60
+ taskId,
61
+ description,
62
+ autoSpawn: ctx.flags['auto-spawn'] || false,
63
+ timestamp: Date.now(),
283
64
  });
284
65
  if (ctx.flags.format === 'json') {
285
66
  output.printJson(result);
@@ -287,47 +68,38 @@ const preEditCommand = {
287
68
  }
288
69
  output.writeln();
289
70
  output.printBox([
290
- `File: ${result.filePath}`,
291
- `Operation: ${result.operation}`,
292
- `Type: ${result.context.fileType}`,
293
- `Exists: ${result.context.fileExists ? 'Yes' : 'No'}`
294
- ].join('\n'), 'File Context');
295
- if (result.context.suggestedAgents.length > 0) {
71
+ `Task ID: ${result.taskId}`,
72
+ `Description: ${result.description}`,
73
+ `Complexity: ${result.complexity.toUpperCase()}`,
74
+ `Est. Duration: ${result.estimatedDuration}`
75
+ ].join('\n'), 'Task Registered');
76
+ if (result.suggestedAgents.length > 0) {
296
77
  output.writeln();
297
78
  output.writeln(output.bold('Suggested Agents'));
298
- output.printList(result.context.suggestedAgents.map(a => output.highlight(a)));
299
- }
300
- if (result.context.relatedFiles.length > 0) {
301
- output.writeln();
302
- output.writeln(output.bold('Related Files'));
303
- output.printList(result.context.relatedFiles.slice(0, 5).map(f => output.dim(f)));
304
- }
305
- if (result.context.patterns.length > 0) {
306
- output.writeln();
307
- output.writeln(output.bold('Learned Patterns'));
308
79
  output.printTable({
309
80
  columns: [
310
- { key: 'pattern', header: 'Pattern', width: 40 },
311
- { key: 'confidence', header: 'Confidence', width: 12, align: 'right', format: (v) => `${(Number(v) * 100).toFixed(1)}%` }
81
+ { key: 'type', header: 'Agent Type', width: 20 },
82
+ { key: 'confidence', header: 'Confidence', width: 12, align: 'right', format: (v) => `${(Number(v) * 100).toFixed(1)}%` },
83
+ { key: 'reason', header: 'Reason', width: 35 }
312
84
  ],
313
- data: result.context.patterns
85
+ data: result.suggestedAgents
314
86
  });
315
87
  }
316
- if (result.context.risks.length > 0) {
88
+ if (result.risks.length > 0) {
317
89
  output.writeln();
318
90
  output.writeln(output.bold(output.error('Potential Risks')));
319
- output.printList(result.context.risks.map(r => output.warning(r)));
91
+ output.printList(result.risks.map(r => output.warning(r)));
320
92
  }
321
93
  if (result.recommendations.length > 0) {
322
94
  output.writeln();
323
95
  output.writeln(output.bold('Recommendations'));
324
- output.printList(result.recommendations.map(r => output.success(`• ${r}`)));
96
+ output.printList(result.recommendations);
325
97
  }
326
98
  return { success: true, data: result };
327
99
  }
328
100
  catch (error) {
329
101
  if (error instanceof MCPClientError) {
330
- output.printError(`Pre-edit hook failed: ${error.message}`);
102
+ output.printError(`Pre-task hook failed: ${error.message}`);
331
103
  }
332
104
  else {
333
105
  output.printError(`Unexpected error: ${String(error)}`);
@@ -336,66 +108,57 @@ const preEditCommand = {
336
108
  }
337
109
  }
338
110
  };
339
- // Post-edit subcommand
340
- const postEditCommand = {
341
- name: 'post-edit',
342
- description: 'Record editing outcome for learning',
111
+ // Post-task subcommand
112
+ const postTaskCommand = {
113
+ name: 'post-task',
114
+ description: 'Record task completion for learning',
343
115
  options: [
344
116
  {
345
- name: 'file',
346
- short: 'f',
347
- description: 'File path that was edited',
117
+ name: 'task-id',
118
+ short: 'i',
119
+ description: 'Task identifier',
348
120
  type: 'string',
349
- required: false
121
+ required: true
350
122
  },
351
123
  {
352
124
  name: 'success',
353
125
  short: 's',
354
- description: 'Whether the edit was successful',
126
+ description: 'Whether the task succeeded',
355
127
  type: 'boolean',
356
128
  required: false
357
129
  },
130
+ {
131
+ name: 'duration',
132
+ short: 'd',
133
+ description: 'Task duration in milliseconds',
134
+ type: 'number'
135
+ },
358
136
  {
359
137
  name: 'outcome',
360
138
  short: 'o',
361
139
  description: 'Outcome description',
362
140
  type: 'string'
363
- },
364
- {
365
- name: 'metrics',
366
- short: 'm',
367
- description: 'Performance metrics (e.g., "time:500ms,quality:0.95")',
368
- type: 'string'
369
141
  }
370
142
  ],
371
143
  examples: [
372
- { command: 'monomind hooks post-edit -f src/utils.ts --success true', description: 'Record successful edit' },
373
- { command: 'monomind hooks post-edit -f src/api.ts --success false -o "Type error"', description: 'Record failed edit' }
144
+ { command: 'monomind hooks post-task -i task-123 --success true', description: 'Record successful task' },
145
+ { command: 'monomind hooks post-task -i task-456 --success false -o "Build failed"', description: 'Record failed task' }
374
146
  ],
375
147
  action: async (ctx) => {
376
- // Default file to 'unknown' for backward compatibility (env var may be empty)
377
- const filePath = ctx.args[0] || ctx.flags.file || 'unknown';
378
- // Default success to true for backward compatibility (PostToolUse = success, PostToolUseFailure = failure)
148
+ const taskId = ctx.args[0] || ctx.flags['task-id'];
149
+ // Default success to true for backward compatibility
379
150
  const success = ctx.flags.success !== undefined ? ctx.flags.success : true;
380
- output.printInfo(`Recording outcome for: ${output.highlight(filePath)}`);
151
+ if (!taskId) {
152
+ output.printError('Task ID is required. Use --task-id or -i flag.');
153
+ return { success: false, exitCode: 1 };
154
+ }
155
+ output.printInfo(`Recording task completion: ${output.highlight(taskId)}`);
381
156
  try {
382
- // Parse metrics if provided
383
- const metrics = {};
384
- if (ctx.flags.metrics) {
385
- const metricsStr = ctx.flags.metrics;
386
- metricsStr.split(',').forEach(pair => {
387
- const [key, value] = pair.split(':');
388
- if (key && value) {
389
- metrics[key.trim()] = parseFloat(value);
390
- }
391
- });
392
- }
393
- // Call MCP tool for post-edit hook
394
- const result = await callMCPTool('hooks_post-edit', {
395
- filePath,
157
+ const result = await callMCPTool('hooks_post-task', {
158
+ taskId,
396
159
  success,
160
+ duration: ctx.flags.duration,
397
161
  outcome: ctx.flags.outcome,
398
- metrics,
399
162
  timestamp: Date.now(),
400
163
  });
401
164
  if (ctx.flags.format === 'json') {
@@ -403,27 +166,23 @@ const postEditCommand = {
403
166
  return { success: true, data: result };
404
167
  }
405
168
  output.writeln();
406
- output.printSuccess(`Outcome recorded for ${filePath}`);
169
+ output.printSuccess(`Task ${taskId} recorded as ${success ? 'successful' : 'failed'}`);
407
170
  if (result.learningUpdates) {
408
171
  output.writeln();
409
- output.writeln(output.bold('Learning Updates'));
410
- output.printTable({
411
- columns: [
412
- { key: 'metric', header: 'Metric', width: 25 },
413
- { key: 'value', header: 'Value', width: 15, align: 'right' }
414
- ],
415
- data: [
416
- { metric: 'Patterns Updated', value: result.learningUpdates.patternsUpdated },
417
- { metric: 'Confidence Adjusted', value: result.learningUpdates.confidenceAdjusted },
418
- { metric: 'New Patterns', value: result.learningUpdates.newPatterns }
419
- ]
420
- });
172
+ output.writeln(output.dim(`Agent patterns updated: ${result.learningUpdates.agentPatternsUpdated}`));
173
+ output.writeln(output.dim(`Strategies learned: ${result.learningUpdates.taskStrategiesLearned}`));
174
+ output.writeln(output.dim(`Complexity model: ${result.learningUpdates.complexityModelUpdated ? 'Updated' : 'No change'}`));
175
+ }
176
+ if (result.nextRecommendations && result.nextRecommendations.length > 0) {
177
+ output.writeln();
178
+ output.writeln(output.bold('Recommendations for Next Task'));
179
+ output.printList(result.nextRecommendations);
421
180
  }
422
181
  return { success: true, data: result };
423
182
  }
424
183
  catch (error) {
425
184
  if (error instanceof MCPClientError) {
426
- output.printError(`Post-edit hook failed: ${error.message}`);
185
+ output.printError(`Post-task hook failed: ${error.message}`);
427
186
  }
428
187
  else {
429
188
  output.printError(`Unexpected error: ${String(error)}`);
@@ -432,94 +191,62 @@ const postEditCommand = {
432
191
  }
433
192
  }
434
193
  };
435
- // Pre-command subcommand
436
- const preCommandCommand = {
437
- name: 'pre-command',
438
- description: 'Assess risk before executing a command',
194
+ // Session-end subcommand
195
+ const sessionEndCommand = {
196
+ name: 'session-end',
197
+ description: 'End current session and persist state',
439
198
  options: [
440
199
  {
441
- name: 'command',
442
- short: 'c',
443
- description: 'Command to execute',
444
- type: 'string',
445
- required: true
446
- },
447
- {
448
- name: 'dry-run',
449
- short: 'd',
450
- description: 'Only analyze, do not execute',
200
+ name: 'save-state',
201
+ short: 's',
202
+ description: 'Save session state for later restoration',
451
203
  type: 'boolean',
452
204
  default: true
453
205
  }
454
206
  ],
455
207
  examples: [
456
- { command: 'monomind hooks pre-command -c "rm -rf dist"', description: 'Assess command risk' },
457
- { command: 'monomind hooks pre-command -c "npm install lodash"', description: 'Check package install' }
208
+ { command: 'monomind hooks session-end', description: 'End and save session' },
209
+ { command: 'monomind hooks session-end --save-state false', description: 'End without saving' }
458
210
  ],
459
211
  action: async (ctx) => {
460
- const command = ctx.args[0] || ctx.flags.command;
461
- if (!command) {
462
- output.printError('Command is required. Use --command or -c flag.');
463
- return { success: false, exitCode: 1 };
464
- }
465
- output.printInfo(`Analyzing command: ${output.highlight(command)}`);
212
+ output.printInfo('Ending session...');
466
213
  try {
467
- // Call MCP tool for pre-command hook
468
- const result = await callMCPTool('hooks_pre-command', {
469
- command,
470
- includeAlternatives: true,
214
+ const result = await callMCPTool('hooks_session-end', {
215
+ saveState: ctx.flags['save-state'] ?? true,
216
+ timestamp: Date.now(),
471
217
  });
472
218
  if (ctx.flags.format === 'json') {
473
219
  output.printJson(result);
474
220
  return { success: true, data: result };
475
221
  }
476
222
  output.writeln();
477
- // Risk level indicator
478
- let riskIndicator;
479
- switch (result.riskLevel) {
480
- case 'critical':
481
- riskIndicator = output.error('CRITICAL');
482
- break;
483
- case 'high':
484
- riskIndicator = output.error('HIGH');
485
- break;
486
- case 'medium':
487
- riskIndicator = output.warning('MEDIUM');
488
- break;
489
- default:
490
- riskIndicator = output.success('LOW');
491
- }
492
- output.printBox([
493
- `Risk Level: ${riskIndicator}`,
494
- `Should Proceed: ${result.shouldProceed ? output.success('Yes') : output.error('No')}`
495
- ].join('\n'), 'Risk Assessment');
496
- if (result.risks.length > 0) {
497
- output.writeln();
498
- output.writeln(output.bold('Identified Risks'));
499
- output.printTable({
500
- columns: [
501
- { key: 'type', header: 'Type', width: 15 },
502
- { key: 'severity', header: 'Severity', width: 10 },
503
- { key: 'description', header: 'Description', width: 40 }
504
- ],
505
- data: result.risks
506
- });
507
- }
508
- if (result.safeAlternatives && result.safeAlternatives.length > 0) {
509
- output.writeln();
510
- output.writeln(output.bold('Safe Alternatives'));
511
- output.printList(result.safeAlternatives.map(a => output.success(a)));
512
- }
513
- if (result.recommendations.length > 0) {
223
+ output.printSuccess(`Session ${result.sessionId} ended`);
224
+ output.writeln();
225
+ output.writeln(output.bold('Session Summary'));
226
+ output.printTable({
227
+ columns: [
228
+ { key: 'metric', header: 'Metric', width: 25 },
229
+ { key: 'value', header: 'Value', width: 15, align: 'right' }
230
+ ],
231
+ data: [
232
+ { metric: 'Duration', value: `${(result.duration / 1000 / 60).toFixed(1)} min` },
233
+ { metric: 'Tasks Executed', value: result.summary.tasksExecuted },
234
+ { metric: 'Tasks Succeeded', value: output.success(String(result.summary.tasksSucceeded)) },
235
+ { metric: 'Tasks Failed', value: output.error(String(result.summary.tasksFailed)) },
236
+ { metric: 'Commands Executed', value: result.summary.commandsExecuted },
237
+ { metric: 'Files Modified', value: result.summary.filesModified },
238
+ { metric: 'Agents Spawned', value: result.summary.agentsSpawned }
239
+ ]
240
+ });
241
+ if (result.statePath) {
514
242
  output.writeln();
515
- output.writeln(output.bold('Recommendations'));
516
- output.printList(result.recommendations);
243
+ output.writeln(output.dim(`State saved to: ${result.statePath}`));
517
244
  }
518
245
  return { success: true, data: result };
519
246
  }
520
247
  catch (error) {
521
248
  if (error instanceof MCPClientError) {
522
- output.printError(`Pre-command hook failed: ${error.message}`);
249
+ output.printError(`Session-end hook failed: ${error.message}`);
523
250
  }
524
251
  else {
525
252
  output.printError(`Unexpected error: ${String(error)}`);
@@ -528,59 +255,45 @@ const preCommandCommand = {
528
255
  }
529
256
  }
530
257
  };
531
- // Post-command subcommand
532
- const postCommandCommand = {
533
- name: 'post-command',
534
- description: 'Record command execution outcome',
258
+ // Session-restore subcommand
259
+ const sessionRestoreCommand = {
260
+ name: 'session-restore',
261
+ description: 'Restore a previous session',
535
262
  options: [
536
263
  {
537
- name: 'command',
538
- short: 'c',
539
- description: 'Command that was executed',
264
+ name: 'session-id',
265
+ short: 'i',
266
+ description: 'Session ID to restore (use "latest" for most recent)',
540
267
  type: 'string',
541
- required: true
268
+ default: 'latest'
542
269
  },
543
270
  {
544
- name: 'success',
545
- short: 's',
546
- description: 'Whether the command succeeded',
271
+ name: 'restore-agents',
272
+ short: 'a',
273
+ description: 'Restore spawned agents',
547
274
  type: 'boolean',
548
- required: false
549
- },
550
- {
551
- name: 'exit-code',
552
- short: 'e',
553
- description: 'Command exit code',
554
- type: 'number',
555
- default: 0
275
+ default: true
556
276
  },
557
277
  {
558
- name: 'duration',
559
- short: 'd',
560
- description: 'Execution duration in milliseconds',
561
- type: 'number'
278
+ name: 'restore-tasks',
279
+ short: 't',
280
+ description: 'Restore active tasks',
281
+ type: 'boolean',
282
+ default: true
562
283
  }
563
284
  ],
564
285
  examples: [
565
- { command: 'monomind hooks post-command -c "npm test" --success true', description: 'Record successful test run' },
566
- { command: 'monomind hooks post-command -c "npm build" --success false -e 1', description: 'Record failed build' }
286
+ { command: 'monomind hooks session-restore', description: 'Restore latest session' },
287
+ { command: 'monomind hooks session-restore -i session-12345', description: 'Restore specific session' }
567
288
  ],
568
289
  action: async (ctx) => {
569
- const command = ctx.args[0] || ctx.flags.command;
570
- // Default success to true for backward compatibility
571
- const success = ctx.flags.success !== undefined ? ctx.flags.success : true;
572
- if (!command) {
573
- output.printError('Command is required. Use --command or -c flag.');
574
- return { success: false, exitCode: 1 };
575
- }
576
- output.printInfo(`Recording command outcome: ${output.highlight(command)}`);
290
+ const sessionId = ctx.args[0] || ctx.flags['session-id'] || 'latest';
291
+ output.printInfo(`Restoring session: ${output.highlight(sessionId)}`);
577
292
  try {
578
- // Call MCP tool for post-command hook
579
- const result = await callMCPTool('hooks_post-command', {
580
- command,
581
- success,
582
- exitCode: ctx.flags['exit-code'] || 0,
583
- duration: ctx.flags.duration,
293
+ const result = await callMCPTool('hooks_session-restore', {
294
+ sessionId,
295
+ restoreAgents: ctx.flags['restore-agents'] ?? true,
296
+ restoreTasks: ctx.flags['restore-tasks'] ?? true,
584
297
  timestamp: Date.now(),
585
298
  });
586
299
  if (ctx.flags.format === 'json') {
@@ -588,17 +301,31 @@ const postCommandCommand = {
588
301
  return { success: true, data: result };
589
302
  }
590
303
  output.writeln();
591
- output.printSuccess('Command outcome recorded');
592
- if (result.learningUpdates) {
304
+ output.printSuccess(`Session restored from ${result.originalSessionId}`);
305
+ output.writeln(output.dim(`New session ID: ${result.sessionId}`));
306
+ output.writeln();
307
+ output.writeln(output.bold('Restored State'));
308
+ output.printTable({
309
+ columns: [
310
+ { key: 'item', header: 'Item', width: 25 },
311
+ { key: 'count', header: 'Count', width: 15, align: 'right' }
312
+ ],
313
+ data: [
314
+ { item: 'Tasks', count: result.restoredState.tasksRestored },
315
+ { item: 'Agents', count: result.restoredState.agentsRestored },
316
+ { item: 'Memory Entries', count: result.restoredState.memoryRestored }
317
+ ]
318
+ });
319
+ if (result.warnings && result.warnings.length > 0) {
593
320
  output.writeln();
594
- output.writeln(output.dim(`Patterns updated: ${result.learningUpdates.commandPatternsUpdated}`));
595
- output.writeln(output.dim(`Risk assessment: ${result.learningUpdates.riskAssessmentUpdated ? 'Updated' : 'No change'}`));
321
+ output.writeln(output.bold(output.warning('Warnings')));
322
+ output.printList(result.warnings.map(w => output.warning(w)));
596
323
  }
597
324
  return { success: true, data: result };
598
325
  }
599
326
  catch (error) {
600
327
  if (error instanceof MCPClientError) {
601
- output.printError(`Post-command hook failed: ${error.message}`);
328
+ output.printError(`Session-restore hook failed: ${error.message}`);
602
329
  }
603
330
  else {
604
331
  output.printError(`Unexpected error: ${String(error)}`);
@@ -607,3092 +334,12 @@ const postCommandCommand = {
607
334
  }
608
335
  }
609
336
  };
610
- // Route subcommand
611
- const routeCommand = {
612
- name: 'route',
613
- description: 'Route task to optimal agent using learned patterns',
614
- options: [
615
- {
616
- name: 'task',
617
- short: 't',
618
- description: 'Task description',
619
- type: 'string',
620
- required: true
621
- },
622
- {
623
- name: 'context',
624
- short: 'c',
625
- description: 'Additional context',
626
- type: 'string'
627
- },
628
- {
629
- name: 'top-k',
630
- short: 'K',
631
- description: 'Number of top agent suggestions',
632
- type: 'number',
633
- default: 3
634
- }
635
- ],
636
- examples: [
637
- { command: 'monomind hooks route -t "Fix authentication bug"', description: 'Route task to optimal agent' },
638
- { command: 'monomind hooks route -t "Optimize database queries" -K 5', description: 'Get top 5 suggestions' }
639
- ],
640
- action: async (ctx) => {
641
- const task = ctx.args[0] || ctx.flags.task;
642
- const topK = ctx.flags['top-k'] || 3;
643
- if (!task) {
644
- output.printError('Task description is required. Use --task or -t flag.');
645
- return { success: false, exitCode: 1 };
646
- }
647
- output.printInfo(`Routing task: ${output.highlight(task)}`);
648
- try {
649
- // Call MCP tool for routing
650
- const result = await callMCPTool('hooks_route', {
651
- task,
652
- context: ctx.flags.context,
653
- topK,
654
- includeEstimates: true,
655
- });
656
- if (ctx.flags.format === 'json') {
657
- output.printJson(result);
658
- return { success: true, data: result };
659
- }
660
- // Show routing method info
661
- if (result.routing) {
662
- output.writeln();
663
- output.writeln(output.bold('Routing Method'));
664
- const methodDisplay = result.routing.method.startsWith('semantic')
665
- ? output.success(`${result.routing.method} (${result.routing.backend || 'semantic'})`)
666
- : 'keyword';
667
- output.printList([
668
- `Method: ${methodDisplay}`,
669
- result.routing.backend ? `Backend: ${result.routing.backend}` : null,
670
- `Latency: ${result.routing.latencyMs.toFixed(3)}ms`,
671
- result.matchedPattern ? `Matched Pattern: ${result.matchedPattern}` : null,
672
- ].filter(Boolean));
673
- // Show semantic matches if available
674
- if (result.semanticMatches && result.semanticMatches.length > 0) {
675
- output.writeln();
676
- output.writeln(output.dim('Semantic Matches:'));
677
- result.semanticMatches.forEach(m => {
678
- output.writeln(` ${m.pattern}: ${(m.score * 100).toFixed(1)}%`);
679
- });
680
- }
681
- }
682
- output.writeln();
683
- output.printBox([
684
- `Agent: ${output.highlight(result.primaryAgent.type)}`,
685
- `Confidence: ${(result.primaryAgent.confidence * 100).toFixed(1)}%`,
686
- `Reason: ${result.primaryAgent.reason}`
687
- ].join('\n'), 'Primary Recommendation');
688
- if (result.alternativeAgents.length > 0) {
689
- output.writeln();
690
- output.writeln(output.bold('Alternative Agents'));
691
- output.printTable({
692
- columns: [
693
- { key: 'type', header: 'Agent Type', width: 20 },
694
- { key: 'confidence', header: 'Confidence', width: 12, align: 'right', format: (v) => `${(Number(v) * 100).toFixed(1)}%` },
695
- { key: 'reason', header: 'Reason', width: 35 }
696
- ],
697
- data: result.alternativeAgents
698
- });
699
- }
700
- if (result.estimatedMetrics) {
701
- output.writeln();
702
- output.writeln(output.bold('Estimated Metrics'));
703
- output.printList([
704
- `Success Probability: ${(result.estimatedMetrics.successProbability * 100).toFixed(1)}%`,
705
- `Estimated Duration: ${result.estimatedMetrics.estimatedDuration}`,
706
- `Complexity: ${result.estimatedMetrics.complexity.toUpperCase()}`
707
- ]);
708
- }
709
- return { success: true, data: result };
710
- }
711
- catch (error) {
712
- if (error instanceof MCPClientError) {
713
- output.printError(`Routing failed: ${error.message}`);
714
- }
715
- else {
716
- output.printError(`Unexpected error: ${String(error)}`);
717
- }
718
- return { success: false, exitCode: 1 };
719
- }
720
- }
721
- };
722
- // Explain subcommand
723
- const explainCommand = {
724
- name: 'explain',
725
- description: 'Explain routing decision with transparency',
726
- options: [
727
- {
728
- name: 'task',
729
- short: 't',
730
- description: 'Task description',
731
- type: 'string',
732
- required: true
733
- },
734
- {
735
- name: 'agent',
736
- short: 'a',
737
- description: 'Agent type to explain',
738
- type: 'string'
739
- },
740
- {
741
- name: 'verbose',
742
- short: 'v',
743
- description: 'Verbose explanation',
744
- type: 'boolean',
745
- default: false
746
- }
747
- ],
748
- examples: [
749
- { command: 'monomind hooks explain -t "Fix authentication bug"', description: 'Explain routing decision' },
750
- { command: 'monomind hooks explain -t "Optimize queries" -a coder --verbose', description: 'Verbose explanation for specific agent' }
751
- ],
752
- action: async (ctx) => {
753
- const task = ctx.args[0] || ctx.flags.task;
754
- if (!task) {
755
- output.printError('Task description is required. Use --task or -t flag.');
756
- return { success: false, exitCode: 1 };
757
- }
758
- output.printInfo(`Explaining routing for: ${output.highlight(task)}`);
759
- try {
760
- // Call MCP tool for explanation
761
- const result = await callMCPTool('hooks_explain', {
762
- task,
763
- agent: ctx.flags.agent,
764
- verbose: ctx.flags.verbose || false,
765
- });
766
- if (ctx.flags.format === 'json') {
767
- output.printJson(result);
768
- return { success: true, data: result };
769
- }
770
- output.writeln();
771
- output.writeln(output.bold('Decision Explanation'));
772
- output.writeln();
773
- output.writeln(result.explanation);
774
- output.writeln();
775
- output.printBox([
776
- `Agent: ${output.highlight(result.decision.agent)}`,
777
- `Confidence: ${(result.decision.confidence * 100).toFixed(1)}%`
778
- ].join('\n'), 'Final Decision');
779
- if (result.decision.reasoning.length > 0) {
780
- output.writeln();
781
- output.writeln(output.bold('Reasoning Steps'));
782
- output.printList(result.decision.reasoning.map((r, i) => `${i + 1}. ${r}`));
783
- }
784
- if (result.factors.length > 0) {
785
- output.writeln();
786
- output.writeln(output.bold('Decision Factors'));
787
- output.printTable({
788
- columns: [
789
- { key: 'factor', header: 'Factor', width: 20 },
790
- { key: 'weight', header: 'Weight', width: 10, align: 'right', format: (v) => `${(Number(v) * 100).toFixed(0)}%` },
791
- { key: 'value', header: 'Value', width: 10, align: 'right', format: (v) => Number(v).toFixed(2) },
792
- { key: 'impact', header: 'Impact', width: 25 }
793
- ],
794
- data: result.factors
795
- });
796
- }
797
- if (result.patterns.length > 0 && ctx.flags.verbose) {
798
- output.writeln();
799
- output.writeln(output.bold('Matched Patterns'));
800
- result.patterns.forEach((p, i) => {
801
- output.writeln();
802
- output.writeln(`${i + 1}. ${output.highlight(p.pattern)} (${(p.matchScore * 100).toFixed(1)}% match)`);
803
- if (p.examples.length > 0) {
804
- output.printList(p.examples.slice(0, 3).map(e => output.dim(` ${e}`)));
805
- }
806
- });
807
- }
808
- return { success: true, data: result };
809
- }
810
- catch (error) {
811
- if (error instanceof MCPClientError) {
812
- output.printError(`Explanation failed: ${error.message}`);
813
- }
814
- else {
815
- output.printError(`Unexpected error: ${String(error)}`);
816
- }
817
- return { success: false, exitCode: 1 };
818
- }
819
- }
820
- };
821
- // Pretrain subcommand
822
- const pretrainCommand = {
823
- name: 'pretrain',
824
- description: 'Bootstrap intelligence from repository (4-step pipeline + embeddings)',
825
- options: [
826
- {
827
- name: 'path',
828
- short: 'p',
829
- description: 'Repository path',
830
- type: 'string',
831
- default: '.'
832
- },
833
- {
834
- name: 'depth',
835
- short: 'd',
836
- description: 'Analysis depth (shallow, medium, deep)',
837
- type: 'string',
838
- default: 'medium',
839
- choices: ['shallow', 'medium', 'deep']
840
- },
841
- {
842
- name: 'skip-cache',
843
- description: 'Skip cached analysis',
844
- type: 'boolean',
845
- default: false
846
- },
847
- {
848
- name: 'with-embeddings',
849
- description: 'Index documents for semantic search during pretraining',
850
- type: 'boolean',
851
- default: true
852
- },
853
- {
854
- name: 'embedding-model',
855
- description: 'ONNX embedding model',
856
- type: 'string',
857
- default: 'Xenova/all-MiniLM-L6-v2',
858
- choices: ['Xenova/all-MiniLM-L6-v2', 'Xenova/all-mpnet-base-v2']
859
- },
860
- {
861
- name: 'file-types',
862
- description: 'File extensions to index (comma-separated)',
863
- type: 'string',
864
- default: 'ts,js,py,md,json'
865
- }
866
- ],
867
- examples: [
868
- { command: 'monomind hooks pretrain', description: 'Pretrain with embeddings indexing' },
869
- { command: 'monomind hooks pretrain -p ../my-project --depth deep', description: 'Deep analysis of specific project' },
870
- { command: 'monomind hooks pretrain --no-with-embeddings', description: 'Skip embedding indexing' },
871
- { command: 'monomind hooks pretrain --file-types ts,tsx,js', description: 'Index only TypeScript/JS files' }
872
- ],
873
- action: async (ctx) => {
874
- const repoPath = ctx.flags.path || '.';
875
- const depth = ctx.flags.depth || 'medium';
876
- const withEmbeddings = ctx.flags['with-embeddings'] !== false && ctx.flags.withEmbeddings !== false;
877
- const embeddingModel = (ctx.flags['embedding-model'] || ctx.flags.embeddingModel || 'Xenova/all-MiniLM-L6-v2');
878
- const fileTypes = (ctx.flags['file-types'] || ctx.flags.fileTypes || 'ts,js,py,md,json');
879
- output.writeln();
880
- output.writeln(output.bold('Pretraining Intelligence (4-Step Pipeline + Embeddings)'));
881
- output.writeln();
882
- const steps = [
883
- { name: 'RETRIEVE', desc: 'Top-k memory injection with MMR diversity' },
884
- { name: 'JUDGE', desc: 'LLM-as-judge trajectory evaluation' },
885
- { name: 'DISTILL', desc: 'Extract strategy memories from trajectories' },
886
- { name: 'CONSOLIDATE', desc: 'Dedup, detect contradictions, prune old patterns' }
887
- ];
888
- // Add embedding steps if enabled
889
- if (withEmbeddings) {
890
- steps.push({ name: 'EMBED', desc: `Index documents with ${embeddingModel} (ONNX)` }, { name: 'HYPERBOLIC', desc: 'Project to Poincaré ball for hierarchy preservation' });
891
- }
892
- const spinner = output.createSpinner({ text: 'Starting pretraining...', spinner: 'dots' });
893
- try {
894
- spinner.start();
895
- // Display progress for each step
896
- for (const step of steps) {
897
- spinner.setText(`${step.name}: ${step.desc}`);
898
- await new Promise(resolve => setTimeout(resolve, 800));
899
- }
900
- // Call MCP tool for pretraining
901
- const result = await callMCPTool('hooks_pretrain', {
902
- path: repoPath,
903
- depth,
904
- skipCache: ctx.flags['skip-cache'] || false,
905
- withEmbeddings,
906
- embeddingModel,
907
- fileTypes: fileTypes.split(',').map((t) => t.trim()),
908
- });
909
- spinner.succeed('Pretraining completed');
910
- if (ctx.flags.format === 'json') {
911
- output.printJson(result);
912
- return { success: true, data: result };
913
- }
914
- output.writeln();
915
- // Base stats
916
- const tableData = [
917
- { metric: 'Files Analyzed', value: result.stats.filesAnalyzed },
918
- { metric: 'Patterns Extracted', value: result.stats.patternsExtracted },
919
- { metric: 'Strategies Learned', value: result.stats.strategiesLearned },
920
- { metric: 'Trajectories Evaluated', value: result.stats.trajectoriesEvaluated },
921
- { metric: 'Contradictions Resolved', value: result.stats.contradictionsResolved },
922
- ];
923
- // Add embedding stats if available
924
- if (withEmbeddings && result.stats.documentsIndexed !== undefined) {
925
- tableData.push({ metric: 'Documents Indexed', value: result.stats.documentsIndexed }, { metric: 'Embeddings Generated', value: result.stats.embeddingsGenerated || 0 }, { metric: 'Hyperbolic Projections', value: result.stats.hyperbolicProjections || 0 });
926
- }
927
- tableData.push({ metric: 'Duration', value: `${(result.duration / 1000).toFixed(1)}s` });
928
- output.printTable({
929
- columns: [
930
- { key: 'metric', header: 'Metric', width: 30 },
931
- { key: 'value', header: 'Value', width: 15, align: 'right' }
932
- ],
933
- data: tableData
934
- });
935
- output.writeln();
936
- output.printSuccess('Repository intelligence bootstrapped successfully');
937
- if (withEmbeddings) {
938
- output.writeln(output.dim(' Semantic search enabled: Use "embeddings search -q <query>" to search'));
939
- }
940
- output.writeln(output.dim(' Next step: Run "monomind hooks build-agents" to generate optimized configs'));
941
- return { success: true, data: result };
942
- }
943
- catch (error) {
944
- spinner.fail('Pretraining failed');
945
- if (error instanceof MCPClientError) {
946
- output.printError(`Pretraining error: ${error.message}`);
947
- }
948
- else {
949
- output.printError(`Unexpected error: ${String(error)}`);
950
- }
951
- return { success: false, exitCode: 1 };
952
- }
953
- }
954
- };
955
- // Build agents subcommand
956
- const buildAgentsCommand = {
957
- name: 'build-agents',
958
- description: 'Generate optimized agent configs from pretrain data',
959
- options: [
960
- {
961
- name: 'output',
962
- short: 'o',
963
- description: 'Output directory for agent configs',
964
- type: 'string',
965
- default: './agents'
966
- },
967
- {
968
- name: 'focus',
969
- short: 'f',
970
- description: 'Focus area (v1-implementation, security, performance, all)',
971
- type: 'string',
972
- default: 'all'
973
- },
974
- {
975
- name: 'config-format',
976
- description: 'Config format (yaml, json)',
977
- type: 'string',
978
- default: 'yaml',
979
- choices: ['yaml', 'json']
980
- }
981
- ],
982
- examples: [
983
- { command: 'monomind hooks build-agents', description: 'Build all agent configs' },
984
- { command: 'monomind hooks build-agents --focus security -o ./config/agents', description: 'Build security-focused configs' }
985
- ],
986
- action: async (ctx) => {
987
- const output_dir = ctx.flags.output || './agents';
988
- const focus = ctx.flags.focus || 'all';
989
- const configFormat = ctx.flags.configFormat || 'yaml';
990
- output.printInfo(`Building agent configs (focus: ${output.highlight(focus)})`);
991
- const spinner = output.createSpinner({ text: 'Generating configs...', spinner: 'dots' });
992
- try {
993
- spinner.start();
994
- // Call MCP tool for building agents
995
- const result = await callMCPTool('hooks_build-agents', {
996
- outputDir: output_dir,
997
- focus,
998
- format: configFormat,
999
- includePretrained: true,
1000
- });
1001
- spinner.succeed(`Generated ${result.agents.length} agent configs`);
1002
- if (ctx.flags.format === 'json') {
1003
- output.printJson(result);
1004
- return { success: true, data: result };
1005
- }
1006
- output.writeln();
1007
- output.writeln(output.bold('Generated Agent Configs'));
1008
- output.printTable({
1009
- columns: [
1010
- { key: 'type', header: 'Agent Type', width: 20 },
1011
- { key: 'configFile', header: 'Config File', width: 30 },
1012
- { key: 'capabilities', header: 'Capabilities', width: 10, align: 'right', format: (v) => String(Array.isArray(v) ? v.length : 0) }
1013
- ],
1014
- data: result.agents
1015
- });
1016
- output.writeln();
1017
- output.printTable({
1018
- columns: [
1019
- { key: 'metric', header: 'Metric', width: 30 },
1020
- { key: 'value', header: 'Value', width: 15, align: 'right' }
1021
- ],
1022
- data: [
1023
- { metric: 'Configs Generated', value: result.stats.configsGenerated },
1024
- { metric: 'Patterns Applied', value: result.stats.patternsApplied },
1025
- { metric: 'Optimizations Included', value: result.stats.optimizationsIncluded }
1026
- ]
1027
- });
1028
- output.writeln();
1029
- output.printSuccess(`Agent configs saved to ${output_dir}`);
1030
- return { success: true, data: result };
1031
- }
1032
- catch (error) {
1033
- spinner.fail('Agent config generation failed');
1034
- if (error instanceof MCPClientError) {
1035
- output.printError(`Build agents error: ${error.message}`);
1036
- }
1037
- else {
1038
- output.printError(`Unexpected error: ${String(error)}`);
1039
- }
1040
- return { success: false, exitCode: 1 };
1041
- }
1042
- }
1043
- };
1044
- // Metrics subcommand
1045
- const metricsCommand = {
1046
- name: 'metrics',
1047
- description: 'View learning metrics dashboard',
1048
- options: [
1049
- {
1050
- name: 'period',
1051
- short: 'p',
1052
- description: 'Time period (1h, 24h, 7d, 30d, all)',
1053
- type: 'string',
1054
- default: '24h'
1055
- },
1056
- {
1057
- name: 'v1-dashboard',
1058
- description: 'Show v1 performance dashboard',
1059
- type: 'boolean',
1060
- default: false
1061
- },
1062
- {
1063
- name: 'category',
1064
- short: 'c',
1065
- description: 'Metric category (patterns, agents, commands, performance)',
1066
- type: 'string'
1067
- }
1068
- ],
1069
- examples: [
1070
- { command: 'monomind hooks metrics', description: 'View 24h metrics' },
1071
- { command: 'monomind hooks metrics --period 7d --v1-dashboard', description: 'v1 metrics for 7 days' }
1072
- ],
1073
- action: async (ctx) => {
1074
- const period = ctx.flags.period || '24h';
1075
- const v1Dashboard = ctx.flags.v1Dashboard;
1076
- output.writeln();
1077
- output.writeln(output.bold(`Learning Metrics Dashboard (${period})`));
1078
- output.writeln();
1079
- try {
1080
- // Call MCP tool for metrics
1081
- const result = await callMCPTool('hooks_metrics', {
1082
- period,
1083
- includev1: v1Dashboard,
1084
- category: ctx.flags.category,
1085
- });
1086
- if (ctx.flags.format === 'json') {
1087
- output.printJson(result);
1088
- return { success: true, data: result };
1089
- }
1090
- // Patterns section
1091
- output.writeln(output.bold('📊 Pattern Learning'));
1092
- output.printTable({
1093
- columns: [
1094
- { key: 'metric', header: 'Metric', width: 25 },
1095
- { key: 'value', header: 'Value', width: 20, align: 'right' }
1096
- ],
1097
- data: [
1098
- { metric: 'Total Patterns', value: result.patterns.total },
1099
- { metric: 'Successful', value: output.success(String(result.patterns.successful)) },
1100
- { metric: 'Failed', value: output.error(String(result.patterns.failed)) },
1101
- { metric: 'Avg Confidence', value: `${(result.patterns.avgConfidence * 100).toFixed(1)}%` }
1102
- ]
1103
- });
1104
- output.writeln();
1105
- // Agent routing section
1106
- output.writeln(output.bold('🤖 Agent Routing'));
1107
- output.printTable({
1108
- columns: [
1109
- { key: 'metric', header: 'Metric', width: 25 },
1110
- { key: 'value', header: 'Value', width: 20, align: 'right' }
1111
- ],
1112
- data: [
1113
- { metric: 'Routing Accuracy', value: `${(result.agents.routingAccuracy * 100).toFixed(1)}%` },
1114
- { metric: 'Total Routes', value: result.agents.totalRoutes },
1115
- { metric: 'Top Agent', value: output.highlight(result.agents.topAgent) }
1116
- ]
1117
- });
1118
- output.writeln();
1119
- // Command execution section
1120
- output.writeln(output.bold('⚡ Command Execution'));
1121
- output.printTable({
1122
- columns: [
1123
- { key: 'metric', header: 'Metric', width: 25 },
1124
- { key: 'value', header: 'Value', width: 20, align: 'right' }
1125
- ],
1126
- data: [
1127
- { metric: 'Total Executed', value: result.commands.totalExecuted },
1128
- { metric: 'Success Rate', value: `${(result.commands.successRate * 100).toFixed(1)}%` },
1129
- { metric: 'Avg Risk Score', value: result.commands.avgRiskScore.toFixed(2) }
1130
- ]
1131
- });
1132
- if (v1Dashboard && result.performance) {
1133
- const p = result.performance;
1134
- output.writeln();
1135
- output.writeln(output.bold('🚀 v1 Performance Gains'));
1136
- output.printList([
1137
- `Memory Reduction: ${output.success(p.memoryReduction ?? 'N/A')}`,
1138
- `Search Improvement: ${output.success(p.searchImprovement ?? 'N/A')}`,
1139
- `Token Reduction: ${output.success(p.tokenReduction ?? 'N/A')}`
1140
- ]);
1141
- }
1142
- return { success: true, data: result };
1143
- }
1144
- catch (error) {
1145
- if (error instanceof MCPClientError) {
1146
- output.printError(`Metrics error: ${error.message}`);
1147
- }
1148
- else {
1149
- output.printError(`Unexpected error: ${String(error)}`);
1150
- }
1151
- return { success: false, exitCode: 1 };
1152
- }
1153
- }
1154
- };
1155
- // Pattern Store command (imported from transfer-store.ts)
1156
- // storeCommand is imported at the top
1157
- // Transfer from project subcommand
1158
- const transferFromProjectCommand = {
1159
- name: 'from-project',
1160
- aliases: ['project'],
1161
- description: 'Transfer patterns from another project',
1162
- options: [
1163
- {
1164
- name: 'source',
1165
- short: 's',
1166
- description: 'Source project path',
1167
- type: 'string',
1168
- required: true
1169
- },
1170
- {
1171
- name: 'filter',
1172
- short: 'f',
1173
- description: 'Filter patterns by type',
1174
- type: 'string'
1175
- },
1176
- {
1177
- name: 'min-confidence',
1178
- short: 'm',
1179
- description: 'Minimum confidence threshold (0-1)',
1180
- type: 'number',
1181
- default: 0.7
1182
- }
1183
- ],
1184
- examples: [
1185
- { command: 'monomind hooks transfer from-project -s ../old-project', description: 'Transfer all patterns' },
1186
- { command: 'monomind hooks transfer from-project -s ../prod --filter security -m 0.9', description: 'Transfer high-confidence security patterns' }
1187
- ],
1188
- action: async (ctx) => {
1189
- const sourcePath = ctx.args[0] || ctx.flags.source;
1190
- const minConfidence = ctx.flags['min-confidence'] || 0.7;
1191
- if (!sourcePath) {
1192
- output.printError('Source project path is required. Use --source or -s flag.');
1193
- return { success: false, exitCode: 1 };
1194
- }
1195
- output.printInfo(`Transferring patterns from: ${output.highlight(sourcePath)}`);
1196
- const spinner = output.createSpinner({ text: 'Analyzing source patterns...', spinner: 'dots' });
1197
- try {
1198
- spinner.start();
1199
- // Call MCP tool for transfer
1200
- const result = await callMCPTool('hooks_transfer', {
1201
- sourcePath,
1202
- filter: ctx.flags.filter,
1203
- minConfidence,
1204
- mergeStrategy: 'keep-highest-confidence',
1205
- });
1206
- spinner.succeed(`Transferred ${result.transferred.total} patterns`);
1207
- if (ctx.flags.format === 'json') {
1208
- output.printJson(result);
1209
- return { success: true, data: result };
1210
- }
1211
- output.writeln();
1212
- output.writeln(output.bold('Transfer Summary'));
1213
- output.printTable({
1214
- columns: [
1215
- { key: 'category', header: 'Category', width: 25 },
1216
- { key: 'count', header: 'Count', width: 15, align: 'right' }
1217
- ],
1218
- data: [
1219
- { category: 'Total Transferred', count: output.success(String(result.transferred.total)) },
1220
- { category: 'Skipped (Low Confidence)', count: result.skipped.lowConfidence },
1221
- { category: 'Skipped (Duplicates)', count: result.skipped.duplicates },
1222
- { category: 'Skipped (Conflicts)', count: result.skipped.conflicts }
1223
- ]
1224
- });
1225
- if (Object.keys(result.transferred.byType).length > 0) {
1226
- output.writeln();
1227
- output.writeln(output.bold('By Pattern Type'));
1228
- output.printTable({
1229
- columns: [
1230
- { key: 'type', header: 'Type', width: 20 },
1231
- { key: 'count', header: 'Count', width: 15, align: 'right' }
1232
- ],
1233
- data: Object.entries(result.transferred.byType).map(([type, count]) => ({ type, count }))
1234
- });
1235
- }
1236
- output.writeln();
1237
- output.printList([
1238
- `Avg Confidence: ${(result.stats.avgConfidence * 100).toFixed(1)}%`,
1239
- `Avg Age: ${result.stats.avgAge}`
1240
- ]);
1241
- return { success: true, data: result };
1242
- }
1243
- catch (error) {
1244
- spinner.fail('Transfer failed');
1245
- if (error instanceof MCPClientError) {
1246
- output.printError(`Transfer error: ${error.message}`);
1247
- }
1248
- else {
1249
- output.printError(`Unexpected error: ${String(error)}`);
1250
- }
1251
- return { success: false, exitCode: 1 };
1252
- }
1253
- }
1254
- };
1255
- // Parent transfer command combining all transfer methods
1256
- const transferCommand = {
1257
- name: 'transfer',
1258
- description: 'Transfer patterns and plugins via IPFS-based decentralized registry',
1259
- subcommands: [storeCommand, transferFromProjectCommand],
1260
- examples: [
1261
- { command: 'monomind hooks transfer store list', description: 'List patterns from registry' },
1262
- { command: 'monomind hooks transfer store search -q routing', description: 'Search patterns' },
1263
- { command: 'monomind hooks transfer store download -p seraphine-genesis', description: 'Download pattern' },
1264
- { command: 'monomind hooks transfer store publish', description: 'Publish pattern to registry' },
1265
- { command: 'monomind hooks transfer from-project -s ../other-project', description: 'Transfer from project' },
1266
- ],
1267
- action: async () => {
1268
- output.writeln();
1269
- output.writeln(output.bold('Pattern Transfer System'));
1270
- output.writeln(output.dim('Decentralized pattern sharing via IPFS'));
1271
- output.writeln();
1272
- output.writeln('Subcommands:');
1273
- output.printList([
1274
- `${output.highlight('store')} - Pattern marketplace (list, search, download, publish)`,
1275
- `${output.highlight('from-project')} - Transfer patterns from another project`,
1276
- ]);
1277
- output.writeln();
1278
- output.writeln(output.bold('IPFS-Based Features:'));
1279
- output.printList([
1280
- 'Decentralized registry via IPNS for discoverability',
1281
- 'Content-addressed storage for integrity',
1282
- 'Ed25519 signatures for verification',
1283
- 'Anonymization levels: minimal, standard, strict, paranoid',
1284
- 'Trust levels: unverified, community, verified, official',
1285
- ]);
1286
- output.writeln();
1287
- output.writeln('Run "monomind hooks transfer <subcommand> --help" for details');
1288
- return { success: true };
1289
- }
1290
- };
1291
- // List subcommand
1292
- const listCommand = {
1293
- name: 'list',
1294
- aliases: ['ls'],
1295
- description: 'List all registered hooks',
1296
- options: [
1297
- {
1298
- name: 'enabled',
1299
- short: 'e',
1300
- description: 'Show only enabled hooks',
1301
- type: 'boolean',
1302
- default: false
1303
- },
1304
- {
1305
- name: 'type',
1306
- short: 't',
1307
- description: 'Filter by hook type',
1308
- type: 'string'
1309
- }
1310
- ],
1311
- action: async (ctx) => {
1312
- try {
1313
- // Call MCP tool for list
1314
- const result = await callMCPTool('hooks_list', {
1315
- enabled: ctx.flags.enabled || undefined,
1316
- type: ctx.flags.type || undefined,
1317
- });
1318
- if (ctx.flags.format === 'json') {
1319
- output.printJson(result);
1320
- return { success: true, data: result };
1321
- }
1322
- output.writeln();
1323
- output.writeln(output.bold('Registered Hooks'));
1324
- output.writeln();
1325
- if (result.hooks.length === 0) {
1326
- output.printInfo('No hooks found matching criteria');
1327
- return { success: true, data: result };
1328
- }
1329
- output.printTable({
1330
- columns: [
1331
- { key: 'name', header: 'Name', width: 20 },
1332
- { key: 'type', header: 'Type', width: 15 },
1333
- { key: 'enabled', header: 'Enabled', width: 10, format: (v) => v ? output.success('Yes') : output.dim('No') },
1334
- { key: 'priority', header: 'Priority', width: 10, align: 'right' },
1335
- { key: 'executionCount', header: 'Executions', width: 12, align: 'right' },
1336
- { key: 'lastExecuted', header: 'Last Executed', width: 20, format: (v) => v ? new Date(String(v)).toLocaleString() : 'Never' }
1337
- ],
1338
- data: result.hooks
1339
- });
1340
- output.writeln();
1341
- output.printInfo(`Total: ${result.total} hooks`);
1342
- return { success: true, data: result };
1343
- }
1344
- catch (error) {
1345
- if (error instanceof MCPClientError) {
1346
- output.printError(`Failed to list hooks: ${error.message}`);
1347
- }
1348
- else {
1349
- output.printError(`Unexpected error: ${String(error)}`);
1350
- }
1351
- return { success: false, exitCode: 1 };
1352
- }
1353
- }
1354
- };
1355
- // Pre-task subcommand
1356
- const preTaskCommand = {
1357
- name: 'pre-task',
1358
- description: 'Record task start and get agent suggestions',
1359
- options: [
1360
- {
1361
- name: 'task-id',
1362
- short: 'i',
1363
- description: 'Unique task identifier (auto-generated if omitted)',
1364
- type: 'string'
1365
- },
1366
- {
1367
- name: 'description',
1368
- short: 'd',
1369
- description: 'Task description',
1370
- type: 'string',
1371
- required: true
1372
- },
1373
- {
1374
- name: 'auto-spawn',
1375
- short: 'a',
1376
- description: 'Auto-spawn suggested agents',
1377
- type: 'boolean',
1378
- default: false
1379
- }
1380
- ],
1381
- examples: [
1382
- { command: 'monomind hooks pre-task -i task-123 -d "Fix auth bug"', description: 'Record task start' },
1383
- { command: 'monomind hooks pre-task -i task-456 -d "Implement feature" --auto-spawn', description: 'With auto-spawn' }
1384
- ],
1385
- action: async (ctx) => {
1386
- const taskId = ctx.flags['task-id'] || `task-${Date.now().toString(36)}`;
1387
- const description = ctx.args[0] || ctx.flags.description;
1388
- if (!description) {
1389
- output.printError('Description is required: --description "your task"');
1390
- return { success: false, exitCode: 1 };
1391
- }
1392
- output.printInfo(`Starting task: ${output.highlight(taskId)}`);
1393
- try {
1394
- const result = await callMCPTool('hooks_pre-task', {
1395
- taskId,
1396
- description,
1397
- autoSpawn: ctx.flags['auto-spawn'] || false,
1398
- timestamp: Date.now(),
1399
- });
1400
- if (ctx.flags.format === 'json') {
1401
- output.printJson(result);
1402
- return { success: true, data: result };
1403
- }
1404
- output.writeln();
1405
- output.printBox([
1406
- `Task ID: ${result.taskId}`,
1407
- `Description: ${result.description}`,
1408
- `Complexity: ${result.complexity.toUpperCase()}`,
1409
- `Est. Duration: ${result.estimatedDuration}`
1410
- ].join('\n'), 'Task Registered');
1411
- if (result.suggestedAgents.length > 0) {
1412
- output.writeln();
1413
- output.writeln(output.bold('Suggested Agents'));
1414
- output.printTable({
1415
- columns: [
1416
- { key: 'type', header: 'Agent Type', width: 20 },
1417
- { key: 'confidence', header: 'Confidence', width: 12, align: 'right', format: (v) => `${(Number(v) * 100).toFixed(1)}%` },
1418
- { key: 'reason', header: 'Reason', width: 35 }
1419
- ],
1420
- data: result.suggestedAgents
1421
- });
1422
- }
1423
- if (result.risks.length > 0) {
1424
- output.writeln();
1425
- output.writeln(output.bold(output.error('Potential Risks')));
1426
- output.printList(result.risks.map(r => output.warning(r)));
1427
- }
1428
- if (result.recommendations.length > 0) {
1429
- output.writeln();
1430
- output.writeln(output.bold('Recommendations'));
1431
- output.printList(result.recommendations);
1432
- }
1433
- // Monograph graph-first reminder — always surface before the agent starts grepping
1434
- if (description) {
1435
- output.writeln();
1436
- output.writeln(output.bold('[MONOGRAPH] Graph-first protocol:'));
1437
- output.printList([
1438
- `mcp__monomind__monograph_suggest task="${description.slice(0, 80)}" — get relevant nodes ranked by task`,
1439
- 'mcp__monomind__monograph_query — BM25 lookup before any grep/find via Bash',
1440
- 'mcp__monomind__monograph_impact — blast radius before editing shared code',
1441
- ]);
1442
- }
1443
- return { success: true, data: result };
1444
- }
1445
- catch (error) {
1446
- if (error instanceof MCPClientError) {
1447
- output.printError(`Pre-task hook failed: ${error.message}`);
1448
- }
1449
- else {
1450
- output.printError(`Unexpected error: ${String(error)}`);
1451
- }
1452
- return { success: false, exitCode: 1 };
1453
- }
1454
- }
1455
- };
1456
- // Post-task subcommand
1457
- const postTaskCommand = {
1458
- name: 'post-task',
1459
- description: 'Record task completion for learning',
1460
- options: [
1461
- {
1462
- name: 'task-id',
1463
- short: 'i',
1464
- description: 'Unique task identifier (auto-generated if not provided)',
1465
- type: 'string',
1466
- required: false
1467
- },
1468
- {
1469
- name: 'success',
1470
- short: 's',
1471
- description: 'Whether the task succeeded',
1472
- type: 'boolean',
1473
- required: false
1474
- },
1475
- {
1476
- name: 'quality',
1477
- short: 'q',
1478
- description: 'Quality score (0-1)',
1479
- type: 'number'
1480
- },
1481
- {
1482
- name: 'agent',
1483
- short: 'a',
1484
- description: 'Agent that executed the task',
1485
- type: 'string'
1486
- }
1487
- ],
1488
- examples: [
1489
- { command: 'monomind hooks post-task -i task-123 --success true', description: 'Record successful completion' },
1490
- { command: 'monomind hooks post-task -i task-456 --success false -q 0.3', description: 'Record failed task' }
1491
- ],
1492
- action: async (ctx) => {
1493
- // Auto-generate task ID if not provided
1494
- const taskId = ctx.flags['task-id'] || `task_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
1495
- // Default success to true for backward compatibility
1496
- const success = ctx.flags.success !== undefined ? ctx.flags.success : true;
1497
- output.printInfo(`Recording outcome for task: ${output.highlight(taskId)}`);
1498
- try {
1499
- const result = await callMCPTool('hooks_post-task', {
1500
- taskId,
1501
- success,
1502
- quality: ctx.flags.quality,
1503
- agent: ctx.flags.agent,
1504
- timestamp: Date.now(),
1505
- });
1506
- if (ctx.flags.format === 'json') {
1507
- output.printJson(result);
1508
- return { success: true, data: result };
1509
- }
1510
- output.writeln();
1511
- output.printSuccess(`Task outcome recorded: ${success ? 'SUCCESS' : 'FAILED'}`);
1512
- output.writeln();
1513
- output.writeln(output.bold('Learning Updates'));
1514
- output.printTable({
1515
- columns: [
1516
- { key: 'metric', header: 'Metric', width: 25 },
1517
- { key: 'value', header: 'Value', width: 20, align: 'right' }
1518
- ],
1519
- data: [
1520
- { metric: 'Patterns Updated', value: result.learningUpdates.patternsUpdated },
1521
- { metric: 'New Patterns', value: result.learningUpdates.newPatterns },
1522
- { metric: 'Duration', value: `${(result.duration / 1000).toFixed(1)}s` },
1523
- { metric: 'Trajectory ID', value: result.learningUpdates.trajectoryId }
1524
- ]
1525
- });
1526
- return { success: true, data: result };
1527
- }
1528
- catch (error) {
1529
- if (error instanceof MCPClientError) {
1530
- output.printError(`Post-task hook failed: ${error.message}`);
1531
- }
1532
- else {
1533
- output.printError(`Unexpected error: ${String(error)}`);
1534
- }
1535
- return { success: false, exitCode: 1 };
1536
- }
1537
- }
1538
- };
1539
- // Session-end subcommand
1540
- const sessionEndCommand = {
1541
- name: 'session-end',
1542
- description: 'End current session and persist state',
1543
- options: [
1544
- {
1545
- name: 'save-state',
1546
- short: 's',
1547
- description: 'Save session state for later restoration',
1548
- type: 'boolean',
1549
- default: true
1550
- }
1551
- ],
1552
- examples: [
1553
- { command: 'monomind hooks session-end', description: 'End and save session' },
1554
- { command: 'monomind hooks session-end --save-state false', description: 'End without saving' }
1555
- ],
1556
- action: async (ctx) => {
1557
- output.printInfo('Ending session...');
1558
- try {
1559
- const result = await callMCPTool('hooks_session-end', {
1560
- saveState: ctx.flags['save-state'] ?? true,
1561
- timestamp: Date.now(),
1562
- });
1563
- if (ctx.flags.format === 'json') {
1564
- output.printJson(result);
1565
- return { success: true, data: result };
1566
- }
1567
- output.writeln();
1568
- output.printSuccess(`Session ${result.sessionId} ended`);
1569
- output.writeln();
1570
- output.writeln(output.bold('Session Summary'));
1571
- output.printTable({
1572
- columns: [
1573
- { key: 'metric', header: 'Metric', width: 25 },
1574
- { key: 'value', header: 'Value', width: 15, align: 'right' }
1575
- ],
1576
- data: [
1577
- { metric: 'Duration', value: `${(result.duration / 1000 / 60).toFixed(1)} min` },
1578
- { metric: 'Tasks Executed', value: result.summary.tasksExecuted },
1579
- { metric: 'Tasks Succeeded', value: output.success(String(result.summary.tasksSucceeded)) },
1580
- { metric: 'Tasks Failed', value: output.error(String(result.summary.tasksFailed)) },
1581
- { metric: 'Commands Executed', value: result.summary.commandsExecuted },
1582
- { metric: 'Files Modified', value: result.summary.filesModified },
1583
- { metric: 'Agents Spawned', value: result.summary.agentsSpawned }
1584
- ]
1585
- });
1586
- if (result.statePath) {
1587
- output.writeln();
1588
- output.writeln(output.dim(`State saved to: ${result.statePath}`));
1589
- }
1590
- return { success: true, data: result };
1591
- }
1592
- catch (error) {
1593
- if (error instanceof MCPClientError) {
1594
- output.printError(`Session-end hook failed: ${error.message}`);
1595
- }
1596
- else {
1597
- output.printError(`Unexpected error: ${String(error)}`);
1598
- }
1599
- return { success: false, exitCode: 1 };
1600
- }
1601
- }
1602
- };
1603
- // Session-restore subcommand
1604
- const sessionRestoreCommand = {
1605
- name: 'session-restore',
1606
- description: 'Restore a previous session',
1607
- options: [
1608
- {
1609
- name: 'session-id',
1610
- short: 'i',
1611
- description: 'Session ID to restore (use "latest" for most recent)',
1612
- type: 'string',
1613
- default: 'latest'
1614
- },
1615
- {
1616
- name: 'restore-agents',
1617
- short: 'a',
1618
- description: 'Restore spawned agents',
1619
- type: 'boolean',
1620
- default: true
1621
- },
1622
- {
1623
- name: 'restore-tasks',
1624
- short: 't',
1625
- description: 'Restore active tasks',
1626
- type: 'boolean',
1627
- default: true
1628
- }
1629
- ],
1630
- examples: [
1631
- { command: 'monomind hooks session-restore', description: 'Restore latest session' },
1632
- { command: 'monomind hooks session-restore -i session-12345', description: 'Restore specific session' }
1633
- ],
1634
- action: async (ctx) => {
1635
- const sessionId = ctx.args[0] || ctx.flags['session-id'] || 'latest';
1636
- output.printInfo(`Restoring session: ${output.highlight(sessionId)}`);
1637
- try {
1638
- const result = await callMCPTool('hooks_session-restore', {
1639
- sessionId,
1640
- restoreAgents: ctx.flags['restore-agents'] ?? true,
1641
- restoreTasks: ctx.flags['restore-tasks'] ?? true,
1642
- timestamp: Date.now(),
1643
- });
1644
- if (ctx.flags.format === 'json') {
1645
- output.printJson(result);
1646
- return { success: true, data: result };
1647
- }
1648
- output.writeln();
1649
- output.printSuccess(`Session restored from ${result.originalSessionId}`);
1650
- output.writeln(output.dim(`New session ID: ${result.sessionId}`));
1651
- output.writeln();
1652
- output.writeln(output.bold('Restored State'));
1653
- output.printTable({
1654
- columns: [
1655
- { key: 'item', header: 'Item', width: 25 },
1656
- { key: 'count', header: 'Count', width: 15, align: 'right' }
1657
- ],
1658
- data: [
1659
- { item: 'Tasks', count: result.restoredState.tasksRestored },
1660
- { item: 'Agents', count: result.restoredState.agentsRestored },
1661
- { item: 'Memory Entries', count: result.restoredState.memoryRestored }
1662
- ]
1663
- });
1664
- if (result.warnings && result.warnings.length > 0) {
1665
- output.writeln();
1666
- output.writeln(output.bold(output.warning('Warnings')));
1667
- output.printList(result.warnings.map(w => output.warning(w)));
1668
- }
1669
- return { success: true, data: result };
1670
- }
1671
- catch (error) {
1672
- if (error instanceof MCPClientError) {
1673
- output.printError(`Session-restore hook failed: ${error.message}`);
1674
- }
1675
- else {
1676
- output.printError(`Unexpected error: ${String(error)}`);
1677
- }
1678
- return { success: false, exitCode: 1 };
1679
- }
1680
- }
1681
- };
1682
- // Intelligence subcommand (JS pattern/trajectory logging)
1683
- const intelligenceCommand = {
1684
- name: 'intelligence',
1685
- description: 'JS pattern/trajectory logging (stats, pattern-*, trajectory-*)',
1686
- options: [
1687
- {
1688
- name: 'mode',
1689
- short: 'm',
1690
- description: 'Intelligence mode (real-time, batch, edge, research, balanced)',
1691
- type: 'string',
1692
- choices: ['real-time', 'batch', 'edge', 'research', 'balanced'],
1693
- default: 'balanced'
1694
- },
1695
- {
1696
- name: 'enable-sona',
1697
- description: 'Enable SONA sub-0.05ms learning',
1698
- type: 'boolean',
1699
- default: true
1700
- },
1701
- {
1702
- name: 'enable-moe',
1703
- description: 'Enable Mixture of Experts routing',
1704
- type: 'boolean',
1705
- default: true
1706
- },
1707
- {
1708
- name: 'enable-hnsw',
1709
- description: 'Enable HNSW 150x faster search',
1710
- type: 'boolean',
1711
- default: true
1712
- },
1713
- {
1714
- name: 'status',
1715
- short: 's',
1716
- description: 'Show current intelligence status',
1717
- type: 'boolean',
1718
- default: false
1719
- },
1720
- {
1721
- name: 'train',
1722
- short: 't',
1723
- description: 'Force training cycle',
1724
- type: 'boolean',
1725
- default: false
1726
- },
1727
- {
1728
- name: 'reset',
1729
- short: 'r',
1730
- description: 'Reset learning state',
1731
- type: 'boolean',
1732
- default: false
1733
- },
1734
- {
1735
- name: 'embedding-provider',
1736
- description: 'Embedding provider (transformers, openai, mock)',
1737
- type: 'string',
1738
- choices: ['transformers', 'openai', 'mock'],
1739
- default: 'transformers'
1740
- }
1741
- ],
1742
- examples: [
1743
- { command: 'monomind hooks intelligence --status', description: 'Show intelligence status' },
1744
- { command: 'monomind hooks intelligence -m real-time', description: 'Enable real-time mode' },
1745
- { command: 'monomind hooks intelligence --train', description: 'Force training cycle' }
1746
- ],
1747
- action: async (ctx) => {
1748
- const mode = ctx.flags.mode || 'balanced';
1749
- const showStatus = ctx.flags.status;
1750
- const forceTraining = ctx.flags.train;
1751
- const reset = ctx.flags.reset;
1752
- const enableSona = ctx.flags['enable-sona'] ?? true;
1753
- const enableMoe = ctx.flags['enable-moe'] ?? true;
1754
- const enableHnsw = ctx.flags['enable-hnsw'] ?? true;
1755
- const embeddingProvider = ctx.flags['embedding-provider'] || 'transformers';
1756
- output.writeln();
1757
- output.writeln(output.bold('Intelligence System'));
1758
- output.writeln();
1759
- if (reset) {
1760
- const confirmed = await confirm({
1761
- message: 'Reset all learning state? This cannot be undone.',
1762
- default: false
1763
- });
1764
- if (!confirmed) {
1765
- output.printInfo('Reset cancelled');
1766
- return { success: true };
1767
- }
1768
- output.printInfo('Resetting learning state...');
1769
- try {
1770
- await callMCPTool('hooks_intelligence-reset', {});
1771
- output.printSuccess('Learning state reset');
1772
- return { success: true };
1773
- }
1774
- catch (error) {
1775
- output.printError(`Reset failed: ${error}`);
1776
- return { success: false, exitCode: 1 };
1777
- }
1778
- }
1779
- const spinner = output.createSpinner({ text: 'Initializing intelligence system...', spinner: 'dots' });
1780
- try {
1781
- spinner.start();
1782
- // Read local intelligence data from disk first
1783
- const { getIntelligenceStats, initializeIntelligence, getPersistenceStatus } = await import('../memory/intelligence.js');
1784
- await initializeIntelligence();
1785
- const localStats = getIntelligenceStats();
1786
- const persistence = getPersistenceStatus();
1787
- // Read patterns.json file size and entry count
1788
- let patternsFileSize = 0;
1789
- let patternsFileEntries = 0;
1790
- if (persistence.patternsExist) {
1791
- try {
1792
- const pStat = statSync(persistence.patternsFile);
1793
- patternsFileSize = pStat.size;
1794
- if (patternsFileSize <= 4_194_304) {
1795
- const pData = JSON.parse(readFileSync(persistence.patternsFile, 'utf-8'));
1796
- if (Array.isArray(pData))
1797
- patternsFileEntries = pData.length;
1798
- }
1799
- }
1800
- catch { /* ignore */ }
1801
- }
1802
- // Read stats.json for trajectory data
1803
- let trajectoriesFromDisk = 0;
1804
- let lastAdaptationFromDisk = null;
1805
- if (persistence.statsExist) {
1806
- try {
1807
- const sStat = statSync(persistence.statsFile);
1808
- if (sStat.size <= 524_288) {
1809
- const sData = JSON.parse(readFileSync(persistence.statsFile, 'utf-8'));
1810
- trajectoriesFromDisk = sData?.trajectoriesRecorded ?? 0;
1811
- lastAdaptationFromDisk = sData?.lastAdaptation ?? null;
1812
- }
1813
- }
1814
- catch { /* ignore */ }
1815
- }
1816
- // Merge local stats with any we can get from MCP
1817
- let mcpResult = null;
1818
- try {
1819
- mcpResult = await callMCPTool('hooks_intelligence', {
1820
- mode,
1821
- enableSona,
1822
- enableMoe,
1823
- enableHnsw,
1824
- embeddingProvider,
1825
- forceTraining,
1826
- showStatus,
1827
- });
1828
- }
1829
- catch {
1830
- // MCP not available, use local data only
1831
- }
1832
- // Build merged result, preferring local real data over MCP zeros
1833
- const hasLocalData = localStats.patternsLearned > 0 || trajectoriesFromDisk > 0 || patternsFileEntries > 0;
1834
- // Use the higher of local vs MCP values for key stats
1835
- const mcpComponents = mcpResult?.components;
1836
- const mcpSona = mcpComponents?.sona;
1837
- const mcpMoe = mcpComponents?.moe;
1838
- const mcpHnsw = mcpComponents?.hnsw;
1839
- const mcpEmb = mcpComponents?.embeddings;
1840
- const mcpPerf = mcpResult?.performance;
1841
- const patternsLearned = Math.max(localStats.patternsLearned, patternsFileEntries, Number(mcpSona?.patternsLearned ?? 0));
1842
- const trajectories = Math.max(localStats.trajectoriesRecorded, trajectoriesFromDisk, Number(mcpSona?.trajectoriesRecorded ?? 0));
1843
- const lastAdaptation = lastAdaptationFromDisk ?? localStats.lastAdaptation;
1844
- const avgAdaptation = localStats.avgAdaptationTime > 0 ? localStats.avgAdaptationTime : Number(mcpSona?.adaptationTimeMs ?? 0);
1845
- const result = {
1846
- mode: String(mcpResult?.mode ?? mode),
1847
- status: (hasLocalData || mcpResult) ? 'active' : 'idle',
1848
- components: {
1849
- sona: {
1850
- enabled: enableSona,
1851
- status: localStats.sonaEnabled ? 'active' : String(mcpSona?.status ?? 'idle'),
1852
- learningTimeMs: avgAdaptation,
1853
- adaptationTimeMs: avgAdaptation,
1854
- trajectoriesRecorded: trajectories,
1855
- patternsLearned,
1856
- avgQuality: Number(mcpSona?.avgQuality ?? (patternsLearned > 0 ? 0.75 : 0)),
1857
- },
1858
- moe: {
1859
- enabled: enableMoe,
1860
- status: String(mcpMoe?.status ?? (hasLocalData ? 'active' : 'idle')),
1861
- expertsActive: Number(mcpMoe?.expertsActive ?? (hasLocalData ? 8 : 0)),
1862
- routingAccuracy: Number(mcpMoe?.routingAccuracy ?? (hasLocalData ? 0.82 : 0)),
1863
- loadBalance: Number(mcpMoe?.loadBalance ?? (hasLocalData ? 0.9 : 0)),
1864
- },
1865
- hnsw: {
1866
- enabled: enableHnsw,
1867
- status: String(mcpHnsw?.status ?? (localStats.reasoningBankSize > 0 ? 'active' : 'idle')),
1868
- indexSize: Math.max(localStats.reasoningBankSize, Number(mcpHnsw?.indexSize ?? 0)),
1869
- searchSpeedup: String(mcpHnsw?.searchSpeedup ?? (localStats.reasoningBankSize > 0 ? 'pure-JS HNSW' : 'N/A')),
1870
- memoryUsage: String(mcpHnsw?.memoryUsage ?? (patternsFileSize > 0 ? `${(patternsFileSize / 1024).toFixed(1)} KB` : 'N/A')),
1871
- dimension: Number(mcpHnsw?.dimension ?? 384),
1872
- },
1873
- embeddings: mcpEmb ? {
1874
- provider: String(mcpEmb.provider ?? embeddingProvider),
1875
- model: String(mcpEmb.model ?? 'default'),
1876
- dimension: Number(mcpEmb.dimension ?? 384),
1877
- cacheHitRate: Number(mcpEmb.cacheHitRate ?? 0),
1878
- } : {
1879
- provider: embeddingProvider,
1880
- model: 'hash-128',
1881
- dimension: 128,
1882
- cacheHitRate: 0,
1883
- },
1884
- },
1885
- performance: mcpPerf ?? {
1886
- memoryReduction: patternsFileSize > 0 ? `${(patternsFileSize / 1024).toFixed(1)} KB on disk` : 'N/A',
1887
- searchImprovement: localStats.reasoningBankSize > 0 ? 'pure-JS HNSW' : 'N/A',
1888
- tokenReduction: 'N/A',
1889
- sweBenchScore: 'N/A',
1890
- },
1891
- lastTrainingMs: lastAdaptation ? Date.now() - lastAdaptation : undefined,
1892
- persistence: {
1893
- dataDir: persistence.dataDir,
1894
- patternsFile: persistence.patternsFile,
1895
- patternsExist: persistence.patternsExist,
1896
- patternsEntries: patternsFileEntries,
1897
- patternsFileSize,
1898
- statsFile: persistence.statsFile,
1899
- statsExist: persistence.statsExist,
1900
- trajectoriesFromDisk,
1901
- },
1902
- };
1903
- if (forceTraining) {
1904
- spinner.setText('Running training cycle...');
1905
- const { recordTrajectory, recordStep, flushPatterns, getIntelligenceStats: getStats, } = await import('../memory/intelligence.js');
1906
- // Record a real trajectory step and then end it with a 'success' verdict
1907
- // so endTrajectory → distillLearning runs the full EWC+LoRA pipeline.
1908
- const content = localStats.patternsLearned > 0
1909
- ? `training cycle: ${localStats.patternsLearned} patterns, ${localStats.trajectoriesRecorded} trajectories`
1910
- : 'bootstrap training: initializing intelligence system';
1911
- await recordStep({ type: 'action', content });
1912
- await recordTrajectory([{ type: 'action', content }], 'success');
1913
- flushPatterns();
1914
- const updatedStats = getStats();
1915
- spinner.succeed(`Training cycle complete — ${updatedStats.patternsLearned} patterns, EWC+LoRA applied`);
1916
- return {
1917
- success: true,
1918
- data: { patternsLearned: updatedStats.patternsLearned, trajectoriesRecorded: updatedStats.trajectoriesRecorded },
1919
- };
1920
- }
1921
- else {
1922
- spinner.succeed(hasLocalData ? 'Intelligence system active (local data loaded)' : 'Intelligence system active');
1923
- }
1924
- if (ctx.flags.format === 'json') {
1925
- output.printJson(result);
1926
- return { success: true, data: result };
1927
- }
1928
- // Status display
1929
- output.writeln();
1930
- output.printBox([
1931
- `Mode: ${output.highlight(result.mode)}`,
1932
- `Status: ${formatIntelligenceStatus(result.status)}`,
1933
- `Last Training: ${result.lastTrainingMs != null ? `${(result.lastTrainingMs / 1000).toFixed(0)}s ago` : 'Never'}`,
1934
- `Data Dir: ${output.dim(persistence.dataDir)}`
1935
- ].join('\n'), 'Intelligence Status');
1936
- // SONA Component
1937
- output.writeln();
1938
- output.writeln(output.bold('SONA (Sub-0.05ms Learning)'));
1939
- const sona = result.components.sona;
1940
- if (sona.enabled) {
1941
- output.printTable({
1942
- columns: [
1943
- { key: 'metric', header: 'Metric', width: 25 },
1944
- { key: 'value', header: 'Value', width: 20, align: 'right' }
1945
- ],
1946
- data: [
1947
- { metric: 'Status', value: formatIntelligenceStatus(sona.status) },
1948
- { metric: 'Learning Time', value: `${(sona.learningTimeMs ?? 0).toFixed(3)}ms` },
1949
- { metric: 'Adaptation Time', value: `${(sona.adaptationTimeMs ?? 0).toFixed(3)}ms` },
1950
- { metric: 'Trajectories', value: sona.trajectoriesRecorded ?? 0 },
1951
- { metric: 'Patterns Learned', value: sona.patternsLearned ?? 0 },
1952
- { metric: 'Avg Quality', value: `${((sona.avgQuality ?? 0) * 100).toFixed(1)}%` }
1953
- ]
1954
- });
1955
- }
1956
- else {
1957
- output.writeln(output.dim(' Disabled'));
1958
- }
1959
- // MoE Component
1960
- output.writeln();
1961
- output.writeln(output.bold('Mixture of Experts (MoE)'));
1962
- const moe = result.components.moe;
1963
- if (moe.enabled) {
1964
- output.printTable({
1965
- columns: [
1966
- { key: 'metric', header: 'Metric', width: 25 },
1967
- { key: 'value', header: 'Value', width: 20, align: 'right' }
1968
- ],
1969
- data: [
1970
- { metric: 'Status', value: formatIntelligenceStatus(moe.status) },
1971
- { metric: 'Active Experts', value: moe.expertsActive ?? 0 },
1972
- { metric: 'Routing Accuracy', value: `${((moe.routingAccuracy ?? 0) * 100).toFixed(1)}%` },
1973
- { metric: 'Load Balance', value: `${((moe.loadBalance ?? 0) * 100).toFixed(1)}%` }
1974
- ]
1975
- });
1976
- }
1977
- else {
1978
- output.writeln(output.dim(' Disabled'));
1979
- }
1980
- // HNSW Component
1981
- output.writeln();
1982
- output.writeln(output.bold('HNSW (Pure-JS Vector Search)'));
1983
- const hnsw = result.components.hnsw;
1984
- if (hnsw.enabled) {
1985
- output.printTable({
1986
- columns: [
1987
- { key: 'metric', header: 'Metric', width: 25 },
1988
- { key: 'value', header: 'Value', width: 20, align: 'right' }
1989
- ],
1990
- data: [
1991
- { metric: 'Status', value: formatIntelligenceStatus(hnsw.status) },
1992
- { metric: 'Index Size', value: (hnsw.indexSize ?? 0).toLocaleString() },
1993
- { metric: 'Search Speedup', value: output.success(hnsw.searchSpeedup ?? 'N/A') },
1994
- { metric: 'Memory Usage', value: hnsw.memoryUsage ?? 'N/A' },
1995
- { metric: 'Dimension', value: hnsw.dimension ?? 384 }
1996
- ]
1997
- });
1998
- }
1999
- else {
2000
- output.writeln(output.dim(' Disabled'));
2001
- }
2002
- // Embeddings
2003
- output.writeln();
2004
- output.writeln(output.bold('Embeddings'));
2005
- const emb = result.components.embeddings;
2006
- if (emb) {
2007
- output.printTable({
2008
- columns: [
2009
- { key: 'metric', header: 'Metric', width: 25 },
2010
- { key: 'value', header: 'Value', width: 20, align: 'right' }
2011
- ],
2012
- data: [
2013
- { metric: 'Provider', value: emb.provider ?? 'N/A' },
2014
- { metric: 'Model', value: emb.model ?? 'N/A' },
2015
- { metric: 'Dimension', value: emb.dimension ?? 384 },
2016
- { metric: 'Cache Hit Rate', value: `${((emb.cacheHitRate ?? 0) * 100).toFixed(1)}%` }
2017
- ]
2018
- });
2019
- }
2020
- else {
2021
- output.writeln(output.dim(' Not initialized'));
2022
- }
2023
- // Persistence info
2024
- if (result.persistence) {
2025
- output.writeln();
2026
- output.writeln(output.bold('Neural Persistence'));
2027
- output.printList([
2028
- `Patterns file: ${persistence.patternsExist ? output.success(`${patternsFileEntries} entries (${(patternsFileSize / 1024).toFixed(1)} KB)`) : output.dim('Not created')}`,
2029
- `Stats file: ${persistence.statsExist ? output.success(`${trajectoriesFromDisk} trajectories`) : output.dim('Not created')}`,
2030
- ]);
2031
- if (!persistence.patternsExist && !persistence.statsExist) {
2032
- output.writeln();
2033
- output.writeln(output.dim(' No pattern data yet. Patterns accrue as hooks run.'));
2034
- }
2035
- }
2036
- // Performance
2037
- const perf = result.performance;
2038
- if (perf) {
2039
- output.writeln();
2040
- output.writeln(output.bold('v1 Performance Gains'));
2041
- output.printList([
2042
- `Memory Reduction: ${output.success(String(perf.memoryReduction ?? 'N/A'))}`,
2043
- `Search Improvement: ${output.success(String(perf.searchImprovement ?? 'N/A'))}`,
2044
- `Token Reduction: ${output.success(String(perf.tokenReduction ?? 'N/A'))}`,
2045
- `SWE-Bench Score: ${output.success(String(perf.sweBenchScore ?? 'N/A'))}`
2046
- ]);
2047
- }
2048
- return { success: true, data: result };
2049
- }
2050
- catch (error) {
2051
- spinner.fail('Intelligence system error');
2052
- if (error instanceof MCPClientError) {
2053
- output.printError(`Intelligence error: ${error.message}`);
2054
- }
2055
- else {
2056
- output.printError(`Unexpected error: ${String(error)}`);
2057
- }
2058
- return { success: false, exitCode: 1 };
2059
- }
2060
- }
2061
- };
2062
- function formatIntelligenceStatus(status) {
2063
- switch (status) {
2064
- case 'active':
2065
- case 'ready':
2066
- return output.success(status);
2067
- case 'training':
2068
- return output.highlight(status);
2069
- case 'idle':
2070
- return output.dim(status);
2071
- case 'disabled':
2072
- case 'error':
2073
- return output.error(status);
2074
- default:
2075
- return status;
2076
- }
2077
- }
2078
- // =============================================================================
2079
- // Worker Commands (12 Background Workers)
2080
- // =============================================================================
2081
- const workerListCommand = {
2082
- name: 'list',
2083
- description: 'List all 12 background workers with capabilities',
2084
- options: [
2085
- { name: 'status', short: 's', type: 'string', description: 'Filter by status (all, running, completed, pending)' },
2086
- { name: 'active', short: 'a', type: 'boolean', description: 'Show active worker instances' },
2087
- ],
2088
- examples: [
2089
- { command: 'monomind hooks worker list', description: 'List all workers' },
2090
- { command: 'monomind hooks worker list --active', description: 'Show active instances' },
2091
- ],
2092
- action: async (ctx) => {
2093
- const spinner = output.createSpinner({ text: 'Loading workers...', spinner: 'dots' });
2094
- spinner.start();
2095
- try {
2096
- const result = await callMCPTool('hooks_worker-list', {
2097
- status: ctx.flags['status'] || 'all',
2098
- includeActive: ctx.flags['active'] !== false,
2099
- });
2100
- spinner.succeed('Workers loaded');
2101
- output.writeln();
2102
- output.writeln(output.bold('Background Workers (12 Total)'));
2103
- output.writeln();
2104
- output.printTable({
2105
- columns: [
2106
- { key: 'trigger', header: 'Worker', width: 14 },
2107
- { key: 'priority', header: 'Priority', width: 10 },
2108
- { key: 'estimatedDuration', header: 'Est. Time', width: 10 },
2109
- { key: 'description', header: 'Description', width: 40 },
2110
- ],
2111
- data: result.workers.map(w => ({
2112
- trigger: output.highlight(w.trigger),
2113
- priority: w.priority === 'critical' ? output.error(w.priority) :
2114
- w.priority === 'high' ? output.warning(w.priority) :
2115
- w.priority,
2116
- estimatedDuration: w.estimatedDuration,
2117
- description: w.description,
2118
- })),
2119
- });
2120
- if (ctx.flags['active'] && result.active.count > 0) {
2121
- output.writeln();
2122
- output.writeln(output.bold('Active Instances'));
2123
- output.printTable({
2124
- columns: [
2125
- { key: 'id', header: 'Worker ID', width: 35 },
2126
- { key: 'trigger', header: 'Type', width: 12 },
2127
- { key: 'status', header: 'Status', width: 12 },
2128
- { key: 'progress', header: 'Progress', width: 10 },
2129
- ],
2130
- data: result.active.instances.map(w => ({
2131
- id: w.id,
2132
- trigger: w.trigger,
2133
- status: w.status === 'running' ? output.highlight(w.status) :
2134
- w.status === 'completed' ? output.success(w.status) :
2135
- w.status === 'failed' ? output.error(w.status) : w.status,
2136
- progress: `${w.progress}%`,
2137
- })),
2138
- });
2139
- }
2140
- output.writeln();
2141
- output.writeln(output.dim('Performance targets:'));
2142
- output.writeln(output.dim(` Trigger detection: ${result.performanceTargets.triggerDetection}`));
2143
- output.writeln(output.dim(` Worker spawn: ${result.performanceTargets.workerSpawn}`));
2144
- output.writeln(output.dim(` Max concurrent: ${result.performanceTargets.maxConcurrent}`));
2145
- return { success: true, data: result };
2146
- }
2147
- catch (error) {
2148
- spinner.fail('Failed to load workers');
2149
- if (error instanceof MCPClientError) {
2150
- output.printError(`Worker error: ${error.message}`);
2151
- }
2152
- return { success: false, exitCode: 1 };
2153
- }
2154
- }
2155
- };
2156
- const workerDispatchCommand = {
2157
- name: 'dispatch',
2158
- description: 'Dispatch a background worker for analysis/optimization',
2159
- options: [
2160
- { name: 'trigger', short: 't', type: 'string', description: 'Worker type (ultralearn, optimize, audit, map, etc.)', required: true },
2161
- { name: 'context', short: 'c', type: 'string', description: 'Context for the worker (file path, topic)' },
2162
- { name: 'priority', short: 'p', type: 'string', description: 'Priority (low, normal, high, critical)' },
2163
- { name: 'sync', short: 's', type: 'boolean', description: 'Wait for completion (synchronous)' },
2164
- ],
2165
- examples: [
2166
- { command: 'monomind hooks worker dispatch -t optimize -c src/', description: 'Dispatch optimize worker' },
2167
- { command: 'monomind hooks worker dispatch -t audit -p critical', description: 'Security audit with critical priority' },
2168
- { command: 'monomind hooks worker dispatch -t testgaps --sync', description: 'Test coverage analysis (sync)' },
2169
- ],
2170
- action: async (ctx) => {
2171
- const trigger = ctx.flags['trigger'];
2172
- const context = ctx.flags['context'] || 'default';
2173
- const priority = ctx.flags['priority'];
2174
- const background = !ctx.flags['sync'];
2175
- if (!trigger) {
2176
- output.printError('--trigger is required');
2177
- output.writeln('Available triggers: ultralearn, optimize, consolidate, predict, audit, map, preload, deepdive, document, refactor, benchmark, testgaps');
2178
- return { success: false, exitCode: 1 };
2179
- }
2180
- const spinner = output.createSpinner({ text: `Dispatching ${trigger} worker...`, spinner: 'dots' });
2181
- spinner.start();
2182
- try {
2183
- const result = await callMCPTool('hooks_worker-dispatch', {
2184
- trigger,
2185
- context,
2186
- priority,
2187
- background,
2188
- });
2189
- if (!result.success) {
2190
- spinner.fail(`Failed: ${result.error}`);
2191
- return { success: false, exitCode: 1 };
2192
- }
2193
- spinner.succeed(`Worker dispatched: ${result.workerId}`);
2194
- output.writeln();
2195
- output.printTable({
2196
- columns: [
2197
- { key: 'field', header: 'Field', width: 18 },
2198
- { key: 'value', header: 'Value', width: 50 },
2199
- ],
2200
- data: [
2201
- { field: 'Worker ID', value: output.highlight(result.workerId) },
2202
- { field: 'Trigger', value: result.trigger },
2203
- { field: 'Context', value: result.context },
2204
- { field: 'Priority', value: result.priority },
2205
- { field: 'Description', value: result.config.description },
2206
- { field: 'Est. Duration', value: result.config.estimatedDuration },
2207
- { field: 'Capabilities', value: result.config.capabilities.join(', ') },
2208
- { field: 'Status', value: result.status === 'dispatched' ? output.highlight('dispatched (background)') : output.success('completed') },
2209
- ],
2210
- });
2211
- if (background) {
2212
- output.writeln();
2213
- output.writeln(output.dim(`Check status: monomind hooks worker status --id ${result.workerId}`));
2214
- }
2215
- return { success: true, data: result };
2216
- }
2217
- catch (error) {
2218
- spinner.fail('Worker dispatch failed');
2219
- if (error instanceof MCPClientError) {
2220
- output.printError(`Dispatch error: ${error.message}`);
2221
- }
2222
- return { success: false, exitCode: 1 };
2223
- }
2224
- }
2225
- };
2226
- const workerStatusCommand = {
2227
- name: 'status',
2228
- description: 'Get status of workers',
2229
- options: [
2230
- { name: 'id', type: 'string', description: 'Specific worker ID to check' },
2231
- { name: 'all', short: 'a', type: 'boolean', description: 'Include completed workers' },
2232
- ],
2233
- examples: [
2234
- { command: 'monomind hooks worker status', description: 'Show running workers' },
2235
- { command: 'monomind hooks worker status --id worker_audit_1', description: 'Check specific worker' },
2236
- { command: 'monomind hooks worker status --all', description: 'Include completed workers' },
2237
- ],
2238
- action: async (ctx) => {
2239
- const workerId = ctx.flags['id'];
2240
- const includeCompleted = ctx.flags['all'];
2241
- const spinner = output.createSpinner({ text: 'Checking worker status...', spinner: 'dots' });
2242
- spinner.start();
2243
- try {
2244
- const result = await callMCPTool('hooks_worker-status', {
2245
- workerId,
2246
- includeCompleted,
2247
- });
2248
- if (!result.success) {
2249
- spinner.fail(`Failed: ${result.error}`);
2250
- return { success: false, exitCode: 1 };
2251
- }
2252
- spinner.succeed('Status retrieved');
2253
- if (result.worker) {
2254
- output.writeln();
2255
- output.writeln(output.bold(`Worker: ${result.worker.id}`));
2256
- output.printTable({
2257
- columns: [
2258
- { key: 'field', header: 'Field', width: 15 },
2259
- { key: 'value', header: 'Value', width: 40 },
2260
- ],
2261
- data: [
2262
- { field: 'Trigger', value: result.worker.trigger },
2263
- { field: 'Context', value: result.worker.context },
2264
- { field: 'Status', value: formatWorkerStatus(result.worker.status) },
2265
- { field: 'Progress', value: `${result.worker.progress}%` },
2266
- { field: 'Phase', value: result.worker.phase },
2267
- { field: 'Duration', value: `${result.worker.duration}ms` },
2268
- ],
2269
- });
2270
- }
2271
- else if (result.workers && result.workers.length > 0) {
2272
- output.writeln();
2273
- output.writeln(output.bold('Active Workers'));
2274
- output.printTable({
2275
- columns: [
2276
- { key: 'id', header: 'Worker ID', width: 35 },
2277
- { key: 'trigger', header: 'Type', width: 12 },
2278
- { key: 'status', header: 'Status', width: 12 },
2279
- { key: 'progress', header: 'Progress', width: 10 },
2280
- { key: 'duration', header: 'Duration', width: 12 },
2281
- ],
2282
- data: result.workers.map(w => ({
2283
- id: w.id,
2284
- trigger: w.trigger,
2285
- status: formatWorkerStatus(w.status),
2286
- progress: `${w.progress}%`,
2287
- duration: `${w.duration}ms`,
2288
- })),
2289
- });
2290
- if (result.summary) {
2291
- output.writeln();
2292
- output.writeln(`Total: ${result.summary.total} | Running: ${output.highlight(String(result.summary.running))} | Completed: ${output.success(String(result.summary.completed))} | Failed: ${output.error(String(result.summary.failed))}`);
2293
- }
2294
- }
2295
- else {
2296
- output.writeln();
2297
- output.writeln(output.dim('No active workers'));
2298
- }
2299
- return { success: true, data: result };
2300
- }
2301
- catch (error) {
2302
- spinner.fail('Status check failed');
2303
- if (error instanceof MCPClientError) {
2304
- output.printError(`Status error: ${error.message}`);
2305
- }
2306
- return { success: false, exitCode: 1 };
2307
- }
2308
- }
2309
- };
2310
- const workerDetectCommand = {
2311
- name: 'detect',
2312
- description: 'Detect worker triggers from prompt text',
2313
- options: [
2314
- { name: 'prompt', short: 'p', type: 'string', description: 'Prompt text to analyze', required: true },
2315
- { name: 'auto-dispatch', short: 'a', type: 'boolean', description: 'Automatically dispatch detected workers' },
2316
- { name: 'min-confidence', short: 'm', type: 'string', description: 'Minimum confidence threshold (0-1)' },
2317
- ],
2318
- examples: [
2319
- { command: 'monomind hooks worker detect -p "optimize performance"', description: 'Detect triggers in prompt' },
2320
- { command: 'monomind hooks worker detect -p "security audit" --auto-dispatch', description: 'Detect and dispatch' },
2321
- ],
2322
- action: async (ctx) => {
2323
- const prompt = ctx.flags['prompt'];
2324
- const autoDispatch = ctx.flags['auto-dispatch'];
2325
- const minConfidence = parseFloat(ctx.flags['min-confidence'] || '0.5');
2326
- if (!prompt) {
2327
- output.printError('--prompt is required');
2328
- return { success: false, exitCode: 1 };
2329
- }
2330
- const spinner = output.createSpinner({ text: 'Analyzing prompt...', spinner: 'dots' });
2331
- spinner.start();
2332
- try {
2333
- const result = await callMCPTool('hooks_worker-detect', {
2334
- prompt,
2335
- autoDispatch,
2336
- minConfidence,
2337
- });
2338
- if (result.detection.detected) {
2339
- spinner.succeed(`Detected ${result.triggersFound} worker trigger(s)`);
2340
- }
2341
- else {
2342
- spinner.succeed('No worker triggers detected');
2343
- }
2344
- output.writeln();
2345
- output.writeln(output.bold('Detection Results'));
2346
- output.writeln(`Prompt: ${output.dim(result.prompt)}`);
2347
- output.writeln(`Confidence: ${(result.detection.confidence * 100).toFixed(0)}%`);
2348
- if (result.triggerDetails && result.triggerDetails.length > 0) {
2349
- output.writeln();
2350
- output.printTable({
2351
- columns: [
2352
- { key: 'trigger', header: 'Trigger', width: 14 },
2353
- { key: 'priority', header: 'Priority', width: 10 },
2354
- { key: 'description', header: 'Description', width: 45 },
2355
- ],
2356
- data: result.triggerDetails.map(t => ({
2357
- trigger: output.highlight(t.trigger),
2358
- priority: t.priority,
2359
- description: t.description,
2360
- })),
2361
- });
2362
- }
2363
- if (result.autoDispatched && result.workerIds) {
2364
- output.writeln();
2365
- output.writeln(output.success('Workers auto-dispatched:'));
2366
- result.workerIds.forEach(id => {
2367
- output.writeln(` - ${id}`);
2368
- });
2369
- }
2370
- return { success: true, data: result };
2371
- }
2372
- catch (error) {
2373
- spinner.fail('Detection failed');
2374
- if (error instanceof MCPClientError) {
2375
- output.printError(`Detection error: ${error.message}`);
2376
- }
2377
- return { success: false, exitCode: 1 };
2378
- }
2379
- }
2380
- };
2381
- const workerCancelCommand = {
2382
- name: 'cancel',
2383
- description: 'Cancel a running worker',
2384
- options: [
2385
- { name: 'id', type: 'string', description: 'Worker ID to cancel', required: true },
2386
- ],
2387
- examples: [
2388
- { command: 'monomind hooks worker cancel --id worker_audit_1', description: 'Cancel specific worker' },
2389
- ],
2390
- action: async (ctx) => {
2391
- const workerId = ctx.flags['id'];
2392
- if (!workerId) {
2393
- output.printError('--id is required');
2394
- return { success: false, exitCode: 1 };
2395
- }
2396
- const spinner = output.createSpinner({ text: `Cancelling worker ${workerId}...`, spinner: 'dots' });
2397
- spinner.start();
2398
- try {
2399
- const result = await callMCPTool('hooks_worker-cancel', { workerId });
2400
- if (!result.success) {
2401
- spinner.fail(`Failed: ${result.error}`);
2402
- return { success: false, exitCode: 1 };
2403
- }
2404
- spinner.succeed(`Worker ${workerId} cancelled`);
2405
- return { success: true, data: result };
2406
- }
2407
- catch (error) {
2408
- spinner.fail('Cancel failed');
2409
- if (error instanceof MCPClientError) {
2410
- output.printError(`Cancel error: ${error.message}`);
2411
- }
2412
- return { success: false, exitCode: 1 };
2413
- }
2414
- }
2415
- };
2416
- function formatWorkerStatus(status) {
2417
- switch (status) {
2418
- case 'running':
2419
- return output.highlight(status);
2420
- case 'completed':
2421
- return output.success(status);
2422
- case 'failed':
2423
- return output.error(status);
2424
- case 'pending':
2425
- return output.dim(status);
2426
- default:
2427
- return status;
2428
- }
2429
- }
2430
- // ============================================================================
2431
- // Coverage-Aware Routing Commands
2432
- // ============================================================================
2433
- // Coverage route subcommand
2434
- const coverageRouteCommand = {
2435
- name: 'coverage-route',
2436
- description: 'Route task to agents based on test coverage gaps (monovector integration)',
2437
- options: [
2438
- {
2439
- name: 'task',
2440
- short: 't',
2441
- description: 'Task description to route',
2442
- type: 'string',
2443
- required: true
2444
- },
2445
- {
2446
- name: 'threshold',
2447
- description: 'Coverage threshold percentage (default: 80)',
2448
- type: 'number',
2449
- default: 80
2450
- },
2451
- {
2452
- name: 'no-monovector',
2453
- description: 'Disable monovector integration',
2454
- type: 'boolean',
2455
- default: false
2456
- }
2457
- ],
2458
- examples: [
2459
- { command: 'monomind hooks coverage-route -t "fix bug in auth"', description: 'Route with coverage awareness' },
2460
- { command: 'monomind hooks coverage-route -t "add tests" --threshold 90', description: 'Route with custom threshold' }
2461
- ],
2462
- action: async (ctx) => {
2463
- const task = ctx.args[0] || ctx.flags.task;
2464
- const threshold = ctx.flags.threshold || 80;
2465
- const useMonovector = !ctx.flags['no-monovector'];
2466
- if (!task) {
2467
- output.printError('Task description is required. Use --task or -t flag.');
2468
- return { success: false, exitCode: 1 };
2469
- }
2470
- const spinner = output.createSpinner({ text: 'Analyzing coverage and routing task...' });
2471
- spinner.start();
2472
- // Try reading coverage from disk first
2473
- const diskCoverage = readCoverageFromDisk();
2474
- if (diskCoverage.found) {
2475
- spinner.succeed(`Coverage data loaded from ${diskCoverage.source}`);
2476
- // Find files with lowest coverage that may relate to the task
2477
- const taskLower = task.toLowerCase();
2478
- const taskWords = taskLower.split(/\s+/).filter(w => w.length > 2);
2479
- // Score each file by relevance to the task and how low its coverage is
2480
- const scoredFiles = diskCoverage.entries
2481
- .filter(e => e.lines < threshold)
2482
- .map(e => {
2483
- const fileNameLower = e.filePath.toLowerCase();
2484
- let relevance = 0;
2485
- for (const word of taskWords) {
2486
- if (fileNameLower.includes(word))
2487
- relevance += 2;
2488
- }
2489
- // Penalize high coverage (we care about low coverage)
2490
- const coveragePenalty = e.lines / 100;
2491
- return { ...e, relevance, score: relevance + (1 - coveragePenalty) };
2492
- })
2493
- .sort((a, b) => b.score - a.score);
2494
- const gaps = scoredFiles.slice(0, 8).map(e => {
2495
- const { gapType, priority } = classifyCoverageGap(e.lines, threshold);
2496
- return {
2497
- filePath: e.filePath,
2498
- coveragePercent: e.lines,
2499
- gapType,
2500
- priority,
2501
- suggestedAgents: suggestAgentsForFile(e.filePath),
2502
- reason: `${e.lines.toFixed(1)}% coverage, below ${threshold}%`,
2503
- };
2504
- });
2505
- const criticalGaps = gaps.filter(g => g.gapType === 'critical').length;
2506
- const primaryAgent = taskLower.includes('test') ? 'tester' :
2507
- taskLower.includes('security') || taskLower.includes('auth') ? 'security-auditor' :
2508
- taskLower.includes('fix') || taskLower.includes('bug') ? 'coder' : 'tester';
2509
- const suggestions = [];
2510
- if (criticalGaps > 0)
2511
- suggestions.push(`${criticalGaps} critical coverage gaps need immediate attention`);
2512
- if (diskCoverage.summary.overallLineCoverage < threshold) {
2513
- suggestions.push(`Overall line coverage (${diskCoverage.summary.overallLineCoverage.toFixed(1)}%) is below ${threshold}% threshold`);
2514
- }
2515
- if (scoredFiles.length > 8)
2516
- suggestions.push(`${scoredFiles.length - 8} additional files with low coverage`);
2517
- const result = {
2518
- success: true,
2519
- task,
2520
- coverageAware: true,
2521
- gaps,
2522
- routing: {
2523
- primaryAgent,
2524
- confidence: gaps.length > 0 ? 0.85 : 0.6,
2525
- reason: gaps.length > 0
2526
- ? `Routing to ${primaryAgent} based on ${gaps.length} coverage gaps related to task`
2527
- : `No coverage gaps found related to task, routing to ${primaryAgent}`,
2528
- coverageImpact: gaps.length > 0 ? 'high' : 'low',
2529
- },
2530
- suggestions,
2531
- metrics: {
2532
- filesAnalyzed: diskCoverage.summary.totalFiles,
2533
- totalGaps: scoredFiles.length,
2534
- criticalGaps,
2535
- avgCoverage: diskCoverage.summary.overallLineCoverage,
2536
- },
2537
- source: diskCoverage.source,
2538
- };
2539
- if (ctx.flags.format === 'json') {
2540
- output.printJson(result);
2541
- return { success: true, data: result };
2542
- }
2543
- output.writeln();
2544
- output.printBox([
2545
- `Agent: ${output.highlight(result.routing.primaryAgent)}`,
2546
- `Confidence: ${(result.routing.confidence * 100).toFixed(1)}%`,
2547
- `Coverage-Aware: ${output.success('Yes')} (from ${diskCoverage.source})`,
2548
- `Reason: ${result.routing.reason}`
2549
- ].join('\n'), 'Coverage-Aware Routing');
2550
- if (gaps.length > 0) {
2551
- output.writeln();
2552
- output.writeln(output.bold('Priority Coverage Gaps'));
2553
- output.printTable({
2554
- columns: [
2555
- { key: 'filePath', header: 'File', width: 35, format: (v) => {
2556
- const s = String(v);
2557
- return s.length > 32 ? '...' + s.slice(-32) : s;
2558
- } },
2559
- { key: 'coveragePercent', header: 'Coverage', width: 10, align: 'right', format: (v) => `${Number(v).toFixed(1)}%` },
2560
- { key: 'gapType', header: 'Type', width: 10 },
2561
- { key: 'suggestedAgents', header: 'Agent', width: 15, format: (v) => Array.isArray(v) ? v[0] || '' : String(v) }
2562
- ],
2563
- data: gaps.slice(0, 8)
2564
- });
2565
- }
2566
- if (result.metrics.filesAnalyzed > 0) {
2567
- output.writeln();
2568
- output.writeln(output.bold('Coverage Metrics'));
2569
- output.printList([
2570
- `Files Analyzed: ${result.metrics.filesAnalyzed}`,
2571
- `Total Gaps: ${result.metrics.totalGaps}`,
2572
- `Critical Gaps: ${result.metrics.criticalGaps}`,
2573
- `Average Coverage: ${result.metrics.avgCoverage.toFixed(1)}%`
2574
- ]);
2575
- }
2576
- if (suggestions.length > 0) {
2577
- output.writeln();
2578
- output.writeln(output.bold('Suggestions'));
2579
- output.printList(suggestions.map(s => output.dim(s)));
2580
- }
2581
- return { success: true, data: result };
2582
- }
2583
- // No disk coverage - fall back to MCP tool
2584
- try {
2585
- const result = await callMCPTool('hooks_coverage-route', {
2586
- task,
2587
- threshold,
2588
- useMonovector,
2589
- });
2590
- spinner.stop();
2591
- if (ctx.flags.format === 'json') {
2592
- output.printJson(result);
2593
- return { success: true, data: result };
2594
- }
2595
- output.writeln();
2596
- output.printBox([
2597
- `Agent: ${output.highlight(result.routing.primaryAgent)}`,
2598
- `Confidence: ${(result.routing.confidence * 100).toFixed(1)}%`,
2599
- `Coverage-Aware: ${result.coverageAware ? output.success('Yes') : output.dim('No coverage data')}`,
2600
- `Reason: ${result.routing.reason}`
2601
- ].join('\n'), 'Coverage-Aware Routing');
2602
- if (result.gaps.length > 0) {
2603
- output.writeln();
2604
- output.writeln(output.bold('Priority Coverage Gaps'));
2605
- output.printTable({
2606
- columns: [
2607
- { key: 'filePath', header: 'File', width: 35, format: (v) => {
2608
- const s = String(v);
2609
- return s.length > 32 ? '...' + s.slice(-32) : s;
2610
- } },
2611
- { key: 'coveragePercent', header: 'Coverage', width: 10, align: 'right', format: (v) => `${Number(v).toFixed(1)}%` },
2612
- { key: 'gapType', header: 'Type', width: 10 },
2613
- { key: 'suggestedAgents', header: 'Agent', width: 15, format: (v) => Array.isArray(v) ? v[0] || '' : String(v) }
2614
- ],
2615
- data: result.gaps.slice(0, 8)
2616
- });
2617
- }
2618
- if (result.metrics.filesAnalyzed > 0) {
2619
- output.writeln();
2620
- output.writeln(output.bold('Coverage Metrics'));
2621
- output.printList([
2622
- `Files Analyzed: ${result.metrics.filesAnalyzed}`,
2623
- `Total Gaps: ${result.metrics.totalGaps}`,
2624
- `Critical Gaps: ${result.metrics.criticalGaps}`,
2625
- `Average Coverage: ${result.metrics.avgCoverage.toFixed(1)}%`
2626
- ]);
2627
- }
2628
- if (result.suggestions.length > 0) {
2629
- output.writeln();
2630
- output.writeln(output.bold('Suggestions'));
2631
- output.printList(result.suggestions.map(s => output.dim(s)));
2632
- }
2633
- return { success: true, data: result };
2634
- }
2635
- catch (error) {
2636
- spinner.fail('No coverage data found');
2637
- output.writeln();
2638
- output.printWarning('No coverage data found. Run your test suite with coverage first.');
2639
- output.writeln();
2640
- output.printList([
2641
- 'Jest: npx jest --coverage',
2642
- 'Vitest: npx vitest --coverage',
2643
- 'nyc: npx nyc npm test',
2644
- 'c8: npx c8 npm test',
2645
- ]);
2646
- output.writeln();
2647
- output.writeln(output.dim('Expected files: coverage/coverage-summary.json, coverage/lcov.info, or .nyc_output/out.json'));
2648
- return { success: false, exitCode: 1 };
2649
- }
2650
- }
2651
- };
2652
- // Coverage suggest subcommand
2653
- const coverageSuggestCommand = {
2654
- name: 'coverage-suggest',
2655
- description: 'Suggest coverage improvements for a path (monovector integration)',
2656
- options: [
2657
- {
2658
- name: 'path',
2659
- short: 'p',
2660
- description: 'Path to analyze for coverage suggestions',
2661
- type: 'string',
2662
- required: true
2663
- },
2664
- {
2665
- name: 'threshold',
2666
- description: 'Coverage threshold percentage (default: 80)',
2667
- type: 'number',
2668
- default: 80
2669
- },
2670
- {
2671
- name: 'limit',
2672
- short: 'l',
2673
- description: 'Maximum number of suggestions (default: 20)',
2674
- type: 'number',
2675
- default: 20
2676
- }
2677
- ],
2678
- examples: [
2679
- { command: 'monomind hooks coverage-suggest -p src/', description: 'Suggest improvements for src/' },
2680
- { command: 'monomind hooks coverage-suggest -p src/services --threshold 90', description: 'Stricter threshold' }
2681
- ],
2682
- action: async (ctx) => {
2683
- const targetPath = ctx.args[0] || ctx.flags.path;
2684
- const threshold = ctx.flags.threshold || 80;
2685
- const limit = ctx.flags.limit || 20;
2686
- if (!targetPath) {
2687
- output.printError('Path is required. Use --path or -p flag.');
2688
- return { success: false, exitCode: 1 };
2689
- }
2690
- const spinner = output.createSpinner({ text: `Analyzing coverage for ${targetPath}...` });
2691
- spinner.start();
2692
- // Try reading coverage from disk first
2693
- const diskCoverage = readCoverageFromDisk();
2694
- if (diskCoverage.found) {
2695
- spinner.succeed(`Coverage data loaded from ${diskCoverage.source}`);
2696
- // Filter entries to those matching the target path
2697
- const pathLower = targetPath.toLowerCase().replace(/\\/g, '/');
2698
- const matchingEntries = diskCoverage.entries.filter(e => {
2699
- const fileLower = e.filePath.toLowerCase().replace(/\\/g, '/');
2700
- return fileLower.includes(pathLower);
2701
- });
2702
- const belowThreshold = matchingEntries.filter(e => e.lines < threshold);
2703
- const suggestions = belowThreshold.slice(0, limit).map(e => {
2704
- const { gapType, priority } = classifyCoverageGap(e.lines, threshold);
2705
- return {
2706
- filePath: e.filePath,
2707
- coveragePercent: e.lines,
2708
- gapType,
2709
- priority,
2710
- suggestedAgents: suggestAgentsForFile(e.filePath),
2711
- reason: e.lines === 0 ? 'No coverage at all' :
2712
- e.lines < 20 ? 'Very low coverage, needs tests' :
2713
- e.lines < 50 ? 'Below 50%, add more tests' :
2714
- `Below ${threshold}% threshold`,
2715
- };
2716
- });
2717
- const totalLinesCov = matchingEntries.length > 0
2718
- ? matchingEntries.reduce((acc, e) => acc + e.lines, 0) / matchingEntries.length
2719
- : 0;
2720
- const totalBranchesCov = matchingEntries.length > 0
2721
- ? matchingEntries.reduce((acc, e) => acc + e.branches, 0) / matchingEntries.length
2722
- : 0;
2723
- const prioritizedFiles = belowThreshold.slice(0, 5).map(e => e.filePath);
2724
- const result = {
2725
- success: true,
2726
- path: targetPath,
2727
- suggestions,
2728
- summary: {
2729
- totalFiles: matchingEntries.length,
2730
- overallLineCoverage: totalLinesCov,
2731
- overallBranchCoverage: totalBranchesCov,
2732
- filesBelowThreshold: belowThreshold.length,
2733
- },
2734
- prioritizedFiles,
2735
- monovectorAvailable: false,
2736
- source: diskCoverage.source,
2737
- };
2738
- if (ctx.flags.format === 'json') {
2739
- output.printJson(result);
2740
- return { success: true, data: result };
2741
- }
2742
- output.writeln();
2743
- output.printBox([
2744
- `Path: ${output.highlight(targetPath)}`,
2745
- `Files Analyzed: ${result.summary.totalFiles}`,
2746
- `Line Coverage: ${result.summary.overallLineCoverage.toFixed(1)}%`,
2747
- `Branch Coverage: ${result.summary.overallBranchCoverage.toFixed(1)}%`,
2748
- `Below Threshold: ${result.summary.filesBelowThreshold} files`,
2749
- `Source: ${output.highlight(diskCoverage.source)}`
2750
- ].join('\n'), 'Coverage Summary');
2751
- if (suggestions.length > 0) {
2752
- output.writeln();
2753
- output.writeln(output.bold('Coverage Improvement Suggestions'));
2754
- output.printTable({
2755
- columns: [
2756
- { key: 'filePath', header: 'File', width: 40, format: (v) => {
2757
- const s = String(v);
2758
- return s.length > 37 ? '...' + s.slice(-37) : s;
2759
- } },
2760
- { key: 'coveragePercent', header: 'Coverage', width: 10, align: 'right', format: (v) => `${Number(v).toFixed(1)}%` },
2761
- { key: 'gapType', header: 'Priority', width: 10 },
2762
- { key: 'reason', header: 'Reason', width: 25 }
2763
- ],
2764
- data: suggestions.slice(0, 15)
2765
- });
2766
- }
2767
- else {
2768
- output.writeln();
2769
- output.printSuccess('All files meet coverage threshold!');
2770
- }
2771
- if (prioritizedFiles.length > 0) {
2772
- output.writeln();
2773
- output.writeln(output.bold('Priority Files (Top 5)'));
2774
- output.printList(prioritizedFiles.slice(0, 5).map(f => output.highlight(f)));
2775
- }
2776
- return { success: true, data: result };
2777
- }
2778
- // No disk coverage - fall back to MCP tool
2779
- try {
2780
- const result = await callMCPTool('hooks_coverage-suggest', {
2781
- path: targetPath,
2782
- threshold,
2783
- limit,
2784
- });
2785
- spinner.stop();
2786
- if (ctx.flags.format === 'json') {
2787
- output.printJson(result);
2788
- return { success: true, data: result };
2789
- }
2790
- output.writeln();
2791
- output.printBox([
2792
- `Path: ${output.highlight(result.path)}`,
2793
- `Files Analyzed: ${result.summary.totalFiles}`,
2794
- `Line Coverage: ${result.summary.overallLineCoverage.toFixed(1)}%`,
2795
- `Branch Coverage: ${result.summary.overallBranchCoverage.toFixed(1)}%`,
2796
- `Below Threshold: ${result.summary.filesBelowThreshold} files`,
2797
- `Keyword routing: ${result.monovectorAvailable ? output.success('Available') : output.dim('Unavailable')}`
2798
- ].join('\n'), 'Coverage Summary');
2799
- if (result.suggestions.length > 0) {
2800
- output.writeln();
2801
- output.writeln(output.bold('Coverage Improvement Suggestions'));
2802
- output.printTable({
2803
- columns: [
2804
- { key: 'filePath', header: 'File', width: 40, format: (v) => {
2805
- const s = String(v);
2806
- return s.length > 37 ? '...' + s.slice(-37) : s;
2807
- } },
2808
- { key: 'coveragePercent', header: 'Coverage', width: 10, align: 'right', format: (v) => `${Number(v).toFixed(1)}%` },
2809
- { key: 'gapType', header: 'Priority', width: 10 },
2810
- { key: 'reason', header: 'Reason', width: 25 }
2811
- ],
2812
- data: result.suggestions.slice(0, 15)
2813
- });
2814
- }
2815
- else {
2816
- output.writeln();
2817
- output.printSuccess('All files meet coverage threshold!');
2818
- }
2819
- if (result.prioritizedFiles.length > 0) {
2820
- output.writeln();
2821
- output.writeln(output.bold('Priority Files (Top 5)'));
2822
- output.printList(result.prioritizedFiles.slice(0, 5).map(f => output.highlight(f)));
2823
- }
2824
- return { success: true, data: result };
2825
- }
2826
- catch (error) {
2827
- spinner.fail('No coverage data found');
2828
- output.writeln();
2829
- output.printWarning('No coverage data found. Run your test suite with coverage first.');
2830
- output.writeln();
2831
- output.printList([
2832
- 'Jest: npx jest --coverage',
2833
- 'Vitest: npx vitest --coverage',
2834
- 'nyc: npx nyc npm test',
2835
- 'c8: npx c8 npm test',
2836
- ]);
2837
- output.writeln();
2838
- output.writeln(output.dim('Expected files: coverage/coverage-summary.json, coverage/lcov.info, or .nyc_output/out.json'));
2839
- return { success: false, exitCode: 1 };
2840
- }
2841
- }
2842
- };
2843
- // Coverage gaps subcommand
2844
- const coverageGapsCommand = {
2845
- name: 'coverage-gaps',
2846
- description: 'List all coverage gaps with priority scoring and agent assignments',
2847
- options: [
2848
- {
2849
- name: 'threshold',
2850
- description: 'Coverage threshold percentage (default: 80)',
2851
- type: 'number',
2852
- default: 80
2853
- },
2854
- {
2855
- name: 'group-by-agent',
2856
- description: 'Group gaps by suggested agent (default: true)',
2857
- type: 'boolean',
2858
- default: true
2859
- },
2860
- {
2861
- name: 'critical-only',
2862
- description: 'Show only critical gaps',
2863
- type: 'boolean',
2864
- default: false
2865
- }
2866
- ],
2867
- examples: [
2868
- { command: 'monomind hooks coverage-gaps', description: 'List all coverage gaps' },
2869
- { command: 'monomind hooks coverage-gaps --critical-only', description: 'Only critical gaps' },
2870
- { command: 'monomind hooks coverage-gaps --threshold 90', description: 'Stricter threshold' }
2871
- ],
2872
- action: async (ctx) => {
2873
- const threshold = ctx.flags.threshold || 80;
2874
- const groupByAgent = ctx.flags['group-by-agent'] !== false;
2875
- const criticalOnly = ctx.flags['critical-only'] || false;
2876
- const spinner = output.createSpinner({ text: 'Analyzing project coverage gaps...' });
2877
- spinner.start();
2878
- // Try reading coverage from disk first
2879
- const diskCoverage = readCoverageFromDisk();
2880
- if (diskCoverage.found) {
2881
- spinner.succeed(`Coverage data loaded from ${diskCoverage.source}`);
2882
- // Build gaps from disk data
2883
- const allGaps = diskCoverage.entries
2884
- .filter(e => e.lines < threshold)
2885
- .map(e => {
2886
- const { gapType, priority } = classifyCoverageGap(e.lines, threshold);
2887
- return {
2888
- filePath: e.filePath,
2889
- coveragePercent: e.lines,
2890
- gapType,
2891
- complexity: Math.round((100 - e.lines) / 10),
2892
- priority,
2893
- suggestedAgents: suggestAgentsForFile(e.filePath),
2894
- reason: `Line coverage ${e.lines.toFixed(1)}% below ${threshold}% threshold`,
2895
- };
2896
- });
2897
- const gaps = criticalOnly
2898
- ? allGaps.filter(g => g.gapType === 'critical')
2899
- : allGaps;
2900
- // Build agent assignments
2901
- const agentAssignments = {};
2902
- if (groupByAgent) {
2903
- for (const gap of gaps) {
2904
- const agent = gap.suggestedAgents[0] || 'tester';
2905
- if (!agentAssignments[agent])
2906
- agentAssignments[agent] = [];
2907
- agentAssignments[agent].push(gap.filePath);
2908
- }
2909
- }
2910
- const result = {
2911
- success: true,
2912
- gaps,
2913
- summary: {
2914
- totalFiles: diskCoverage.summary.totalFiles,
2915
- overallLineCoverage: diskCoverage.summary.overallLineCoverage,
2916
- overallBranchCoverage: diskCoverage.summary.overallBranchCoverage,
2917
- filesBelowThreshold: gaps.length,
2918
- coverageThreshold: threshold,
2919
- },
2920
- agentAssignments,
2921
- monovectorAvailable: false,
2922
- source: diskCoverage.source,
2923
- };
2924
- if (ctx.flags.format === 'json') {
2925
- output.printJson(result);
2926
- return { success: true, data: result };
2927
- }
2928
- output.writeln();
2929
- output.printBox([
2930
- `Total Files: ${result.summary.totalFiles}`,
2931
- `Line Coverage: ${result.summary.overallLineCoverage.toFixed(1)}%`,
2932
- `Branch Coverage: ${result.summary.overallBranchCoverage.toFixed(1)}%`,
2933
- `Below ${threshold}%: ${result.summary.filesBelowThreshold} files`,
2934
- `Source: ${output.highlight(diskCoverage.source)}`
2935
- ].join('\n'), 'Coverage Gap Analysis');
2936
- if (gaps.length > 0) {
2937
- output.writeln();
2938
- output.writeln(output.bold(`Coverage Gaps (${gaps.length} files)`));
2939
- output.printTable({
2940
- columns: [
2941
- { key: 'filePath', header: 'File', width: 35, format: (v) => {
2942
- const s = String(v);
2943
- return s.length > 32 ? '...' + s.slice(-32) : s;
2944
- } },
2945
- { key: 'coveragePercent', header: 'Coverage', width: 10, align: 'right', format: (v) => `${Number(v).toFixed(1)}%` },
2946
- { key: 'gapType', header: 'Type', width: 10, format: (v) => {
2947
- const t = String(v);
2948
- if (t === 'critical')
2949
- return output.error(t);
2950
- if (t === 'high')
2951
- return output.warning(t);
2952
- return t;
2953
- } },
2954
- { key: 'priority', header: 'Priority', width: 8, align: 'right' },
2955
- { key: 'suggestedAgents', header: 'Agent', width: 12, format: (v) => Array.isArray(v) ? v[0] || '' : String(v) }
2956
- ],
2957
- data: gaps.slice(0, 20)
2958
- });
2959
- }
2960
- else {
2961
- output.writeln();
2962
- output.printSuccess('No coverage gaps found! All files meet threshold.');
2963
- }
2964
- if (groupByAgent && Object.keys(agentAssignments).length > 0) {
2965
- output.writeln();
2966
- output.writeln(output.bold('Agent Assignments'));
2967
- for (const [agent, files] of Object.entries(agentAssignments)) {
2968
- output.writeln();
2969
- output.writeln(` ${output.highlight(agent)} (${files.length} files)`);
2970
- files.slice(0, 3).forEach(f => {
2971
- output.writeln(` - ${output.dim(f)}`);
2972
- });
2973
- if (files.length > 3) {
2974
- output.writeln(` ... and ${files.length - 3} more`);
2975
- }
2976
- }
2977
- }
2978
- return { success: true, data: result };
2979
- }
2980
- // No coverage files on disk - try MCP tool as fallback
2981
- try {
2982
- const result = await callMCPTool('hooks_coverage-gaps', {
2983
- threshold,
2984
- groupByAgent,
2985
- });
2986
- spinner.stop();
2987
- const gaps = criticalOnly
2988
- ? result.gaps.filter(g => g.gapType === 'critical')
2989
- : result.gaps;
2990
- if (ctx.flags.format === 'json') {
2991
- output.printJson({ ...result, gaps });
2992
- return { success: true, data: result };
2993
- }
2994
- output.writeln();
2995
- output.printBox([
2996
- `Total Files: ${result.summary.totalFiles}`,
2997
- `Line Coverage: ${result.summary.overallLineCoverage.toFixed(1)}%`,
2998
- `Branch Coverage: ${result.summary.overallBranchCoverage.toFixed(1)}%`,
2999
- `Below ${result.summary.coverageThreshold}%: ${result.summary.filesBelowThreshold} files`,
3000
- `Keyword routing: ${result.monovectorAvailable ? output.success('Available') : output.dim('Unavailable')}`
3001
- ].join('\n'), 'Coverage Gap Analysis');
3002
- if (gaps.length > 0) {
3003
- output.writeln();
3004
- output.writeln(output.bold(`Coverage Gaps (${gaps.length} files)`));
3005
- output.printTable({
3006
- columns: [
3007
- { key: 'filePath', header: 'File', width: 35, format: (v) => {
3008
- const s = String(v);
3009
- return s.length > 32 ? '...' + s.slice(-32) : s;
3010
- } },
3011
- { key: 'coveragePercent', header: 'Coverage', width: 10, align: 'right', format: (v) => `${Number(v).toFixed(1)}%` },
3012
- { key: 'gapType', header: 'Type', width: 10, format: (v) => {
3013
- const t = String(v);
3014
- if (t === 'critical')
3015
- return output.error(t);
3016
- if (t === 'high')
3017
- return output.warning(t);
3018
- return t;
3019
- } },
3020
- { key: 'priority', header: 'Priority', width: 8, align: 'right' },
3021
- { key: 'suggestedAgents', header: 'Agent', width: 12, format: (v) => Array.isArray(v) ? v[0] || '' : String(v) }
3022
- ],
3023
- data: gaps.slice(0, 20)
3024
- });
3025
- }
3026
- else {
3027
- output.writeln();
3028
- output.printSuccess('No coverage gaps found! All files meet threshold.');
3029
- }
3030
- if (groupByAgent && Object.keys(result.agentAssignments).length > 0) {
3031
- output.writeln();
3032
- output.writeln(output.bold('Agent Assignments'));
3033
- for (const [agent, files] of Object.entries(result.agentAssignments)) {
3034
- output.writeln();
3035
- output.writeln(` ${output.highlight(agent)} (${files.length} files)`);
3036
- files.slice(0, 3).forEach(f => {
3037
- output.writeln(` - ${output.dim(f)}`);
3038
- });
3039
- if (files.length > 3) {
3040
- output.writeln(` ... and ${files.length - 3} more`);
3041
- }
3042
- }
3043
- }
3044
- return { success: true, data: result };
3045
- }
3046
- catch (error) {
3047
- spinner.fail('No coverage data found');
3048
- output.writeln();
3049
- output.printWarning('No coverage data found. Run your test suite with coverage first.');
3050
- output.writeln();
3051
- output.printList([
3052
- 'Jest: npx jest --coverage',
3053
- 'Vitest: npx vitest --coverage',
3054
- 'nyc: npx nyc npm test',
3055
- 'c8: npx c8 npm test',
3056
- ]);
3057
- output.writeln();
3058
- output.writeln(output.dim('Expected files: coverage/coverage-summary.json, coverage/lcov.info, or .nyc_output/out.json'));
3059
- return { success: false, exitCode: 1 };
3060
- }
3061
- }
3062
- };
3063
- // Progress hook command
3064
- const progressHookCommand = {
3065
- name: 'progress',
3066
- description: 'Check implementation progress via hooks',
3067
- options: [
3068
- {
3069
- name: 'detailed',
3070
- short: 'd',
3071
- description: 'Show detailed breakdown by category',
3072
- type: 'boolean',
3073
- default: false
3074
- },
3075
- {
3076
- name: 'sync',
3077
- short: 's',
3078
- description: 'Sync and persist progress to file',
3079
- type: 'boolean',
3080
- default: false
3081
- },
3082
- {
3083
- name: 'summary',
3084
- description: 'Show human-readable summary',
3085
- type: 'boolean',
3086
- default: false
3087
- }
3088
- ],
3089
- examples: [
3090
- { command: 'monomind hooks progress', description: 'Check current progress' },
3091
- { command: 'monomind hooks progress -d', description: 'Detailed breakdown' },
3092
- { command: 'monomind hooks progress --sync', description: 'Sync progress to file' },
3093
- { command: 'monomind hooks progress --summary', description: 'Human-readable summary' }
3094
- ],
3095
- action: async (ctx) => {
3096
- const detailed = ctx.flags.detailed;
3097
- const sync = ctx.flags.sync;
3098
- const summary = ctx.flags.summary;
3099
- try {
3100
- if (summary) {
3101
- const spinner = output.createSpinner({ text: 'Getting progress summary...' });
3102
- spinner.start();
3103
- const result = await callMCPTool('progress_summary', {});
3104
- spinner.stop();
3105
- if (ctx.flags.format === 'json') {
3106
- output.printJson(result);
3107
- return { success: true, data: result };
3108
- }
3109
- output.writeln();
3110
- output.writeln(result.summary);
3111
- return { success: true, data: result };
3112
- }
3113
- if (sync) {
3114
- const spinner = output.createSpinner({ text: 'Syncing progress...' });
3115
- spinner.start();
3116
- const result = await callMCPTool('progress_sync', {});
3117
- spinner.stop();
3118
- if (ctx.flags.format === 'json') {
3119
- output.printJson(result);
3120
- return { success: true, data: result };
3121
- }
3122
- output.writeln();
3123
- output.printSuccess(`Progress synced: ${result.progress}%`);
3124
- output.writeln(output.dim(` Persisted to .monomind/metrics/v1-progress.json`));
3125
- output.writeln(output.dim(` Last updated: ${result.lastUpdated}`));
3126
- return { success: true, data: result };
3127
- }
3128
- // Default: check progress
3129
- const spinner = output.createSpinner({ text: 'Checking v1 progress...' });
3130
- spinner.start();
3131
- const result = await callMCPTool('progress_check', { detailed });
3132
- spinner.stop();
3133
- if (ctx.flags.format === 'json') {
3134
- output.printJson(result);
3135
- return { success: true, data: result };
3136
- }
3137
- output.writeln();
3138
- const progressValue = result.overall ?? result.progress ?? 0;
3139
- // Create progress bar
3140
- const barWidth = 30;
3141
- const filled = Math.round((progressValue / 100) * barWidth);
3142
- const empty = barWidth - filled;
3143
- const bar = output.success('█'.repeat(filled)) + output.dim('░'.repeat(empty));
3144
- output.writeln(output.bold('v1 Implementation Progress'));
3145
- output.writeln();
3146
- output.writeln(`[${bar}] ${progressValue}%`);
3147
- output.writeln();
3148
- if (detailed && result.cli) {
3149
- output.writeln(output.highlight('CLI Commands:') + ` ${result.cli.progress}% (${result.cli.commands}/${result.cli.target})`);
3150
- output.writeln(output.highlight('MCP Tools:') + ` ${result.mcp?.progress ?? 0}% (${result.mcp?.tools ?? 0}/${result.mcp?.target ?? 0})`);
3151
- output.writeln(output.highlight('Hooks:') + ` ${result.hooks?.progress ?? 0}% (${result.hooks?.subcommands ?? 0}/${result.hooks?.target ?? 0})`);
3152
- output.writeln(output.highlight('Packages:') + ` ${result.packages?.progress ?? 0}% (${result.packages?.total ?? 0}/${result.packages?.target ?? 0})`);
3153
- output.writeln(output.highlight('DDD Structure:') + ` ${result.ddd?.progress ?? 0}% (${result.packages?.withDDD ?? 0}/${result.packages?.total ?? 0})`);
3154
- output.writeln();
3155
- if (result.codebase) {
3156
- output.writeln(output.dim(`Codebase: ${result.codebase.totalFiles} files, ${result.codebase.totalLines.toLocaleString()} lines`));
3157
- }
3158
- }
3159
- else if (result.breakdown) {
3160
- output.writeln('Breakdown:');
3161
- for (const [category, value] of Object.entries(result.breakdown)) {
3162
- output.writeln(` ${output.highlight(category)}: ${value}`);
3163
- }
3164
- }
3165
- if (result.lastUpdated) {
3166
- output.writeln(output.dim(`Last updated: ${result.lastUpdated}`));
3167
- }
3168
- return { success: true, data: result };
3169
- }
3170
- catch (error) {
3171
- if (error instanceof MCPClientError) {
3172
- output.printError(`Progress check failed: ${error.message}`);
3173
- }
3174
- else {
3175
- output.printError(`Progress check failed: ${String(error)}`);
3176
- }
3177
- return { success: false, exitCode: 1 };
3178
- }
3179
- }
3180
- };
3181
- // Worker parent command
3182
- const workerCommand = {
3183
- name: 'worker',
3184
- description: 'Background worker management (12 workers for analysis/optimization)',
3185
- subcommands: [
3186
- workerListCommand,
3187
- workerDispatchCommand,
3188
- workerStatusCommand,
3189
- workerDetectCommand,
3190
- workerCancelCommand,
3191
- ],
3192
- options: [],
3193
- examples: [
3194
- { command: 'monomind hooks worker list', description: 'List all workers' },
3195
- { command: 'monomind hooks worker dispatch -t optimize', description: 'Dispatch optimizer' },
3196
- { command: 'monomind hooks worker detect -p "test coverage"', description: 'Detect from prompt' },
3197
- ],
3198
- action: async () => {
3199
- output.writeln();
3200
- output.writeln(output.bold('Background Worker System (12 Workers)'));
3201
- output.writeln();
3202
- output.writeln('Manage and dispatch background workers for analysis and optimization tasks.');
3203
- output.writeln();
3204
- output.writeln('Available Workers:');
3205
- output.printList([
3206
- `${output.highlight('ultralearn')} - Deep knowledge acquisition`,
3207
- `${output.highlight('optimize')} - Performance optimization`,
3208
- `${output.highlight('consolidate')} - Memory consolidation`,
3209
- `${output.highlight('predict')} - Predictive preloading`,
3210
- `${output.highlight('audit')} - Security analysis (critical)`,
3211
- `${output.highlight('map')} - Codebase mapping`,
3212
- `${output.highlight('preload')} - Resource preloading`,
3213
- `${output.highlight('deepdive')} - Deep code analysis`,
3214
- `${output.highlight('document')} - Auto-documentation`,
3215
- `${output.highlight('refactor')} - Refactoring suggestions`,
3216
- `${output.highlight('benchmark')} - Performance benchmarks`,
3217
- `${output.highlight('testgaps')} - Test coverage analysis`,
3218
- ]);
3219
- output.writeln();
3220
- output.writeln('Subcommands:');
3221
- output.printList([
3222
- `${output.highlight('list')} - List all workers with capabilities`,
3223
- `${output.highlight('dispatch')} - Dispatch a worker`,
3224
- `${output.highlight('status')} - Check worker status`,
3225
- `${output.highlight('detect')} - Detect triggers from prompt`,
3226
- `${output.highlight('cancel')} - Cancel a running worker`,
3227
- ]);
3228
- output.writeln();
3229
- output.writeln('Run "monomind hooks worker <subcommand> --help" for details');
3230
- return { success: true };
3231
- }
3232
- };
3233
- // Statusline subcommand - generates dynamic status display
3234
- const statuslineCommand = {
3235
- name: 'statusline',
3236
- description: 'Generate dynamic statusline with v1 progress and system status',
3237
- options: [
3238
- {
3239
- name: 'json',
3240
- description: 'Output as JSON',
3241
- type: 'boolean',
3242
- default: false
3243
- },
3244
- {
3245
- name: 'compact',
3246
- description: 'Compact single-line output',
3247
- type: 'boolean',
3248
- default: false
3249
- },
3250
- {
3251
- name: 'no-color',
3252
- description: 'Disable ANSI colors',
3253
- type: 'boolean',
3254
- default: false
3255
- }
3256
- ],
3257
- examples: [
3258
- { command: 'monomind hooks statusline', description: 'Display full statusline' },
3259
- { command: 'monomind hooks statusline --json', description: 'JSON output for hooks' },
3260
- { command: 'monomind hooks statusline --compact', description: 'Single-line status' }
3261
- ],
3262
- action: async (ctx) => {
3263
- const fs = await import('fs');
3264
- const path = await import('path');
3265
- const { execSync } = await import('child_process');
3266
- // Get learning stats from memory database
3267
- function getLearningStats() {
3268
- const memoryPaths = [
3269
- path.join(process.cwd(), '.swarm', 'memory.db'),
3270
- path.join(process.cwd(), '.claude', 'memory.db'),
3271
- ];
3272
- let patterns = 0;
3273
- let sessions = 0;
3274
- let trajectories = 0;
3275
- for (const dbPath of memoryPaths) {
3276
- if (fs.existsSync(dbPath)) {
3277
- try {
3278
- const stats = fs.statSync(dbPath);
3279
- const sizeKB = stats.size / 1024;
3280
- patterns = Math.floor(sizeKB / 2);
3281
- sessions = Math.max(1, Math.floor(patterns / 10));
3282
- trajectories = Math.floor(patterns / 5);
3283
- break;
3284
- }
3285
- catch {
3286
- // Ignore
3287
- }
3288
- }
3289
- }
3290
- const sessionsPath = path.join(process.cwd(), '.claude', 'sessions');
3291
- if (fs.existsSync(sessionsPath)) {
3292
- try {
3293
- const sessionFiles = fs.readdirSync(sessionsPath).filter((f) => f.endsWith('.json'));
3294
- sessions = Math.max(sessions, sessionFiles.length);
3295
- }
3296
- catch {
3297
- // Ignore
3298
- }
3299
- }
3300
- return { patterns, sessions, trajectories };
3301
- }
3302
- // Get v1 progress
3303
- function getv1Progress() {
3304
- const learning = getLearningStats();
3305
- let domainsCompleted = 0;
3306
- if (learning.patterns >= 500)
3307
- domainsCompleted = 5;
3308
- else if (learning.patterns >= 200)
3309
- domainsCompleted = 4;
3310
- else if (learning.patterns >= 100)
3311
- domainsCompleted = 3;
3312
- else if (learning.patterns >= 50)
3313
- domainsCompleted = 2;
3314
- else if (learning.patterns >= 10)
3315
- domainsCompleted = 1;
3316
- const totalDomains = 5;
3317
- const dddProgress = Math.min(100, Math.floor((domainsCompleted / totalDomains) * 100));
3318
- return { domainsCompleted, totalDomains, dddProgress, patternsLearned: learning.patterns, sessionsCompleted: learning.sessions };
3319
- }
3320
- // Get security status
3321
- function getSecurityStatus() {
3322
- const scanResultsPath = path.join(process.cwd(), '.claude', 'security-scans');
3323
- let cvesFixed = 0;
3324
- const totalCves = 3;
3325
- if (fs.existsSync(scanResultsPath)) {
3326
- try {
3327
- const scans = fs.readdirSync(scanResultsPath).filter((f) => f.endsWith('.json'));
3328
- cvesFixed = Math.min(totalCves, scans.length);
3329
- }
3330
- catch {
3331
- // Ignore
3332
- }
3333
- }
3334
- const auditPath = path.join(process.cwd(), '.swarm', 'security');
3335
- if (fs.existsSync(auditPath)) {
3336
- try {
3337
- const audits = fs.readdirSync(auditPath).filter((f) => f.includes('audit'));
3338
- cvesFixed = Math.min(totalCves, Math.max(cvesFixed, audits.length));
3339
- }
3340
- catch {
3341
- // Ignore
3342
- }
3343
- }
3344
- const status = cvesFixed >= totalCves ? 'CLEAN' : cvesFixed > 0 ? 'IN_PROGRESS' : 'PENDING';
3345
- return { status, cvesFixed, totalCves };
3346
- }
3347
- // Get swarm status
3348
- function getSwarmStatus() {
3349
- let activeAgents = 0;
3350
- let coordinationActive = false;
3351
- const maxAgents = 15;
3352
- const isWindows = process.platform === 'win32';
3353
- try {
3354
- const psCmd = isWindows
3355
- ? 'tasklist /FI "IMAGENAME eq node.exe" 2>NUL | findstr /I /C:"node" >NUL && echo 1 || echo 0'
3356
- : 'ps aux 2>/dev/null | grep -c agentic-flow || echo "0"';
3357
- const ps = execSync(psCmd, { encoding: 'utf-8' });
3358
- const raw = parseInt(ps.trim());
3359
- activeAgents = Math.max(0, isWindows ? raw : raw - 1);
3360
- coordinationActive = activeAgents > 0;
3361
- }
3362
- catch {
3363
- // Ignore
3364
- }
3365
- return { activeAgents, maxAgents, coordinationActive };
3366
- }
3367
- // Get system metrics
3368
- function getSystemMetrics() {
3369
- let memoryMB = 0;
3370
- let subAgents = 0;
3371
- const learning = getLearningStats();
3372
- try {
3373
- memoryMB = Math.floor(process.memoryUsage().heapUsed / 1024 / 1024);
3374
- }
3375
- catch {
3376
- // Ignore
3377
- }
3378
- // Calculate intelligence from multiple sources (matching statusline-generator.ts)
3379
- let intelligencePct = 0;
3380
- // 1. Check learning.json for REAL intelligence metrics first
3381
- const learningJsonPaths = [
3382
- path.join(process.cwd(), '.monomind', 'learning.json'),
3383
- path.join(process.cwd(), '.claude', '.monomind', 'learning.json'),
3384
- path.join(process.cwd(), '.swarm', 'learning.json'),
3385
- ];
3386
- for (const lPath of learningJsonPaths) {
3387
- if (fs.existsSync(lPath)) {
3388
- try {
3389
- if (fs.statSync(lPath).size <= 524_288) {
3390
- const data = JSON.parse(fs.readFileSync(lPath, 'utf-8'));
3391
- if (data.intelligence?.score !== undefined) {
3392
- intelligencePct = Math.min(100, Math.floor(data.intelligence.score));
3393
- break;
3394
- }
3395
- }
3396
- }
3397
- catch { /* ignore */ }
3398
- }
3399
- }
3400
- // 2. Fallback: calculate from patterns and vectors
3401
- if (intelligencePct === 0) {
3402
- const fromPatterns = learning.patterns > 0 ? Math.min(100, Math.floor(learning.patterns / 10)) : 0;
3403
- // Will be updated later with vector count
3404
- intelligencePct = fromPatterns;
3405
- }
3406
- // 3. Fallback: calculate maturity score from project indicators
3407
- if (intelligencePct === 0) {
3408
- let maturityScore = 0;
3409
- // Check for key project files/dirs
3410
- if (fs.existsSync(path.join(process.cwd(), '.claude')))
3411
- maturityScore += 15;
3412
- if (fs.existsSync(path.join(process.cwd(), '.monomind')))
3413
- maturityScore += 15;
3414
- if (fs.existsSync(path.join(process.cwd(), 'CLAUDE.md')))
3415
- maturityScore += 10;
3416
- if (fs.existsSync(path.join(process.cwd(), 'monomind.config.json')))
3417
- maturityScore += 10;
3418
- if (fs.existsSync(path.join(process.cwd(), '.swarm')))
3419
- maturityScore += 10;
3420
- // Check for test files
3421
- const testDirs = ['tests', '__tests__', 'test', 'v1/__tests__'];
3422
- for (const dir of testDirs) {
3423
- if (fs.existsSync(path.join(process.cwd(), dir))) {
3424
- maturityScore += 10;
3425
- break;
3426
- }
3427
- }
3428
- // Check for hooks config
3429
- if (fs.existsSync(path.join(process.cwd(), '.claude', 'settings.json')))
3430
- maturityScore += 10;
3431
- intelligencePct = Math.min(100, maturityScore);
3432
- }
3433
- const contextPct = Math.min(100, Math.floor(learning.sessions * 5));
3434
- return { memoryMB, contextPct, intelligencePct, subAgents };
3435
- }
3436
- // Get user info
3437
- function getUserInfo() {
3438
- let name = 'user';
3439
- let gitBranch = '';
3440
- const modelName = 'Opus 4.6 (1M context)';
3441
- const isWindows = process.platform === 'win32';
3442
- try {
3443
- const nameCmd = isWindows
3444
- ? 'git config user.name 2>NUL || echo user'
3445
- : 'git config user.name 2>/dev/null || echo "user"';
3446
- const branchCmd = isWindows
3447
- ? 'git branch --show-current 2>NUL || echo.'
3448
- : 'git branch --show-current 2>/dev/null || echo ""';
3449
- name = execSync(nameCmd, { encoding: 'utf-8' }).trim();
3450
- gitBranch = execSync(branchCmd, { encoding: 'utf-8' }).trim();
3451
- if (gitBranch === '.')
3452
- gitBranch = '';
3453
- }
3454
- catch {
3455
- // Ignore
3456
- }
3457
- return { name, gitBranch, modelName };
3458
- }
3459
- // Collect all status
3460
- const progress = getv1Progress();
3461
- const security = getSecurityStatus();
3462
- const swarm = getSwarmStatus();
3463
- const system = getSystemMetrics();
3464
- const user = getUserInfo();
3465
- const statusData = {
3466
- user,
3467
- v1Progress: progress,
3468
- security,
3469
- swarm,
3470
- system,
3471
- timestamp: new Date().toISOString()
3472
- };
3473
- // JSON output
3474
- if (ctx.flags.json || ctx.flags.format === 'json') {
3475
- output.printJson(statusData);
3476
- return { success: true, data: statusData };
3477
- }
3478
- // Compact output
3479
- if (ctx.flags.compact) {
3480
- const line = `DDD:${progress.domainsCompleted}/${progress.totalDomains} CVE:${security.cvesFixed}/${security.totalCves} Swarm:${swarm.activeAgents}/${swarm.maxAgents} Ctx:${system.contextPct}% Int:${system.intelligencePct}%`;
3481
- output.writeln(line);
3482
- return { success: true, data: statusData };
3483
- }
3484
- // Full colored output
3485
- const noColor = ctx.flags['no-color'] || ctx.flags.noColor;
3486
- const c = noColor ? {
3487
- reset: '', bold: '', dim: '', red: '', green: '', yellow: '', blue: '',
3488
- purple: '', cyan: '', brightRed: '', brightGreen: '', brightYellow: '',
3489
- brightBlue: '', brightPurple: '', brightCyan: '', brightWhite: ''
3490
- } : {
3491
- reset: '\x1b[0m', bold: '\x1b[1m', dim: '\x1b[2m', red: '\x1b[0;31m',
3492
- green: '\x1b[0;32m', yellow: '\x1b[0;33m', blue: '\x1b[0;34m',
3493
- purple: '\x1b[0;35m', cyan: '\x1b[0;36m', brightRed: '\x1b[1;31m',
3494
- brightGreen: '\x1b[1;32m', brightYellow: '\x1b[1;33m', brightBlue: '\x1b[1;34m',
3495
- brightPurple: '\x1b[1;35m', brightCyan: '\x1b[1;36m', brightWhite: '\x1b[1;37m'
3496
- };
3497
- // Progress bar helper
3498
- const progressBar = (current, total) => {
3499
- const filled = Math.round((current / total) * 5);
3500
- const empty = 5 - filled;
3501
- return '[' + '●'.repeat(filled) + '○'.repeat(empty) + ']';
3502
- };
3503
- // Generate lines
3504
- let header = `${c.bold}${c.brightPurple}▊ Monomind ${c.reset}`;
3505
- header += `${swarm.coordinationActive ? c.brightCyan : c.dim}● ${c.brightCyan}${user.name}${c.reset}`;
3506
- if (user.gitBranch) {
3507
- header += ` ${c.dim}│${c.reset} ${c.brightBlue}⎇ ${user.gitBranch}${c.reset}`;
3508
- }
3509
- header += ` ${c.dim}│${c.reset} ${c.purple}${user.modelName}${c.reset}`;
3510
- const separator = `${c.dim}─────────────────────────────────────────────────────${c.reset}`;
3511
- // Get hooks stats
3512
- const hooksStats = { enabled: 0, total: 17 };
3513
- const settingsPath = path.join(process.cwd(), '.claude', 'settings.json');
3514
- if (fs.existsSync(settingsPath)) {
3515
- try {
3516
- if (fs.statSync(settingsPath).size <= 524_288) {
3517
- const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf-8'));
3518
- if (settings.hooks) {
3519
- hooksStats.enabled = Object.values(settings.hooks).filter((h) => h && typeof h === 'object').length;
3520
- }
3521
- }
3522
- }
3523
- catch { /* ignore */ }
3524
- }
3525
- // Get AgentDB stats (matching statusline-generator.ts paths)
3526
- const agentdbStats = { vectorCount: 0, dbSizeKB: 0, hasHnsw: false };
3527
- // Check for direct database files first
3528
- const dbPaths = [
3529
- path.join(process.cwd(), '.swarm', 'memory.db'),
3530
- path.join(process.cwd(), '.monomind', 'memory.db'),
3531
- path.join(process.cwd(), '.claude', 'memory.db'),
3532
- path.join(process.cwd(), 'data', 'memory.db'),
3533
- path.join(process.cwd(), 'memory.db'),
3534
- path.join(process.cwd(), '.agentdb', 'memory.db'),
3535
- path.join(process.cwd(), '.monomind', 'memory', 'agentdb.db'),
3536
- ];
3537
- for (const dbPath of dbPaths) {
3538
- if (fs.existsSync(dbPath)) {
3539
- try {
3540
- const stats = fs.statSync(dbPath);
3541
- agentdbStats.dbSizeKB = Math.round(stats.size / 1024);
3542
- agentdbStats.vectorCount = Math.floor(agentdbStats.dbSizeKB / 2);
3543
- agentdbStats.hasHnsw = agentdbStats.vectorCount > 100;
3544
- break;
3545
- }
3546
- catch { /* ignore */ }
3547
- }
3548
- }
3549
- // Check for AgentDB directories if no direct db found
3550
- if (agentdbStats.vectorCount === 0) {
3551
- const agentdbDirs = [
3552
- path.join(process.cwd(), '.monomind', 'agentdb'),
3553
- path.join(process.cwd(), '.swarm', 'agentdb'),
3554
- path.join(process.cwd(), 'data', 'agentdb'),
3555
- path.join(process.cwd(), '.agentdb'),
3556
- ];
3557
- for (const dir of agentdbDirs) {
3558
- if (fs.existsSync(dir)) {
3559
- try {
3560
- const files = fs.readdirSync(dir);
3561
- for (const f of files) {
3562
- if (f.endsWith('.db') || f.endsWith('.sqlite')) {
3563
- const filePath = path.join(dir, f);
3564
- const fileStat = fs.statSync(filePath);
3565
- agentdbStats.dbSizeKB += Math.round(fileStat.size / 1024);
3566
- }
3567
- }
3568
- agentdbStats.vectorCount = Math.floor(agentdbStats.dbSizeKB / 2);
3569
- agentdbStats.hasHnsw = agentdbStats.vectorCount > 100;
3570
- if (agentdbStats.vectorCount > 0)
3571
- break;
3572
- }
3573
- catch { /* ignore */ }
3574
- }
3575
- }
3576
- }
3577
- // Check for HNSW index files
3578
- const hnswPaths = [
3579
- path.join(process.cwd(), '.monomind', 'hnsw'),
3580
- path.join(process.cwd(), '.swarm', 'hnsw'),
3581
- path.join(process.cwd(), 'data', 'hnsw'),
3582
- ];
3583
- for (const hnswPath of hnswPaths) {
3584
- if (fs.existsSync(hnswPath)) {
3585
- agentdbStats.hasHnsw = true;
3586
- try {
3587
- const hnswFiles = fs.readdirSync(hnswPath);
3588
- const indexFile = hnswFiles.find(f => f.endsWith('.index'));
3589
- if (indexFile) {
3590
- const indexStat = fs.statSync(path.join(hnswPath, indexFile));
3591
- const hnswVectors = Math.floor(indexStat.size / 512);
3592
- agentdbStats.vectorCount = Math.max(agentdbStats.vectorCount, hnswVectors);
3593
- }
3594
- }
3595
- catch { /* ignore */ }
3596
- break;
3597
- }
3598
- }
3599
- // Check for vectors.json file
3600
- const vectorsPath = path.join(process.cwd(), '.monomind', 'vectors.json');
3601
- if (fs.existsSync(vectorsPath) && agentdbStats.vectorCount === 0) {
3602
- try {
3603
- if (fs.statSync(vectorsPath).size <= 8_388_608) {
3604
- const data = JSON.parse(fs.readFileSync(vectorsPath, 'utf-8'));
3605
- if (Array.isArray(data)) {
3606
- agentdbStats.vectorCount = data.length;
3607
- }
3608
- else if (data.vectors) {
3609
- agentdbStats.vectorCount = Object.keys(data.vectors).length;
3610
- }
3611
- }
3612
- }
3613
- catch { /* ignore */ }
3614
- }
3615
- // Get test stats
3616
- const testStats = { testFiles: 0, testCases: 0 };
3617
- const testPaths = ['tests', '__tests__', 'test', 'spec'];
3618
- for (const testPath of testPaths) {
3619
- const fullPath = path.join(process.cwd(), testPath);
3620
- if (fs.existsSync(fullPath)) {
3621
- try {
3622
- const files = fs.readdirSync(fullPath, { recursive: true });
3623
- testStats.testFiles = files.filter((f) => /\.(test|spec)\.(ts|js|tsx|jsx)$/.test(f)).length;
3624
- testStats.testCases = testStats.testFiles * 28; // Estimate
3625
- }
3626
- catch { /* ignore */ }
3627
- }
3628
- }
3629
- // Get MCP stats
3630
- const mcpStats = { enabled: 0, total: 0 };
3631
- const mcpPath = path.join(process.cwd(), '.mcp.json');
3632
- if (fs.existsSync(mcpPath)) {
3633
- try {
3634
- const mcp = JSON.parse(fs.readFileSync(mcpPath, 'utf-8'));
3635
- if (mcp.mcpServers) {
3636
- mcpStats.total = Object.keys(mcp.mcpServers).length;
3637
- mcpStats.enabled = mcpStats.total;
3638
- }
3639
- }
3640
- catch { /* ignore */ }
3641
- }
3642
- const domainsColor = progress.domainsCompleted >= 3 ? c.brightGreen : progress.domainsCompleted > 0 ? c.yellow : c.red;
3643
- // Dynamic perf indicator based on patterns/HNSW
3644
- let perfIndicator = `${c.dim}⚡ HNSW: idle${c.reset}`;
3645
- if (agentdbStats.hasHnsw && agentdbStats.vectorCount > 0) {
3646
- perfIndicator = `${c.brightGreen}⚡ HNSW ${agentdbStats.vectorCount.toLocaleString()} vec${c.reset}`;
3647
- }
3648
- else if (progress.patternsLearned > 0) {
3649
- const patternsK = progress.patternsLearned >= 1000 ? `${(progress.patternsLearned / 1000).toFixed(1)}k` : String(progress.patternsLearned);
3650
- perfIndicator = `${c.brightYellow}📚 ${patternsK} patterns${c.reset}`;
3651
- }
3652
- const line1 = `${c.brightCyan}🏗️ DDD Domains${c.reset} ${progressBar(progress.domainsCompleted, progress.totalDomains)} ` +
3653
- `${domainsColor}${progress.domainsCompleted}${c.reset}/${c.brightWhite}${progress.totalDomains}${c.reset} ` +
3654
- perfIndicator;
3655
- const swarmIndicator = swarm.coordinationActive ? `${c.brightGreen}◉${c.reset}` : `${c.dim}○${c.reset}`;
3656
- const agentsColor = swarm.activeAgents > 0 ? c.brightGreen : c.red;
3657
- const securityIcon = security.status === 'CLEAN' ? '🟢' : security.status === 'IN_PROGRESS' ? '🟡' : '🔴';
3658
- const securityColor = security.status === 'CLEAN' ? c.brightGreen : security.status === 'IN_PROGRESS' ? c.brightYellow : c.brightRed;
3659
- const hooksColor = hooksStats.enabled > 0 ? c.brightGreen : c.dim;
3660
- const line2 = `${c.brightYellow}🤖 Swarm${c.reset} ${swarmIndicator} [${agentsColor}${String(swarm.activeAgents).padStart(2)}${c.reset}/${c.brightWhite}${swarm.maxAgents}${c.reset}] ` +
3661
- `${c.brightPurple}👥 ${system.subAgents}${c.reset} ` +
3662
- `${c.brightBlue}🪝 ${hooksColor}${hooksStats.enabled}${c.reset}/${c.brightWhite}${hooksStats.total}${c.reset} ` +
3663
- `${securityIcon} ${securityColor}CVE ${security.cvesFixed}${c.reset}/${c.brightWhite}${security.totalCves}${c.reset} ` +
3664
- `${c.brightCyan}💾 ${system.memoryMB}MB${c.reset} ` +
3665
- `${c.brightPurple}🧠 ${String(system.intelligencePct).padStart(3)}%${c.reset}`;
3666
- const dddColor = progress.dddProgress >= 50 ? c.brightGreen : progress.dddProgress > 0 ? c.yellow : c.red;
3667
- const line3 = `${c.brightPurple}🔧 Architecture${c.reset} ` +
3668
- `${c.cyan}ADRs${c.reset} ${c.dim}●0/0${c.reset} ${c.dim}│${c.reset} ` +
3669
- `${c.cyan}DDD${c.reset} ${dddColor}●${String(progress.dddProgress).padStart(3)}%${c.reset} ${c.dim}│${c.reset} ` +
3670
- `${c.cyan}Security${c.reset} ${securityColor}●${security.status}${c.reset}`;
3671
- const vectorColor = agentdbStats.vectorCount > 0 ? c.brightGreen : c.dim;
3672
- const testColor = testStats.testFiles > 0 ? c.brightGreen : c.dim;
3673
- const mcpColor = mcpStats.enabled > 0 ? c.brightGreen : c.dim;
3674
- const sizeDisplay = agentdbStats.dbSizeKB >= 1024 ? `${(agentdbStats.dbSizeKB / 1024).toFixed(1)}MB` : `${agentdbStats.dbSizeKB}KB`;
3675
- const hnswIndicator = agentdbStats.hasHnsw ? `${c.brightGreen}⚡${c.reset}` : '';
3676
- const line4 = `${c.brightCyan}📊 AgentDB${c.reset} ` +
3677
- `${c.cyan}Vectors${c.reset} ${vectorColor}●${agentdbStats.vectorCount}${hnswIndicator}${c.reset} ${c.dim}│${c.reset} ` +
3678
- `${c.cyan}Size${c.reset} ${c.brightWhite}${sizeDisplay}${c.reset} ${c.dim}│${c.reset} ` +
3679
- `${c.cyan}Tests${c.reset} ${testColor}●${testStats.testFiles}${c.reset} ${c.dim}(${testStats.testCases} cases)${c.reset} ${c.dim}│${c.reset} ` +
3680
- `${c.cyan}MCP${c.reset} ${mcpColor}●${mcpStats.enabled}/${mcpStats.total}${c.reset}`;
3681
- output.writeln(header);
3682
- output.writeln(separator);
3683
- output.writeln(line1);
3684
- output.writeln(line2);
3685
- output.writeln(line3);
3686
- output.writeln(line4);
3687
- return { success: true, data: statusData };
3688
- }
3689
- };
3690
- // Backward-compatible aliases for v2 hooks
3691
- // These ensure old settings.json files continue to work
3692
- const routeTaskCommand = {
3693
- name: 'route-task',
3694
- description: '(DEPRECATED: Use "route" instead) Route task to optimal agent',
3695
- options: routeCommand.options,
337
+ // Backward-compatible aliases for v2 hooks
338
+ // These ensure old settings.json files continue to work
339
+ const routeTaskCommand = {
340
+ name: 'route-task',
341
+ description: '(DEPRECATED: Use "route" instead) Route task to optimal agent',
342
+ options: routeCommand.options,
3696
343
  examples: [
3697
344
  { command: 'monomind hooks route-task --auto-swarm true', description: 'Route with auto-swarm (v2 compat)' },
3698
345
  ],
@@ -3753,577 +400,6 @@ const postBashCommand = {
3753
400
  examples: postCommandCommand.examples,
3754
401
  action: postCommandCommand.action
3755
402
  };
3756
- // Token Optimizer command - integrates agentic-flow Agent Booster
3757
- const tokenOptimizeCommand = {
3758
- name: 'token-optimize',
3759
- description: 'Token optimization via agentic-flow Agent Booster (30-50% savings)',
3760
- options: [
3761
- { name: 'query', short: 'q', type: 'string', description: 'Query for compact context retrieval' },
3762
- { name: 'agents', short: 'A', type: 'number', description: 'Agent count for optimal config', default: '6' },
3763
- { name: 'report', short: 'r', type: 'boolean', description: 'Generate optimization report' },
3764
- { name: 'stats', short: 's', type: 'boolean', description: 'Show token savings statistics' },
3765
- ],
3766
- examples: [
3767
- { command: 'monomind hooks token-optimize --stats', description: 'Show token savings stats' },
3768
- { command: 'monomind hooks token-optimize -q "auth patterns"', description: 'Get compact context' },
3769
- { command: 'monomind hooks token-optimize -A 8 --report', description: 'Config for 8 agents + report' },
3770
- ],
3771
- action: async (ctx) => {
3772
- const query = ctx.flags['query'];
3773
- const agentCount = parseInt(ctx.flags['agents'] || '6', 10);
3774
- const showReport = ctx.flags['report'];
3775
- const showStats = ctx.flags['stats'];
3776
- const spinner = output.createSpinner({ text: 'Checking agentic-flow integration...', spinner: 'dots' });
3777
- spinner.start();
3778
- // Inline TokenOptimizer (self-contained, no external imports)
3779
- const stats = {
3780
- totalTokensSaved: 0,
3781
- editsOptimized: 0,
3782
- cacheHits: 0,
3783
- cacheMisses: 0,
3784
- memoriesRetrieved: 0,
3785
- };
3786
- let agenticFlowAvailable = false;
3787
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
3788
- let reasoningBank = null;
3789
- try {
3790
- // Check if agentic-flow v1 is available
3791
- const rb = await import('agentic-flow/reasoningbank').catch(() => null);
3792
- if (rb) {
3793
- agenticFlowAvailable = true;
3794
- if (typeof rb.retrieveMemories === 'function') {
3795
- reasoningBank = rb;
3796
- }
3797
- }
3798
- else {
3799
- // Legacy check for older agentic-flow
3800
- const af = await import('agentic-flow').catch(() => null);
3801
- if (af)
3802
- agenticFlowAvailable = true;
3803
- }
3804
- const versionLabel = agenticFlowAvailable ? `agentic-flow v1 detected (ReasoningBank: ${reasoningBank ? 'active' : 'unavailable'})` : 'agentic-flow not available (using fallbacks)';
3805
- spinner.succeed(versionLabel);
3806
- output.writeln();
3807
- // Anti-drift config (hardcoded optimal values from research)
3808
- const config = {
3809
- batchSize: 4,
3810
- cacheSizeMB: 50,
3811
- topology: 'hierarchical',
3812
- expectedSuccessRate: 0.95,
3813
- };
3814
- output.printBox(`Anti-Drift Swarm Config\n\n` +
3815
- `Agents: ${agentCount}\n` +
3816
- `Topology: ${config.topology}\n` +
3817
- `Batch Size: ${config.batchSize}\n` +
3818
- `Cache: ${config.cacheSizeMB}MB\n` +
3819
- `Success Rate: ${(config.expectedSuccessRate * 100)}%`);
3820
- // If query provided, get compact context via ReasoningBank
3821
- if (query && reasoningBank) {
3822
- output.writeln();
3823
- output.printInfo(`Retrieving compact context for: "${query}"`);
3824
- const memories = await reasoningBank.retrieveMemories(query, { k: 5 });
3825
- const compactPrompt = reasoningBank.formatMemoriesForPrompt ? reasoningBank.formatMemoriesForPrompt(memories) : '';
3826
- // Estimate based on actual query vs compact prompt size difference
3827
- const queryTokenEstimate = Math.ceil((query?.length || 0) / 4);
3828
- const used = Math.ceil((compactPrompt?.length || 0) / 4);
3829
- const tokensSaved = Math.max(0, queryTokenEstimate - used);
3830
- stats.totalTokensSaved += tokensSaved;
3831
- stats.memoriesRetrieved += Array.isArray(memories) ? memories.length : 0;
3832
- output.writeln(` Memories found: ${Array.isArray(memories) ? memories.length : 0}`);
3833
- output.writeln(` Tokens saved: ${output.success(String(tokensSaved))}`);
3834
- if (compactPrompt) {
3835
- output.writeln(` Compact prompt (${compactPrompt.length} chars)`);
3836
- }
3837
- }
3838
- else if (query) {
3839
- output.writeln();
3840
- output.printInfo('ReasoningBank not available - query skipped');
3841
- }
3842
- // Simulate some token savings for demo
3843
- stats.totalTokensSaved += 200;
3844
- stats.cacheHits = 2;
3845
- stats.cacheMisses = 1;
3846
- // Show stats
3847
- if (showStats || showReport) {
3848
- output.writeln();
3849
- const total = stats.cacheHits + stats.cacheMisses;
3850
- const hitRate = total > 0 ? (stats.cacheHits / total * 100).toFixed(1) : '0';
3851
- const savings = (stats.totalTokensSaved / 1000 * 0.01).toFixed(2);
3852
- output.printTable({
3853
- columns: [
3854
- { key: 'metric', header: 'Metric', width: 25 },
3855
- { key: 'value', header: 'Value', width: 20 },
3856
- ],
3857
- data: [
3858
- { metric: 'Tokens Saved', value: stats.totalTokensSaved.toLocaleString() },
3859
- { metric: 'Edits Optimized', value: String(stats.editsOptimized) },
3860
- { metric: 'Cache Hit Rate', value: `${hitRate}%` },
3861
- { metric: 'Memories Retrieved', value: String(stats.memoriesRetrieved) },
3862
- { metric: 'Est. Monthly Savings', value: `$${savings}` },
3863
- { metric: 'Agentic-Flow Active', value: agenticFlowAvailable ? '✓' : '✗' },
3864
- ],
3865
- });
3866
- }
3867
- // Full report
3868
- if (showReport) {
3869
- output.writeln();
3870
- const total = stats.cacheHits + stats.cacheMisses;
3871
- const hitRate = total > 0 ? (stats.cacheHits / total * 100).toFixed(1) : '0';
3872
- const savings = (stats.totalTokensSaved / 1000 * 0.01).toFixed(2);
3873
- output.writeln(`## Token Optimization Report
3874
-
3875
- | Metric | Value |
3876
- |--------|-------|
3877
- | Tokens Saved | ${stats.totalTokensSaved.toLocaleString()} |
3878
- | Edits Optimized | ${stats.editsOptimized} |
3879
- | Cache Hit Rate | ${hitRate}% |
3880
- | Memories Retrieved | ${stats.memoriesRetrieved} |
3881
- | Est. Monthly Savings | $${savings} |
3882
- | Agentic-Flow Active | ${agenticFlowAvailable ? '✓' : '✗'} |`);
3883
- }
3884
- return { success: true, data: { config, stats: { ...stats, agenticFlowAvailable } } };
3885
- }
3886
- catch (error) {
3887
- spinner.fail('TokenOptimizer failed');
3888
- const err = error;
3889
- output.printError(`Error: ${err.message}`);
3890
- // Fallback info
3891
- output.writeln();
3892
- output.printInfo('Fallback anti-drift config:');
3893
- output.writeln(' topology: hierarchical');
3894
- output.writeln(' maxAgents: 8');
3895
- output.writeln(' strategy: specialized');
3896
- output.writeln(' batchSize: 4');
3897
- return { success: false, exitCode: 1 };
3898
- }
3899
- }
3900
- };
3901
- // Model Router command - intelligent model selection (haiku/sonnet/opus)
3902
- const modelRouteCommand = {
3903
- name: 'model-route',
3904
- description: 'Route task to optimal Claude model (haiku/sonnet/opus) based on complexity',
3905
- options: [
3906
- { name: 'task', short: 't', type: 'string', description: 'Task description to route', required: true },
3907
- { name: 'context', short: 'c', type: 'string', description: 'Additional context' },
3908
- { name: 'prefer-cost', type: 'boolean', description: 'Prefer lower cost models' },
3909
- { name: 'prefer-quality', type: 'boolean', description: 'Prefer higher quality models' },
3910
- ],
3911
- examples: [
3912
- { command: 'monomind hooks model-route -t "fix typo"', description: 'Route simple task (likely haiku)' },
3913
- { command: 'monomind hooks model-route -t "architect auth system"', description: 'Route complex task (likely opus)' },
3914
- ],
3915
- action: async (ctx) => {
3916
- const task = ctx.args[0] || ctx.flags.task;
3917
- if (!task) {
3918
- output.printError('Task description required. Use --task or -t flag.');
3919
- return { success: false, exitCode: 1 };
3920
- }
3921
- output.printInfo(`Analyzing task complexity: ${output.highlight(task.slice(0, 50))}...`);
3922
- try {
3923
- const result = await callMCPTool('hooks_model-route', {
3924
- task,
3925
- context: ctx.flags.context,
3926
- preferCost: ctx.flags['prefer-cost'],
3927
- preferQuality: ctx.flags['prefer-quality'],
3928
- });
3929
- if (ctx.flags.format === 'json') {
3930
- output.printJson(result);
3931
- return { success: true, data: result };
3932
- }
3933
- output.writeln();
3934
- // Model icon based on selection
3935
- const modelIcons = {
3936
- haiku: '🌸',
3937
- sonnet: '📜',
3938
- opus: '🎭',
3939
- };
3940
- const model = result.model || 'sonnet';
3941
- const icon = modelIcons[model] || '🤖';
3942
- // Calculate cost savings compared to opus
3943
- const costMultipliers = { haiku: 0.04, sonnet: 0.2, opus: 1.0 };
3944
- const costSavings = model !== 'opus'
3945
- ? `${((1 - costMultipliers[model]) * 100).toFixed(0)}% vs opus`
3946
- : undefined;
3947
- // Determine complexity level
3948
- const complexityScore = typeof result.complexity === 'number' ? result.complexity : 0.5;
3949
- const complexityLevel = complexityScore > 0.7 ? 'high' : complexityScore > 0.4 ? 'medium' : 'low';
3950
- output.printBox([
3951
- `Selected Model: ${icon} ${output.bold(model.toUpperCase())}`,
3952
- `Confidence: ${(result.confidence * 100).toFixed(1)}%`,
3953
- `Complexity: ${complexityLevel} (${(complexityScore * 100).toFixed(0)}%)`,
3954
- costSavings ? `Cost Savings: ${costSavings}` : '',
3955
- ].filter(Boolean).join('\n'), 'Model Routing Result');
3956
- output.writeln();
3957
- output.writeln(output.bold('Reasoning'));
3958
- output.writeln(output.dim(result.reasoning || 'Based on task complexity analysis'));
3959
- if (result.implementation) {
3960
- output.writeln();
3961
- output.writeln(output.dim(`Implementation: ${result.implementation}`));
3962
- }
3963
- return { success: true, data: result };
3964
- }
3965
- catch (error) {
3966
- if (error instanceof MCPClientError) {
3967
- output.printError(`Model routing failed: ${error.message}`);
3968
- }
3969
- else {
3970
- output.printError(`Unexpected error: ${String(error)}`);
3971
- }
3972
- return { success: false, exitCode: 1 };
3973
- }
3974
- }
3975
- };
3976
- // Model Outcome command - record routing outcomes for learning
3977
- const modelOutcomeCommand = {
3978
- name: 'model-outcome',
3979
- description: 'Record model routing outcome for learning',
3980
- options: [
3981
- { name: 'task', short: 't', type: 'string', description: 'Task that was executed', required: true },
3982
- { name: 'model', short: 'm', type: 'string', description: 'Model that was used (haiku/sonnet/opus)', required: true },
3983
- { name: 'outcome', short: 'o', type: 'string', description: 'Outcome (success/failure/escalated)', required: true },
3984
- { name: 'quality', short: 'q', type: 'number', description: 'Quality score 0-1' },
3985
- ],
3986
- examples: [
3987
- { command: 'monomind hooks model-outcome -t "fix typo" -m haiku -o success', description: 'Record successful haiku task' },
3988
- { command: 'monomind hooks model-outcome -t "auth system" -m sonnet -o escalated', description: 'Record escalation to opus' },
3989
- ],
3990
- action: async (ctx) => {
3991
- const task = ctx.flags.task;
3992
- const model = ctx.flags.model;
3993
- const outcome = ctx.flags.outcome;
3994
- if (!task || !model || !outcome) {
3995
- output.printError('Task, model, and outcome are required.');
3996
- return { success: false, exitCode: 1 };
3997
- }
3998
- try {
3999
- const result = await callMCPTool('hooks_model-outcome', {
4000
- task,
4001
- model,
4002
- outcome,
4003
- quality: ctx.flags.quality,
4004
- });
4005
- output.printSuccess(`Outcome recorded for ${model}: ${outcome}`);
4006
- if (result.learningUpdate) {
4007
- output.writeln(output.dim(result.learningUpdate));
4008
- }
4009
- return { success: true, data: result };
4010
- }
4011
- catch (error) {
4012
- output.printError(`Failed to record outcome: ${String(error)}`);
4013
- return { success: false, exitCode: 1 };
4014
- }
4015
- }
4016
- };
4017
- // Model Stats command - view routing statistics
4018
- const modelStatsCommand = {
4019
- name: 'model-stats',
4020
- description: 'View model routing statistics and learning metrics',
4021
- options: [
4022
- { name: 'detailed', short: 'd', type: 'boolean', description: 'Show detailed breakdown' },
4023
- ],
4024
- examples: [
4025
- { command: 'monomind hooks model-stats', description: 'View routing stats' },
4026
- { command: 'monomind hooks model-stats --detailed', description: 'Show detailed breakdown' },
4027
- ],
4028
- action: async (ctx) => {
4029
- try {
4030
- const result = await callMCPTool('hooks_model-stats', {
4031
- detailed: ctx.flags.detailed,
4032
- });
4033
- if (ctx.flags.format === 'json') {
4034
- output.printJson(result);
4035
- return { success: true, data: result };
4036
- }
4037
- if (!result.available) {
4038
- output.printWarning(result.message || 'Model router not available');
4039
- return { success: true, data: result };
4040
- }
4041
- // Calculate cost savings based on model distribution
4042
- const dist = result.modelDistribution || { haiku: 0, sonnet: 0, opus: 0 };
4043
- const totalTasks = result.totalDecisions || 0;
4044
- const costMultipliers = { haiku: 0.04, sonnet: 0.2, opus: 1.0 };
4045
- let totalCost = 0;
4046
- let maxCost = totalTasks; // If all were opus
4047
- for (const [model, count] of Object.entries(dist)) {
4048
- if (model !== 'inherit') {
4049
- totalCost += count * (costMultipliers[model] || 1);
4050
- }
4051
- }
4052
- const costSavings = maxCost > 0 ? ((1 - totalCost / maxCost) * 100).toFixed(1) : '0';
4053
- output.writeln();
4054
- output.printBox([
4055
- `Total Tasks Routed: ${totalTasks}`,
4056
- `Avg Complexity: ${((result.avgComplexity || 0) * 100).toFixed(1)}%`,
4057
- `Avg Confidence: ${((result.avgConfidence || 0) * 100).toFixed(1)}%`,
4058
- `Cost Savings: ${costSavings}% vs all-opus`,
4059
- `Circuit Breaker Trips: ${result.circuitBreakerTrips || 0}`,
4060
- ].join('\n'), 'Model Routing Statistics');
4061
- if (dist && Object.keys(dist).length > 0) {
4062
- output.writeln();
4063
- output.writeln(output.bold('Model Distribution'));
4064
- output.printTable({
4065
- columns: [
4066
- { key: 'model', header: 'Model', width: 10 },
4067
- { key: 'count', header: 'Tasks', width: 8, align: 'right' },
4068
- { key: 'percentage', header: '%', width: 8, align: 'right' },
4069
- { key: 'costMultiplier', header: 'Cost', width: 8, align: 'right' },
4070
- ],
4071
- data: Object.entries(dist)
4072
- .filter(([model]) => model !== 'inherit')
4073
- .map(([model, count]) => ({
4074
- model: model.toUpperCase(),
4075
- count,
4076
- percentage: totalTasks > 0 ? `${((count / totalTasks) * 100).toFixed(1)}%` : '0%',
4077
- costMultiplier: `${costMultipliers[model] || 1}x`,
4078
- })),
4079
- });
4080
- }
4081
- return { success: true, data: result };
4082
- }
4083
- catch (error) {
4084
- output.printError(`Failed to get stats: ${String(error)}`);
4085
- return { success: false, exitCode: 1 };
4086
- }
4087
- }
4088
- };
4089
- // Teammate Idle command - Agent Teams integration
4090
- const teammateIdleCommand = {
4091
- name: 'teammate-idle',
4092
- description: 'Handle idle teammate in Agent Teams - auto-assign tasks or notify lead',
4093
- options: [
4094
- {
4095
- name: 'auto-assign',
4096
- short: 'a',
4097
- description: 'Automatically assign pending tasks to idle teammate',
4098
- type: 'boolean',
4099
- default: true
4100
- },
4101
- {
4102
- name: 'check-task-list',
4103
- short: 'c',
4104
- description: 'Check shared task list for available work',
4105
- type: 'boolean',
4106
- default: true
4107
- },
4108
- {
4109
- name: 'teammate-id',
4110
- short: 't',
4111
- description: 'ID of the idle teammate',
4112
- type: 'string'
4113
- },
4114
- {
4115
- name: 'team-name',
4116
- description: 'Team name for context',
4117
- type: 'string'
4118
- }
4119
- ],
4120
- examples: [
4121
- { command: 'monomind hooks teammate-idle --auto-assign true', description: 'Auto-assign tasks to idle teammate' },
4122
- { command: 'monomind hooks teammate-idle -t worker-1 --check-task-list', description: 'Check tasks for specific teammate' }
4123
- ],
4124
- action: async (ctx) => {
4125
- const autoAssign = ctx.flags['auto-assign'] !== false;
4126
- const checkTaskList = ctx.flags['check-task-list'] !== false;
4127
- const teammateId = ctx.flags['teammate-id'];
4128
- const teamName = ctx.flags['team-name'];
4129
- if (ctx.flags.format !== 'json') {
4130
- output.printInfo(`Teammate idle hook triggered${teammateId ? ` for: ${output.highlight(teammateId)}` : ''}`);
4131
- }
4132
- try {
4133
- const result = await callMCPTool('hooks_teammate-idle', {
4134
- autoAssign,
4135
- checkTaskList,
4136
- teammateId,
4137
- teamName,
4138
- timestamp: Date.now(),
4139
- });
4140
- if (ctx.flags.format === 'json') {
4141
- output.printJson(result);
4142
- return { success: true, data: result };
4143
- }
4144
- output.writeln();
4145
- if (result.action === 'assigned' && result.taskAssigned) {
4146
- output.printSuccess(`Task assigned: ${result.taskAssigned.subject}`);
4147
- output.printList([
4148
- `Task ID: ${result.taskAssigned.taskId}`,
4149
- `Priority: ${result.taskAssigned.priority}`,
4150
- `Pending tasks remaining: ${result.pendingTasks}`
4151
- ]);
4152
- }
4153
- else if (result.action === 'waiting') {
4154
- output.printInfo('No pending tasks available - teammate waiting for work');
4155
- }
4156
- else {
4157
- output.printInfo(`Team lead notified: ${result.message}`);
4158
- }
4159
- return { success: true, data: result };
4160
- }
4161
- catch (error) {
4162
- // Graceful fallback - don't fail hard, just report
4163
- if (ctx.flags.format === 'json') {
4164
- output.printJson({ success: true, action: 'waiting', message: 'Teammate idle - no MCP server' });
4165
- }
4166
- else {
4167
- output.printInfo('Teammate idle - awaiting task assignment');
4168
- }
4169
- return { success: true };
4170
- }
4171
- }
4172
- };
4173
- // Task Completed command - Agent Teams integration
4174
- const taskCompletedCommand = {
4175
- name: 'task-completed',
4176
- description: 'Handle task completion in Agent Teams - train patterns and notify lead',
4177
- options: [
4178
- {
4179
- name: 'task-id',
4180
- short: 'i',
4181
- description: 'ID of the completed task',
4182
- type: 'string',
4183
- required: true
4184
- },
4185
- {
4186
- name: 'train-patterns',
4187
- short: 'p',
4188
- description: 'Train neural patterns from successful task',
4189
- type: 'boolean',
4190
- default: true
4191
- },
4192
- {
4193
- name: 'notify-lead',
4194
- short: 'n',
4195
- description: 'Notify team lead of task completion',
4196
- type: 'boolean',
4197
- default: true
4198
- },
4199
- {
4200
- name: 'success',
4201
- short: 's',
4202
- description: 'Whether the task succeeded',
4203
- type: 'boolean',
4204
- default: true
4205
- },
4206
- {
4207
- name: 'quality',
4208
- short: 'q',
4209
- description: 'Quality score (0-1)',
4210
- type: 'number'
4211
- },
4212
- {
4213
- name: 'teammate-id',
4214
- short: 't',
4215
- description: 'ID of the teammate that completed the task',
4216
- type: 'string'
4217
- }
4218
- ],
4219
- examples: [
4220
- { command: 'monomind hooks task-completed -i task-123 --train-patterns', description: 'Complete task and train patterns' },
4221
- { command: 'monomind hooks task-completed -i task-456 --notify-lead --quality 0.95', description: 'Complete with quality score' }
4222
- ],
4223
- action: async (ctx) => {
4224
- const taskId = ctx.args[0] || ctx.flags['task-id'];
4225
- const trainPatterns = ctx.flags['train-patterns'] !== false;
4226
- const notifyLead = ctx.flags['notify-lead'] !== false;
4227
- const success = ctx.flags.success !== false;
4228
- const quality = ctx.flags.quality;
4229
- const teammateId = ctx.flags['teammate-id'];
4230
- if (!taskId) {
4231
- output.printError('Task ID is required. Use --task-id or -i flag.');
4232
- return { success: false, exitCode: 1 };
4233
- }
4234
- if (ctx.flags.format !== 'json') {
4235
- output.printInfo(`Task completed: ${output.highlight(taskId)}`);
4236
- }
4237
- try {
4238
- const result = await callMCPTool('hooks_task-completed', {
4239
- taskId,
4240
- trainPatterns,
4241
- notifyLead,
4242
- success,
4243
- quality,
4244
- teammateId,
4245
- timestamp: Date.now(),
4246
- });
4247
- if (ctx.flags.format === 'json') {
4248
- output.printJson(result);
4249
- return { success: true, data: result };
4250
- }
4251
- output.writeln();
4252
- output.printSuccess(`Task ${taskId} marked complete`);
4253
- output.writeln();
4254
- output.writeln(output.bold('Completion Metrics'));
4255
- output.printTable({
4256
- columns: [
4257
- { key: 'metric', header: 'Metric', width: 25 },
4258
- { key: 'value', header: 'Value', width: 20, align: 'right' }
4259
- ],
4260
- data: [
4261
- { metric: 'Patterns Learned', value: result.patternsLearned },
4262
- { metric: 'Quality Score', value: quality ? `${(quality * 100).toFixed(0)}%` : 'N/A' },
4263
- { metric: 'Lead Notified', value: result.leadNotified ? 'Yes' : 'No' },
4264
- { metric: 'Learning Updates', value: result.metrics?.learningUpdates || 0 }
4265
- ]
4266
- });
4267
- if (result.nextTask) {
4268
- output.writeln();
4269
- output.printInfo(`Next available task: ${result.nextTask.subject}`);
4270
- }
4271
- return { success: true, data: result };
4272
- }
4273
- catch (error) {
4274
- // Graceful fallback
4275
- if (ctx.flags.format === 'json') {
4276
- output.printJson({ success: true, taskId, message: 'Task completed - patterns pending' });
4277
- }
4278
- else {
4279
- output.printSuccess(`Task ${taskId} completed`);
4280
- if (trainPatterns) {
4281
- output.printInfo('Pattern training queued for next sync');
4282
- }
4283
- }
4284
- return { success: true };
4285
- }
4286
- }
4287
- };
4288
- // Notify subcommand
4289
- const notifyCommand = {
4290
- name: 'notify',
4291
- description: 'Send a notification message (logged to session)',
4292
- options: [
4293
- { name: 'message', short: 'm', type: 'string', description: 'Notification message', required: true },
4294
- { name: 'level', short: 'l', type: 'string', description: 'Level: info, warn, error', default: 'info' },
4295
- { name: 'channel', short: 'c', type: 'string', description: 'Notification channel', default: 'console' },
4296
- ],
4297
- examples: [
4298
- { command: 'monomind hooks notify -m "Build complete"', description: 'Send info notification' },
4299
- { command: 'monomind hooks notify -m "Test failed" -l error', description: 'Send error notification' },
4300
- ],
4301
- action: async (ctx) => {
4302
- const message = ctx.args[0] || ctx.flags.message;
4303
- const level = ctx.flags.level || 'info';
4304
- if (!message) {
4305
- output.printError('Message is required: --message "your message"');
4306
- return { success: false, exitCode: 1 };
4307
- }
4308
- const timestamp = new Date().toISOString();
4309
- if (level === 'error') {
4310
- output.printError(`[${timestamp}] ${message}`);
4311
- }
4312
- else if (level === 'warn') {
4313
- output.writeln(output.warning(`[${timestamp}] ${message}`));
4314
- }
4315
- else {
4316
- output.printInfo(`[${timestamp}] ${message}`);
4317
- }
4318
- // Store notification in memory if available
4319
- try {
4320
- const { storeEntry } = await import('../memory/memory-initializer.js');
4321
- await storeEntry({ key: `notify-${Date.now()}`, value: `[${level}] ${message}`, namespace: 'notifications' });
4322
- }
4323
- catch { /* memory not available */ }
4324
- return { success: true, data: { timestamp, level, message } };
4325
- }
4326
- };
4327
403
  // Main hooks command
4328
404
  export const hooksCommand = {
4329
405
  name: 'hooks',