@monoes/monomindcli 2.6.1 → 2.7.1

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/bin/cli.js CHANGED
@@ -72,7 +72,22 @@ if (isMCPMode) {
72
72
  // (or a slow trickle without newlines) cannot OOM-kill the process.
73
73
  const MAX_BUFFER_BYTES = 10 * 1024 * 1024;
74
74
  let buffer = '';
75
+ // Tracks handleMessage() calls still in flight when stdin closes — without
76
+ // this, 'end' calling process.exit(0) unconditionally could kill the process
77
+ // mid-handler and silently drop the response a client is still waiting on
78
+ // (issue #39).
79
+ let inFlight = 0;
80
+ let stdinEnded = false;
75
81
  process.stdin.setEncoding('utf8');
82
+ // process.stdout.write() to a pipe is NOT guaranteed synchronous — for a
83
+ // large payload (e.g. a full tools/list response) the OS write can still be
84
+ // in flight when the call returns. console.log() doesn't expose that, so
85
+ // calling process.exit() right after it can truncate/drop the very response
86
+ // being protected. Wait for the actual flush callback before allowing exit.
87
+ function writeLine(str) {
88
+ return new Promise((resolve) => process.stdout.write(str + '\n', resolve));
89
+ }
90
+
76
91
  process.stdin.on('data', async (chunk) => {
77
92
  buffer += chunk;
78
93
  if (buffer.length > MAX_BUFFER_BYTES) {
@@ -81,38 +96,51 @@ if (isMCPMode) {
81
96
  }
82
97
  let lines = buffer.split('\n');
83
98
  buffer = lines.pop() || '';
99
+ const toProcess = lines.filter((line) => line.trim());
100
+ // Reserve the WHOLE batch synchronously, before any `await` yields control —
101
+ // otherwise 'end' can fire in the gap between two lines of the same chunk
102
+ // (inFlight transiently reads 0 after line 1 finishes but before line 2's
103
+ // own increment runs), exiting mid-batch and dropping the rest silently.
104
+ inFlight += toProcess.length;
84
105
 
85
- for (const line of lines) {
86
- if (line.trim()) {
87
- let parsed;
88
- try {
89
- parsed = JSON.parse(line);
90
- } catch {
91
- console.log(JSON.stringify({
92
- jsonrpc: '2.0',
93
- id: null,
94
- error: { code: -32700, message: 'Parse error' },
95
- }));
96
- continue;
97
- }
98
- try {
99
- const response = await handleMessage(parsed);
100
- if (response) {
101
- console.log(JSON.stringify(response));
102
- }
103
- } catch (error) {
104
- console.log(JSON.stringify({
105
- jsonrpc: '2.0',
106
- id: parsed.id ?? null,
107
- error: { code: -32603, message: error instanceof Error ? error.message : 'Internal error' },
108
- }));
106
+ for (const line of toProcess) {
107
+ let parsed;
108
+ try {
109
+ parsed = JSON.parse(line);
110
+ } catch {
111
+ await writeLine(JSON.stringify({
112
+ jsonrpc: '2.0',
113
+ id: null,
114
+ error: { code: -32700, message: 'Parse error' },
115
+ }));
116
+ inFlight--;
117
+ if (stdinEnded && inFlight === 0) process.exit(0);
118
+ continue;
119
+ }
120
+ try {
121
+ const response = await handleMessage(parsed);
122
+ if (response) {
123
+ await writeLine(JSON.stringify(response));
109
124
  }
125
+ } catch (error) {
126
+ await writeLine(JSON.stringify({
127
+ jsonrpc: '2.0',
128
+ id: parsed.id ?? null,
129
+ error: { code: -32603, message: error instanceof Error ? error.message : 'Internal error' },
130
+ }));
131
+ } finally {
132
+ inFlight--;
133
+ if (stdinEnded && inFlight === 0) process.exit(0);
110
134
  }
111
135
  }
112
136
  });
113
137
 
114
138
  process.stdin.on('end', () => {
115
- process.exit(0);
139
+ stdinEnded = true;
140
+ if (inFlight === 0) { process.exit(0); return; }
141
+ // Bounded safety net: don't hang forever if a handler is genuinely stuck.
142
+ const EXIT_GRACE_MS = 30000;
143
+ setTimeout(() => process.exit(0), EXIT_GRACE_MS).unref();
116
144
  });
117
145
 
118
146
  async function handleMessage(message) {
@@ -1 +1 @@
1
- {"version":3,"file":"mcp-generator.d.ts","sourceRoot":"","sources":["../../../src/init/mcp-generator.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAa,MAAM,YAAY,CAAC;AAqCzD;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,CA+B9D;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,CAG5D;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,EAAE,CAelE;AAED;;GAEG;AACH,wBAAgB,uBAAuB,IAAI;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAW5E"}
1
+ {"version":3,"file":"mcp-generator.d.ts","sourceRoot":"","sources":["../../../src/init/mcp-generator.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAa,MAAM,YAAY,CAAC;AAqCzD;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,CAqC9D;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,CAG5D;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,EAAE,CAelE;AAED;;GAEG;AACH,wBAAgB,uBAAuB,IAAI;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAW5E"}
@@ -49,7 +49,15 @@ export function generateMCPConfig(options) {
49
49
  MONOMIND_TOPOLOGY: options.runtime.topology,
50
50
  MONOMIND_MAX_AGENTS: String(options.runtime.maxAgents),
51
51
  MONOMIND_MEMORY_BACKEND: options.runtime.memoryBackend,
52
- }, { autoStart: config.autoStart });
52
+ }
53
+ // No `autoStart` here: Claude Code's .mcp.json schema for stdio servers
54
+ // is only command/args/env — `autoStart` isn't a field it reads, and
55
+ // nothing in monomind reads it back from this file either (it's a
56
+ // separate, monomind-internal setting stored in .monomind/config.yaml).
57
+ // Writing it here was pure dead-end noise in a file whose schema we
58
+ // don't own — worth removing even though Claude Code likely just
59
+ // ignores unknown fields, since "likely ignores" isn't "verified inert".
60
+ );
53
61
  }
54
62
  // Monograph knowledge graph — built into monomind MCP server since v1.8.0.
55
63
  // Available as mcp__monomind__monograph_build, monograph_query, monograph_suggest, monograph_health.
@@ -1 +1 @@
1
- {"version":3,"file":"mcp-generator.js","sourceRoot":"","sources":["../../../src/init/mcp-generator.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH;;GAEG;AACH,SAAS,SAAS;IAChB,OAAO,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC;AACtC,CAAC;AAED;;;;GAIG;AACH,SAAS,oBAAoB,CAC3B,OAAiB,EACjB,GAA2B,EAC3B,eAAe,GAA4B,EAAE;IAE7C,IAAI,SAAS,EAAE,EAAE,CAAC;QAChB,OAAO;YACL,OAAO,EAAE,KAAK;YACd,IAAI,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC;YACrC,GAAG;YACH,GAAG,eAAe;SACnB,CAAC;IACJ,CAAC;IAED,oDAAoD;IACpD,OAAO;QACL,OAAO,EAAE,KAAK;QACd,IAAI,EAAE,CAAC,IAAI,EAAE,GAAG,OAAO,CAAC;QACxB,GAAG;QACH,GAAG,eAAe;KACnB,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,iBAAiB,CAAC,OAAoB;IACpD,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC;IAC3B,MAAM,UAAU,GAA2B,EAAE,CAAC;IAE9C,MAAM,MAAM,GAAG;QACb,0BAA0B,EAAE,OAAO;KACpC,CAAC;IAEF,6BAA6B;IAC7B,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;QACpB,UAAU,CAAC,UAAU,CAAC,GAAG,oBAAoB,CAC3C,CAAC,iBAAiB,EAAE,KAAK,EAAE,OAAO,CAAC,EACnC;YACE,GAAG,MAAM;YACT,aAAa,EAAE,IAAI;YACnB,sBAAsB,EAAE,MAAM;YAC9B,iBAAiB,EAAE,OAAO,CAAC,OAAO,CAAC,QAAQ;YAC3C,mBAAmB,EAAE,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC;YACtD,uBAAuB,EAAE,OAAO,CAAC,OAAO,CAAC,aAAa;SACvD,EACD,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,CAChC,CAAC;IACJ,CAAC;IAID,2EAA2E;IAC3E,qGAAqG;IACrG,qFAAqF;IAErF,OAAO,EAAE,UAAU,EAAE,CAAC;AACxB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,OAAoB;IAClD,MAAM,MAAM,GAAG,iBAAiB,CAAC,OAAO,CAAC,CAAC;IAC1C,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;AACzC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,mBAAmB,CAAC,OAAoB;IACtD,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC;IAE3B,IAAI,SAAS,EAAE,EAAE,CAAC;QAChB,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;YACpB,QAAQ,CAAC,IAAI,CAAC,oEAAoE,CAAC,CAAC;QACtF,CAAC;IACH,CAAC;SAAM,CAAC;QACN,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;YACpB,QAAQ,CAAC,IAAI,CAAC,6DAA6D,CAAC,CAAC;QAC/E,CAAC;IACH,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,uBAAuB;IACrC,IAAI,SAAS,EAAE,EAAE,CAAC;QAChB,OAAO;YACL,QAAQ,EAAE,SAAS;YACnB,IAAI,EAAE,8DAA8D;SACrE,CAAC;IACJ,CAAC;IACD,OAAO;QACL,QAAQ,EAAE,OAAO,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO;QAC3D,IAAI,EAAE,sCAAsC;KAC7C,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"mcp-generator.js","sourceRoot":"","sources":["../../../src/init/mcp-generator.ts"],"names":[],"mappings":"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"}
@@ -5,6 +5,18 @@
5
5
  * All monograph_* tools are backed by @monoes/monograph package.
6
6
  */
7
7
  import type { MCPTool } from './types.js';
8
+ /**
9
+ * BM25 ranks purely on literal keyword overlap, so a doc/concept node whose
10
+ * prose happens to mention task-related words (e.g. a report mentioning
11
+ * "retry" constants) can outrank the actual code symbol the task is really
12
+ * about, which is conceptually related but lexically different (issue #38).
13
+ * Prefer code-symbol hits (Function/Class/Method/...) over doc-type hits
14
+ * (Document/Concept/Section/...) when there are any, falling back to the
15
+ * full hit list so a genuinely doc-only task still gets results.
16
+ */
17
+ export declare function preferSymbolHits<T extends {
18
+ label: string;
19
+ }>(hits: T[], symbolLabels: ReadonlySet<string>): T[];
8
20
  /**
9
21
  * Full tool list regardless of gating — used by the graphify compat shims,
10
22
  * which must resolve targets (e.g. monograph_community) even when the
@@ -1 +1 @@
1
- {"version":3,"file":"monograph-tools.d.ts","sourceRoot":"","sources":["../../../src/mcp-tools/monograph-tools.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AA03E1C;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,EAAE,OAAO,EAAuD,CAAC;AAE/F,eAAO,MAAM,cAAc,EAAE,OAAO,EAAsD,CAAC"}
1
+ {"version":3,"file":"monograph-tools.d.ts","sourceRoot":"","sources":["../../../src/mcp-tools/monograph-tools.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAue1C;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,SAAS;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,EAAE,IAAI,EAAE,CAAC,EAAE,EAAE,YAAY,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAG/G;AAu5DD;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,EAAE,OAAO,EAAuD,CAAC;AAE/F,eAAO,MAAM,cAAc,EAAE,OAAO,EAAsD,CAAC"}
@@ -493,6 +493,19 @@ const monographSurprisesTool = {
493
493
  },
494
494
  };
495
495
  // ── monograph_suggest ─────────────────────────────────────────────────────────
496
+ /**
497
+ * BM25 ranks purely on literal keyword overlap, so a doc/concept node whose
498
+ * prose happens to mention task-related words (e.g. a report mentioning
499
+ * "retry" constants) can outrank the actual code symbol the task is really
500
+ * about, which is conceptually related but lexically different (issue #38).
501
+ * Prefer code-symbol hits (Function/Class/Method/...) over doc-type hits
502
+ * (Document/Concept/Section/...) when there are any, falling back to the
503
+ * full hit list so a genuinely doc-only task still gets results.
504
+ */
505
+ export function preferSymbolHits(hits, symbolLabels) {
506
+ const symbolHits = hits.filter(h => symbolLabels.has(h.label));
507
+ return symbolHits.length > 0 ? symbolHits : hits;
508
+ }
496
509
  const monographSuggestTool = {
497
510
  name: 'monograph_suggest',
498
511
  description: 'Get graph-topology-derived questions to explore the codebase. Pass task= to score by task relevance via BM25/FTS5.',
@@ -554,7 +567,9 @@ const monographSuggestTool = {
554
567
  let hitIds = [];
555
568
  if (task) {
556
569
  const hits = await hybridQuery(db, task, { limit: 20 });
557
- hitIds = [...new Set(hits.map(h => h.id))];
570
+ const { SYMBOL_NODE_LABELS } = await import('@monoes/monograph');
571
+ const relevantHits = preferSymbolHits(hits, SYMBOL_NODE_LABELS);
572
+ hitIds = [...new Set(relevantHits.map(h => h.id))];
558
573
  if (hitIds.length === 0) {
559
574
  return text('No suggestions for this task. Run monograph_build first or try a different query.' + stalenessAnnotation);
560
575
  }