monomind 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/package.json +3 -3
- package/packages/@monomind/cli/bin/cli.js +53 -25
- package/packages/@monomind/cli/dist/src/init/mcp-generator.js +9 -1
- package/packages/@monomind/cli/dist/src/mcp-tools/monograph-tools.d.ts +12 -0
- package/packages/@monomind/cli/dist/src/mcp-tools/monograph-tools.js +16 -1
- package/packages/@monomind/cli/dist/src/orgrt/daemon.js +1 -1
- package/packages/@monomind/cli/dist/src/orgrt/types.d.ts +48 -0
- package/packages/@monomind/cli/dist/src/orgrt/types.js +4 -0
- package/packages/@monomind/cli/package.json +5 -5
- package/scripts/growth-content-calendar.mjs +30 -7
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "monomind",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.7.1",
|
|
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",
|
|
@@ -62,8 +62,8 @@
|
|
|
62
62
|
},
|
|
63
63
|
"dependencies": {
|
|
64
64
|
"@anthropic-ai/claude-agent-sdk": "^0.3.207",
|
|
65
|
-
"@monoes/monobrowse": "^1.0.
|
|
66
|
-
"@monoes/monograph": "^1.5.
|
|
65
|
+
"@monoes/monobrowse": "^1.0.6",
|
|
66
|
+
"@monoes/monograph": "^1.5.1",
|
|
67
67
|
"@noble/ed25519": "^2.1.0",
|
|
68
68
|
"mammoth": "^1.12.0",
|
|
69
69
|
"pdf-parse": "^2.4.5",
|
|
@@ -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
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
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
|
-
|
|
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) {
|
|
@@ -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
|
-
}
|
|
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.
|
|
@@ -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
|
|
@@ -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
|
-
|
|
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
|
}
|
|
@@ -118,7 +118,7 @@ export class OrgDaemon {
|
|
|
118
118
|
const runtime = { mailbox, policy, status: 'running', done: Promise.resolve() };
|
|
119
119
|
const sessionOpts = {
|
|
120
120
|
org: name, role, bus, policy, mailbox, cwd, def,
|
|
121
|
-
maxTurns: def.run_config.max_turns_per_message,
|
|
121
|
+
maxTurns: role.max_turns_per_message ?? def.run_config.max_turns_per_message,
|
|
122
122
|
deliver: (from, to, subject, body) => this.deliver(name, from, to, subject, body),
|
|
123
123
|
askHuman: (r, question) => this.askHuman(name, r, question),
|
|
124
124
|
onComplete: role.id === bossRole.id
|
|
@@ -97,6 +97,10 @@ export declare const RoleSchema: z.ZodObject<{
|
|
|
97
97
|
webAllow: z.ZodOptional<z.ZodOptional<z.ZodArray<z.ZodString, "many">>>;
|
|
98
98
|
maxTokens: z.ZodOptional<z.ZodOptional<z.ZodNumber>>;
|
|
99
99
|
}, z.ZodTypeAny, "passthrough">>>;
|
|
100
|
+
/** Per-role override of run_config.max_turns_per_message — roles that legitimately
|
|
101
|
+
* need many more turns per message (e.g. a developer doing sequential build/fix/verify
|
|
102
|
+
* cycles) than others (e.g. docs, pm) shouldn't be forced onto one global budget. */
|
|
103
|
+
max_turns_per_message: z.ZodOptional<z.ZodNumber>;
|
|
100
104
|
}, "passthrough", z.ZodTypeAny, z.objectOutputType<{
|
|
101
105
|
id: z.ZodString;
|
|
102
106
|
title: z.ZodDefault<z.ZodString>;
|
|
@@ -154,6 +158,10 @@ export declare const RoleSchema: z.ZodObject<{
|
|
|
154
158
|
webAllow: z.ZodOptional<z.ZodOptional<z.ZodArray<z.ZodString, "many">>>;
|
|
155
159
|
maxTokens: z.ZodOptional<z.ZodOptional<z.ZodNumber>>;
|
|
156
160
|
}, z.ZodTypeAny, "passthrough">>>;
|
|
161
|
+
/** Per-role override of run_config.max_turns_per_message — roles that legitimately
|
|
162
|
+
* need many more turns per message (e.g. a developer doing sequential build/fix/verify
|
|
163
|
+
* cycles) than others (e.g. docs, pm) shouldn't be forced onto one global budget. */
|
|
164
|
+
max_turns_per_message: z.ZodOptional<z.ZodNumber>;
|
|
157
165
|
}, z.ZodTypeAny, "passthrough">, z.objectInputType<{
|
|
158
166
|
id: z.ZodString;
|
|
159
167
|
title: z.ZodDefault<z.ZodString>;
|
|
@@ -211,6 +219,10 @@ export declare const RoleSchema: z.ZodObject<{
|
|
|
211
219
|
webAllow: z.ZodOptional<z.ZodOptional<z.ZodArray<z.ZodString, "many">>>;
|
|
212
220
|
maxTokens: z.ZodOptional<z.ZodOptional<z.ZodNumber>>;
|
|
213
221
|
}, z.ZodTypeAny, "passthrough">>>;
|
|
222
|
+
/** Per-role override of run_config.max_turns_per_message — roles that legitimately
|
|
223
|
+
* need many more turns per message (e.g. a developer doing sequential build/fix/verify
|
|
224
|
+
* cycles) than others (e.g. docs, pm) shouldn't be forced onto one global budget. */
|
|
225
|
+
max_turns_per_message: z.ZodOptional<z.ZodNumber>;
|
|
214
226
|
}, z.ZodTypeAny, "passthrough">>;
|
|
215
227
|
export declare const OrgDefSchema: z.ZodObject<{
|
|
216
228
|
name: z.ZodString;
|
|
@@ -305,6 +317,10 @@ export declare const OrgDefSchema: z.ZodObject<{
|
|
|
305
317
|
webAllow: z.ZodOptional<z.ZodOptional<z.ZodArray<z.ZodString, "many">>>;
|
|
306
318
|
maxTokens: z.ZodOptional<z.ZodOptional<z.ZodNumber>>;
|
|
307
319
|
}, z.ZodTypeAny, "passthrough">>>;
|
|
320
|
+
/** Per-role override of run_config.max_turns_per_message — roles that legitimately
|
|
321
|
+
* need many more turns per message (e.g. a developer doing sequential build/fix/verify
|
|
322
|
+
* cycles) than others (e.g. docs, pm) shouldn't be forced onto one global budget. */
|
|
323
|
+
max_turns_per_message: z.ZodOptional<z.ZodNumber>;
|
|
308
324
|
}, "passthrough", z.ZodTypeAny, z.objectOutputType<{
|
|
309
325
|
id: z.ZodString;
|
|
310
326
|
title: z.ZodDefault<z.ZodString>;
|
|
@@ -362,6 +378,10 @@ export declare const OrgDefSchema: z.ZodObject<{
|
|
|
362
378
|
webAllow: z.ZodOptional<z.ZodOptional<z.ZodArray<z.ZodString, "many">>>;
|
|
363
379
|
maxTokens: z.ZodOptional<z.ZodOptional<z.ZodNumber>>;
|
|
364
380
|
}, z.ZodTypeAny, "passthrough">>>;
|
|
381
|
+
/** Per-role override of run_config.max_turns_per_message — roles that legitimately
|
|
382
|
+
* need many more turns per message (e.g. a developer doing sequential build/fix/verify
|
|
383
|
+
* cycles) than others (e.g. docs, pm) shouldn't be forced onto one global budget. */
|
|
384
|
+
max_turns_per_message: z.ZodOptional<z.ZodNumber>;
|
|
365
385
|
}, z.ZodTypeAny, "passthrough">, z.objectInputType<{
|
|
366
386
|
id: z.ZodString;
|
|
367
387
|
title: z.ZodDefault<z.ZodString>;
|
|
@@ -419,6 +439,10 @@ export declare const OrgDefSchema: z.ZodObject<{
|
|
|
419
439
|
webAllow: z.ZodOptional<z.ZodOptional<z.ZodArray<z.ZodString, "many">>>;
|
|
420
440
|
maxTokens: z.ZodOptional<z.ZodOptional<z.ZodNumber>>;
|
|
421
441
|
}, z.ZodTypeAny, "passthrough">>>;
|
|
442
|
+
/** Per-role override of run_config.max_turns_per_message — roles that legitimately
|
|
443
|
+
* need many more turns per message (e.g. a developer doing sequential build/fix/verify
|
|
444
|
+
* cycles) than others (e.g. docs, pm) shouldn't be forced onto one global budget. */
|
|
445
|
+
max_turns_per_message: z.ZodOptional<z.ZodNumber>;
|
|
422
446
|
}, z.ZodTypeAny, "passthrough">>, "many">;
|
|
423
447
|
}, "passthrough", z.ZodTypeAny, z.objectOutputType<{
|
|
424
448
|
name: z.ZodString;
|
|
@@ -513,6 +537,10 @@ export declare const OrgDefSchema: z.ZodObject<{
|
|
|
513
537
|
webAllow: z.ZodOptional<z.ZodOptional<z.ZodArray<z.ZodString, "many">>>;
|
|
514
538
|
maxTokens: z.ZodOptional<z.ZodOptional<z.ZodNumber>>;
|
|
515
539
|
}, z.ZodTypeAny, "passthrough">>>;
|
|
540
|
+
/** Per-role override of run_config.max_turns_per_message — roles that legitimately
|
|
541
|
+
* need many more turns per message (e.g. a developer doing sequential build/fix/verify
|
|
542
|
+
* cycles) than others (e.g. docs, pm) shouldn't be forced onto one global budget. */
|
|
543
|
+
max_turns_per_message: z.ZodOptional<z.ZodNumber>;
|
|
516
544
|
}, "passthrough", z.ZodTypeAny, z.objectOutputType<{
|
|
517
545
|
id: z.ZodString;
|
|
518
546
|
title: z.ZodDefault<z.ZodString>;
|
|
@@ -570,6 +598,10 @@ export declare const OrgDefSchema: z.ZodObject<{
|
|
|
570
598
|
webAllow: z.ZodOptional<z.ZodOptional<z.ZodArray<z.ZodString, "many">>>;
|
|
571
599
|
maxTokens: z.ZodOptional<z.ZodOptional<z.ZodNumber>>;
|
|
572
600
|
}, z.ZodTypeAny, "passthrough">>>;
|
|
601
|
+
/** Per-role override of run_config.max_turns_per_message — roles that legitimately
|
|
602
|
+
* need many more turns per message (e.g. a developer doing sequential build/fix/verify
|
|
603
|
+
* cycles) than others (e.g. docs, pm) shouldn't be forced onto one global budget. */
|
|
604
|
+
max_turns_per_message: z.ZodOptional<z.ZodNumber>;
|
|
573
605
|
}, z.ZodTypeAny, "passthrough">, z.objectInputType<{
|
|
574
606
|
id: z.ZodString;
|
|
575
607
|
title: z.ZodDefault<z.ZodString>;
|
|
@@ -627,6 +659,10 @@ export declare const OrgDefSchema: z.ZodObject<{
|
|
|
627
659
|
webAllow: z.ZodOptional<z.ZodOptional<z.ZodArray<z.ZodString, "many">>>;
|
|
628
660
|
maxTokens: z.ZodOptional<z.ZodOptional<z.ZodNumber>>;
|
|
629
661
|
}, z.ZodTypeAny, "passthrough">>>;
|
|
662
|
+
/** Per-role override of run_config.max_turns_per_message — roles that legitimately
|
|
663
|
+
* need many more turns per message (e.g. a developer doing sequential build/fix/verify
|
|
664
|
+
* cycles) than others (e.g. docs, pm) shouldn't be forced onto one global budget. */
|
|
665
|
+
max_turns_per_message: z.ZodOptional<z.ZodNumber>;
|
|
630
666
|
}, z.ZodTypeAny, "passthrough">>, "many">;
|
|
631
667
|
}, z.ZodTypeAny, "passthrough">, z.objectInputType<{
|
|
632
668
|
name: z.ZodString;
|
|
@@ -721,6 +757,10 @@ export declare const OrgDefSchema: z.ZodObject<{
|
|
|
721
757
|
webAllow: z.ZodOptional<z.ZodOptional<z.ZodArray<z.ZodString, "many">>>;
|
|
722
758
|
maxTokens: z.ZodOptional<z.ZodOptional<z.ZodNumber>>;
|
|
723
759
|
}, z.ZodTypeAny, "passthrough">>>;
|
|
760
|
+
/** Per-role override of run_config.max_turns_per_message — roles that legitimately
|
|
761
|
+
* need many more turns per message (e.g. a developer doing sequential build/fix/verify
|
|
762
|
+
* cycles) than others (e.g. docs, pm) shouldn't be forced onto one global budget. */
|
|
763
|
+
max_turns_per_message: z.ZodOptional<z.ZodNumber>;
|
|
724
764
|
}, "passthrough", z.ZodTypeAny, z.objectOutputType<{
|
|
725
765
|
id: z.ZodString;
|
|
726
766
|
title: z.ZodDefault<z.ZodString>;
|
|
@@ -778,6 +818,10 @@ export declare const OrgDefSchema: z.ZodObject<{
|
|
|
778
818
|
webAllow: z.ZodOptional<z.ZodOptional<z.ZodArray<z.ZodString, "many">>>;
|
|
779
819
|
maxTokens: z.ZodOptional<z.ZodOptional<z.ZodNumber>>;
|
|
780
820
|
}, z.ZodTypeAny, "passthrough">>>;
|
|
821
|
+
/** Per-role override of run_config.max_turns_per_message — roles that legitimately
|
|
822
|
+
* need many more turns per message (e.g. a developer doing sequential build/fix/verify
|
|
823
|
+
* cycles) than others (e.g. docs, pm) shouldn't be forced onto one global budget. */
|
|
824
|
+
max_turns_per_message: z.ZodOptional<z.ZodNumber>;
|
|
781
825
|
}, z.ZodTypeAny, "passthrough">, z.objectInputType<{
|
|
782
826
|
id: z.ZodString;
|
|
783
827
|
title: z.ZodDefault<z.ZodString>;
|
|
@@ -835,6 +879,10 @@ export declare const OrgDefSchema: z.ZodObject<{
|
|
|
835
879
|
webAllow: z.ZodOptional<z.ZodOptional<z.ZodArray<z.ZodString, "many">>>;
|
|
836
880
|
maxTokens: z.ZodOptional<z.ZodOptional<z.ZodNumber>>;
|
|
837
881
|
}, z.ZodTypeAny, "passthrough">>>;
|
|
882
|
+
/** Per-role override of run_config.max_turns_per_message — roles that legitimately
|
|
883
|
+
* need many more turns per message (e.g. a developer doing sequential build/fix/verify
|
|
884
|
+
* cycles) than others (e.g. docs, pm) shouldn't be forced onto one global budget. */
|
|
885
|
+
max_turns_per_message: z.ZodOptional<z.ZodNumber>;
|
|
838
886
|
}, z.ZodTypeAny, "passthrough">>, "many">;
|
|
839
887
|
}, z.ZodTypeAny, "passthrough">>;
|
|
840
888
|
export type OrgDef = z.infer<typeof OrgDefSchema>;
|
|
@@ -32,6 +32,10 @@ export const RoleSchema = z.object({
|
|
|
32
32
|
}).partial().optional(),
|
|
33
33
|
provider: ProviderSchema.optional(),
|
|
34
34
|
policy: RolePolicySchema.optional(),
|
|
35
|
+
/** Per-role override of run_config.max_turns_per_message — roles that legitimately
|
|
36
|
+
* need many more turns per message (e.g. a developer doing sequential build/fix/verify
|
|
37
|
+
* cycles) than others (e.g. docs, pm) shouldn't be forced onto one global budget. */
|
|
38
|
+
max_turns_per_message: z.number().int().positive().optional(),
|
|
35
39
|
}).passthrough();
|
|
36
40
|
export const OrgDefSchema = z.object({
|
|
37
41
|
name: z.string().min(1),
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@monoes/monomindcli",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.7.1",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"description": "CLI engine for Monomind
|
|
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",
|
|
7
7
|
"types": "dist/src/index.d.ts",
|
|
8
8
|
"sideEffects": false,
|
|
@@ -95,9 +95,9 @@
|
|
|
95
95
|
},
|
|
96
96
|
"dependencies": {
|
|
97
97
|
"@anthropic-ai/claude-agent-sdk": "^0.3.207",
|
|
98
|
-
"@monoes/monobrowse": "^1.0.
|
|
98
|
+
"@monoes/monobrowse": "^1.0.6",
|
|
99
99
|
"@monoes/monodesign": "^1.2.0",
|
|
100
|
-
"@monoes/monograph": "^1.5.
|
|
100
|
+
"@monoes/monograph": "^1.5.1",
|
|
101
101
|
"@noble/ed25519": "^2.1.0",
|
|
102
102
|
"mammoth": "^1.12.0",
|
|
103
103
|
"pdf-parse": "^2.4.5",
|
|
@@ -107,9 +107,9 @@
|
|
|
107
107
|
},
|
|
108
108
|
"optionalDependencies": {
|
|
109
109
|
"@huggingface/transformers": "^3.8.1",
|
|
110
|
-
"@monoes/memory": "^1.0.10",
|
|
111
110
|
"@monoes/hooks": "^1.0.0",
|
|
112
111
|
"@monoes/mcp": "^1.0.0",
|
|
112
|
+
"@monoes/memory": "^1.0.10",
|
|
113
113
|
"@monoes/routing": "^1.0.0",
|
|
114
114
|
"monofence-ai": "*",
|
|
115
115
|
"sql.js": "^1.14.1"
|
|
@@ -30,17 +30,20 @@ function parseArticles(text, org) {
|
|
|
30
30
|
const [, slug, title] = m;
|
|
31
31
|
let heroImage = null;
|
|
32
32
|
let supportingImages = [];
|
|
33
|
+
let diagrams = [];
|
|
33
34
|
const bodyLines = [];
|
|
34
35
|
for (const line of lines.slice(1)) {
|
|
35
36
|
const heroMatch = line.match(/^HeroImage:\s*(.+?)\s*$/i);
|
|
36
37
|
const suppMatch = line.match(/^SupportingImages:\s*(.+?)\s*$/i);
|
|
38
|
+
const diagMatch = line.match(/^Diagrams:\s*(.+?)\s*$/i);
|
|
37
39
|
if (heroMatch) { heroImage = heroMatch[1]; continue; }
|
|
38
40
|
if (suppMatch) { supportingImages = suppMatch[1].split(',').map((s) => s.trim()).filter(Boolean); continue; }
|
|
41
|
+
if (diagMatch) { diagrams = diagMatch[1].split(',').map((s) => s.trim()).filter(Boolean); continue; }
|
|
39
42
|
bodyLines.push(line);
|
|
40
43
|
}
|
|
41
44
|
const body = bodyLines.join('\n').trim();
|
|
42
45
|
if (!body) continue;
|
|
43
|
-
articles.push({ org, slug: slug.trim(), title: title.trim(), heroImage, supportingImages, body });
|
|
46
|
+
articles.push({ org, slug: slug.trim(), title: title.trim(), heroImage, supportingImages, diagrams, body });
|
|
44
47
|
}
|
|
45
48
|
return articles;
|
|
46
49
|
}
|
|
@@ -80,15 +83,25 @@ function escapeHtml(s) {
|
|
|
80
83
|
return String(s).replace(/[&<>"']/g, (c) => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c]));
|
|
81
84
|
}
|
|
82
85
|
|
|
83
|
-
function mdToHtml(md) {
|
|
84
|
-
// Minimal renderer: paragraphs, headings, code fences, bold/italic,
|
|
85
|
-
|
|
86
|
+
function mdToHtml(md, copiedSet) {
|
|
87
|
+
// Minimal renderer: paragraphs, headings, code fences, bold/italic, inline images, tables.
|
|
88
|
+
const figures = [];
|
|
89
|
+
const placeholder = (i) => `[[[FIGURE-${i}]]]`;
|
|
90
|
+
const withPlaceholders = md.replace(/!\[(.*?)\]\((?:\.\/)?(.*?)\)/g, (_, alt, src) => {
|
|
91
|
+
const idx = figures.length;
|
|
92
|
+
const visible = copiedSet.has(src);
|
|
93
|
+
figures.push(visible ? `<figure class="inline-diagram"><img src="./${escapeHtml(src)}" alt="${escapeHtml(alt)}"><figcaption>${escapeHtml(alt)}</figcaption></figure>` : '');
|
|
94
|
+
return placeholder(idx);
|
|
95
|
+
});
|
|
96
|
+
let html = escapeHtml(withPlaceholders)
|
|
86
97
|
.replace(/^### (.+)$/gm, '<h4>$1</h4>')
|
|
87
98
|
.replace(/^## (.+)$/gm, '<h3>$1</h3>')
|
|
88
99
|
.replace(/^# (.+)$/gm, '<h2>$1</h2>')
|
|
89
100
|
.replace(/```([\s\S]*?)```/g, (_, code) => `<pre><code>${code}</code></pre>`)
|
|
90
101
|
.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>')
|
|
91
102
|
.replace(/\n\n/g, '</p><p>');
|
|
103
|
+
figures.forEach((fig, i) => { html = html.replace(placeholder(i), fig); });
|
|
104
|
+
return html;
|
|
92
105
|
}
|
|
93
106
|
|
|
94
107
|
function render() {
|
|
@@ -121,7 +134,7 @@ function render() {
|
|
|
121
134
|
// copy every referenced image (article hero/supporting + post images) alongside html/md
|
|
122
135
|
const copied = new Set();
|
|
123
136
|
const allImageNames = new Set([
|
|
124
|
-
...articles.flatMap((a) => [a.heroImage, ...a.supportingImages].filter(Boolean)),
|
|
137
|
+
...articles.flatMap((a) => [a.heroImage, ...a.supportingImages, ...a.diagrams].filter(Boolean)),
|
|
125
138
|
...posts.map((p) => p.image).filter(Boolean),
|
|
126
139
|
]);
|
|
127
140
|
for (const name of allImageNames) {
|
|
@@ -172,12 +185,14 @@ function render() {
|
|
|
172
185
|
function articleCard(a) {
|
|
173
186
|
const img = a.heroImage && copied.has(a.heroImage) ? `<img class="hero" src="./${escapeHtml(a.heroImage)}" alt="">` : '';
|
|
174
187
|
const supp = a.supportingImages.filter((n) => copied.has(n)).map((n) => `<img class="supporting" src="./${escapeHtml(n)}" alt="">`).join('');
|
|
188
|
+
const missingDiagrams = a.diagrams.filter((n) => !copied.has(n));
|
|
175
189
|
return `<div class="article">
|
|
176
190
|
<div class="article-slug">${escapeHtml(a.slug)}</div>
|
|
177
191
|
<h3 class="article-title">${escapeHtml(a.title)}</h3>
|
|
178
192
|
${img}
|
|
179
193
|
${supp ? `<div class="supporting-row">${supp}</div>` : ''}
|
|
180
|
-
<div class="article-body"><p>${mdToHtml(a.body)}</p></div>
|
|
194
|
+
<div class="article-body"><p>${mdToHtml(a.body, copied)}</p></div>
|
|
195
|
+
${missingDiagrams.length ? `<div class="missing-note">Missing diagram file(s) referenced but not found: ${missingDiagrams.map(escapeHtml).join(', ')}</div>` : ''}
|
|
181
196
|
</div>`;
|
|
182
197
|
}
|
|
183
198
|
|
|
@@ -211,6 +226,11 @@ main { max-width:860px; margin:0 auto; padding:0 1.5rem 4rem; }
|
|
|
211
226
|
.supporting-row img { width:100%; border-radius:8px; }
|
|
212
227
|
.article-body { font-size:.92rem; color:var(--text); }
|
|
213
228
|
.article-body p { white-space:pre-wrap; }
|
|
229
|
+
.article-body h2, .article-body h3, .article-body h4 { white-space:normal; }
|
|
230
|
+
.inline-diagram { margin:1.2rem 0; }
|
|
231
|
+
.inline-diagram img { width:100%; border-radius:8px; background:var(--espresso); }
|
|
232
|
+
.inline-diagram figcaption { font-size:.78rem; color:var(--muted); margin-top:.3rem; text-align:center; }
|
|
233
|
+
.missing-note { margin-top:1rem; padding:.6rem .8rem; border:1px dashed var(--border); border-radius:8px; font-size:.78rem; color:var(--muted); }
|
|
214
234
|
.card { background:var(--card); border:1px solid var(--border); border-radius:12px; padding:1.1rem 1.3rem; margin-bottom:1rem; }
|
|
215
235
|
.channel { font-size:.72rem; font-weight:700; text-transform:uppercase; letter-spacing:.04em; color:var(--accent); margin-bottom:.6rem; }
|
|
216
236
|
.based-on { font-weight:400; text-transform:none; letter-spacing:0; color:var(--muted); font-style:italic; }
|
|
@@ -231,7 +251,10 @@ ${!articles.length && !posts.length ? '<div class="empty">Nothing finalized yet
|
|
|
231
251
|
</body></html>`;
|
|
232
252
|
fs.writeFileSync(path.join(OUT_DIR, 'content-calendar.html'), html);
|
|
233
253
|
|
|
234
|
-
|
|
254
|
+
const totalDiagrams = articles.reduce((n, a) => n + a.diagrams.length, 0);
|
|
255
|
+
const underDiagrammed = articles.filter((a) => a.diagrams.length < 2).map((a) => a.slug);
|
|
256
|
+
console.log(`Wrote ${articles.length} article(s), ${posts.length} post(s), ${copied.size} image(s) (incl. ${totalDiagrams} diagram(s)) -> ${path.relative(ROOT, OUT_DIR)}`);
|
|
257
|
+
if (underDiagrammed.length) console.log(`Note: fewer than 2 diagrams on: ${underDiagrammed.join(', ')}`);
|
|
235
258
|
}
|
|
236
259
|
|
|
237
260
|
render();
|