@monoes/monomindcli 2.16.0 → 2.16.2

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 (233) hide show
  1. package/.claude/helpers/handlers/edit-handler.cjs +14 -12
  2. package/.claude/helpers/handlers/route-handler.cjs +22 -3
  3. package/.claude/helpers/hook-handler.cjs +17 -2
  4. package/.claude/helpers/jev-picker.cjs +1 -1
  5. package/.claude/helpers/monograph-freshen.cjs +31 -113
  6. package/.claude/helpers/utils/monograph-resolve.cjs +497 -0
  7. package/.claude/helpers/utils/monograph.cjs +8 -51
  8. package/dist/src/capabilities/cap-code.d.ts.map +1 -1
  9. package/dist/src/capabilities/cap-code.js +10 -5
  10. package/dist/src/capabilities/cap-code.js.map +1 -1
  11. package/dist/src/capabilities/manager.d.ts +2 -1
  12. package/dist/src/capabilities/manager.d.ts.map +1 -1
  13. package/dist/src/capabilities/manager.js +4 -1
  14. package/dist/src/capabilities/manager.js.map +1 -1
  15. package/dist/src/commands/doctor-hook-monograph-checks.d.ts +8 -0
  16. package/dist/src/commands/doctor-hook-monograph-checks.d.ts.map +1 -0
  17. package/dist/src/commands/doctor-hook-monograph-checks.js +79 -0
  18. package/dist/src/commands/doctor-hook-monograph-checks.js.map +1 -0
  19. package/dist/src/commands/doctor-project-checks.js +1 -1
  20. package/dist/src/commands/doctor-project-checks.js.map +1 -1
  21. package/dist/src/commands/doctor.d.ts.map +1 -1
  22. package/dist/src/commands/doctor.js +4 -1
  23. package/dist/src/commands/doctor.js.map +1 -1
  24. package/dist/src/commands/init-wizard.d.ts.map +1 -1
  25. package/dist/src/commands/init-wizard.js +2 -0
  26. package/dist/src/commands/init-wizard.js.map +1 -1
  27. package/dist/src/commands/init.d.ts.map +1 -1
  28. package/dist/src/commands/init.js +25 -26
  29. package/dist/src/commands/init.js.map +1 -1
  30. package/dist/src/commands/monograph.d.ts.map +1 -1
  31. package/dist/src/commands/monograph.js +16 -1
  32. package/dist/src/commands/monograph.js.map +1 -1
  33. package/dist/src/commands/org-observe.d.ts.map +1 -1
  34. package/dist/src/commands/org-observe.js +5 -9
  35. package/dist/src/commands/org-observe.js.map +1 -1
  36. package/dist/src/commands/org-run-end.d.ts +26 -0
  37. package/dist/src/commands/org-run-end.d.ts.map +1 -0
  38. package/dist/src/commands/org-run-end.js +40 -0
  39. package/dist/src/commands/org-run-end.js.map +1 -0
  40. package/dist/src/commands/org.d.ts +2 -1
  41. package/dist/src/commands/org.d.ts.map +1 -1
  42. package/dist/src/commands/org.js +36 -15
  43. package/dist/src/commands/org.js.map +1 -1
  44. package/dist/src/commands/search-universal.d.ts.map +1 -1
  45. package/dist/src/commands/search-universal.js +1 -2
  46. package/dist/src/commands/search-universal.js.map +1 -1
  47. package/dist/src/init/executor.d.ts.map +1 -1
  48. package/dist/src/init/executor.js +7 -0
  49. package/dist/src/init/executor.js.map +1 -1
  50. package/dist/src/init/init-memory.d.ts +14 -0
  51. package/dist/src/init/init-memory.d.ts.map +1 -0
  52. package/dist/src/init/init-memory.js +44 -0
  53. package/dist/src/init/init-memory.js.map +1 -0
  54. package/dist/src/init/types.d.ts +13 -0
  55. package/dist/src/init/types.d.ts.map +1 -1
  56. package/dist/src/init/types.js.map +1 -1
  57. package/dist/src/init/upgrade.d.ts.map +1 -1
  58. package/dist/src/init/upgrade.js +9 -0
  59. package/dist/src/init/upgrade.js.map +1 -1
  60. package/dist/src/orgrt/agent-runner.d.ts +7 -0
  61. package/dist/src/orgrt/agent-runner.d.ts.map +1 -1
  62. package/dist/src/orgrt/agent-runner.js +5 -1
  63. package/dist/src/orgrt/agent-runner.js.map +1 -1
  64. package/dist/src/orgrt/antigravity-runner.d.ts.map +1 -1
  65. package/dist/src/orgrt/antigravity-runner.js +8 -14
  66. package/dist/src/orgrt/antigravity-runner.js.map +1 -1
  67. package/dist/src/orgrt/bash-timeout.d.ts +14 -0
  68. package/dist/src/orgrt/bash-timeout.d.ts.map +1 -0
  69. package/dist/src/orgrt/bash-timeout.js +18 -0
  70. package/dist/src/orgrt/bash-timeout.js.map +1 -0
  71. package/dist/src/orgrt/block-recheck.d.ts +30 -0
  72. package/dist/src/orgrt/block-recheck.d.ts.map +1 -0
  73. package/dist/src/orgrt/block-recheck.js +81 -0
  74. package/dist/src/orgrt/block-recheck.js.map +1 -0
  75. package/dist/src/orgrt/checkpoint-ops.d.ts.map +1 -1
  76. package/dist/src/orgrt/checkpoint-ops.js +2 -1
  77. package/dist/src/orgrt/checkpoint-ops.js.map +1 -1
  78. package/dist/src/orgrt/checkpoint.d.ts +10 -2
  79. package/dist/src/orgrt/checkpoint.d.ts.map +1 -1
  80. package/dist/src/orgrt/checkpoint.js +8 -2
  81. package/dist/src/orgrt/checkpoint.js.map +1 -1
  82. package/dist/src/orgrt/codex-runner.d.ts.map +1 -1
  83. package/dist/src/orgrt/codex-runner.js +8 -14
  84. package/dist/src/orgrt/codex-runner.js.map +1 -1
  85. package/dist/src/orgrt/completion-gate.d.ts +10 -0
  86. package/dist/src/orgrt/completion-gate.d.ts.map +1 -1
  87. package/dist/src/orgrt/completion-gate.js +40 -2
  88. package/dist/src/orgrt/completion-gate.js.map +1 -1
  89. package/dist/src/orgrt/copilot-runner.d.ts.map +1 -1
  90. package/dist/src/orgrt/copilot-runner.js +7 -10
  91. package/dist/src/orgrt/copilot-runner.js.map +1 -1
  92. package/dist/src/orgrt/crush-runner.d.ts.map +1 -1
  93. package/dist/src/orgrt/crush-runner.js +7 -10
  94. package/dist/src/orgrt/crush-runner.js.map +1 -1
  95. package/dist/src/orgrt/cumulative-meter.d.ts +26 -0
  96. package/dist/src/orgrt/cumulative-meter.d.ts.map +1 -0
  97. package/dist/src/orgrt/cumulative-meter.js +46 -0
  98. package/dist/src/orgrt/cumulative-meter.js.map +1 -0
  99. package/dist/src/orgrt/daemon.d.ts +6 -2
  100. package/dist/src/orgrt/daemon.d.ts.map +1 -1
  101. package/dist/src/orgrt/daemon.js +31 -31
  102. package/dist/src/orgrt/daemon.js.map +1 -1
  103. package/dist/src/orgrt/dashboard-health.d.ts +54 -0
  104. package/dist/src/orgrt/dashboard-health.d.ts.map +1 -0
  105. package/dist/src/orgrt/dashboard-health.js +143 -0
  106. package/dist/src/orgrt/dashboard-health.js.map +1 -0
  107. package/dist/src/orgrt/decisions.d.ts +7 -2
  108. package/dist/src/orgrt/decisions.d.ts.map +1 -1
  109. package/dist/src/orgrt/decisions.js +47 -11
  110. package/dist/src/orgrt/decisions.js.map +1 -1
  111. package/dist/src/orgrt/forwarder.d.ts +2 -1
  112. package/dist/src/orgrt/forwarder.d.ts.map +1 -1
  113. package/dist/src/orgrt/forwarder.js +87 -27
  114. package/dist/src/orgrt/forwarder.js.map +1 -1
  115. package/dist/src/orgrt/grok-runner.d.ts.map +1 -1
  116. package/dist/src/orgrt/grok-runner.js +7 -12
  117. package/dist/src/orgrt/grok-runner.js.map +1 -1
  118. package/dist/src/orgrt/hermes-runner.d.ts.map +1 -1
  119. package/dist/src/orgrt/hermes-runner.js +9 -15
  120. package/dist/src/orgrt/hermes-runner.js.map +1 -1
  121. package/dist/src/orgrt/kimicode-runner.d.ts +1 -1
  122. package/dist/src/orgrt/kimicode-runner.d.ts.map +1 -1
  123. package/dist/src/orgrt/kimicode-runner.js +9 -15
  124. package/dist/src/orgrt/kimicode-runner.js.map +1 -1
  125. package/dist/src/orgrt/mailbox.d.ts +14 -0
  126. package/dist/src/orgrt/mailbox.d.ts.map +1 -1
  127. package/dist/src/orgrt/mailbox.js +31 -1
  128. package/dist/src/orgrt/mailbox.js.map +1 -1
  129. package/dist/src/orgrt/opencode-runner.d.ts.map +1 -1
  130. package/dist/src/orgrt/opencode-runner.js +7 -13
  131. package/dist/src/orgrt/opencode-runner.js.map +1 -1
  132. package/dist/src/orgrt/org-harness-tools.d.ts +8 -0
  133. package/dist/src/orgrt/org-harness-tools.d.ts.map +1 -0
  134. package/dist/src/orgrt/org-harness-tools.js +21 -0
  135. package/dist/src/orgrt/org-harness-tools.js.map +1 -0
  136. package/dist/src/orgrt/pi-rpc-runner.d.ts.map +1 -1
  137. package/dist/src/orgrt/pi-rpc-runner.js +9 -13
  138. package/dist/src/orgrt/pi-rpc-runner.js.map +1 -1
  139. package/dist/src/orgrt/pi-runner.d.ts.map +1 -1
  140. package/dist/src/orgrt/pi-runner.js +7 -11
  141. package/dist/src/orgrt/pi-runner.js.map +1 -1
  142. package/dist/src/orgrt/policy-git.d.ts +5 -1
  143. package/dist/src/orgrt/policy-git.d.ts.map +1 -1
  144. package/dist/src/orgrt/policy-git.js +56 -29
  145. package/dist/src/orgrt/policy-git.js.map +1 -1
  146. package/dist/src/orgrt/policy.d.ts +7 -0
  147. package/dist/src/orgrt/policy.d.ts.map +1 -1
  148. package/dist/src/orgrt/policy.js +10 -1
  149. package/dist/src/orgrt/policy.js.map +1 -1
  150. package/dist/src/orgrt/prompt-vars.d.ts +8 -1
  151. package/dist/src/orgrt/prompt-vars.d.ts.map +1 -1
  152. package/dist/src/orgrt/prompt-vars.js +60 -11
  153. package/dist/src/orgrt/prompt-vars.js.map +1 -1
  154. package/dist/src/orgrt/qwen-rpc-runner.d.ts.map +1 -1
  155. package/dist/src/orgrt/qwen-rpc-runner.js +9 -14
  156. package/dist/src/orgrt/qwen-rpc-runner.js.map +1 -1
  157. package/dist/src/orgrt/qwen-runner.d.ts.map +1 -1
  158. package/dist/src/orgrt/qwen-runner.js +7 -12
  159. package/dist/src/orgrt/qwen-runner.js.map +1 -1
  160. package/dist/src/orgrt/report-budget.d.ts +4 -0
  161. package/dist/src/orgrt/report-budget.d.ts.map +1 -0
  162. package/dist/src/orgrt/report-budget.js +12 -0
  163. package/dist/src/orgrt/report-budget.js.map +1 -0
  164. package/dist/src/orgrt/reporting.d.ts +4 -0
  165. package/dist/src/orgrt/reporting.d.ts.map +1 -1
  166. package/dist/src/orgrt/reporting.js +8 -2
  167. package/dist/src/orgrt/reporting.js.map +1 -1
  168. package/dist/src/orgrt/role-sandbox.d.ts +4 -1
  169. package/dist/src/orgrt/role-sandbox.d.ts.map +1 -1
  170. package/dist/src/orgrt/role-sandbox.js +24 -10
  171. package/dist/src/orgrt/role-sandbox.js.map +1 -1
  172. package/dist/src/orgrt/role-trace.d.ts +27 -0
  173. package/dist/src/orgrt/role-trace.d.ts.map +1 -0
  174. package/dist/src/orgrt/role-trace.js +28 -0
  175. package/dist/src/orgrt/role-trace.js.map +1 -0
  176. package/dist/src/orgrt/sandbox-deny-write.d.ts +52 -0
  177. package/dist/src/orgrt/sandbox-deny-write.d.ts.map +1 -0
  178. package/dist/src/orgrt/sandbox-deny-write.js +89 -0
  179. package/dist/src/orgrt/sandbox-deny-write.js.map +1 -0
  180. package/dist/src/orgrt/sandbox-fault.d.ts +50 -0
  181. package/dist/src/orgrt/sandbox-fault.d.ts.map +1 -0
  182. package/dist/src/orgrt/sandbox-fault.js +139 -0
  183. package/dist/src/orgrt/sandbox-fault.js.map +1 -0
  184. package/dist/src/orgrt/session.d.ts +8 -6
  185. package/dist/src/orgrt/session.d.ts.map +1 -1
  186. package/dist/src/orgrt/session.js +121 -67
  187. package/dist/src/orgrt/session.js.map +1 -1
  188. package/dist/src/orgrt/task-dag.d.ts +17 -3
  189. package/dist/src/orgrt/task-dag.d.ts.map +1 -1
  190. package/dist/src/orgrt/task-dag.js +16 -5
  191. package/dist/src/orgrt/task-dag.js.map +1 -1
  192. package/dist/src/orgrt/tool-fence.d.ts +21 -3
  193. package/dist/src/orgrt/tool-fence.d.ts.map +1 -1
  194. package/dist/src/orgrt/tool-fence.js +45 -7
  195. package/dist/src/orgrt/tool-fence.js.map +1 -1
  196. package/dist/src/orgrt/tool-providers.d.ts +4 -0
  197. package/dist/src/orgrt/tool-providers.d.ts.map +1 -1
  198. package/dist/src/orgrt/tool-providers.js +10 -0
  199. package/dist/src/orgrt/tool-providers.js.map +1 -1
  200. package/dist/src/orgrt/types.d.ts +19 -0
  201. package/dist/src/orgrt/types.d.ts.map +1 -1
  202. package/dist/src/orgrt/types.js +29 -0
  203. package/dist/src/orgrt/types.js.map +1 -1
  204. package/dist/src/orgrt/vercel-runner.d.ts +23 -0
  205. package/dist/src/orgrt/vercel-runner.d.ts.map +1 -1
  206. package/dist/src/orgrt/vercel-runner.js +2 -26
  207. package/dist/src/orgrt/vercel-runner.js.map +1 -1
  208. package/dist/src/platform-adapters/merge.d.ts +11 -0
  209. package/dist/src/platform-adapters/merge.d.ts.map +1 -1
  210. package/dist/src/platform-adapters/merge.js +28 -0
  211. package/dist/src/platform-adapters/merge.js.map +1 -1
  212. package/dist/src/platform-adapters/mutation.d.ts +3 -0
  213. package/dist/src/platform-adapters/mutation.d.ts.map +1 -1
  214. package/dist/src/platform-adapters/mutation.js +43 -0
  215. package/dist/src/platform-adapters/mutation.js.map +1 -1
  216. package/dist/src/platform-adapters/operations.d.ts +1 -0
  217. package/dist/src/platform-adapters/operations.d.ts.map +1 -1
  218. package/dist/src/platform-adapters/operations.js +25 -9
  219. package/dist/src/platform-adapters/operations.js.map +1 -1
  220. package/dist/src/platform-adapters/renderers/skills.d.ts.map +1 -1
  221. package/dist/src/platform-adapters/renderers/skills.js +11 -2
  222. package/dist/src/platform-adapters/renderers/skills.js.map +1 -1
  223. package/dist/src/platform-adapters/shared-surface.d.ts +34 -0
  224. package/dist/src/platform-adapters/shared-surface.d.ts.map +1 -0
  225. package/dist/src/platform-adapters/shared-surface.js +89 -0
  226. package/dist/src/platform-adapters/shared-surface.js.map +1 -0
  227. package/dist/src/platform-adapters/types.d.ts +7 -0
  228. package/dist/src/platform-adapters/types.d.ts.map +1 -1
  229. package/dist/src/utils/resource-governor.d.ts.map +1 -1
  230. package/dist/src/utils/resource-governor.js +35 -2
  231. package/dist/src/utils/resource-governor.js.map +1 -1
  232. package/dist/tsconfig.tsbuildinfo +1 -1
  233. package/package.json +3 -3
