@noesis-brain/mcp-server 2.1.0 → 2.1.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/dist/agent/runner.d.ts +4 -0
- package/dist/agent/runner.d.ts.map +1 -1
- package/dist/agent/runner.js +99 -11
- package/dist/agent/runner.js.map +1 -1
- package/dist/tools/backlog.d.ts.map +1 -1
- package/dist/tools/backlog.js +11 -7
- package/dist/tools/backlog.js.map +1 -1
- package/dist/tools/index.d.ts +40 -0
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +96 -70
- package/dist/tools/index.js.map +1 -1
- package/package.json +1 -1
- package/scripts/noesis-capture-watcher.mjs +51 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@noesis-brain/mcp-server",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.1",
|
|
4
4
|
"description": "Official MCP server for Noesis — the markdown notes manager for Claude Code. Search, sync, and manage your .md knowledge base via 19 AI-powered tools.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -88,6 +88,13 @@ const stripAnsi = (s) => String(s == null ? '' : s).replace(ANSI_RE, '');
|
|
|
88
88
|
const GIT_CRLF_WARN_RE = /warning: in the working copy of '[^']*', LF will be replaced by CRLF the next time Git touches it\.?\s*/g;
|
|
89
89
|
const stripCmdNoise = (s) => String(s == null ? '' : s).replace(GIT_CRLF_WARN_RE, '');
|
|
90
90
|
|
|
91
|
+
// Some Windows console tools (`wsl --status`, certain `reg`/`netsh` invocations)
|
|
92
|
+
// write UTF-16LE to a piped stdout; captured as UTF-8 that leaves NUL bytes
|
|
93
|
+
// interleaved through the text. Postgres rejects NUL in text columns outright,
|
|
94
|
+
// and render() joins the whole note into one string before the cloud push, so
|
|
95
|
+
// a single stray NUL anywhere fails the ENTIRE push, not just that one entry.
|
|
96
|
+
const stripNulBytes = (s) => String(s == null ? '' : s).replace(/\u0000/g, '');
|
|
97
|
+
|
|
91
98
|
// Drop Claude Code image placeholders ("[Image #1]", "[Image: source: <path>]")
|
|
92
99
|
// that carry no readable signal in a text capture.
|
|
93
100
|
const stripImageRefs = (s) =>
|
|
@@ -315,8 +322,8 @@ function frontmatter(meta) {
|
|
|
315
322
|
|
|
316
323
|
function resultText(res) {
|
|
317
324
|
const c = res?.content;
|
|
318
|
-
if (typeof c === 'string') return stripCmdNoise(stripAnsi(c));
|
|
319
|
-
if (Array.isArray(c)) return stripCmdNoise(stripAnsi(c.filter((b) => b && b.type === 'text').map((b) => b.text).join(' ')));
|
|
325
|
+
if (typeof c === 'string') return stripNulBytes(stripCmdNoise(stripAnsi(c)));
|
|
326
|
+
if (Array.isArray(c)) return stripNulBytes(stripCmdNoise(stripAnsi(c.filter((b) => b && b.type === 'text').map((b) => b.text).join(' '))));
|
|
320
327
|
return '';
|
|
321
328
|
}
|
|
322
329
|
|
|
@@ -330,6 +337,46 @@ function blockquote(text) {
|
|
|
330
337
|
return text.split('\n').map((l) => (l.length ? `> ${l}` : '>')).join('\n');
|
|
331
338
|
}
|
|
332
339
|
|
|
340
|
+
// Returns the exact CommonMark closing fence needed to balance `text` if it ends
|
|
341
|
+
// inside an open fenced code block (``` or ~~~), or '' if none is open. Mirrors
|
|
342
|
+
// the fence-parity rules in frontend/src/utils/codeFences.ts.
|
|
343
|
+
function unterminatedFenceCloser(text) {
|
|
344
|
+
let openChar = '';
|
|
345
|
+
let openLen = 0;
|
|
346
|
+
for (const raw of text.split('\n')) {
|
|
347
|
+
const trimmed = raw.trim();
|
|
348
|
+
const m = /^(`{3,}|~{3,})(.*)$/.exec(trimmed);
|
|
349
|
+
if (!m) continue;
|
|
350
|
+
const char = m[1][0];
|
|
351
|
+
const len = m[1].length;
|
|
352
|
+
const info = m[2].trim();
|
|
353
|
+
if (!openChar) {
|
|
354
|
+
openChar = char;
|
|
355
|
+
openLen = len;
|
|
356
|
+
} else if (char === openChar && len >= openLen && info === '') {
|
|
357
|
+
openChar = '';
|
|
358
|
+
openLen = 0;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
return openChar ? openChar.repeat(openLen) : '';
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// Truncates `text` to at most `maxLen` characters, closing any fenced code block
|
|
365
|
+
// left dangling by the cut. An unclosed ``` fence would otherwise mark the rest
|
|
366
|
+
// of the note as code (per CommonMark), silently swallowing every later heading
|
|
367
|
+
// from both the TOC panel and the rendered body until EOF.
|
|
368
|
+
//
|
|
369
|
+
// The fence check runs even when THIS function doesn't truncate: Claude Code's
|
|
370
|
+
// own transcript can already contain a tool result that IT truncated upstream
|
|
371
|
+
// (its own "… (truncated)" marker, mid-fence, before this script ever sees the
|
|
372
|
+
// text) -- observed in the wild at ~11968 chars, under this function's 12000
|
|
373
|
+
// cap. Closing only on our own cut would miss that case entirely.
|
|
374
|
+
function truncateSafely(text, maxLen) {
|
|
375
|
+
const cut = text.length > maxLen ? `${text.slice(0, maxLen)}\n\n… (truncated)` : text;
|
|
376
|
+
const closer = unterminatedFenceCloser(cut);
|
|
377
|
+
return closer ? `${cut}\n${closer}` : cut;
|
|
378
|
+
}
|
|
379
|
+
|
|
333
380
|
function isSystemInjection(text) {
|
|
334
381
|
const t = text.trimStart();
|
|
335
382
|
return t.startsWith('<system-reminder') || t.startsWith('[SYSTEM NOTIFICATION') ||
|
|
@@ -415,7 +462,7 @@ function renderPlanBlock(toolUse, resultMap) {
|
|
|
415
462
|
const body = txt
|
|
416
463
|
.replace(/<\/details>/gi, '</details>') // never let an inner tag close our wrapper
|
|
417
464
|
.replace(/^(#{1,6})\s+(.*)$/gm, '**$2**'); // demote headings so they don't pollute the note outline
|
|
418
|
-
const capped = body
|
|
465
|
+
const capped = truncateSafely(body, 12000);
|
|
419
466
|
lines.push('', '<details>', '<summary>Plan / approval</summary>', '', capped, '', '</details>', '');
|
|
420
467
|
} else {
|
|
421
468
|
lines.push(' - ↳ _(awaiting approval)_');
|
|
@@ -428,8 +475,7 @@ function renderUserPrompt(turn, text, entry, queued = false) {
|
|
|
428
475
|
const imageOnly = !clean;
|
|
429
476
|
const heading = imageOnly ? '(image)' : (firstWords(clean, 9) || '(empty)');
|
|
430
477
|
const ts = tsStamp(entry.timestamp);
|
|
431
|
-
const body = imageOnly ? '_(image attachment)_'
|
|
432
|
-
: (clean.length > 3000 ? `${clean.slice(0, 3000)}\n\n… (truncated)` : clean);
|
|
478
|
+
const body = imageOnly ? '_(image attachment)_' : truncateSafely(clean, 3000);
|
|
433
479
|
const parts = [`### ${turn}. ${heading}`];
|
|
434
480
|
const meta = [ts, queued ? '_(queued while working)_' : ''].filter(Boolean).join(' · ');
|
|
435
481
|
if (meta) parts.push(`*${meta}*`);
|