monomind 2.1.6 → 2.1.7

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "monomind",
3
- "version": "2.1.6",
3
+ "version": "2.1.7",
4
4
  "description": "Open-source CLI extension for Claude Code. Adds an MCP server with a codebase knowledge graph, persistent memory, multi-agent coordination, and reusable slash commands. MIT licensed, runs locally, no data leaves your machine.",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -136,6 +136,39 @@ module.exports = {
136
136
  console.log('[STALE_HELPERS] Refreshed ' + healed.length + ' helper(s) from bundled version: ' + healed.join(', '));
137
137
  }
138
138
  }
139
+
140
+ // Fallback for pure `npx monomind@latest ...` usage — the local scan above
141
+ // only finds a bundled copy via node_modules or a global npm install, but a
142
+ // per-invocation `npx` run leaves no reliably-discoverable copy there (each
143
+ // invocation caches under a hashed, non-predictable ~/.npm/_npx/<hash>/ dir
144
+ // that this process has no correct way to pick the newest of without risking
145
+ // healing "backward" to some older cached version). `npx monomind@latest`
146
+ // itself already resolves this correctly (it always fetches/uses latest),
147
+ // and `doctor --fix` reuses the exact same bundled-package resolution that
148
+ // `init upgrade` uses — so shell out to it instead of re-solving the same
149
+ // problem here. Rate-limited to once per 6h (mirrors the metrics-worker
150
+ // staleness gate below) and fully non-blocking: spawned detached+unref with
151
+ // stdio ignored, session start never waits on it, and a fresh session or
152
+ // hook a few seconds later just picks up whatever it left behind.
153
+ try {
154
+ var _healCheckPath = path.join(CWD, '.monomind', 'helpers-heal-check.json');
155
+ var _lastHealCheck = 0;
156
+ try { _lastHealCheck = JSON.parse(fs.readFileSync(_healCheckPath, 'utf-8')).ts || 0; } catch (_) {}
157
+ var HEAL_CHECK_STALE_MS = 6 * 60 * 60 * 1000; // 6 hours
158
+ if (Date.now() - _lastHealCheck > HEAL_CHECK_STALE_MS) {
159
+ fs.mkdirSync(path.dirname(_healCheckPath), { recursive: true });
160
+ try { fs.writeFileSync(_healCheckPath, JSON.stringify({ ts: Date.now() }), 'utf-8'); } catch (_) {}
161
+ var _spawn = require('child_process').spawn;
162
+ var _child = _spawn('npx', ['-y', 'monomind@latest', 'doctor', '--fix', '--component', 'helpers'], {
163
+ cwd: CWD,
164
+ detached: true,
165
+ stdio: 'ignore',
166
+ env: process.env,
167
+ });
168
+ _child.on('error', function() {}); // offline / npx unavailable — silently skip
169
+ _child.unref();
170
+ }
171
+ } catch (e) { /* non-fatal — background self-heal is best-effort */ }
139
172
  }
140
173
  } catch (e) { /* non-fatal */ }
141
174
 
@@ -2,7 +2,7 @@
2
2
  * Doctor — project/monomind health checks
3
3
  * Config, memory, API keys, MCP, monograph, helpers, routing, gates, gitignore, worker metrics
4
4
  */
5
- import { existsSync, readFileSync, statSync, mkdirSync, copyFileSync } from 'fs';
5
+ import { existsSync, readFileSync, readdirSync, statSync, mkdirSync, copyFileSync } from 'fs';
6
6
  import { join, dirname } from 'path';
7
7
  import { fileURLToPath } from 'url';
8
8
  import { execSync } from 'child_process';
@@ -234,21 +234,47 @@ function _resolveBundledHelper(relativePath) {
234
234
  return null;
235
235
  }
236
236
  }