@@ -5,7 +5,7 @@
5
5
 
6
6
  const path = require('path');
7
7
  const fs = require('fs');
8
- const { claimLock } = require('../utils/fs-helpers.cjs');
8
+ const { claimRebuildLock, startRebuild } = require('../utils/monograph-resolve.cjs');
9
9
 
10
10
  module.exports = {
11
11
  handle: async function(hCtx) {
@@ -102,18 +102,20 @@ module.exports = {
102
102
  // check and spawns its own rebuild + ua-enrich child (see the
103
103
  // 896-orphan-process incident fixed in control-start.cjs). claimLock
104
104
  // mirrors that same TOCTOU-safe stale-lock-breaking pattern.
105
- if (claimLock(lockFile, COOLDOWN_MS)) {
105
+ // #328: the rebuild child now owns the lock (its PID replaces ours)
106
+ // and removes it when done, so a running rebuild blocks a second one
107
+ // and a lock left by an exited process is stale after the cooldown.
108
+ if (claimRebuildLock(lockFile, COOLDOWN_MS, 10 * 60 * 1000)) {
106
109
  var { spawn: spawnRebuild } = require('child_process');
107
- var rebuildScript = "import { buildAsync } from '@monoes/monograph'; await buildAsync(" + JSON.stringify(CWD) + ");";
108
- var graphDir = path.join(CWD, '.monomind', 'graph');
109
- var logPath = path.join(graphDir, 'build.log');
110
- var logFd;
111
- try { logFd = fs.openSync(logPath, 'a'); } catch(e) { logFd = 'ignore'; }
112
- var child = spawnRebuild(process.execPath, ['--input-type=module', '--eval', rebuildScript], {
113
- detached: true, stdio: ['ignore', logFd, logFd], cwd: CWD,
114
- });
115
- child.unref();
116
- console.log('[MONOGRAPH] Incremental rebuild triggered for ' + path.basename(editedFile2));
110
+ // Resolves @monoes/monograph to an absolute path (project, global
111
+ // npm, npx cache) and imports it by file URL — a bare-specifier
112
+ // import only finds a project-local install. Falls back to
113
+ // `monomind monograph build`; with neither, it records one deduped
114
+ // failure line instead of an ERR_MODULE_NOT_FOUND per edit.
115
+ var rebuild = startRebuild(CWD, { lockPath: lockFile });
116
+ if (rebuild.mode !== 'none') {
117
+ console.log('[MONOGRAPH] Incremental rebuild triggered for ' + path.basename(editedFile2));
118
+ }
117
119
 
118
120
  // Option C: fire ua-enrich.mjs in background after monograph rebuild
119
121
  var uaEnrichScript = path.join(CWD, 'scripts', 'ua-enrich.mjs');
@@ -77,6 +77,17 @@ function _loadJevPicker() {
77
77
  return _jevPicker;
78
78
  }
79
79
 
80
+ // hook-handler.cjs force-exits every hook at this deadline. A configured Jev
81
+ // may hold the route hook for its whole window, so that hook gets the window
82
+ // plus 1.5 s to persist its route; without Jev it keeps the 5 s exit.
83
+ var HOOK_EXIT_MS = 5000;
84
+ function routeDeadlineMs(env) {
85
+ env = env || process.env;
86
+ var jp = _loadJevPicker();
87
+ if (!jp || jp.resolveProviders(env).length === 0) return HOOK_EXIT_MS;
88
+ return Math.max(HOOK_EXIT_MS, jp.resolveHookTimeoutMs(env) + 1500);
89
+ }
90
+
80
91
  var JEV_BREAKER_MS = 5 * 60 * 1000;
81
92
  function _jevBreakerPath(CWD) { return path.join(CWD, '.monomind', 'jev-breaker.json'); }
82
93
  function _jevBreakerOpen(CWD) {
@@ -136,6 +147,7 @@ function _applyJevPick(result, jev) {
136
147
 
137
148
 
138
149
  module.exports = {
150
+ routeDeadlineMs: routeDeadlineMs,
139
151
  handle: async function(hCtx) {
140
152
  var hookStart = Date.now();
141
153
  var prompt = hCtx.prompt;
@@ -283,9 +295,10 @@ module.exports = {
283
295
  // This ENHANCES the keyword route — it only overrides when the
284
296
  // embedding match is meaningfully more confident, and never blocks or
285
297
  // delays routing beyond a 2s budget (fails silently otherwise).
286
- // A slow or failed Jev attempt may already have spent most of the 5 s
287
- // hook exit, so the budget shrinks to keep the route persist below in time.
288
- var intelBudgetMs = Math.min(2000, Math.max(0, 4500 - (Date.now() - hookStart)));
298
+ // A slow or failed Jev attempt may already have spent most of the time
299
+ // before the hook exit, so the budget shrinks to keep the route persist
300
+ // below in time (500 ms before routeDeadlineMs).
301
+ var intelBudgetMs = Math.min(2000, Math.max(0, routeDeadlineMs(process.env) - 500 - (Date.now() - hookStart)));
289
302
  if (result.routingMethod !== 'jev' && intelBudgetMs > 0) try {
290
303
  var intelResult = await Promise.race([
291
304
  (async function() {
@@ -965,6 +978,12 @@ module.exports = {
965
978
  advisoryLog('[MONOGRAPH] ' + nodeCount + ' nodes. Call mcp__monomind__monograph_suggest to find relevant files.');
966
979
  hCtx._recordGraphTelemetry('preresolve_miss');
967
980
  }
981
+ } else if (!hintDb || nodeCount > 100) {
982
+ // #328: the gate and these hints switch off when the hooks can't
983
+ // open the graph or it is past the 50-commit limit. Say so once per
984
+ // cause (not per prompt), even under MONOMIND_HOOK_QUIET.
985
+ var offNotice = require('../utils/monograph-resolve.cjs').graphOffNoticeOnce(CWD, hintDb || null);
986
+ if (offNotice) console.log(offNotice);
968
987
  }
969
988
  } catch(e) {}
970
989
 
@@ -239,10 +239,23 @@ async function readStdin() {
239
239
  });
240
240
  }
241
241
 
242
+ // Every hook force-exits after 5 s, except the route hook while a Jev decision
243
+ // model is configured: it may wait out the Jev window (route-handler.cjs
244
+ // routeDeadlineMs). The security gates never load the route handler.
245
+ function safetyTimeoutMs(cmd, env) {
246
+ if (cmd !== 'route') return 5000;
247
+ try {
248
+ return require('./handlers/route-handler.cjs').routeDeadlineMs(env || process.env);
249
+ } catch (e) {
250
+ return 5000;
251
+ }
252
+ }
253
+
242
254
  async function main() {
243
255
  // Global safety timeout: hooks must NEVER hang (#1530, #1531)
256
+ var safetyMs = safetyTimeoutMs(command);
244
257
  var safetyTimer = setTimeout(function() {
245
- process.stderr.write("[WARN] Hook handler global timeout (5s), forcing exit\n");
258
+ process.stderr.write("[WARN] Hook handler global timeout (" + (safetyMs / 1000) + "s), forcing exit\n");
246
259
  // If a security gate was still pending when the timer fired, the operation
247
260
  // was never evaluated — fail CLOSED rather than letting a hang act as an
248
261
  // allow. (`_securityGateCompleted` is `var`-hoisted, so it reads as
@@ -256,7 +269,7 @@ async function main() {
256
269
  process.exit(2);
257
270
  }
258
271
  process.exit(0);
259
- }, 5000);
272
+ }, safetyMs);
260
273
  safetyTimer.unref();
261
274
 
262
275
  let stdinData = '';
@@ -940,6 +953,8 @@ if (command && handlers[command]) {
940
953
  }
941
954
  }
942
955
 
956
+ module.exports = { safetyTimeoutMs: safetyTimeoutMs };
957
+
943
958
  // Only run when executed directly (every real hook event spawns this file as a
944
959
  // process). A require() — e.g. from tests verifying it loads — must not exit.
945
960
  if (require.main === module) {
@@ -33,7 +33,7 @@ var NONE_ID = '__none__';
33
33
  var OFF_VALUES = ['0', 'off', 'false', 'no'];
34
34
  var ON_VALUES = ['1', 'on', 'true', 'yes'];
35
35
  var DEFAULT_HOOK_TIMEOUT_MS = 1500;
36
- var MAX_HOOK_TIMEOUT_MS = 4000;
36
+ var MAX_HOOK_TIMEOUT_MS = 10000;
37
37
 
38
38
  class JevError extends Error {
39
39
  constructor(message, provider, status) {
@@ -5,91 +5,38 @@
5
5
  if (String(process.env.MONOMIND_SDK_AGENT || '') === '1') process.exit(0);
6
6
  const path = require('path');
7
7
  const fs = require('fs');
8
- const { spawn } = require('child_process');
9
- const { pathToFileURL } = require('url');
10
- const { claimLock } = require('./utils/fs-helpers.cjs');
8
+ const mg = require('./utils/monograph-resolve.cjs');
11
9
 
12
10
  const projectDir = process.env.CLAUDE_PROJECT_DIR || process.cwd();
13
11
  const graphDir = path.join(projectDir, '.monomind', 'graph');
14
12
  fs.mkdirSync(graphDir, { recursive: true });
13
+ const quiet = String(process.env.MONOMIND_HOOK_QUIET || '') === '1';
15
14
 
16
- const logPath = path.join(graphDir, 'build.log');
17
- let logFd;
18
- try { logFd = fs.openSync(logPath, 'a'); } catch { logFd = 'ignore'; }
19
-
20
- // Resolve the monograph entry point — searches several common layouts
21
- function resolveMonographEntry(dir) {
22
- // pnpm virtual store — the reliable pre-built copy not affected by workspace symlinks
23
- const pnpmStore = (() => {
24
- try {
25
- const storeBase = path.join(dir, 'node_modules', '.pnpm');
26
- if (!fs.existsSync(storeBase)) return null;
27
- const entries = fs.readdirSync(storeBase).filter(e => e.startsWith('@monoes+monograph@'));
28
- for (const e of entries.sort().reverse()) { // newest version first
29
- const p = path.join(storeBase, e, 'node_modules', '@monoes', 'monograph', 'dist', 'src', 'index.js');
30
- if (fs.existsSync(p)) return p;
31
- }
32
- } catch {}
33
- return null;
34
- })();
35
-
36
- const candidates = [
37
- // Monorepo workspace build FIRST — it carries unpublished fixes; the pnpm store
38
- // holds registry tarballs that can lag behind the workspace source.
39
- path.join(dir, 'packages', '@monomind', 'monograph', 'dist', 'src', 'index.js'),
40
- // Installed as a flat dependency (follows workspace symlinks when linked)
41
- path.join(dir, 'node_modules', '@monoes', 'monograph', 'dist', 'src', 'index.js'),
42
- path.join(dir, 'node_modules', '@monomind', 'monograph', 'dist', 'src', 'index.js'),
43
- // Monorepo: monomind root is the monograph package
44
- path.join(dir, 'dist', 'src', 'index.js'),
45
- // pnpm store registry copy
46
- pnpmStore,
47
- ].filter(Boolean);
48
- for (const c of candidates) {
49
- if (fs.existsSync(c)) return c;
15
+ // Same resolution as the post-edit rebuild and every other hook consumer:
16
+ // project install, global npm root, then the copy bundled with the monomind
17
+ // CLI in the npx cache (#328).
18
+ const resolved = mg.resolveMonographEntry(projectDir);
19
+ const native = resolved ? mg.checkNative(resolved, projectDir) : null;
20
+ const canRebuild = resolved ? native.ok : !!mg.findMonomindBin(projectDir);
21
+ if (!canRebuild) {
22
+ if (!resolved) {
23
+ mg.recordRebuildFailure(projectDir, 'unresolvable',
24
+ '@monoes/monograph not found in the project, the global npm root or the npx cache', mg.GLOBAL_FIX);
25
+ } else {
26
+ mg.recordRebuildFailure(projectDir, 'native', 'better-sqlite3 failed to load: ' + native.error, native.fix);
50
27
  }
51
-
52
- // Global npm / homebrew install of @monomind/cli (most common for npx/global users).
53
- // Resolved lazily — shells out to `npm root -g` (~65ms) — so that cost is only paid
54
- // when none of the faster candidates above matched.
55
- try {
56
- const { execSync } = require('child_process');
57
- const globalRoot = execSync('npm root -g', { encoding: 'utf-8', timeout: 5000 }).trim();
58
- const p = path.join(globalRoot, '@monoes', 'monograph', 'dist', 'src', 'index.js');
59
- if (fs.existsSync(p)) return p;
60
- } catch {}
61
-
62
- return null;
63
- }
64
-
65
- const entryPoint = resolveMonographEntry(projectDir);
66
- if (!entryPoint) {
67
- console.error('[graph] @monoes/monograph not found — skipping build');
28
+ // SessionStart stdout reaches the session; stderr of this hook does not.
29
+ const warning = mg.sessionWarning(mg.diagnose(projectDir));
30
+ if (warning) console.log(warning);
31
+ else if (!resolved) console.error('[graph] @monoes/monograph not found — skipping build');
68
32
  process.exit(0);
69
33
  }
70
34
 
71
35
  // Skip if index is already fresh — don't waste CPU on every session start
72
36
  const dbPath = path.join(projectDir, '.monomind', 'monograph.db');
73
- if (fs.existsSync(dbPath)) {
74
- try {
75
- const Database = require(require.resolve('better-sqlite3', { paths: [path.dirname(entryPoint)] }));
76
- const db = new Database(dbPath, { readonly: true, timeout: 5000 });
77
- try {
78
- const row = db.prepare("SELECT value FROM index_meta WHERE key='last_commit_hash'").get();
79
- if (row && row.value && /^[0-9a-f]{7,40}$/i.test(row.value)) {
80
- const { execFileSync } = require('child_process');
81
- const out = execFileSync('git', ['rev-list', '--count', row.value + '..HEAD'], {
82
- cwd: projectDir, encoding: 'utf-8', timeout: 2000, stdio: ['pipe', 'pipe', 'pipe']
83
- }).trim();
84
- const behind = parseInt(out, 10);
85
- if (behind === 0) {
86
- if (String(process.env.MONOMIND_HOOK_QUIET || '') !== '1') console.log('[graph] index is fresh — skipping rebuild');
87
- db.close();
88
- process.exit(0);
89
- }
90
- }
91
- } finally { try { db.close(); } catch {} }
92
- } catch { /* can't check — proceed with build */ }
37
+ if (fs.existsSync(dbPath) && mg.commitsBehind(projectDir, mg.readIndexedCommit(dbPath)) === 0) {
38
+ if (!quiet) console.log('[graph] index is fresh — skipping rebuild');
39
+ process.exit(0);
93
40
  }
94
41
 
95
42
  // Skip if another build is already in progress (avoids SQLite BUSY on concurrent init + session-start)
@@ -99,51 +46,22 @@ if (fs.existsSync(dbPath)) {
99
46
  // both pass the check and both spawn a detached rebuild child. claimLock
100
47
  // uses the same TOCTOU-safe stale-lock-break (atomic rename-to-claim) as
101
48
  // control-start.cjs's spawn lock (see P2-25) — a lock older than 5 minutes
102
- // is treated as abandoned and safely reclaimed.
49
+ // is treated as abandoned and safely reclaimed, and so is one whose build
50
+ // process has exited.
103
51
  const lockPath = path.join(graphDir, 'build.lock');
104
- if (!claimLock(lockPath, 5 * 60 * 1000)) {
105
- if (String(process.env.MONOMIND_HOOK_QUIET || '') !== '1') console.log('[graph] build already in progress — skipping');
52
+ if (!mg.claimRebuildLock(lockPath, 0, 5 * 60 * 1000)) {
53
+ if (!quiet) console.log('[graph] build already in progress — skipping');
106
54
  process.exit(0);
107
55
  }
108
56
 
109
- // Spawn a detached node process to run buildAsync from @monoes/monograph (ESM).
110
- // After the build, VACUUM the DB if it has >50% bloat (reclaim space from
111
- // delete/insert churn; opens are ~5x faster on a tight DB).
112
- const dbPathStr = JSON.stringify(path.join(projectDir, '.monomind', 'monograph.db'));
113
- const script = `
114
- import { buildAsync } from ${JSON.stringify(pathToFileURL(entryPoint).href)};
115
- import { unlinkSync, statSync } from 'fs';
116
- import { execFileSync } from 'child_process';
117
- try {
118
- await buildAsync(${JSON.stringify(projectDir)});
119
- // Vacuum if bloat ratio is high — keeps openDb fast over time.
120
- try {
121
- const dbPath = ${dbPathStr};
122
- const fileMB = statSync(dbPath).size / 1024 / 1024;
123
- // execFileSync with array argv — no shell involved, so a project
124
- // directory name containing '"' or '$(...)' cannot break out and
125
- // execute arbitrary commands (P3-8).
126
- const liveMB = parseInt(
127
- execFileSync('sqlite3', [dbPath, 'SELECT SUM(pgsize)/1024/1024 FROM dbstat;'],
128
- { encoding: 'utf-8', timeout: 30000 }).trim(), 10);
129
- if (fileMB > 100 && liveMB / fileMB < 0.5) {
130
- execFileSync('sqlite3', [dbPath, 'VACUUM;'], { timeout: 120000 });
131
- }
132
- } catch (_) {}
133
- } finally {
134
- try { unlinkSync(${JSON.stringify(lockPath)}); } catch {}
135
- try { unlinkSync(${JSON.stringify(path.join(graphDir, 'build.pid'))}); } catch {}
136
- }`;
137
- const child = spawn(process.execPath, ['--input-type=module', '--eval', script], {
138
- detached: true,
139
- stdio: ['ignore', logFd, logFd],
140
- cwd: projectDir,
141
- });
142
- child.unref();
57
+ // Detached rebuild; after the build, VACUUM the DB if it has >50% bloat
58
+ // (reclaim space from delete/insert churn; opens are ~5x faster on a tight DB).
59
+ const pidPath = path.join(graphDir, 'build.pid');
60
+ const started = mg.startRebuild(projectDir, { resolved, lockPath, cleanup: [pidPath], vacuum: true });
143
61
 
144
62
  // Track PID so control-stop.cjs can kill it on session exit
145
63
  try {
146
- fs.writeFileSync(path.join(graphDir, 'build.pid'), String(child.pid), 'utf-8');
64
+ if (started.pid) fs.writeFileSync(pidPath, String(started.pid), 'utf-8');
147
65
  } catch { /* best-effort */ }
148
66
 
149
- if (String(process.env.MONOMIND_HOOK_QUIET || '') !== '1') console.log('[graph] background build started for ' + projectDir);
67
+ if (!quiet) console.log('[graph] background build started for ' + projectDir);