@monoes/monograph 1.2.1 → 1.2.3
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/.monomind/loops/mastermind-review-1782162423447.json +16 -0
- package/dist/src/claude-cli.d.ts +3 -0
- package/dist/src/claude-cli.d.ts.map +1 -0
- package/dist/src/claude-cli.js +61 -0
- package/dist/src/claude-cli.js.map +1 -0
- package/dist/src/cli/skill-gen.js +3 -0
- package/dist/src/cli/skill-gen.js.map +1 -1
- package/dist/src/graph/node-search.d.ts.map +1 -1
- package/dist/src/graph/node-search.js.map +1 -1
- package/dist/src/graph/pagerank.d.ts.map +1 -1
- package/dist/src/graph/pagerank.js.map +1 -1
- package/dist/src/graph/subgraph.d.ts.map +1 -1
- package/dist/src/graph/subgraph.js.map +1 -1
- package/dist/src/mcp-tools/wiki-build.d.ts.map +1 -1
- package/dist/src/mcp-tools/wiki-build.js +0 -4
- package/dist/src/mcp-tools/wiki-build.js.map +1 -1
- package/dist/src/pipeline/orchestrator.d.ts +2 -0
- package/dist/src/pipeline/phases/llm-extract.d.ts.map +1 -1
- package/dist/src/pipeline/phases/llm-extract.js +10 -22
- package/dist/src/pipeline/phases/llm-extract.js.map +1 -1
- package/dist/src/wiki/wiki-generator.d.ts.map +1 -1
- package/dist/src/wiki/wiki-generator.js +9 -13
- package/dist/src/wiki/wiki-generator.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/claude-cli.ts +59 -0
- package/src/cli/skill-gen.ts +3 -0
- package/src/graph/node-search.ts +3 -2
- package/src/graph/pagerank.ts +3 -2
- package/src/graph/subgraph.ts +2 -1
- package/src/mcp-tools/wiki-build.ts +0 -5
- package/src/pipeline/phases/llm-extract.ts +10 -21
- package/src/wiki/wiki-generator.ts +9 -13
package/package.json
CHANGED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thin wrapper around `claude --print` for LLM calls inside monograph.
|
|
3
|
+
* Reuses Claude Code's existing auth — no ANTHROPIC_API_KEY needed.
|
|
4
|
+
*/
|
|
5
|
+
import { spawn, execSync } from 'child_process';
|
|
6
|
+
|
|
7
|
+
const DEFAULT_TIMEOUT_MS = 60_000;
|
|
8
|
+
let _available: boolean | null = null;
|
|
9
|
+
|
|
10
|
+
export function isClaudeCliAvailable(): boolean {
|
|
11
|
+
if (_available !== null) return _available;
|
|
12
|
+
try {
|
|
13
|
+
execSync('claude --version', { encoding: 'utf-8', stdio: 'pipe', timeout: 5000, windowsHide: true });
|
|
14
|
+
_available = true;
|
|
15
|
+
} catch {
|
|
16
|
+
_available = false;
|
|
17
|
+
}
|
|
18
|
+
return _available;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function claudeCliCall(prompt: string, timeoutMs = DEFAULT_TIMEOUT_MS): Promise<string> {
|
|
22
|
+
return new Promise((resolve, reject) => {
|
|
23
|
+
const child = spawn(
|
|
24
|
+
'claude',
|
|
25
|
+
['--print', '--model', 'haiku', '--strict-mcp-config', '--no-session-persistence', '--', prompt],
|
|
26
|
+
{ stdio: ['ignore', 'pipe', 'pipe'], windowsHide: true },
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
let stdout = '';
|
|
30
|
+
let stderr = '';
|
|
31
|
+
let settled = false;
|
|
32
|
+
|
|
33
|
+
const timer = setTimeout(() => {
|
|
34
|
+
if (settled) return;
|
|
35
|
+
settled = true;
|
|
36
|
+
try { child.kill('SIGTERM'); } catch { /* ignore */ }
|
|
37
|
+
setTimeout(() => { try { child.kill('SIGKILL'); } catch { /* ignore */ } }, 3000);
|
|
38
|
+
reject(new Error(`claude --print timed out after ${timeoutMs}ms`));
|
|
39
|
+
}, timeoutMs);
|
|
40
|
+
|
|
41
|
+
child.stdout.on('data', (d: Buffer) => { stdout += d.toString(); });
|
|
42
|
+
child.stderr.on('data', (d: Buffer) => { stderr += d.toString(); });
|
|
43
|
+
|
|
44
|
+
child.on('close', (code) => {
|
|
45
|
+
if (settled) return;
|
|
46
|
+
settled = true;
|
|
47
|
+
clearTimeout(timer);
|
|
48
|
+
if (code === 0) resolve(stdout.trim());
|
|
49
|
+
else reject(new Error(stderr.trim() || `claude exited with code ${code}`));
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
child.on('error', (err) => {
|
|
53
|
+
if (settled) return;
|
|
54
|
+
settled = true;
|
|
55
|
+
clearTimeout(timer);
|
|
56
|
+
reject(err);
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
}
|
package/src/cli/skill-gen.ts
CHANGED
|
@@ -245,6 +245,9 @@ function renderSkillMarkdown(
|
|
|
245
245
|
lines.push('---');
|
|
246
246
|
lines.push(`name: ${toKebabName(label)}`);
|
|
247
247
|
lines.push(`description: "Skill for the ${label} community. ${members.length} symbols."`);
|
|
248
|
+
lines.push(`type: Code Community`);
|
|
249
|
+
lines.push(`tags: [monograph, code-community]`);
|
|
250
|
+
lines.push(`timestamp: ${new Date().toISOString()}`);
|
|
248
251
|
lines.push('---');
|
|
249
252
|
lines.push('');
|
|
250
253
|
|
package/src/graph/node-search.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import Database from 'better-sqlite3';
|
|
1
2
|
import type { MonographNode, NodeLabel } from '../types.js';
|
|
2
3
|
import type { MonographDb } from '../storage/db.js';
|
|
3
4
|
|
|
@@ -55,13 +56,13 @@ function rowToNode(r: {
|
|
|
55
56
|
// so the same query shape reuses the compiled statement across calls.
|
|
56
57
|
|
|
57
58
|
// WeakMap ensures the cache is garbage-collected when the DB object is released.
|
|
58
|
-
const stmtCache = new WeakMap<object, Map<string,
|
|
59
|
+
const stmtCache = new WeakMap<object, Map<string, Database.Statement>>();
|
|
59
60
|
|
|
60
61
|
function getCachedStmt(
|
|
61
62
|
db: MonographDb,
|
|
62
63
|
key: string,
|
|
63
64
|
buildSql: () => string,
|
|
64
|
-
):
|
|
65
|
+
): Database.Statement {
|
|
65
66
|
let dbCache = stmtCache.get(db);
|
|
66
67
|
if (!dbCache) {
|
|
67
68
|
dbCache = new Map();
|
package/src/graph/pagerank.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import Database from 'better-sqlite3';
|
|
1
2
|
import type { MonographDb } from '../storage/db.js';
|
|
2
3
|
|
|
3
4
|
export interface PageRankOptions {
|
|
@@ -13,8 +14,8 @@ export interface PageRankOptions {
|
|
|
13
14
|
// Per-DB statement cache — avoids recompiling SQL on every pageRank() call.
|
|
14
15
|
// ---------------------------------------------------------------------------
|
|
15
16
|
interface StmtCache {
|
|
16
|
-
selectNodes:
|
|
17
|
-
selectEdges:
|
|
17
|
+
selectNodes: Database.Statement;
|
|
18
|
+
selectEdges: Database.Statement;
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
const _stmtCache = new Map<string, StmtCache>();
|
package/src/graph/subgraph.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import Database from 'better-sqlite3';
|
|
1
2
|
import type { MonographNode, MonographEdge } from '../types.js';
|
|
2
3
|
import type { MonographDb } from '../storage/db.js';
|
|
3
4
|
|
|
@@ -66,7 +67,7 @@ export function extractInducedSubgraph(db: MonographDb, nodeIds: string[]): Indu
|
|
|
66
67
|
const stmts = getStmtCaches(db);
|
|
67
68
|
|
|
68
69
|
// Chunk helper — caches prepared statements by chunk size to avoid re-prepare per call.
|
|
69
|
-
function queryChunked<T>(ids: string[], chunkSize: number, stmtCache: Map<number,
|
|
70
|
+
function queryChunked<T>(ids: string[], chunkSize: number, stmtCache: Map<number, Database.Statement>, sql: (ph: string) => string): T[] {
|
|
70
71
|
const results: T[] = [];
|
|
71
72
|
for (let i = 0; i < ids.length; i += chunkSize) {
|
|
72
73
|
const chunk = ids.slice(i, i + chunkSize);
|
|
@@ -25,11 +25,6 @@ export async function runWikiBuildTool(
|
|
|
25
25
|
db: MonographDb,
|
|
26
26
|
input: WikiBuildToolInput,
|
|
27
27
|
): Promise<WikiBuildToolResult> {
|
|
28
|
-
const apiKey = process.env['ANTHROPIC_API_KEY'];
|
|
29
|
-
if (!apiKey && !input.llmClient) {
|
|
30
|
-
return { error: 'ANTHROPIC_API_KEY not set' };
|
|
31
|
-
}
|
|
32
|
-
|
|
33
28
|
const opts: GenerateAllWikiPagesOptions = {
|
|
34
29
|
force: input.force ?? false,
|
|
35
30
|
model: input.model,
|
|
@@ -37,25 +37,11 @@ interface Triple {
|
|
|
37
37
|
edge: string;
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
async function callClaude(content: string
|
|
40
|
+
async function callClaude(content: string): Promise<Triple[] | null> {
|
|
41
41
|
try {
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
'x-api-key': apiKey,
|
|
46
|
-
'anthropic-version': '2023-06-01',
|
|
47
|
-
'content-type': 'application/json',
|
|
48
|
-
},
|
|
49
|
-
body: JSON.stringify({
|
|
50
|
-
model: 'claude-haiku-4-5-20251001',
|
|
51
|
-
max_tokens: 512,
|
|
52
|
-
system: SYSTEM_PROMPT,
|
|
53
|
-
messages: [{ role: 'user', content: content.slice(0, 1500) }],
|
|
54
|
-
}),
|
|
55
|
-
});
|
|
56
|
-
if (!res.ok) return null;
|
|
57
|
-
const data = await res.json() as { content: Array<{ text: string }> };
|
|
58
|
-
const text = data.content[0]?.text ?? '[]';
|
|
42
|
+
const { claudeCliCall } = await import('../../claude-cli.js');
|
|
43
|
+
const prompt = `${SYSTEM_PROMPT}\n\n${content.slice(0, 1500)}`;
|
|
44
|
+
const text = await claudeCliCall(prompt, 30_000);
|
|
59
45
|
const parsed = JSON.parse(text.match(/\[[\s\S]*\]/)?.[0] ?? '[]');
|
|
60
46
|
return Array.isArray(parsed) ? parsed as Triple[] : null;
|
|
61
47
|
} catch {
|
|
@@ -68,10 +54,14 @@ export const llmExtractPhase: PipelinePhase<LlmExtractOutput> = {
|
|
|
68
54
|
deps: ['docs-parse', 'pdf-parse'],
|
|
69
55
|
|
|
70
56
|
async execute(ctx: PipelineContext) {
|
|
71
|
-
const apiKey = process.env.ANTHROPIC_API_KEY;
|
|
72
57
|
const maxSections = ctx.options.llmMaxSections;
|
|
73
58
|
|
|
74
|
-
if (
|
|
59
|
+
if (maxSections <= 0 || ctx.options.codeOnly) {
|
|
60
|
+
return { triplesExtracted: 0, sectionsProcessed: 0 };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const { isClaudeCliAvailable } = await import('../../claude-cli.js');
|
|
64
|
+
if (!isClaudeCliAvailable()) {
|
|
75
65
|
return { triplesExtracted: 0, sectionsProcessed: 0 };
|
|
76
66
|
}
|
|
77
67
|
|
|
@@ -107,7 +97,6 @@ export const llmExtractPhase: PipelinePhase<LlmExtractOutput> = {
|
|
|
107
97
|
|
|
108
98
|
const triples = await callClaude(
|
|
109
99
|
`Section: "${section.name}"\n\n${content}`,
|
|
110
|
-
apiKey,
|
|
111
100
|
);
|
|
112
101
|
if (!triples || triples.length === 0) continue;
|
|
113
102
|
|
|
@@ -145,22 +145,18 @@ export async function generateWikiPage(
|
|
|
145
145
|
const result = await callLLM(prompt, options.llmConfig);
|
|
146
146
|
content = result.text;
|
|
147
147
|
} else {
|
|
148
|
-
const
|
|
149
|
-
if (!
|
|
150
|
-
throw new Error('
|
|
148
|
+
const { claudeCliCall, isClaudeCliAvailable } = await import('../claude-cli.js');
|
|
149
|
+
if (!isClaudeCliAvailable()) {
|
|
150
|
+
throw new Error('Claude Code CLI not found. Install with: npm install -g @anthropic-ai/claude-code');
|
|
151
151
|
}
|
|
152
|
-
|
|
153
|
-
const client = new Anthropic({ apiKey });
|
|
154
|
-
const msg = await client.messages.create({
|
|
155
|
-
model: options?.model ?? 'claude-haiku-4-5-20251001',
|
|
156
|
-
max_tokens: 1024,
|
|
157
|
-
messages: [{ role: 'user', content: prompt }],
|
|
158
|
-
});
|
|
159
|
-
const block = msg.content[0];
|
|
160
|
-
content = (block?.type === 'text') ? block.text : '';
|
|
152
|
+
content = await claudeCliCall(prompt);
|
|
161
153
|
}
|
|
162
154
|
|
|
163
|
-
// 7. Persist to DB
|
|
155
|
+
// 7. Persist to DB — prepend OKF frontmatter if not already present
|
|
156
|
+
if (!content.startsWith('---')) {
|
|
157
|
+
const safeLabel = label.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
|
158
|
+
content = `---\ntype: Code Community Wiki\ntitle: "${safeLabel}"\ntags: [monograph, wiki]\ntimestamp: ${new Date().toISOString()}\n---\n\n${content}`;
|
|
159
|
+
}
|
|
164
160
|
upsertWikiPage(db, communityIdStr, content);
|
|
165
161
|
|
|
166
162
|
return content;
|