237
+ // Top-level critical helpers, plus every file bundled under handlers/ and utils/
238
+ // (capture-handler.cjs, gates-handler.cjs, route-handler.cjs, session-handler.cjs,
239
+ // etc. — where most actual hook-behavior fixes live). Subdir membership is
240
+ // discovered from the bundled package itself, not hardcoded, so a helper added
241
+ // upstream is picked up automatically instead of silently never syncing.
242
+ function _allTrackedHelperNames() {
243
+ const names = ['hook-handler.cjs', 'statusline.cjs', 'router.cjs', 'graphify-freshen.cjs'];
244
+ for (const sub of ['handlers', 'utils']) {
245
+ const bundledSubDir = _resolveBundledHelper(join('.claude', 'helpers', sub));
246
+ if (!bundledSubDir)
247
+ continue;
248
+ try {
249
+ for (const f of readdirSync(bundledSubDir)) {
250
+ if (f.startsWith('._'))
251
+ continue;
252
+ if (statSync(join(bundledSubDir, f)).isDirectory())
253
+ continue;
254
+ names.push(join(sub, f));
255
+ }
256
+ }
257
+ catch { /* skip */ }
258
+ }
259
+ return names;
260
+ }
237
261
  async function _detectStaleHelpers() {
238
262
  const stale = [];
239
263
  const missing = [];
240
- const helpers = ['hook-handler.cjs', 'statusline.cjs', 'router.cjs', 'graphify-freshen.cjs'];
241
264
  const crypto = await import('node:crypto');
242
- for (const name of helpers) {
243
- const local = join(process.cwd(), '.claude', 'helpers', name);
244
- if (!existsSync(local) || statSync(local).size > MAX_DOCTOR_HELPER_BYTES)
245
- continue;
265
+ for (const name of _allTrackedHelperNames()) {
246
266
  const bundled = _resolveBundledHelper(join('.claude', 'helpers', name));
247
- if (!bundled) {
248
- missing.push(name);
267
+ if (!bundled || statSync(bundled).size > MAX_DOCTOR_HELPER_BYTES) {
268
+ if (!bundled)
269
+ missing.push(name);
249
270
  continue;
250
271
  }
251
- if (statSync(bundled).size > MAX_DOCTOR_HELPER_BYTES)
272
+ const local = join(process.cwd(), '.claude', 'helpers', name);
273
+ if (!existsSync(local)) {
274
+ stale.push(name);
275
+ continue;
276
+ } // bundled has it, project doesn't — needs creating
277
+ if (statSync(local).size > MAX_DOCTOR_HELPER_BYTES)
252
278
  continue;
253
279
  try {
254
280
  const hashLocal = crypto.createHash('sha256').update(readFileSync(local)).digest('hex');
@@ -268,6 +294,7 @@ export async function fixStaleHelpers() {
268
294
  const bundled = _resolveBundledHelper(join('.claude', 'helpers', name));
269
295
  if (bundled) {
270
296
  try {
297
+ mkdirSync(dirname(local), { recursive: true });
271
298
  copyFileSync(bundled, local);
272
299
  fixed++;
273
300
  }
@@ -19,7 +19,7 @@ export const doctorCommand = {
19
19
  name: 'doctor',
20
20
  description: 'System diagnostics and health checks',
21
21
  options: [
22
- { name: 'fix', short: 'f', description: 'Show fix commands for issues', type: 'boolean', default: false },
22
+ { name: 'fix', short: 'f', description: 'Apply local fixes (helper files, monoes tool shims) and show fix commands for the rest', type: 'boolean', default: false },
23
23
  { name: 'install', short: 'i', description: 'Auto-install missing dependencies (Claude Code CLI)', type: 'boolean', default: false },
24
24
  {
25
25
  name: 'component', short: 'c',
@@ -122,9 +122,16 @@ export const doctorCommand = {
122
122
  spinner.stop();
123
123
  output.writeln(output.error('Failed to run health checks'));
124
124
  }
125
- if (autoInstall) {
125
+ // `--fix` applies the lightweight, local, no-network fixes (helper files,
126
+ // monoes CLI tool shims) — its description says "show fix commands" but
127
+ // silently doing nothing for these two beyond printing a hint is a worse
128
+ // outcome than just fixing them, and copying a bundled file locally is
129
+ // nothing like installing a package. `--install` additionally covers the
130
+ // Claude Code CLI, which is a real install (network fetch + binary setup)
131
+ // — kept opt-in separately so `--fix` alone never triggers that.
132
+ if (autoInstall || showFix) {
126
133
  const claudeResult = results.find(r => r.name === 'Claude Code CLI');
127
- if (claudeResult && claudeResult.status !== 'pass') {
134
+ if (autoInstall && claudeResult && claudeResult.status !== 'pass') {
128
135
  if (await installClaudeCode()) {
129
136
  const newCheck = await checkClaudeCode();
130
137
  const idx = results.findIndex(r => r.name === 'Claude Code CLI');
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@monoes/monomindcli",
3
- "version": "2.1.6",
3
+ "version": "2.1.7",
4
4
  "type": "module",
5
5
  "description": "CLI engine for Monomind — an open-source MCP server that extends Claude Code with a codebase knowledge graph (tree-sitter + SQLite), persistent memory, multi-agent task coordination, and session hooks. MIT licensed, fully local.",
6
6
  "main": "dist/src/index.js",