@monoes/monomindcli 2.16.12 → 2.16.14

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 (386) hide show
  1. package/.claude/agents/core/coder.md +1 -1
  2. package/.claude/agents/engineering/engineering-devops-automator.md +1 -1
  3. package/.claude/agents/github/repo-architect.md +1 -1
  4. package/.claude/agents/testing/production-validator.md +1 -1
  5. package/.claude/agents/testing/testing-tool-evaluator.md +1 -1
  6. package/.claude/commands/github/pr-manager.md +1 -1
  7. package/.claude/helpers/pick-rank.cjs +34 -4
  8. package/.claude/skills/monolean-audit/SKILL.md +1 -1
  9. package/README.md +1 -1
  10. package/dist/src/commands/cleanup-data.d.ts +30 -0
  11. package/dist/src/commands/cleanup-data.d.ts.map +1 -0
  12. package/dist/src/commands/cleanup-data.js +138 -0
  13. package/dist/src/commands/cleanup-data.js.map +1 -0
  14. package/dist/src/commands/cleanup-scratch.d.ts +43 -0
  15. package/dist/src/commands/cleanup-scratch.d.ts.map +1 -0
  16. package/dist/src/commands/cleanup-scratch.js +125 -0
  17. package/dist/src/commands/cleanup-scratch.js.map +1 -0
  18. package/dist/src/commands/cleanup.d.ts +2 -39
  19. package/dist/src/commands/cleanup.d.ts.map +1 -1
  20. package/dist/src/commands/cleanup.js +5 -247
  21. package/dist/src/commands/cleanup.js.map +1 -1
  22. package/dist/src/commands/doctor-gitignore-checks.d.ts +10 -0
  23. package/dist/src/commands/doctor-gitignore-checks.d.ts.map +1 -0
  24. package/dist/src/commands/doctor-gitignore-checks.js +141 -0
  25. package/dist/src/commands/doctor-gitignore-checks.js.map +1 -0
  26. package/dist/src/commands/doctor-helpers-checks.d.ts +10 -0
  27. package/dist/src/commands/doctor-helpers-checks.d.ts.map +1 -0
  28. package/dist/src/commands/doctor-helpers-checks.js +292 -0
  29. package/dist/src/commands/doctor-helpers-checks.js.map +1 -0
  30. package/dist/src/commands/doctor-memory-checks.d.ts +47 -0
  31. package/dist/src/commands/doctor-memory-checks.d.ts.map +1 -0
  32. package/dist/src/commands/doctor-memory-checks.js +298 -0
  33. package/dist/src/commands/doctor-memory-checks.js.map +1 -0
  34. package/dist/src/commands/doctor-monograph-checks.d.ts +9 -0
  35. package/dist/src/commands/doctor-monograph-checks.d.ts.map +1 -0
  36. package/dist/src/commands/doctor-monograph-checks.js +331 -0
  37. package/dist/src/commands/doctor-monograph-checks.js.map +1 -0
  38. package/dist/src/commands/doctor-project-checks.d.ts +7 -66
  39. package/dist/src/commands/doctor-project-checks.d.ts.map +1 -1
  40. package/dist/src/commands/doctor-project-checks.js +10 -1331
  41. package/dist/src/commands/doctor-project-checks.js.map +1 -1
  42. package/dist/src/commands/doctor-routing-checks.d.ts +10 -0
  43. package/dist/src/commands/doctor-routing-checks.d.ts.map +1 -0
  44. package/dist/src/commands/doctor-routing-checks.js +194 -0
  45. package/dist/src/commands/doctor-routing-checks.js.map +1 -0
  46. package/dist/src/commands/doctor-worker-checks.d.ts +17 -0
  47. package/dist/src/commands/doctor-worker-checks.d.ts.map +1 -0
  48. package/dist/src/commands/doctor-worker-checks.js +122 -0
  49. package/dist/src/commands/doctor-worker-checks.js.map +1 -0
  50. package/dist/src/commands/hooks-routing-commands.js +1 -1
  51. package/dist/src/commands/hooks-routing-commands.js.map +1 -1
  52. package/dist/src/commands/init.d.ts.map +1 -1
  53. package/dist/src/commands/init.js +6 -15
  54. package/dist/src/commands/init.js.map +1 -1
  55. package/dist/src/commands/org-control.d.ts +117 -0
  56. package/dist/src/commands/org-control.d.ts.map +1 -0
  57. package/dist/src/commands/org-control.js +274 -0
  58. package/dist/src/commands/org-control.js.map +1 -0
  59. package/dist/src/commands/org-lifecycle.d.ts +7 -0
  60. package/dist/src/commands/org-lifecycle.d.ts.map +1 -0
  61. package/dist/src/commands/org-lifecycle.js +298 -0
  62. package/dist/src/commands/org-lifecycle.js.map +1 -0
  63. package/dist/src/commands/org-manage.d.ts +7 -0
  64. package/dist/src/commands/org-manage.d.ts.map +1 -0
  65. package/dist/src/commands/org-manage.js +357 -0
  66. package/dist/src/commands/org-manage.js.map +1 -0
  67. package/dist/src/commands/org-memory-command.d.ts +3 -0
  68. package/dist/src/commands/org-memory-command.d.ts.map +1 -0
  69. package/dist/src/commands/org-memory-command.js +178 -0
  70. package/dist/src/commands/org-memory-command.js.map +1 -0
  71. package/dist/src/commands/org-observe-approvals.d.ts +8 -0
  72. package/dist/src/commands/org-observe-approvals.d.ts.map +1 -0
  73. package/dist/src/commands/org-observe-approvals.js +211 -0
  74. package/dist/src/commands/org-observe-approvals.js.map +1 -0
  75. package/dist/src/commands/org-observe-checkpoint.d.ts +26 -0
  76. package/dist/src/commands/org-observe-checkpoint.d.ts.map +1 -0
  77. package/dist/src/commands/org-observe-checkpoint.js +180 -0
  78. package/dist/src/commands/org-observe-checkpoint.js.map +1 -0
  79. package/dist/src/commands/org-observe-config.d.ts +8 -0
  80. package/dist/src/commands/org-observe-config.d.ts.map +1 -0
  81. package/dist/src/commands/org-observe-config.js +160 -0
  82. package/dist/src/commands/org-observe-config.js.map +1 -0
  83. package/dist/src/commands/org-observe-gates.d.ts +4 -0
  84. package/dist/src/commands/org-observe-gates.d.ts.map +1 -0
  85. package/dist/src/commands/org-observe-gates.js +182 -0
  86. package/dist/src/commands/org-observe-gates.js.map +1 -0
  87. package/dist/src/commands/org-observe-inbox.d.ts +18 -0
  88. package/dist/src/commands/org-observe-inbox.d.ts.map +1 -0
  89. package/dist/src/commands/org-observe-inbox.js +154 -0
  90. package/dist/src/commands/org-observe-inbox.js.map +1 -0
  91. package/dist/src/commands/org-observe-logs.d.ts +31 -0
  92. package/dist/src/commands/org-observe-logs.d.ts.map +1 -0
  93. package/dist/src/commands/org-observe-logs.js +272 -0
  94. package/dist/src/commands/org-observe-logs.js.map +1 -0
  95. package/dist/src/commands/org-observe-questions.d.ts +8 -0
  96. package/dist/src/commands/org-observe-questions.d.ts.map +1 -0
  97. package/dist/src/commands/org-observe-questions.js +201 -0
  98. package/dist/src/commands/org-observe-questions.js.map +1 -0
  99. package/dist/src/commands/org-observe-report.d.ts +8 -0
  100. package/dist/src/commands/org-observe-report.d.ts.map +1 -0
  101. package/dist/src/commands/org-observe-report.js +366 -0
  102. package/dist/src/commands/org-observe-report.js.map +1 -0
  103. package/dist/src/commands/org-observe-shared.d.ts +20 -0
  104. package/dist/src/commands/org-observe-shared.d.ts.map +1 -0
  105. package/dist/src/commands/org-observe-shared.js +55 -0
  106. package/dist/src/commands/org-observe-shared.js.map +1 -0
  107. package/dist/src/commands/org-observe.d.ts +9 -101
  108. package/dist/src/commands/org-observe.d.ts.map +1 -1
  109. package/dist/src/commands/org-observe.js +12 -1675
  110. package/dist/src/commands/org-observe.js.map +1 -1
  111. package/dist/src/commands/org-poll.d.ts +57 -0
  112. package/dist/src/commands/org-poll.d.ts.map +1 -0
  113. package/dist/src/commands/org-poll.js +194 -0
  114. package/dist/src/commands/org-poll.js.map +1 -0
  115. package/dist/src/commands/org-run.d.ts +3 -0
  116. package/dist/src/commands/org-run.d.ts.map +1 -0
  117. package/dist/src/commands/org-run.js +449 -0
  118. package/dist/src/commands/org-run.js.map +1 -0
  119. package/dist/src/commands/org-serve.d.ts +19 -0
  120. package/dist/src/commands/org-serve.d.ts.map +1 -0
  121. package/dist/src/commands/org-serve.js +395 -0
  122. package/dist/src/commands/org-serve.js.map +1 -0
  123. package/dist/src/commands/org-subcommands-config.d.ts +8 -0
  124. package/dist/src/commands/org-subcommands-config.d.ts.map +1 -0
  125. package/dist/src/commands/org-subcommands-config.js +93 -0
  126. package/dist/src/commands/org-subcommands-config.js.map +1 -0
  127. package/dist/src/commands/org-subcommands-hil.d.ts +11 -0
  128. package/dist/src/commands/org-subcommands-hil.d.ts.map +1 -0
  129. package/dist/src/commands/org-subcommands-hil.js +216 -0
  130. package/dist/src/commands/org-subcommands-hil.js.map +1 -0
  131. package/dist/src/commands/org-subcommands-observe.d.ts +12 -0
  132. package/dist/src/commands/org-subcommands-observe.d.ts.map +1 -0
  133. package/dist/src/commands/org-subcommands-observe.js +249 -0
  134. package/dist/src/commands/org-subcommands-observe.js.map +1 -0
  135. package/dist/src/commands/org-subcommands-runtime.d.ts +12 -0
  136. package/dist/src/commands/org-subcommands-runtime.d.ts.map +1 -0
  137. package/dist/src/commands/org-subcommands-runtime.js +163 -0
  138. package/dist/src/commands/org-subcommands-runtime.js.map +1 -0
  139. package/dist/src/commands/org.d.ts +3 -151
  140. package/dist/src/commands/org.d.ts.map +1 -1
  141. package/dist/src/commands/org.js +43 -2735
  142. package/dist/src/commands/org.js.map +1 -1
  143. package/dist/src/index.js +2 -2
  144. package/dist/src/index.js.map +1 -1
  145. package/dist/src/init/never-commit.d.ts +3 -3
  146. package/dist/src/init/types.d.ts +1 -1
  147. package/dist/src/init/types.js +1 -1
  148. package/dist/src/knowledge/document-store.js +1 -1
  149. package/dist/src/knowledge/document-store.js.map +1 -1
  150. package/dist/src/mcp-tools/hooks-edit-command.d.ts +10 -0
  151. package/dist/src/mcp-tools/hooks-edit-command.d.ts.map +1 -0
  152. package/dist/src/mcp-tools/hooks-edit-command.js +247 -0
  153. package/dist/src/mcp-tools/hooks-edit-command.js.map +1 -0
  154. package/dist/src/mcp-tools/hooks-learning.d.ts +9 -0
  155. package/dist/src/mcp-tools/hooks-learning.d.ts.map +1 -0
  156. package/dist/src/mcp-tools/hooks-learning.js +408 -0
  157. package/dist/src/mcp-tools/hooks-learning.js.map +1 -0
  158. package/dist/src/mcp-tools/hooks-metrics-list.d.ts +8 -0
  159. package/dist/src/mcp-tools/hooks-metrics-list.d.ts.map +1 -0
  160. package/dist/src/mcp-tools/hooks-metrics-list.js +205 -0
  161. package/dist/src/mcp-tools/hooks-metrics-list.js.map +1 -0
  162. package/dist/src/mcp-tools/hooks-route.d.ts +12 -0
  163. package/dist/src/mcp-tools/hooks-route.d.ts.map +1 -0
  164. package/dist/src/mcp-tools/hooks-route.js +251 -0
  165. package/dist/src/mcp-tools/hooks-route.js.map +1 -0
  166. package/dist/src/mcp-tools/hooks-routing.d.ts +6 -27
  167. package/dist/src/mcp-tools/hooks-routing.d.ts.map +1 -1
  168. package/dist/src/mcp-tools/hooks-routing.js +6 -1727
  169. package/dist/src/mcp-tools/hooks-routing.js.map +1 -1
  170. package/dist/src/mcp-tools/hooks-session.d.ts +9 -0
  171. package/dist/src/mcp-tools/hooks-session.d.ts.map +1 -0
  172. package/dist/src/mcp-tools/hooks-session.js +243 -0
  173. package/dist/src/mcp-tools/hooks-session.js.map +1 -0
  174. package/dist/src/mcp-tools/hooks-task.d.ts +17 -0
  175. package/dist/src/mcp-tools/hooks-task.d.ts.map +1 -0
  176. package/dist/src/mcp-tools/hooks-task.js +427 -0
  177. package/dist/src/mcp-tools/hooks-task.js.map +1 -0
  178. package/dist/src/mcp-tools/hooks-tools.d.ts +2 -1
  179. package/dist/src/mcp-tools/hooks-tools.d.ts.map +1 -1
  180. package/dist/src/mcp-tools/hooks-tools.js +2 -1
  181. package/dist/src/mcp-tools/hooks-tools.js.map +1 -1
  182. package/dist/src/mcp-tools/memory-tools.js +1 -1
  183. package/dist/src/mcp-tools/memory-tools.js.map +1 -1
  184. package/dist/src/memory/bm25-index.d.ts +2 -2
  185. package/dist/src/memory/bm25-index.js +2 -2
  186. package/dist/src/memory/embedding-operations.js +2 -2
  187. package/dist/src/memory/embedding-operations.js.map +1 -1
  188. package/dist/src/memory/entity-name-key.d.ts +1 -1
  189. package/dist/src/memory/entity-name-key.js +1 -1
  190. package/dist/src/memory/hnsw-operations.d.ts +1 -1
  191. package/dist/src/memory/hnsw-operations.js +1 -1
  192. package/dist/src/memory/memory-bridge-backend.d.ts +43 -0
  193. package/dist/src/memory/memory-bridge-backend.d.ts.map +1 -0
  194. package/dist/src/memory/memory-bridge-backend.js +363 -0
  195. package/dist/src/memory/memory-bridge-backend.js.map +1 -0
  196. package/dist/src/memory/memory-bridge-core.d.ts +42 -0
  197. package/dist/src/memory/memory-bridge-core.d.ts.map +1 -0
  198. package/dist/src/memory/memory-bridge-core.js +126 -0
  199. package/dist/src/memory/memory-bridge-core.js.map +1 -0
  200. package/dist/src/memory/memory-bridge-entries.d.ts +131 -0
  201. package/dist/src/memory/memory-bridge-entries.d.ts.map +1 -0
  202. package/dist/src/memory/memory-bridge-entries.js +227 -0
  203. package/dist/src/memory/memory-bridge-entries.js.map +1 -0
  204. package/dist/src/memory/memory-bridge-feedback.d.ts +60 -0
  205. package/dist/src/memory/memory-bridge-feedback.d.ts.map +1 -0
  206. package/dist/src/memory/memory-bridge-feedback.js +118 -0
  207. package/dist/src/memory/memory-bridge-feedback.js.map +1 -0
  208. package/dist/src/memory/memory-bridge-paths.d.ts +123 -0
  209. package/dist/src/memory/memory-bridge-paths.d.ts.map +1 -0
  210. package/dist/src/memory/memory-bridge-paths.js +335 -0
  211. package/dist/src/memory/memory-bridge-paths.js.map +1 -0
  212. package/dist/src/memory/memory-bridge-search.d.ts +48 -0
  213. package/dist/src/memory/memory-bridge-search.d.ts.map +1 -0
  214. package/dist/src/memory/memory-bridge-search.js +383 -0
  215. package/dist/src/memory/memory-bridge-search.js.map +1 -0
  216. package/dist/src/memory/memory-bridge-sessions.d.ts +38 -0
  217. package/dist/src/memory/memory-bridge-sessions.d.ts.map +1 -0
  218. package/dist/src/memory/memory-bridge-sessions.js +109 -0
  219. package/dist/src/memory/memory-bridge-sessions.js.map +1 -0
  220. package/dist/src/memory/memory-bridge-store.d.ts +58 -0
  221. package/dist/src/memory/memory-bridge-store.d.ts.map +1 -0
  222. package/dist/src/memory/memory-bridge-store.js +201 -0
  223. package/dist/src/memory/memory-bridge-store.js.map +1 -0
  224. package/dist/src/memory/memory-bridge.d.ts +10 -438
  225. package/dist/src/memory/memory-bridge.d.ts.map +1 -1
  226. package/dist/src/memory/memory-bridge.js +14 -1787
  227. package/dist/src/memory/memory-bridge.js.map +1 -1
  228. package/dist/src/memory/memory-kg-claims.d.ts +52 -0
  229. package/dist/src/memory/memory-kg-claims.d.ts.map +1 -0
  230. package/dist/src/memory/memory-kg-claims.js +99 -0
  231. package/dist/src/memory/memory-kg-claims.js.map +1 -0
  232. package/dist/src/memory/memory-kg-index.d.ts +86 -0
  233. package/dist/src/memory/memory-kg-index.d.ts.map +1 -0
  234. package/dist/src/memory/memory-kg-index.js +256 -0
  235. package/dist/src/memory/memory-kg-index.js.map +1 -0
  236. package/dist/src/memory/memory-kg-ingest.d.ts +44 -0
  237. package/dist/src/memory/memory-kg-ingest.d.ts.map +1 -0
  238. package/dist/src/memory/memory-kg-ingest.js +361 -0
  239. package/dist/src/memory/memory-kg-ingest.js.map +1 -0
  240. package/dist/src/memory/memory-kg-model.d.ts +168 -0
  241. package/dist/src/memory/memory-kg-model.d.ts.map +1 -0
  242. package/dist/src/memory/memory-kg-model.js +158 -0
  243. package/dist/src/memory/memory-kg-model.js.map +1 -0
  244. package/dist/src/memory/memory-kg-names.d.ts +64 -0
  245. package/dist/src/memory/memory-kg-names.d.ts.map +1 -0
  246. package/dist/src/memory/memory-kg-names.js +187 -0
  247. package/dist/src/memory/memory-kg-names.js.map +1 -0
  248. package/dist/src/memory/memory-kg-promote.d.ts +79 -0
  249. package/dist/src/memory/memory-kg-promote.d.ts.map +1 -0
  250. package/dist/src/memory/memory-kg-promote.js +248 -0
  251. package/dist/src/memory/memory-kg-promote.js.map +1 -0
  252. package/dist/src/memory/memory-kg-rebuild.d.ts +77 -0
  253. package/dist/src/memory/memory-kg-rebuild.d.ts.map +1 -0
  254. package/dist/src/memory/memory-kg-rebuild.js +299 -0
  255. package/dist/src/memory/memory-kg-rebuild.js.map +1 -0
  256. package/dist/src/memory/memory-kg-rollback.d.ts +47 -0
  257. package/dist/src/memory/memory-kg-rollback.d.ts.map +1 -0
  258. package/dist/src/memory/memory-kg-rollback.js +193 -0
  259. package/dist/src/memory/memory-kg-rollback.js.map +1 -0
  260. package/dist/src/memory/memory-kg-rules.d.ts +64 -0
  261. package/dist/src/memory/memory-kg-rules.d.ts.map +1 -0
  262. package/dist/src/memory/memory-kg-rules.js +251 -0
  263. package/dist/src/memory/memory-kg-rules.js.map +1 -0
  264. package/dist/src/memory/memory-kg-scan.d.ts +118 -0
  265. package/dist/src/memory/memory-kg-scan.d.ts.map +1 -0
  266. package/dist/src/memory/memory-kg-scan.js +209 -0
  267. package/dist/src/memory/memory-kg-scan.js.map +1 -0
  268. package/dist/src/memory/memory-kg-search.d.ts +80 -0
  269. package/dist/src/memory/memory-kg-search.d.ts.map +1 -0
  270. package/dist/src/memory/memory-kg-search.js +239 -0
  271. package/dist/src/memory/memory-kg-search.js.map +1 -0
  272. package/dist/src/memory/memory-kg.d.ts +19 -547
  273. package/dist/src/memory/memory-kg.d.ts.map +1 -1
  274. package/dist/src/memory/memory-kg.js +10 -2389
  275. package/dist/src/memory/memory-kg.js.map +1 -1
  276. package/dist/src/orgrt/daemon-types.d.ts +196 -0
  277. package/dist/src/orgrt/daemon-types.d.ts.map +1 -0
  278. package/dist/src/orgrt/daemon-types.js +38 -0
  279. package/dist/src/orgrt/daemon-types.js.map +1 -0
  280. package/dist/src/orgrt/daemon.d.ts +70 -300
  281. package/dist/src/orgrt/daemon.d.ts.map +1 -1
  282. package/dist/src/orgrt/daemon.js +41 -2536
  283. package/dist/src/orgrt/daemon.js.map +1 -1
  284. package/dist/src/orgrt/idle-watchdog.d.ts +39 -0
  285. package/dist/src/orgrt/idle-watchdog.d.ts.map +1 -0
  286. package/dist/src/orgrt/idle-watchdog.js +289 -0
  287. package/dist/src/orgrt/idle-watchdog.js.map +1 -0
  288. package/dist/src/orgrt/org-reload.d.ts +7 -0
  289. package/dist/src/orgrt/org-reload.d.ts.map +1 -0
  290. package/dist/src/orgrt/org-reload.js +111 -0
  291. package/dist/src/orgrt/org-reload.js.map +1 -0
  292. package/dist/src/orgrt/org-start-steps.d.ts +21 -0
  293. package/dist/src/orgrt/org-start-steps.d.ts.map +1 -0
  294. package/dist/src/orgrt/org-start-steps.js +305 -0
  295. package/dist/src/orgrt/org-start-steps.js.map +1 -0
  296. package/dist/src/orgrt/org-start.d.ts +7 -0
  297. package/dist/src/orgrt/org-start.d.ts.map +1 -0
  298. package/dist/src/orgrt/org-start.js +435 -0
  299. package/dist/src/orgrt/org-start.js.map +1 -0
  300. package/dist/src/orgrt/org-state-file.d.ts +8 -0
  301. package/dist/src/orgrt/org-state-file.d.ts.map +1 -0
  302. package/dist/src/orgrt/org-state-file.js +116 -0
  303. package/dist/src/orgrt/org-state-file.js.map +1 -0
  304. package/dist/src/orgrt/org-stop.d.ts +6 -0
  305. package/dist/src/orgrt/org-stop.d.ts.map +1 -0
  306. package/dist/src/orgrt/org-stop.js +332 -0
  307. package/dist/src/orgrt/org-stop.js.map +1 -0
  308. package/dist/src/orgrt/org-tools.d.ts +11 -0
  309. package/dist/src/orgrt/org-tools.d.ts.map +1 -0
  310. package/dist/src/orgrt/org-tools.js +305 -0
  311. package/dist/src/orgrt/org-tools.js.map +1 -0
  312. package/dist/src/orgrt/role-incarnation.d.ts +18 -0
  313. package/dist/src/orgrt/role-incarnation.d.ts.map +1 -0
  314. package/dist/src/orgrt/role-incarnation.js +374 -0
  315. package/dist/src/orgrt/role-incarnation.js.map +1 -0
  316. package/dist/src/orgrt/role-respawn.d.ts +7 -0
  317. package/dist/src/orgrt/role-respawn.d.ts.map +1 -0
  318. package/dist/src/orgrt/role-respawn.js +318 -0
  319. package/dist/src/orgrt/role-respawn.js.map +1 -0
  320. package/dist/src/orgrt/role-session-opts.d.ts +389 -0
  321. package/dist/src/orgrt/role-session-opts.d.ts.map +1 -0
  322. package/dist/src/orgrt/role-session-opts.js +289 -0
  323. package/dist/src/orgrt/role-session-opts.js.map +1 -0
  324. package/dist/src/orgrt/runner-resolve.d.ts +43 -0
  325. package/dist/src/orgrt/runner-resolve.d.ts.map +1 -0
  326. package/dist/src/orgrt/runner-resolve.js +77 -0
  327. package/dist/src/orgrt/runner-resolve.js.map +1 -0
  328. package/dist/src/orgrt/session-gate.d.ts +34 -0
  329. package/dist/src/orgrt/session-gate.d.ts.map +1 -0
  330. package/dist/src/orgrt/session-gate.js +74 -0
  331. package/dist/src/orgrt/session-gate.js.map +1 -0
  332. package/dist/src/orgrt/session-prompt.d.ts +31 -0
  333. package/dist/src/orgrt/session-prompt.d.ts.map +1 -0
  334. package/dist/src/orgrt/session-prompt.js +115 -0
  335. package/dist/src/orgrt/session-prompt.js.map +1 -0
  336. package/dist/src/orgrt/session-run.d.ts +18 -0
  337. package/dist/src/orgrt/session-run.d.ts.map +1 -0
  338. package/dist/src/orgrt/session-run.js +429 -0
  339. package/dist/src/orgrt/session-run.js.map +1 -0
  340. package/dist/src/orgrt/session-stream.d.ts +21 -0
  341. package/dist/src/orgrt/session-stream.d.ts.map +1 -0
  342. package/dist/src/orgrt/session-stream.js +173 -0
  343. package/dist/src/orgrt/session-stream.js.map +1 -0
  344. package/dist/src/orgrt/session-types.d.ts +219 -0
  345. package/dist/src/orgrt/session-types.d.ts.map +1 -0
  346. package/dist/src/orgrt/session-types.js +2 -0
  347. package/dist/src/orgrt/session-types.js.map +1 -0
  348. package/dist/src/orgrt/session-usage.d.ts +29 -0
  349. package/dist/src/orgrt/session-usage.d.ts.map +1 -0
  350. package/dist/src/orgrt/session-usage.js +64 -0
  351. package/dist/src/orgrt/session-usage.js.map +1 -0
  352. package/dist/src/orgrt/session.d.ts +5 -281
  353. package/dist/src/orgrt/session.d.ts.map +1 -1
  354. package/dist/src/orgrt/session.js +8 -1117
  355. package/dist/src/orgrt/session.js.map +1 -1
  356. package/dist/src/ui/org-hil.mjs +1 -1
  357. package/dist/src/ui/routes-org-agents.mjs +318 -0
  358. package/dist/src/ui/routes-org-approvals.mjs +133 -0
  359. package/dist/src/ui/routes-org-config.mjs +459 -0
  360. package/dist/src/ui/routes-org-control.mjs +300 -0
  361. package/dist/src/ui/routes-org-files.mjs +172 -0
  362. package/dist/src/ui/routes-org-helpers.mjs +84 -0
  363. package/dist/src/ui/routes-org-knowledge.mjs +262 -0
  364. package/dist/src/ui/routes-org-lifecycle.mjs +224 -0
  365. package/dist/src/ui/routes-org-live.mjs +278 -0
  366. package/dist/src/ui/routes-org-mastermind.mjs +346 -0
  367. package/dist/src/ui/routes-org-planning.mjs +398 -0
  368. package/dist/src/ui/routes-org-runs.mjs +430 -0
  369. package/dist/src/ui/routes-org-status.mjs +342 -0
  370. package/dist/src/ui/routes-org-workflows.mjs +212 -0
  371. package/dist/src/ui/routes-org.mjs +26 -3847
  372. package/dist/src/ui/server.mjs +1 -1
  373. package/dist/src/utils/native-error.d.ts +1 -1
  374. package/dist/src/utils/native-error.js +1 -1
  375. package/dist/src/utils/paths.d.ts +2 -1
  376. package/dist/src/utils/paths.d.ts.map +1 -1
  377. package/dist/src/utils/paths.js +2 -1
  378. package/dist/src/utils/paths.js.map +1 -1
  379. package/dist/tsconfig.tsbuildinfo +1 -1
  380. package/org-skills/adversarial-reviewer/SKILL.md +1 -1
  381. package/org-skills/board-deck-builder/SKILL.md +1 -1
  382. package/org-skills/code-reviewer/SKILL.md +1 -1
  383. package/org-skills/debugging-wizard/SKILL.md +1 -1
  384. package/org-skills/scientific-thinking-literature-review/SKILL.md +1 -1
  385. package/org-skills/scientific-thinking-scholar-evaluation/SKILL.md +1 -1
  386. package/package.json +5 -5
