monomind 2.5.5 → 2.5.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.5.5",
3
+ "version": "2.5.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",
@@ -63,7 +63,7 @@
63
63
  "dependencies": {
64
64
  "@anthropic-ai/claude-agent-sdk": "^0.3.207",
65
65
  "@monoes/monobrowse": "^1.0.3",
66
- "@monoes/monograph": "^1.4.0",
66
+ "@monoes/monograph": "^1.4.1",
67
67
  "@noble/ed25519": "^2.1.0",
68
68
  "mammoth": "^1.12.0",
69
69
  "pdf-parse": "^2.4.5",
@@ -84,17 +84,27 @@ if (isMCPMode) {
84
84
 
85
85
  for (const line of lines) {
86
86
  if (line.trim()) {
87
+ let parsed;
87
88
  try {
88
- const message = JSON.parse(line);
89
- const response = await handleMessage(message);
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);
90
100
  if (response) {
91
101
  console.log(JSON.stringify(response));
92
102
  }
93
103
  } catch (error) {
94
104
  console.log(JSON.stringify({
95
105
  jsonrpc: '2.0',
96
- id: null,
97
- error: { code: -32700, message: 'Parse error' },
106
+ id: parsed.id ?? null,
107
+ error: { code: -32603, message: error instanceof Error ? error.message : 'Internal error' },
98
108
  }));
99
109
  }
100
110
  }
@@ -132,7 +142,7 @@ if (isMCPMode) {
132
142
  };
133
143
 
134
144
  case 'tools/list': {
135
- const tools = listMCPTools();
145
+ const tools = await listMCPTools();
136
146
  return {
137
147
  jsonrpc: '2.0',
138
148
  id: message.id,
@@ -150,7 +160,7 @@ if (isMCPMode) {
150
160
  const toolName = params.name;
151
161
  const toolParams = params.arguments || {};
152
162
 
153
- if (!hasTool(toolName)) {
163
+ if (!await hasTool(toolName)) {
154
164
  return {
155
165
  jsonrpc: '2.0',
156
166
  id: message.id,
@@ -41,22 +41,31 @@ process.stdin.on('data', async (chunk) => {
41
41
 
42
42
  for (const line of lines) {
43
43
  if (line.trim()) {
44
+ let parsed;
44
45
  try {
45
- const message = JSON.parse(line);
46
- const response = await handleMessage(message);
46
+ parsed = JSON.parse(line);
47
+ } catch {
48
+ console.log(JSON.stringify({
49
+ jsonrpc: '2.0',
50
+ id: null,
51
+ error: { code: -32700, message: 'Parse error' },
52
+ }));
53
+ continue;
54
+ }
55
+ try {
56
+ const response = await handleMessage(parsed);
47
57
  if (response) {
48
58
  console.log(JSON.stringify(response));
49
59
  }
50
60
  } catch (error) {
51
61
  console.error(
52
- `[${new Date().toISOString()}] ERROR [monomind-mcp] Failed to parse:`,
62
+ `[${new Date().toISOString()}] ERROR [monomind-mcp] handler error:`,
53
63
  error instanceof Error ? error.message : String(error)
54
64
  );
55
- // Send parse error response
56
65
  console.log(JSON.stringify({
57
66
  jsonrpc: '2.0',
58
- id: null,
59
- error: { code: -32700, message: 'Parse error' },
67
+ id: parsed.id ?? null,
68
+ error: { code: -32603, message: error instanceof Error ? error.message : 'Internal error' },
60
69
  }));
61
70
  }
62
71
  }
@@ -122,7 +131,7 @@ async function handleMessage(message) {
122
131
  }
123
132
 
124
133
  case 'tools/list': {
125
- const tools = listMCPTools();
134
+ const tools = await listMCPTools();
126
135
  return {
127
136
  jsonrpc: '2.0',
128
137
  id: message.id,
@@ -140,7 +149,7 @@ async function handleMessage(message) {
140
149
  const toolName = params.name;
141
150
  const toolParams = params.arguments || {};
142
151
 
143
- if (!hasTool(toolName)) {
152
+ if (!await hasTool(toolName)) {
144
153
  return {
145
154
  jsonrpc: '2.0',
146
155
  id: message.id,
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@monoes/monomindcli",
3
- "version": "2.5.5",
3
+ "version": "2.5.7",
4
4
  "type": "module",
5
5
  "description": "CLI engine for Monomind \u2014 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",
@@ -97,7 +97,7 @@
97
97
  "@anthropic-ai/claude-agent-sdk": "^0.3.207",
98
98
  "@monoes/monobrowse": "^1.0.3",
99
99
  "@monoes/monodesign": "^1.2.0",
100
- "@monoes/monograph": "^1.4.0",
100
+ "@monoes/monograph": "^1.4.1",
101
101
  "@noble/ed25519": "^2.1.0",
102
102
  "mammoth": "^1.12.0",
103
103
  "pdf-parse": "^2.4.5",
@@ -0,0 +1,140 @@
1
+ #!/usr/bin/env node
2
+ // Renders the public content calendar (HTML + MD) from each growth org's
3
+ // workspace/posts.md — the single canonical, ready-to-publish post ledger.
4
+ // Regenerate after any growth org run:
5
+ // node scripts/growth-content-calendar.mjs
6
+ import fs from 'node:fs';
7
+ import path from 'node:path';
8
+
9
+ const ROOT = process.cwd();
10
+ const ORGS = [
11
+ { name: 'monomind-growth', dir: path.join(ROOT, '.monomind/orgs/monomind-growth/workspace') },
12
+ { name: 'growth-execution', dir: path.join(ROOT, '.monomind/orgs/growth-execution/workspace') },
13
+ ];
14
+ const OUT_DIR = path.join(ROOT, '.monomind/orgs/monomind-growth/workspace/reports/content-calendar');
15
+
16
+ // Record format in posts.md (one per finalized, ready-to-publish post):
17
+ //
18
+ // ### <Channel> — <YYYY-MM-DD>
19
+ // Image: <filename in ../assets/>
20
+ // <final publish-ready text, one or more lines, until the next "### " or EOF>
21
+ function parsePosts(text, org, sourceFile) {
22
+ const posts = [];
23
+ const blocks = text.split(/^### /m).slice(1);
24
+ for (const block of blocks) {
25
+ const lines = block.split('\n');
26
+ const headerLine = lines[0];
27
+ const m = headerLine.match(/^(.+?)\s*—\s*(\d{4}-\d{2}-\d{2})\s*$/);
28
+ if (!m) continue;
29
+ const [, channel, date] = m;
30
+ let image = null;
31
+ const bodyLines = [];
32
+ for (const line of lines.slice(1)) {
33
+ const imgMatch = line.match(/^Image:\s*(.+?)\s*$/i);
34
+ if (imgMatch) { image = imgMatch[1]; continue; }
35
+ bodyLines.push(line);
36
+ }
37
+ const text = bodyLines.join('\n').trim();
38
+ if (!text) continue;
39
+ posts.push({ org, sourceFile, channel: channel.trim(), date, text, image });
40
+ }
41
+ return posts;
42
+ }
43
+
44
+ function escapeHtml(s) {
45
+ return String(s).replace(/[&<>"']/g, (c) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[c]));
46
+ }
47
+
48
+ function render() {
49
+ fs.mkdirSync(OUT_DIR, { recursive: true });
50
+
51
+ let posts = [];
52
+ for (const org of ORGS) {
53
+ const postsFile = path.join(org.dir, 'posts.md');
54
+ if (!fs.existsSync(postsFile)) continue;
55
+ const text = fs.readFileSync(postsFile, 'utf8');
56
+ posts.push(...parsePosts(text, org.name, path.relative(ROOT, postsFile)));
57
+ }
58
+
59
+ // dedupe identical posts mirrored across both orgs
60
+ const merged = new Map();
61
+ for (const p of posts) {
62
+ const key = `${p.channel}|${p.date}|${p.text}`;
63
+ const existing = merged.get(key);
64
+ if (existing) { if (!existing.orgs.includes(p.org)) existing.orgs.push(p.org); }
65
+ else merged.set(key, { ...p, orgs: [p.org] });
66
+ }
67
+ posts = [...merged.values()].sort((a, b) => b.date.localeCompare(a.date));
68
+
69
+ // copy referenced images alongside html/md
70
+ const copied = new Set();
71
+ for (const p of posts) {
72
+ if (!p.image) continue;
73
+ for (const org of ORGS) {
74
+ const src = path.join(org.dir, 'assets', p.image);
75
+ if (fs.existsSync(src)) { fs.copyFileSync(src, path.join(OUT_DIR, p.image)); copied.add(p.image); break; }
76
+ }
77
+ }
78
+
79
+ // --- MD ---
80
+ const md = [];
81
+ md.push('# Monomind Growth — Content Calendar (auto-generated)');
82
+ md.push('');
83
+ md.push('Regenerated by `scripts/growth-content-calendar.mjs` from each org\'s `workspace/posts.md`. Newest publish date first.');
84
+ md.push('');
85
+ let lastDate = null;
86
+ for (const p of posts) {
87
+ if (p.date !== lastDate) { md.push(`\n## ${p.date}`); lastDate = p.date; }
88
+ md.push(`\n### ${p.channel}\n`);
89
+ md.push(p.text);
90
+ if (p.image) md.push(`\n![${p.channel} image](./${p.image})`);
91
+ }
92
+ if (!posts.length) md.push('\n_No finalized posts yet — workspace/posts.md is empty for both orgs._');
93
+ fs.writeFileSync(path.join(OUT_DIR, 'content-calendar.md'), md.join('\n') + '\n');
94
+
95
+ // --- HTML ---
96
+ const groups = [];
97
+ { let cur = null; for (const p of posts) { if (p.date !== cur?.date) { cur = { date: p.date, items: [] }; groups.push(cur); } cur.items.push(p); } }
98
+
99
+ function card(p) {
100
+ const img = p.image && copied.has(p.image) ? `<img class="thumb" src="./${escapeHtml(p.image)}" alt="">` : '';
101
+ return `<div class="card">
102
+ <div class="channel">${escapeHtml(p.channel)}</div>
103
+ ${img}
104
+ <div class="text">${escapeHtml(p.text).replace(/\n/g, '<br>')}</div>
105
+ </div>`;
106
+ }
107
+
108
+ const html = `<!doctype html>
109
+ <html><head><meta charset="utf-8"><title>Monomind Growth — Content Calendar</title>
110
+ <style>
111
+ :root { color-scheme: light dark; --bg:#0b0d12; --card:#151922; --line:#232837; --text:#e7ebf3; --muted:#8a93a6; --accent:#7cc4ff; }
112
+ @media (prefers-color-scheme: light) { :root { --bg:#f6f7fb; --card:#ffffff; --line:#e4e7ef; --text:#171923; --muted:#5a6270; --accent:#2563eb; } }
113
+ * { box-sizing: border-box; }
114
+ body { margin:0; background:var(--bg); color:var(--text); font:15px/1.55 -apple-system,BlinkMacSystemFont,Segoe UI,sans-serif; }
115
+ header { padding:2.5rem 1.5rem 1rem; max-width:820px; margin:0 auto; }
116
+ h1 { margin:0 0 .3rem; font-size:1.6rem; }
117
+ .sub { color:var(--muted); font-size:.9rem; }
118
+ main { max-width:820px; margin:0 auto; padding:0 1.5rem 4rem; }
119
+ .date-heading { font-size:1.05rem; font-weight:700; color:var(--accent); border-bottom:1px solid var(--line); padding:1.6rem 0 .5rem; margin-bottom:1rem; }
120
+ .card { background:var(--card); border:1px solid var(--line); border-radius:12px; padding:1.1rem 1.3rem; margin-bottom:1rem; }
121
+ .channel { font-size:.72rem; font-weight:700; text-transform:uppercase; letter-spacing:.04em; color:var(--accent); margin-bottom:.6rem; }
122
+ .thumb { max-width:100%; border-radius:8px; margin-bottom:.7rem; display:block; }
123
+ .text { font-size:.92rem; white-space:normal; }
124
+ .empty { color:var(--muted); padding:2rem 0; }
125
+ </style></head>
126
+ <body>
127
+ <header>
128
+ <h1>Monomind Growth — Content Calendar</h1>
129
+ <div class="sub">Auto-generated by <code>scripts/growth-content-calendar.mjs</code> from workspace/posts.md. Newest publish date first.</div>
130
+ </header>
131
+ <main>
132
+ ${groups.length ? groups.map((g) => `<div class="date-heading">${g.date}</div>${g.items.map(card).join('\n')}`).join('\n') : '<div class="empty">No finalized posts yet — workspace/posts.md is empty for both orgs.</div>'}
133
+ </main>
134
+ </body></html>`;
135
+ fs.writeFileSync(path.join(OUT_DIR, 'content-calendar.html'), html);
136
+
137
+ console.log(`Wrote ${posts.length} post(s), ${copied.size} image(s) -> ${path.relative(ROOT, OUT_DIR)}`);
138
+ }
139
+
140
+ render();