@@ -3,18 +3,18 @@
3
3
  * Config, memory, API keys, MCP, monograph, helpers, routing, gates, gitignore, worker metrics
4
4
  */
5
5
  import { execSync } from 'node:child_process';
6
- import { appendFileSync, copyFileSync, existsSync, mkdirSync, readdirSync, readFileSync, rmSync, statSync, writeFileSync, } from 'node:fs';
7
- import { createRequire } from 'node:module';
6
+ import { existsSync, readdirSync, readFileSync, rmSync, statSync } from 'node:fs';
8
7
  import { homedir } from 'node:os';
9
- import { dirname, join, sep } from 'node:path';
10
- import { fileURLToPath } from 'node:url';
11
- import { isKeptUserEdit } from '../init/file-guard.js';
12
- import { DOCTOR_TRACKED_HELPERS, OBSOLETE_HELPER_NAMES } from '../init/helpers-generator.js';
13
- import { MONOMIND_NEVER_COMMIT } from '../init/never-commit.js';
8
+ import { join } from 'node:path';
14
9
  import { mcpAddHint } from '../platform-adapters/renderers/mcp.js';
15
- import { classifyNativeModuleError, extractNativeModulePackageName, } from '../utils/native-error.js';
16
- import { CONFIG_JSON_CANDIDATE_PATHS, CONFIG_YAML_CANDIDATE_PATHS, MEMORY_DB_CANDIDATE_PATHS, } from '../utils/paths.js';
17
- import { MAX_DOCTOR_BUILD_LOG_SCAN_BYTES, MAX_DOCTOR_CONFIG_BYTES, MAX_DOCTOR_GITIGNORE_BYTES, MAX_DOCTOR_HELPER_BYTES, MAX_DOCTOR_PKG_BYTES, } from './doctor-env-checks.js';
10
+ import { CONFIG_JSON_CANDIDATE_PATHS, CONFIG_YAML_CANDIDATE_PATHS } from '../utils/paths.js';
11
+ import { MAX_DOCTOR_CONFIG_BYTES } from './doctor-env-checks.js';
12
+ export { checkGitignoreCoverage, fixGitignoreCoverage } from './doctor-gitignore-checks.js';
13
+ export { checkGuidanceGates, checkHelpersFresh, fixStaleHelpers } from './doctor-helpers-checks.js';
14
+ export { checkDocumentExtractors, checkMemoryDatabase, checkMemoryKnowledgeGraph, checkMemoryProficiency, checkProjectRoot, checkSecondBrainModel, } from './doctor-memory-checks.js';
15
+ export { checkMonoesMemory, checkMonograph, checkMonographFreshness, } from './doctor-monograph-checks.js';
16
+ export { checkAgentRegistry, checkMonoesIntegration } from './doctor-routing-checks.js';
17
+ export { checkMetricsFreshness, checkSecurityAuditFindings } from './doctor-worker-checks.js';
18
18
  export async function checkConfigFile() {
19
19
  const jsonPaths = CONFIG_JSON_CANDIDATE_PATHS;
20
20
  for (const configPath of jsonPaths) {
@@ -45,218 +45,6 @@ export async function checkConfigFile() {
45
45
  fix: 'monomind config init',
46
46
  };
47
47
  }
48
- /** o-16 (AC-6): the marker walk that resolves "this project" for the Memory
49
- * & Knowledge store can silently adopt a wrong ancestor — the incident that
50
- * motivated this check was a shared scratch parent's bare `.monomind`
51
- * capturing an unrelated fixture, with no visible symptom until a search
52
- * came back empty. Disclose the resolved root and why it was chosen, so a
53
- * user can see which directory their brain is keyed to without reading code
54
- * or debug logs. Always 'info': the resolution itself is never a health
55
- * problem, only the failure to see it was.
56
- *
57
- * Named/scoped to "Memory Project Root", not a bare "Project Root" (o-16
58
- * revision 1, reviewer MAJOR 2): `MONOMIND_PROJECT_ROOT` has a SECOND,
59
- * independent consumer — `mcp-tools/guidance-tools.ts:findProjectRoot()`,
60
- * which uses a different marker (`.claude`) and can legitimately resolve a
61
- * different directory (see the divergence explained at
62
- * `memory-bridge.ts`'s `getProjectRoot()` doc comment). A generic "Project
63
- * Root" label would claim there is one true answer when this commit is
64
- * what creates a state where two resolvers can disagree — this check must
65
- * not be the thing that misleads about that. */
66
- export async function checkProjectRoot() {
67
- const name = 'Memory Project Root';
68
- const { getProjectRootResolution } = await import('../memory/memory-bridge.js');
69
- const { root, reason, ignoredBareMonomind, invalidAnchor } = getProjectRootResolution();
70
- const reasonText = {
71
- 'explicit-anchor': 'MONOMIND_PROJECT_ROOT anchor',
72
- git: 'nearest .git ancestor',
73
- 'monomind-at-start': 'starting directory carries .monomind',
74
- 'monomind-with-marker': '.monomind ancestor confirmed by a project manifest',
75
- 'start-fallback': 'no adoptable marker found — using the current directory',
76
- };
77
- // o-16 revision 1 (dev-lead Addition 1): the anchor's own disclosure must
78
- // cover the ambiguous case it exists to resolve — a SET-but-INVALID
79
- // anchor — not just the happy path, or a typo'd MONOMIND_PROJECT_ROOT
80
- // silently does nothing and the user has no way to find out why. Appended
81
- // regardless of which `reason` the walk ultimately fell back to.
82
- const invalidAnchorNote = invalidAnchor
83
- ? ` [MONOMIND_PROJECT_ROOT="${invalidAnchor.value}" ignored — ${invalidAnchor.problem}]`
84
- : '';
85
- if (reason === 'start-fallback' && ignoredBareMonomind) {
86
- return {
87
- name,
88
- status: 'info',
89
- message: `${root} (${reasonText[reason]}; ignored bare .monomind at ${ignoredBareMonomind} — add a project marker there, or set MONOMIND_PROJECT_ROOT, to adopt it)${invalidAnchorNote}`,
90
- };
91
- }
92
- return { name, status: 'info', message: `${root} (${reasonText[reason]})${invalidAnchorNote}` };
93
- }
94
- export async function checkMemoryDatabase() {
95
- const dbPaths = MEMORY_DB_CANDIDATE_PATHS;
96
- for (const dbPath of dbPaths) {
97
- if (existsSync(dbPath)) {
98
- try {
99
- const sizeMB = (statSync(dbPath).size / 1024 / 1024).toFixed(2);
100
- return { name: 'Memory Database', status: 'pass', message: `${dbPath} (${sizeMB} MB)` };
101
- }
102
- catch {
103
- return { name: 'Memory Database', status: 'warn', message: `${dbPath} (unable to stat)` };
104
- }
105
- }
106
- }
107
- return {
108
- name: 'Memory Database',
109
- status: 'warn',
110
- message: 'Not initialized',
111
- fix: 'monomind memory init',
112
- };
113
- }
114
- /** Memory knowledge graph (cognee port): entities/relations/rules distilled
115
- * from sessions and org runs. Informational — an empty graph on a fresh
116
- * project is normal, so it never fails. */
117
- export async function checkMemoryKnowledgeGraph() {
118
- const name = 'Memory Knowledge Graph';
119
- try {
120
- const kg = await import('../memory/memory-kg.js');
121
- const bridge = await import('../memory/memory-bridge.js');
122
- const project = await kg.kgStats();
123
- // Org KG facts are namespaced per org (kg:nodes:org:<name> etc.), so an
124
- // unscoped kgStats against the org store reads the now-unused flat
125
- // namespaces and reports zero for every org. Sum each org's own scope
126
- // instead — the org store holds all of them, keyed by scope.
127
- const orgDb = join(process.cwd(), '.monomind', 'org-memory');
128
- let org = null;
129
- if (existsSync(orgDb)) {
130
- const { orgKgScope } = await import('../orgrt/org-memory.js');
131
- const { listOrgConfigFiles } = await import('./org.js');
132
- const orgsDir = join(process.cwd(), '.monomind', 'orgs');
133
- const orgNames = existsSync(orgsDir)
134
- ? listOrgConfigFiles(orgsDir).map((f) => f.replace(/\.json$/, ''))
135
- : [];
136
- org = { nodes: 0, edges: 0, rules: 0 };
137
- for (const orgName of orgNames) {
138
- const s = await kg.kgStats({ dbPath: orgDb, scope: orgKgScope(orgName) });
139
- org.nodes += s.nodes;
140
- org.edges += s.edges;
141
- org.rules += s.rules;
142
- }
143
- }
144
- await bridge.shutdownBridge().catch(() => {
145
- /* best effort */
146
- });
147
- const total = project.nodes + (org?.nodes ?? 0);
148
- const fmt = (s) => `${s.nodes}n/${s.edges}e/${s.rules}r`;
149
- if (total === 0) {
150
- return {
151
- name,
152
- status: 'warn',
153
- message: 'Empty — sessions/org runs have not distilled knowledge yet',
154
- fix: 'org runs call org_learn automatically; sessions use memory_kg_ingest',
155
- };
156
- }
157
- return {
158
- name,
159
- status: 'pass',
160
- message: `project ${fmt(project)}${org ? ` · org ${fmt(org)}` : ''}`,
161
- };
162
- }
163
- catch (err) {
164
- return {
165
- name,
166
- status: 'warn',
167
- message: `Check failed: ${err instanceof Error ? err.message : String(err)}`,
168
- };
169
- }
170
- }
171
- /** Count live (non-tombstoned) ingested documents under a brain root, without
172
- * creating the directory — `listDocuments` mkdir's its metadata dir, which
173
- * would materialize an empty global brain just by running doctor. */
174
- async function countBrainDocs(root) {
175
- if (!existsSync(join(root, '.monomind', 'knowledge', 'doc-metadata.jsonl')))
176
- return 0;
177
- try {
178
- const { listDocuments } = await import('../knowledge/document-pipeline.js');
179
- return listDocuments(root).length;
180
- }
181
- catch {
182
- return 0;
183
- }
184
- }
185
- /** Second Brain: when there is any indexed knowledge — ingested docs in this
186
- * project, the personal global brain, or the monograph god-node chunk —
187
- * semantic search needs the local embedding model. Its absence is silent
188
- * (search degrades to keyword matching), so surface it here instead. */
189
- export async function checkSecondBrainModel() {
190
- const name = 'Second Brain Model';
191
- const { BRIDGE_EMBEDDING_MODEL, getGlobalBrainDir, getProjectRoot } = await import('../memory/memory-bridge.js');
192
- const projectDocs = await countBrainDocs(getProjectRoot());
193
- const globalDocs = await countBrainDocs(getGlobalBrainDir());
194
- // chunks.jsonl holds the monograph god-node chunk — knowledge with no doc
195
- // ingest. Unlike the document store it is written per-CWD by the session
196
- // hook, not at the project root, so check both rather than assuming.
197
- const chunksIn = (root) => existsSync(join(root, '.monomind', 'knowledge', 'chunks.jsonl'));
198
- const hasChunks = chunksIn(getProjectRoot()) || chunksIn(process.cwd());
199
- if (!projectDocs && !globalDocs && !hasChunks) {
200
- return { name, status: 'pass', message: 'No knowledge indexed (nothing to check)' };
201
- }
202
- const indexed = [
203
- projectDocs ? `${projectDocs} project doc${projectDocs === 1 ? '' : 's'}` : null,
204
- globalDocs ? `${globalDocs} global doc${globalDocs === 1 ? '' : 's'}` : null,
205
- !projectDocs && !globalDocs ? 'monograph god-nodes' : null,
206
- ]
207
- .filter(Boolean)
208
- .join(' · ');
209
- let entryPath = null;
210
- try {
211
- // exports map blocks package.json — resolve the entry file, then walk UP
212
- // with path ops (no separator-sensitive regex: '/dist/'.replace broke on
213
- // Windows paths, and any install layout without a dist segment).
214
- // import.meta.resolve is missing on Node 20.0–20.5; fall back to CJS resolve.
215
- const viaEsm = import.meta.resolve?.('@huggingface/transformers');
216
- if (viaEsm)
217
- entryPath = fileURLToPath(await viaEsm);
218
- }
219
- catch {
220
- /* try CJS below */
221
- }
222
- if (!entryPath) {
223
- try {
224
- const { createRequire } = await import('node:module');
225
- entryPath = createRequire(import.meta.url).resolve('@huggingface/transformers');
226
- }
227
- catch {
228
- return {
229
- name,
230
- status: 'warn',
231
- message: '@huggingface/transformers not installed — semantic search degraded to keyword matching',
232
- fix: 'reinstall monomind (the model dependency is optional and may have failed to build)',
233
- };
234
- }
235
- }
236
- // Walk up from the entry file to the package root (the dir named 'transformers').
237
- let pkgDir = dirname(entryPath);
238
- while (pkgDir !== dirname(pkgDir) && !pkgDir.endsWith('transformers'))
239
- pkgDir = dirname(pkgDir);
240
- // The cached model is whichever one the bridge actually loads. This used to
241
- // look for '.cache/Xenova', a model the bridge stopped using — so the check
242
- // reported "not downloaded" with a fully provisioned cache sitting on disk.
243
- const modelCache = join(pkgDir, '.cache', ...BRIDGE_EMBEDDING_MODEL.split('/'));
244
- if (pkgDir.endsWith('transformers') && existsSync(modelCache)) {
245
- return {
246
- name,
247
- status: 'pass',
248
- message: `Local embedding model cached — semantic search active (${indexed})`,
249
- };
250
- }
251
- return {
252
- name,
253
- status: 'warn',
254
- message: 'Embedding model not downloaded yet — searches use keyword matching until it is',
255
- // Query-time paths all pass local_files_only and never fetch; this is the
256
- // one command that downloads the weights.
257
- fix: 'run once while online: monomind doc eval --provision-model (downloads ~270MB locally, one time)',
258
- };
259
- }
260
48
  export async function checkApiKeys() {
261
49
  const keys = ['ANTHROPIC_API_KEY', 'CLAUDE_API_KEY', 'OPENAI_API_KEY'];
262
50
  const found = keys.filter((k) => process.env[k]);
@@ -399,1070 +187,6 @@ async function probeConfiguredMcpServer(entry, count, configPath, timeoutMs) {
399
187
  fix: `Run it by hand to confirm: ${commandLine} # a pinned scoped package needs: npx -y --package=@monoes/monomindcli@<version> monomind mcp start`,
400
188
  };
401
189
  }
402
- // Resolve an installed package's package.json through Node's real ESM
403
- // resolution algorithm instead of guessing relative node_modules layouts.
404
- // The old candidate-path approach assumed a nested install shape that never
405
- // matches how npm actually hoists dependencies (flat local installs, npx's
406
- // isolated cache dir, and global installs all put @monoes/* packages as
407
- // siblings, not nested under this package's dist/ output) — so it always
408
- // reported "not found" even when the package was present and importable.
409
- // import.meta.resolve() is used (rather than createRequire().resolve()())
410
- // because @monoes/monograph and @monoes/memory are ESM-only (no "require"
411
- // export condition), so a CJS require.resolve() fails outright even when the
412
- // package is installed. Their exports maps also don't expose a "./package.json"
413
- // subpath, so the entry file is resolved first and package.json is found by
414
- // walking up from it.
415
- function _findInstalledPackageJson(specifier) {
416
- try {
417
- const entryUrl = import.meta.resolve(specifier);
418
- let dir = dirname(fileURLToPath(entryUrl));
419
- for (;;) {
420
- const candidate = join(dir, 'package.json');
421
- if (existsSync(candidate) && statSync(candidate).size <= MAX_DOCTOR_PKG_BYTES) {
422
- try {
423
- const pkg = JSON.parse(readFileSync(candidate, 'utf-8'));
424
- if (pkg.name === specifier)
425
- return { path: candidate, pkg };
426
- }
427
- catch {
428
- /* keep walking */
429
- }
430
- }
431
- const parent = dirname(dir);
432
- if (parent === dir)
433
- return null;
434
- dir = parent;
435
- }
436
- }
437
- catch {
438
- return null;
439
- }
440
- }
441
- export async function checkMonograph() {
442
- const found = _findInstalledPackageJson('@monoes/monograph');
443
- if (found) {
444
- return {
445
- name: 'Monograph',
446
- status: 'pass',
447
- message: `v${found.pkg.version || '?'} available (knowledge graph engine)`,
448
- };
449
- }
450
- return {
451
- name: 'Monograph',
452
- status: 'warn',
453
- message: 'Package not found (knowledge graph disabled)',
454
- fix: 'npm install -g monomind@latest',
455
- };
456
- }
457
- function formatAge(ms) {
458
- // Filesystem mtime can round to whole seconds and land a hair ahead of
459
- // Date.now() for a file written moments ago — clamp so that never reads
460
- // as a negative, nonsensical age.
461
- const minutes = Math.floor(Math.max(0, ms) / 60000);
462
- return minutes < 60 ? `${minutes}m` : `${Math.floor(minutes / 60)}h`;
463
- }
464
- /**
465
- * Best-effort "when was this package's own install last touched" probe,
466
- * used only to soften a build.log-based failure (see #244) — never to
467
- * harden one, so a wrong or missing answer here is always safe. Resolves
468
- * `pkgName` from `cwd` (works through pnpm's symlinked store too, since
469
- * `require.resolve` follows symlinks to the real file) and returns the
470
- * latest mtime among its package.json and any `build/`/`prebuilds/` binary
471
- * output, or null if the package can't be resolved at all — the caller
472
- * treats null as "no evidence either way," not as "confirmed still broken."
473
- */
474
- function findNativeModuleLastModifiedMs(pkgName, cwd) {
475
- try {
476
- const require = createRequire(join(cwd, 'noop.js'));
477
- const entry = require.resolve(pkgName, { paths: [cwd] });
478
- let pkgDir = dirname(entry);
479
- for (let i = 0; i < 12 && !existsSync(join(pkgDir, 'package.json')); i++) {
480
- const parent = dirname(pkgDir);
481
- if (parent === pkgDir)
482
- return null;
483
- pkgDir = parent;
484
- }
485
- let latestMs = statSync(join(pkgDir, 'package.json')).mtimeMs;
486
- for (const sub of ['build', 'prebuilds']) {
487
- const subDir = join(pkgDir, sub);
488
- if (!existsSync(subDir))
489
- continue;
490
- const stack = [subDir];
491
- while (stack.length > 0) {
492
- const current = stack.pop();
493
- for (const entryName of readdirSync(current)) {
494
- const full = join(current, entryName);
495
- const st = statSync(full);
496
- if (st.isDirectory())
497
- stack.push(full);
498
- else if (st.mtimeMs > latestMs)
499
- latestMs = st.mtimeMs;
500
- }
501
- }
502
- }
503
- return latestMs;
504
- }
505
- catch {
506
- return null;
507
- }
508
- }
509
- export async function checkMonographFreshness() {
510
- try {
511
- const cwd = process.cwd();
512
- const dbPath = join(cwd, '.monomind', 'monograph.db');
513
- const lockPath = join(cwd, '.monomind', 'graph', 'build.lock');
514
- const statsPath = join(cwd, '.monomind', 'graph', 'stats.json');
515
- const logPath = join(cwd, '.monomind', 'graph', 'build.log');
516
- const hasDb = existsSync(dbPath);
517
- if (!hasDb && !existsSync(statsPath)) {
518
- // No graph yet — but that's one of three very different situations: a
519
- // build is genuinely still running, one already ran and crashed (e.g.
520
- // executor.ts's initKnowledgeGraph or monograph-freshen.cjs's detached
521
- // spawn hit a native-module ABI mismatch), or nothing was ever
522
- // attempted. Both spawners share this same build.lock/build.log pair,
523
- // so this one check covers either origin.
524
- //
525
- // Lock existence (not freshness) is the right signal here: both
526
- // spawners write build.lock with their OWN pid (the short-lived parent
527
- // that immediately exits after spawning a detached child — not the
528
- // child actually doing the build, so checking that pid's liveness
529
- // would be checking the wrong process) and unlink it in a `finally`
530
- // that runs on both success and a thrown/rejected build — so it's only
531
- // ever left behind while the child is still actually running, or in
532
- // the rare case of a hard OS-level kill that bypassed the `finally`.
533
- // A large monorepo's first index can legitimately run well past any
534
- // fixed "freshness" window, so age must not turn a live build into a
535
- // false failure report. Neither spawner passes buildAsync an
536
- // onProgress callback, so a clean run writes nothing to build.log
537
- // until it's done — there's no heartbeat to check the log against, so
538
- // this is worded to cover both "still running" and "was killed
539
- // without cleaning up" rather than asserting liveness that can't
540
- // actually be confirmed from disk.
541
- if (existsSync(lockPath)) {
542
- const ageMs = (() => {
543
- try {
544
- return Date.now() - statSync(lockPath).mtimeMs;
545
- }
546
- catch {
547
- return 0;
548
- }
549
- })();
550
- return {
551
- name: 'Graph freshness',
552
- status: 'warn',
553
- message: `Monograph build in progress, or was interrupted — started ${formatAge(ageMs)} ago`,
554
- };
555
- }
556
- if (existsSync(logPath)) {
557
- let logTail = '';
558
- try {
559
- const raw = readFileSync(logPath, 'utf8');
560
- // Bound the scan to the last MAX_BUILD_LOG_SCAN_BYTES, not the last
561
- // 4000 — that window was cutting the actual "Error:"/"Exception:"
562
- // text out of real Node uncaught-exception dumps. A native-module
563
- // load failure (e.g. `bindings` walking a dozen candidate .node
564
- // paths before giving up) prints its message and stack FIRST, then
565
- // a long list of tried paths — a single such dump easily runs
566
- // 7-8KB, so a 4000-char tail can land entirely inside the path
567
- // list and see no error keyword at all, even though the log is
568
- // nothing but a crash report. 64KB comfortably fits many realistic
569
- // crash dumps (including chained causes) while still bounding this
570
- // append-only, never-truncated file for a long-lived project that
571
- // has failed and retried many times.
572
- logTail =
573
- raw.length > MAX_DOCTOR_BUILD_LOG_SCAN_BYTES
574
- ? raw.slice(-MAX_DOCTOR_BUILD_LOG_SCAN_BYTES)
575
- : raw;
576
- }
577
- catch {
578
- /* unreadable log — treat as no evidence, fall through below */
579
- }
580
- // build.log only proves a build ran at some point — a *successful*
581
- // build also writes into it before producing monograph.db, so its
582
- // mere existence isn't evidence of failure (e.g. monograph.db was
583
- // deleted afterward, leaving a clean run's log behind). Only treat
584
- // this as a failure when the tail actually looks error-shaped;
585
- // otherwise this isn't distinguishable from "never built" and
586
- // should read that way rather than as an unsupported "fail".
587
- const classified = logTail ? classifyNativeModuleError(logTail) : null;
588
- const looksLikeFailure = classified !== null || /error|exception|traceback/i.test(logTail);
589
- if (looksLikeFailure) {
590
- let logMtimeMs = 0;
591
- let logAgeStr = '';
592
- try {
593
- logMtimeMs = statSync(logPath).mtimeMs;
594
- logAgeStr = ` (${formatAge(Date.now() - logMtimeMs)} ago)`;
595
- }
596
- catch {
597
- /* ignore */
598
- }
599
- if (classified) {
600
- // build.log is append-only (see the comment above), so this
601
- // failure might be historical — already fixed by a `npm
602
- // rebuild` that ran after this log entry, just not yet
603
- // confirmed by a fresh `monograph build`. When the implicated
604
- // module's own files are demonstrably newer than the log entry,
605
- // that's concrete on-disk evidence it was touched since, so
606
- // soften to a warning instead of asserting it's still broken.
607
- // No evidence either way (can't resolve the module, or its
608
- // files are no newer) falls through to the unchanged 'fail'.
609
- const pkgName = extractNativeModulePackageName(logTail);
610
- const moduleMs = pkgName ? findNativeModuleLastModifiedMs(pkgName, cwd) : null;
611
- if (pkgName && moduleMs !== null && moduleMs > logMtimeMs) {
612
- return {
613
- name: 'Graph freshness',
614
- status: 'warn',
615
- message: `${classified}${logAgeStr} — but \`${pkgName}\` was modified more recently ` +
616
- `(${formatAge(Date.now() - moduleMs)} ago) than this log entry, so it may ` +
617
- 'already be fixed. Run monograph build to confirm.',
618
- fix: 'mcp__monomind__monograph_build codeOnly:true',
619
- };
620
- }
621
- }
622
- return classified
623
- ? { name: 'Graph freshness', status: 'fail', message: `${classified}${logAgeStr}` }
624
- : {
625
- name: 'Graph freshness',
626
- status: 'fail',
627
- message: `Monograph build failed${logAgeStr} — see .monomind/graph/build.log for details`,
628
- fix: 'cat .monomind/graph/build.log',
629
- };
630
- }
631
- }
632
- return {
633
- name: 'Graph freshness',
634
- status: 'warn',
635
- message: 'No monograph graph built yet',
636
- fix: 'mcp__monomind__monograph_build codeOnly:true',
637
- };
638
- }
639
- // Deliberately NOT folding build.lock's mtime into this — a lock only
640
- // means "a build started," not "the graph was built at this time," and
641
- // counting it here would report a stale graph as freshly-built the
642
- // moment a rebuild kicks off, before it's actually produced anything.
643
- let buildMs = 0;
644
- if (hasDb) {
645
- try {
646
- buildMs = Math.max(buildMs, statSync(dbPath).mtimeMs);
647
- }
648
- catch {
649
- /* ignore */
650
- }
651
- }
652
- try {
653
- buildMs = Math.max(buildMs, statSync(statsPath).mtimeMs);
654
- }
655
- catch {
656
- /* ignore */
657
- }
658
- if (buildMs === 0)
659
- return {
660
- name: 'Graph freshness',
661
- status: 'warn',
662
- message: 'Graph exists but build time unknown',
663
- };
664
- const buildIso = new Date(buildMs).toISOString();
665
- let commitsBehind = 0;
666
- try {
667
- const out = execSync(`git rev-list --count --since='${buildIso}' HEAD 2>/dev/null`, {
668
- encoding: 'utf8',
669
- timeout: 2000,
670
- cwd,
671
- }).trim();
672
- commitsBehind = parseInt(out, 10) || 0;
673
- }
674
- catch {
675
- /* git unavailable */
676
- }
677
- const ageStr = `${formatAge(Date.now() - buildMs)} ago`;
678
- if (commitsBehind === 0)
679
- return {
680
- name: 'Graph freshness',
681
- status: 'pass',
682
- message: `FRESH — built ${ageStr}, 0 commits behind`,
683
- };
684
- if (commitsBehind <= 5)
685
- return {
686
- name: 'Graph freshness',
687
- status: 'warn',
688
- message: `${commitsBehind} commit(s) behind — built ${ageStr}`,
689
- fix: 'mcp__monomind__monograph_build codeOnly:true',
690
- };
691
- return {
692
- name: 'Graph freshness',
693
- status: 'fail',
694
- message: `STALE — ${commitsBehind} commits behind (built ${ageStr})`,
695
- fix: 'mcp__monomind__monograph_build codeOnly:true',
696
- };
697
- }
698
- catch {
699
- return { name: 'Graph freshness', status: 'warn', message: 'Could not check graph freshness' };
700
- }
701
- }
702
- export async function checkMonoesMemory() {
703
- // See _findInstalledPackageJson() above checkMonograph(): resolve through
704
- // Node's ESM module resolution instead of guessing a node_modules layout
705
- // that doesn't match how npm actually hoists dependencies.
706
- const found = _findInstalledPackageJson('@monoes/memory');
707
- if (found) {
708
- return {
709
- name: 'Vector Memory',
710
- status: 'pass',
711
- message: `@monoes/memory v${found.pkg.version || '?'} (HNSW search enabled)`,
712
- };
713
- }
714
- return {
715
- name: 'Vector Memory',
716
- status: 'warn',
717
- message: '@monoes/memory not installed (vector search disabled)',
718
- fix: 'npm install @monoes/memory',
719
- };
720
- }
721
- function _resolveBundledHelper(relativePath) {
722
- try {
723
- const thisFile = fileURLToPath(import.meta.url);
724
- let dir = dirname(thisFile);
725
- for (;;) {
726
- const candidate = join(dir, 'package.json');
727
- if (existsSync(candidate) && statSync(candidate).size <= MAX_DOCTOR_PKG_BYTES) {
728
- try {
729
- const pkg = JSON.parse(readFileSync(candidate, 'utf8'));
730
- if (pkg.name === '@monomind/cli' ||
731
- pkg.name === 'monomind' ||
732
- pkg.name === '@monoes/monomindcli') {
733
- const helperPath = join(dir, relativePath);
734
- return existsSync(helperPath) ? helperPath : null;
735
- }
736
- }
737
- catch {
738
- /* keep walking */
739
- }
740
- }
741
- const parent = dirname(dir);
742
- if (parent === dir)
743
- return null;
744
- dir = parent;
745
- }
746
- }
747
- catch {
748
- return null;
749
- }
750
- }
751
- // Top-level critical helpers, plus every file bundled under handlers/ and utils/
752
- // (capture-handler.cjs, gates-handler.cjs, route-handler.cjs, session-handler.cjs,
753
- // etc. — where most actual hook-behavior fixes live). Subdir membership is
754
- // discovered from the bundled package itself, not hardcoded, so a helper added
755
- // upstream is picked up automatically instead of silently never syncing.
756
- function _allTrackedHelperNames() {
757
- const names = [...DOCTOR_TRACKED_HELPERS];
758
- for (const sub of ['handlers', 'utils']) {
759
- const bundledSubDir = _resolveBundledHelper(join('.claude', 'helpers', sub));
760
- if (!bundledSubDir)
761
- continue;
762
- try {
763
- for (const f of readdirSync(bundledSubDir)) {
764
- if (f.startsWith('._'))
765
- continue;
766
- if (statSync(join(bundledSubDir, f)).isDirectory())
767
- continue;
768
- names.push(join(sub, f));
769
- }
770
- }
771
- catch {
772
- /* skip */
773
- }
774
- }
775
- return names;
776
- }
777
- // Top-level helpers the bundle ships. Their mere ABSENCE is a defect no
778
- // matter which one it is — handlers/ scripts require() some of them at module
779
- // load, and a missing one fails the hook closed (issue #225: doctor reported
780
- // "helpers match bundled version" while audit-log-writer.cjs was absent and
781
- // every PreToolUse hook was crashing). Existence is checked for all of them;
782
- // content is still only hash-compared for _allTrackedHelperNames(), because
783
- // the rest are user-editable scaffolds (memory.cjs, session.cjs) whose local
784
- // edits are legitimate and must not be reported as staleness.
785
- function _bundledTopLevelHelperNames() {
786
- const bundledDir = _resolveBundledHelper(join('.claude', 'helpers'));
787
- if (!bundledDir)
788
- return [];
789
- try {
790
- return readdirSync(bundledDir, { withFileTypes: true })
791
- .filter((e) => e.isFile() && !e.name.startsWith('._'))
792
- .map((e) => e.name);
793
- }
794
- catch {
795
- return [];
796
- }
797
- }
798
- // The installed helper copies doctor compares against the bundle: the Claude
799
- // Code tree, plus the copy init writes to .gemini/helpers (Antigravity's status
800
- // bar runs .gemini/helpers/statusline.cjs), which is only checked when present.
801
- const CLAUDE_HELPERS = join('.claude', 'helpers');
802
- const GEMINI_HELPERS = join('.gemini', 'helpers');
803
- async function _detectStaleHelpers(helpersDir = CLAUDE_HELPERS) {
804
- const stale = [];
805
- const missing = [];
806
- // Helpers this product shipped under a name it has since renamed away from
807
- // (e.g. graphify-freshen.cjs -> monograph-freshen.cjs). Reported separately
808
- // from `stale`/`missing`: unlike those, doctor --fix does not delete these
809
- // on its own — settings.json may still have a hook command pointing at one,
810
- // and only `init --force` (which also refreshes settings.json in the same
811
- // pass) can safely remove it. See writeHelpers/writeSettings.
812
- const orphaned = OBSOLETE_HELPER_NAMES.filter((name) => existsSync(join(process.cwd(), '.claude', 'helpers', name)));
813
- const crypto = await import('node:crypto');
814
- const hashChecked = new Set(_allTrackedHelperNames());
815
- for (const name of new Set([..._allTrackedHelperNames(), ..._bundledTopLevelHelperNames()])) {
816
- const bundled = _resolveBundledHelper(join('.claude', 'helpers', name));
817
- // Oversized-bundled is reported the same as missing-bundled: doctor can't
818
- // verify freshness either way, and silently excluding it from both `stale`
819
- // and `missing` would let checkHelpersFresh() report "pass" without ever
820
- // having actually compared this file.
821
- if (!bundled || statSync(bundled).size > MAX_DOCTOR_HELPER_BYTES) {
822
- missing.push(name);
823
- continue;
824
- }
825
- const local = join(process.cwd(), helpersDir, name);
826
- if (!existsSync(local)) {
827
- stale.push(name);
828
- continue;
829
- } // bundled has it, project doesn't — needs creating
830
- if (!hashChecked.has(name))
831
- continue; // existence-only (see above)
832
- if (statSync(local).size > MAX_DOCTOR_HELPER_BYTES)
833
- continue;
834
- try {
835
- const hashLocal = crypto.createHash('sha256').update(readFileSync(local)).digest('hex');
836
- const hashBundled = crypto.createHash('sha256').update(readFileSync(bundled)).digest('hex');
837
- if (hashLocal !== hashBundled)
838
- stale.push(name);
839
- }
840
- catch {
841
- /* skip */
842
- }
843
- }
844
- return { stale, missing, orphaned };
845
- }
846
- /** Stale helpers in the Gemini copy, as `.gemini/helpers/<name>`; none if not installed. */
847
- async function _detectStaleGeminiHelpers() {
848
- if (!existsSync(join(process.cwd(), GEMINI_HELPERS)))
849
- return [];
850
- const { stale } = await _detectStaleHelpers(GEMINI_HELPERS);
851
- return stale.map((name) => `.gemini/helpers/${name.split(sep).join('/')}`);
852
- }
853
- export async function fixStaleHelpers() {
854
- const { stale } = await _detectStaleHelpers();
855
- const targets = stale.map((name) => ({ name, local: join(process.cwd(), CLAUDE_HELPERS, name) }));
856
- if (existsSync(join(process.cwd(), GEMINI_HELPERS))) {
857
- for (const name of (await _detectStaleHelpers(GEMINI_HELPERS)).stale) {
858
- targets.push({ name, local: join(process.cwd(), GEMINI_HELPERS, name) });
859
- }
860
- }
861
- let fixed = 0;
862
- for (const { name, local } of targets) {
863
- // A helper `monomind init` kept because the user edited it is theirs.
864
- if (isKeptUserEdit(process.cwd(), local))
865
- continue;
866
- const bundled = _resolveBundledHelper(join('.claude', 'helpers', name));
867
- if (bundled) {
868
- try {
869
- mkdirSync(dirname(local), { recursive: true });
870
- copyFileSync(bundled, local);
871
- fixed++;
872
- }
873
- catch (e) {
874
- if (process.env.DEBUG || process.env.MONOMIND_DEBUG)
875
- console.error(`[fixStaleHelpers] failed to copy ${name}:`, e);
876
- }
877
- }
878
- }
879
- return fixed > 0;
880
- }
881
- export async function checkHelpersFresh() {
882
- try {
883
- const { stale: claudeStale, missing, orphaned } = await _detectStaleHelpers();
884
- const stale = [...claudeStale, ...(await _detectStaleGeminiHelpers())];
885
- if (orphaned.length > 0) {
886
- return {
887
- name: 'Helper Files',
888
- status: 'warn',
889
- message: `Found ${orphaned.length} hook(s) from before a rename, still on disk: ${orphaned.join(', ')}. Run \`monomind init --force\` to update your hooks and rebuild the Monograph graph.`,
890
- fix: 'monomind init --force',
891
- // `--fix` only copies stale helpers; it never removes these.
892
- fixSafety: 'manual',
893
- };
894
- }
895
- if (stale.length === 0 && missing.length === 0) {
896
- return {
897
- name: 'Helper Files',
898
- status: 'pass',
899
- message: 'Project helpers match bundled version',
900
- };
901
- }
902
- if (stale.length > 0) {
903
- return {
904
- name: 'Helper Files',
905
- status: 'warn',
906
- message: `${stale.length} stale helper(s): ${stale.join(', ')}`,
907
- fix: 'monomind init upgrade',
908
- fixSafety: 'auto',
909
- };
910
- }
911
- return {
912
- name: 'Helper Files',
913
- status: 'warn',
914
- message: `Could not locate bundled copies of: ${missing.join(', ')}`,
915
- fix: 'Reinstall monomind or run `monomind init upgrade`',
916
- fixSafety: 'manual',
917
- };
918
- }
919
- catch (e) {
920
- return {
921
- name: 'Helper Files',
922
- status: 'warn',
923
- message: `check failed: ${e instanceof Error ? e.message : 'unknown'}`,
924
- };
925
- }
926
- }
927
- function fmtPct(v) {
928
- return v === null ? 'n/a' : `${Math.round(v * 100)}%`;
929
- }
930
- async function routingAccuracyLine() {
931
- try {
932
- const { computeRoutingAccuracy, computeAdherence } = await import('../monovector/route-outcomes.js');
933
- const baseDir = join(process.cwd(), '.monomind');
934
- const acc = await computeRoutingAccuracy(baseDir, 100);
935
- const adh = await computeAdherence(baseDir);
936
- const adhStr = ` | adherence ${fmtPct(adh.adherence)} (n=${adh.sample})`;
937
- if (acc.accuracy === null)
938
- return `routing accuracy (last 100): no outcome data yet${adhStr}`;
939
- const trend = acc.recentVsPrior === null
940
- ? ''
941
- : ` trend ${acc.recentVsPrior >= 0 ? '+' : ''}${Math.round(acc.recentVsPrior * 100)}%`;
942
- return `routing accuracy (last ${acc.window}): ${fmtPct(acc.accuracy)} [native ${fmtPct(acc.byMode.native)} / js ${fmtPct(acc.byMode.js)}]${trend}${adhStr}`;
943
- }
944
- catch (e) {
945
- if (process.env.DEBUG || process.env.MONOMIND_DEBUG)
946
- console.error('[routingAccuracyLine] compute failed:', e);
947
- return 'routing accuracy (last 100): no outcome data yet';
948
- }
949
- }
950
- /** Fallback when route-outcomes.jsonl has no data: summarize .monomind/routing-feedback.jsonl honestly. */
951
- function routingFeedbackFallback() {
952
- try {
953
- const p = join(process.cwd(), '.monomind', 'routing-feedback.jsonl');
954
- if (!existsSync(p) || statSync(p).size > 512 * 1024)
955
- return null;
956
- const records = readFileSync(p, 'utf-8')
957
- .trim()
958
- .split('\n')
959
- .filter(Boolean)
960
- .map((l) => {
961
- try {
962
- return JSON.parse(l);
963
- }
964
- catch {
965
- return null;
966
- }
967
- })
968
- .filter((r) => r !== null);
969
- if (records.length === 0)
970
- return null;
971
- const sessions = new Set(records.map((r) => r.sessionId).filter(Boolean));
972
- const byAgent = new Map();
973
- for (const r of records) {
974
- const a = typeof r.suggestedAgent === 'string' ? r.suggestedAgent : 'unknown';
975
- byAgent.set(a, (byAgent.get(a) ?? 0) + 1);
976
- }
977
- const top = [...byAgent.entries()]
978
- .sort((a, b) => b[1] - a[1])
979
- .slice(0, 3)
980
- .map(([a, n]) => `${a}=${n}`)
981
- .join(', ');
982
- const flags = records
983
- .map((r) => r.intelligenceFeedback)
984
- .filter((v) => typeof v === 'boolean');
985
- const trueCount = flags.filter(Boolean).length;
986
- const base = `routing feedback (fallback): ${records.length} decisions across ${sessions.size} sessions; top agents: ${top}`;
987
- if (flags.length > 0 && trueCount === flags.length) {
988
- return {
989
- message: `${base}; success flag is degenerate (100% true — sessionSuccess heuristic never records failure)`,
990
- degenerate: true,
991
- };
992
- }
993
- if (flags.length === 0)
994
- return { message: `${base}; no evidence-based success flags yet`, degenerate: true };
995
- return {
996
- message: `${base}; success rate ${Math.round((trueCount / flags.length) * 100)}% (n=${flags.length})`,
997
- degenerate: false,
998
- };
999
- }
1000
- catch (e) {
1001
- if (process.env.DEBUG || process.env.MONOMIND_DEBUG)
1002
- console.error('[routingFeedbackFallback] parse failed:', e);
1003
- return null;
1004
- }
1005
- }
1006
- export async function checkMonoesIntegration() {
1007
- try {
1008
- const line = await routingAccuracyLine();
1009
- if (line.startsWith('routing accuracy (last 100): no outcome data yet')) {
1010
- const fb = routingFeedbackFallback();
1011
- if (fb)
1012
- return {
1013
- name: 'Routing Learning',
1014
- status: fb.degenerate ? 'warn' : 'pass',
1015
- message: fb.message,
1016
- };
1017
- }
1018
- return { name: 'Routing Learning', status: 'pass', message: line };
1019
- }
1020
- catch (err) {
1021
- return {
1022
- name: 'Routing Learning',
1023
- status: 'warn',
1024
- message: `Could not compute routing accuracy: ${err instanceof Error ? err.message : String(err)}`,
1025
- };
1026
- }
1027
- }
1028
- // i-052: derived from MONOMIND_NEVER_COMMIT (write-runtime-config.ts), the
1029
- // single source of truth for files that must never be committed — this was
1030
- // the THIRD independently-maintained list that omitted `dashboard-token`,
1031
- // and it is the one written specifically to catch gitignore gaps. Consuming
1032
- // the shared list here means `monomind doctor --fix` (fixGitignoreCoverage
1033
- // below) appends any missing entry for existing users for free.
1034
- const REQUIRED_GITIGNORE_PATTERNS = [
1035
- { pattern: '.monomind/sessions/', reason: 'session files contain cwd and machine paths' },
1036
- { pattern: '.monomind/data/', reason: 'intelligence data with edit file paths' },
1037
- { pattern: '.monomind/metrics/', reason: 'metrics with file path references' },
1038
- { pattern: '.monomind/knowledge/', reason: 'knowledge chunks with local file content' },
1039
- { pattern: '.monomind/*.json', reason: 'root-level runtime JSON (control, registry, routing)' },
1040
- { pattern: '.monomind/*.jsonl', reason: 'root-level event logs (decisions, routing-feedback)' },
1041
- { pattern: '**/.monomind/sessions/', reason: 'nested session files in sub-packages' },
1042
- { pattern: '**/.monomind/*.json', reason: 'nested runtime JSON in sub-packages' },
1043
- { pattern: 'data/sessions/', reason: 'session files with machine paths' },
1044
- { pattern: 'data/mastermind-*.json', reason: 'mastermind session data' },
1045
- { pattern: 'data/mastermind-*.jsonl', reason: 'mastermind event logs' },
1046
- { pattern: '**/.claude-flow/', reason: 'claude-flow runtime data with paths' },
1047
- { pattern: '.monomind/monoswarm/', reason: 'monoswarm state files' },
1048
- ...MONOMIND_NEVER_COMMIT.map(({ file, reason }) => ({ pattern: `.monomind/${file}`, reason })),
1049
- ];
1050
- /** Strip the parts that don't change what a pattern matches, so `**​/.monomind/`,
1051
- * `.monomind/` and `.monomind` all compare equal. A gitignore pattern with no
1052
- * leading slash already matches at every depth, so `**​/` is redundant. */
1053
- function normalizeGitignorePattern(p) {
1054
- return p
1055
- .replace(/^\/+/, '')
1056
- .replace(/^\*\*\//, '')
1057
- .replace(/\/+$/, '');
1058
- }
1059
- /** True when ignoring `line` already protects everything `required` covers.
1060
- * Beyond exact matches this understands directory containment: a repo with a
1061
- * blanket `.monomind/` is fully covered for `.monomind/sessions/`,
1062
- * `.monomind/*.json` and friends, and should not be nagged to add each one. */
1063
- function gitignoreLineCovers(line, required) {
1064
- const l = normalizeGitignorePattern(line);
1065
- const r = normalizeGitignorePattern(required);
1066
- if (!l)
1067
- return false;
1068
- if (l === r)
1069
- return true;
1070
- // A bare directory entry covers every path beneath it, but only when the
1071
- // entry itself is literal — `.monomind/*.json` must not be treated as an
1072
- // ancestor of `.monomind/sessions`.
1073
- return !l.includes('*') && r.startsWith(`${l}/`);
1074
- }
1075
- export async function checkGitignoreCoverage(root = process.cwd()) {
1076
- const gitignorePath = join(root, '.gitignore');
1077
- if (!existsSync(gitignorePath)) {
1078
- // The old hint was `echo ".monomind/\n**/.monomind/" >> .gitignore`, which
1079
- // writes a literal backslash-n (sh/bash echo does not interpret escapes
1080
- // without -e) — one junk line matching nothing. `--fix` now writes the file
1081
- // directly, so point at that rather than at a command to copy-paste.
1082
- return {
1083
- name: 'Gitignore Coverage',
1084
- status: 'warn',
1085
- message: 'No .gitignore found — all monomind runtime paths are unprotected',
1086
- fix: 'monomind doctor --fix (creates .gitignore covering .monomind/ and secrets)',
1087
- };
1088
- }
1089
- if (statSync(gitignorePath).size > MAX_DOCTOR_GITIGNORE_BYTES) {
1090
- return { name: 'Gitignore Coverage', status: 'warn', message: '.gitignore too large to parse' };
1091
- }
1092
- const lines = readFileSync(gitignorePath, 'utf-8')
1093
- .split('\n')
1094
- .map((l) => l.trim())
1095
- .filter((l) => l && !l.startsWith('#'));
1096
- const missing = REQUIRED_GITIGNORE_PATTERNS.filter(({ pattern }) => !lines.some((l) => gitignoreLineCovers(l, pattern)));
1097
- if (missing.length === 0)
1098
- return {
1099
- name: 'Gitignore Coverage',
1100
- status: 'pass',
1101
- message: 'All monomind runtime paths are gitignored',
1102
- };
1103
- const missingList = missing.map((m) => m.pattern).join(', ');
1104
- return {
1105
- name: 'Gitignore Coverage',
1106
- status: 'warn',
1107
- message: `${missing.length} runtime path(s) not in .gitignore: ${missingList}`,
1108
- fix: 'monomind doctor --fix (appends the missing entries)',
1109
- };
1110
- }
1111
- /** Patterns written by `doctor --fix` when there is no .gitignore at all.
1112
- * Covers monomind runtime state plus the standard secrets files, since a repo
1113
- * with no .gitignore has nothing protecting those either. The runtime paths
1114
- * are the specific ones the check requires, never a blanket `.monomind/`:
1115
- * git cannot re-include a file below an ignored directory, so a blanket line
1116
- * disables init's own `.monomind/.gitignore` allow-list (config.yaml,
1117
- * CAPABILITIES.md, org definitions). */
1118
- const GITIGNORE_FIX_PATTERNS = [
1119
- '# monomind runtime state',
1120
- ...REQUIRED_GITIGNORE_PATTERNS.map(({ pattern }) => pattern),
1121
- '',
1122
- '# secrets / local env',
1123
- '.env',
1124
- '.env.*',
1125
- '!.env.example',
1126
- '*.pem',
1127
- '*.key',
1128
- '',
1129
- '# dependencies / build output',
1130
- 'node_modules/',
1131
- 'dist/',
1132
- ];
1133
- /** Creates or appends the missing gitignore entries. Returns true if it wrote.
1134
- * Append-only and idempotent: never rewrites lines the user already has. */
1135
- export async function fixGitignoreCoverage(root = process.cwd()) {
1136
- const gitignorePath = join(root, '.gitignore');
1137
- try {
1138
- if (!existsSync(gitignorePath)) {
1139
- writeFileSync(gitignorePath, `${GITIGNORE_FIX_PATTERNS.join('\n')}\n`, 'utf-8');
1140
- return true;
1141
- }
1142
- if (statSync(gitignorePath).size > MAX_DOCTOR_GITIGNORE_BYTES)
1143
- return false;
1144
- const existing = readFileSync(gitignorePath, 'utf-8');
1145
- const lines = existing
1146
- .split('\n')
1147
- .map((l) => l.trim())
1148
- .filter((l) => l && !l.startsWith('#'));
1149
- const missing = REQUIRED_GITIGNORE_PATTERNS.map((m) => m.pattern).filter((pattern) => !lines.some((l) => gitignoreLineCovers(l, pattern)));
1150
- if (missing.length === 0)
1151
- return false;
1152
- const prefix = existing.endsWith('\n') || existing === '' ? '' : '\n';
1153
- appendFileSync(gitignorePath, `${prefix}\n# monomind runtime state\n${missing.join('\n')}\n`, 'utf-8');
1154
- return true;
1155
- }
1156
- catch {
1157
- return false;
1158
- }
1159
- }
1160
- export async function checkAgentRegistry(opts = {}) {
1161
- try {
1162
- const { buildUnifiedRegistry, computeAgentRoots } = await import('../agents/registry-builder.js');
1163
- const { findProjectRoot, registryPath } = await import('../agents/registry-freshness.js');
1164
- const project = findProjectRoot(process.cwd());
1165
- const cwd = project ?? process.cwd();
1166
- const roots = computeAgentRoots(cwd);
1167
- const write = project !== null && !opts.readOnly;
1168
- if (write)
1169
- mkdirSync(join(cwd, '.monomind'), { recursive: true });
1170
- // Rebuilds fresh in-memory (and, inside a project, refreshes
1171
- // .monomind/registry.json on disk, never replacing a non-empty registry
1172
- // with an empty one) rather than reading a file a separate startup task
1173
- // may also be writing. Outside a project, and in read-only mode (issue
1174
- // #335), nothing is written.
1175
- const registry = buildUnifiedRegistry(roots, write ? registryPath(cwd) : undefined, {
1176
- base: cwd,
1177
- });
1178
- const entries = registry.agents;
1179
- // An extra root (MONOMIND_EXTRA_AGENT_PATHS or a sibling agency-agents dir)
1180
- // wins slug conflicts over .claude/agents — say so instead of hiding it.
1181
- const extras = roots.filter((r) => r.origin === 'extra').map((r) => r.dir);
1182
- // User agents (~/.claude/agents) are indexed too; project agents win.
1183
- const shadowed = registry.shadowed.length;
1184
- const extraNote = `; ${registry.counts.user} from ~/.claude/agents` +
1185
- (shadowed ? ` (${shadowed} shadowed by a project agent)` : '') +
1186
- (extras.length ? `; extra agent roots (win on conflict): ${extras.join(', ')}` : '');
1187
- if (entries.length === 0) {
1188
- return {
1189
- name: 'Agent Registry',
1190
- status: 'warn',
1191
- message: `No agents found under .claude/agents or ~/.claude/agents${extraNote}`,
1192
- fix: 'monomind init (installs agent definitions)',
1193
- };
1194
- }
1195
- let missingSlug = 0, missingName = 0, missingDescription = 0, missingWhenToUse = 0;
1196
- for (const agent of entries) {
1197
- if (!agent.slug)
1198
- missingSlug++;
1199
- if (!agent.name)
1200
- missingName++;
1201
- if (!agent.description)
1202
- missingDescription++;
1203
- // The pick index leads with when_to_use; older installs lack it.
1204
- if (!agent.whenToUse)
1205
- missingWhenToUse++;
1206
- }
1207
- const dupes = registry.duplicates;
1208
- const total = missingSlug + missingName + missingDescription + missingWhenToUse + dupes.length;
1209
- if (total === 0) {
1210
- return {
1211
- name: 'Agent Registry',
1212
- status: 'pass',
1213
- message: `${entries.length} agent(s), all metadata complete${extraNote}`,
1214
- };
1215
- }
1216
- const parts = [
1217
- missingSlug > 0 ? `${missingSlug} missing slug` : null,
1218
- missingName > 0 ? `${missingName} missing name` : null,
1219
- missingDescription > 0 ? `${missingDescription} missing description` : null,
1220
- missingWhenToUse > 0 ? `${missingWhenToUse} missing when_to_use` : null,
1221
- dupes.length > 0
1222
- ? `duplicate slug ${dupes.map((d) => `"${d.slug}" (kept ${d.kept}, dropped ${d.dropped.join(', ')})`).join('; ')}`
1223
- : null,
1224
- ]
1225
- .filter(Boolean)
1226
- .join(', ');
1227
- return {
1228
- name: 'Agent Registry',
1229
- status: 'warn',
1230
- message: `${total} metadata issue(s) across ${entries.length} agent(s): ${parts}${extraNote}`,
1231
- fix: dupes.length
1232
- ? 'Give each duplicated agent a unique `slug:` in its frontmatter'
1233
- : missingWhenToUse > 0
1234
- ? 'monomind init upgrade (refreshes unedited bundled agents; add `when_to_use:` to your own agents in .claude/agents/*.md)'
1235
- : 'Add the missing field(s) to frontmatter in .claude/agents/*.md',
1236
- };
1237
- }
1238
- catch {
1239
- return {
1240
- name: 'Agent Registry',
1241
- status: 'warn',
1242
- message: 'Could not build/parse agent registry',
1243
- };
1244
- }
1245
- }
1246
- export async function checkGuidanceGates() {
1247
- const settingsPath = join(process.cwd(), '.claude', 'settings.json');
1248
- const gatesHandlerPath = join(process.cwd(), '.claude', 'helpers', 'handlers', 'gates-handler.cjs');
1249
- if (!existsSync(gatesHandlerPath)) {
1250
- return {
1251
- name: 'Guidance Gates',
1252
- status: 'warn',
1253
- message: 'gates-handler.cjs not found — enforcement gates not installed',
1254
- fix: 'monomind init (then monomind guidance setup)',
1255
- };
1256
- }
1257
- if (!existsSync(settingsPath)) {
1258
- return {
1259
- name: 'Guidance Gates',
1260
- status: 'warn',
1261
- message: 'gates-handler.cjs present but .claude/settings.json missing',
1262
- fix: 'monomind guidance setup',
1263
- };
1264
- }
1265
- try {
1266
- if (statSync(settingsPath).size > MAX_DOCTOR_CONFIG_BYTES) {
1267
- return {
1268
- name: 'Guidance Gates',
1269
- status: 'warn',
1270
- message: 'settings.json too large to parse',
1271
- };
1272
- }
1273
- const settings = JSON.parse(readFileSync(settingsPath, 'utf-8'));
1274
- const preToolUse = settings?.hooks?.PreToolUse ?? [];
1275
- // Match on capability, not on an exact matcher string. The matcher is a
1276
- // regex alternation that legitimately changes as tools are added — it
1277
- // gained `|NotebookEdit` when that tool turned out to bypass the secret
1278
- // gate — and an `=== 'Write|Edit|MultiEdit'` test reported the gate as
1279
- // INACTIVE the moment it did, telling users their secrets gate was off
1280
- // while it was demonstrably blocking secrets, and sending them to
1281
- // `guidance setup`, which would then add a duplicate entry.
1282
- const hasPreWrite = preToolUse.some((e) => /\bWrite\b/.test(e.matcher ?? '') && e.hooks.some((h) => h.command?.includes('pre-write')));
1283
- const hasPreBash = preToolUse.some((e) => e.matcher === 'Bash' && e.hooks.some((h) => h.command?.includes('pre-bash')));
1284
- if (!hasPreWrite && !hasPreBash)
1285
- return {
1286
- name: 'Guidance Gates',
1287
- status: 'warn',
1288
- message: 'gates-handler.cjs present but no gates registered',
1289
- fix: 'monomind guidance setup',
1290
- };
1291
- if (!hasPreWrite)
1292
- return {
1293
- name: 'Guidance Gates',
1294
- status: 'warn',
1295
- message: 'pre-write hook not registered — secrets gate inactive',
1296
- fix: 'monomind guidance setup',
1297
- };
1298
- if (!hasPreBash)
1299
- return {
1300
- name: 'Guidance Gates',
1301
- status: 'warn',
1302
- message: 'pre-bash hook not registered — destructive-ops gate inactive',
1303
- fix: 'monomind guidance setup',
1304
- };
1305
- return {
1306
- name: 'Guidance Gates',
1307
- status: 'pass',
1308
- message: 'destructive-ops (pre-bash) + secrets (pre-write) gates active',
1309
- };
1310
- }
1311
- catch {
1312
- return {
1313
- name: 'Guidance Gates',
1314
- status: 'warn',
1315
- message: 'Could not parse .claude/settings.json',
1316
- fix: 'monomind guidance setup --force',
1317
- };
1318
- }
1319
- }
1320
- // Workers refresh at session start when output is >6h old — allow a grace
1321
- // window beyond that before flagging staleness.
1322
- const METRICS_FRESHNESS_MS = 12 * 60 * 60 * 1000; // 12 hours
1323
- const MAX_DOCTOR_METRICS_BYTES = 5 * 1024 * 1024;
1324
- function readMetricsJSON(name) {
1325
- try {
1326
- const p = join(process.cwd(), '.monomind', 'metrics', name);
1327
- if (!existsSync(p) || statSync(p).size > MAX_DOCTOR_METRICS_BYTES)
1328
- return null;
1329
- return JSON.parse(readFileSync(p, 'utf-8'));
1330
- }
1331
- catch (e) {
1332
- if (process.env.DEBUG || process.env.MONOMIND_DEBUG)
1333
- console.error(`[readMetricsJSON] failed to read ${name}:`, e);
1334
- return null;
1335
- }
1336
- }
1337
- /**
1338
- * Worker metrics freshness — reports the age of the @monoes/hooks worker
1339
- * output files (written at session start with a 6h staleness gate, or on
1340
- * demand via `monomind hooks worker run <name>`), so missing/stale worker
1341
- * output is visible without digging through .monomind/metrics.
1342
- */
1343
- export async function checkMetricsFreshness() {
1344
- const metricsDir = join(process.cwd(), '.monomind', 'metrics');
1345
- const knownOutputs = [
1346
- 'codebase-map.json',
1347
- 'security-audit.json',
1348
- 'performance.json',
1349
- 'consolidation.json',
1350
- 'ddd-progress.json',
1351
- ];
1352
- if (!existsSync(metricsDir)) {
1353
- return {
1354
- name: 'Worker Metrics',
1355
- status: 'warn',
1356
- message: 'No .monomind/metrics — workers have not run yet (they run at session start)',
1357
- fix: 'monomind hooks worker run map',
1358
- };
1359
- }
1360
- const now = Date.now();
1361
- const fresh = [];
1362
- const stale = [];
1363
- for (const name of knownOutputs) {
1364
- const p = join(metricsDir, name);
1365
- if (!existsSync(p))
1366
- continue;
1367
- try {
1368
- const ageMs = now - statSync(p).mtimeMs;
1369
- if (ageMs <= METRICS_FRESHNESS_MS)
1370
- fresh.push(name);
1371
- else
1372
- stale.push(name);
1373
- }
1374
- catch {
1375
- /* skip unreadable */
1376
- }
1377
- }
1378
- if (fresh.length === 0 && stale.length === 0) {
1379
- return {
1380
- name: 'Worker Metrics',
1381
- status: 'warn',
1382
- message: 'No worker output files found yet (they run at session start)',
1383
- fix: 'monomind hooks worker run map',
1384
- };
1385
- }
1386
- if (stale.length === 0) {
1387
- return {
1388
- name: 'Worker Metrics',
1389
- status: 'pass',
1390
- message: `${fresh.length} metrics file(s) fresh (<12h)`,
1391
- };
1392
- }
1393
- return {
1394
- name: 'Worker Metrics',
1395
- status: 'warn',
1396
- message: `${stale.length} stale (>12h): ${stale.join(', ')}${fresh.length > 0 ? ` — ${fresh.length} fresh` : ''}`,
1397
- fix: 'monomind hooks worker run <name> # map, audit, consolidate, ddd',
1398
- };
1399
- }
1400
- /**
1401
- * Surfaces critical findings from the security-audit worker output.
1402
- */
1403
- export async function checkSecurityAuditFindings() {
1404
- const audit = readMetricsJSON('security-audit.json');
1405
- if (!audit) {
1406
- return {
1407
- name: 'Security Audit',
1408
- status: 'warn',
1409
- message: 'No security-audit.json yet',
1410
- fix: 'monomind hooks worker run audit',
1411
- };
1412
- }
1413
- const riskLevel = (audit.riskLevel || 'low').toLowerCase();
1414
- const recommendations = audit.recommendations || [];
1415
- const priorityTargets = audit.priorityScanTargets || [];
1416
- const criticalCount = recommendations.length +
1417
- (riskLevel === 'high' || riskLevel === 'critical' ? priorityTargets.length : 0);
1418
- if (riskLevel === 'critical' || riskLevel === 'high') {
1419
- return {
1420
- name: 'Security Audit',
1421
- status: 'fail',
1422
- message: `risk=${riskLevel}, ${criticalCount} critical finding(s) — ${priorityTargets.length} priority scan target(s)`,
1423
- fix: 'Review .monomind/metrics/security-audit.json priorityScanTargets and recommendations',
1424
- };
1425
- }
1426
- if (recommendations.length > 0) {
1427
- return {
1428
- name: 'Security Audit',
1429
- status: 'warn',
1430
- message: `risk=${riskLevel}, ${recommendations.length} recommendation(s)`,
1431
- };
1432
- }
1433
- return { name: 'Security Audit', status: 'pass', message: `risk=${riskLevel}, no open findings` };
1434
- }
1435
- /**
1436
- * AutoMem proficiency check — reports memory learning health
1437
- */
1438
- export async function checkMemoryProficiency() {
1439
- try {
1440
- const intelligenceMod = await import('../memory/intelligence.js');
1441
- const stats = intelligenceMod.getMemoryProficiencyStats();
1442
- const bankStats = intelligenceMod.getIntelligenceStats();
1443
- if (stats.totalDecisions === 0) {
1444
- return {
1445
- name: 'Memory Proficiency',
1446
- status: 'pass',
1447
- message: 'AutoMem active — no decisions recorded yet (builds up over sessions)',
1448
- };
1449
- }
1450
- const successPct = Math.round(stats.successRate * 100);
1451
- const hitPct = Math.round(stats.patternHitRate * 100);
1452
- const msg = `${stats.totalDecisions} decisions, ${successPct}% success, ${stats.proficiencyPatterns} patterns, ${hitPct}% hit rate (bank: ${bankStats.patternsLearned} total)`;
1453
- if (stats.successRate < 0.5) {
1454
- return { name: 'Memory Proficiency', status: 'warn', message: msg };
1455
- }
1456
- return { name: 'Memory Proficiency', status: 'pass', message: msg };
1457
- }
1458
- catch {
1459
- return {
1460
- name: 'Memory Proficiency',
1461
- status: 'warn',
1462
- message: 'Could not load intelligence module',
1463
- };
1464
- }
1465
- }
1466
190
  /** AppleDouble sidecars (`._name`) under `.claude/`.
1467
191
  *
1468
192
  * macOS writes these whenever a file carrying extended attributes is copied to
@@ -1543,49 +267,4 @@ export function fixAppleDoubleSidecars(cwd) {
1543
267
  }
1544
268
  return removed;
1545
269
  }
1546
- /** Second Brain document ingestion: whether the "documents" capability is
1547
- * active for this project. Read directly from capabilities.json rather than
1548
- * re-running the directory scan — doctor should report on what's actually
1549
- * activated, not re-detect. */
1550
- function isDocumentsCapabilityActive(cwd) {
1551
- try {
1552
- const capsPath = join(cwd, '.monomind', 'capabilities.json');
1553
- if (!existsSync(capsPath))
1554
- return false;
1555
- const parsed = JSON.parse(readFileSync(capsPath, 'utf8'));
1556
- return Array.isArray(parsed.active) && parsed.active.includes('documents');
1557
- }
1558
- catch {
1559
- return false;
1560
- }
1561
- }
1562
- /** Per-extractor health for Second Brain document ingestion (MEM-8/MEM-9):
1563
- * one check per format family (PDF, DOCX, XLSX/XLS/ODS, PPTX/ODT/ODP/EPUB,
1564
- * legacy DOC/PPT/Pages) instead of a single blanket "documents" check, so
1565
- * `doctor` can report exactly which formats are functional vs broken.
1566
- * No-op (empty array) when the documents capability isn't active — nothing
1567
- * to report if this project doesn't ingest documents. */
1568
- export async function checkDocumentExtractors() {
1569
- if (!isDocumentsCapabilityActive(process.cwd()))
1570
- return [];
1571
- try {
1572
- const { documentsCapability } = await import('../capabilities/cap-documents.js');
1573
- const checks = (await documentsCapability.healthChecks?.()) ?? [];
1574
- return checks.map((c) => ({
1575
- name: c.name,
1576
- status: c.status,
1577
- message: c.message,
1578
- fix: c.hint ?? c.fix,
1579
- }));
1580
- }
1581
- catch (err) {
1582
- return [
1583
- {
1584
- name: 'Document Extractors',
1585
- status: 'warn',
1586
- message: `Check failed: ${err instanceof Error ? err.message : String(err)}`,
1587
- },
1588
- ];
1589
- }
1590
- }
1591
270
  //# sourceMappingURL=doctor-project-checks.js.map