@monoes/monograph 1.2.2 → 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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@monoes/monograph",
3
- "version": "1.2.2",
3
+ "version": "1.2.3",
4
4
  "type": "module",
5
5
  "description": "Native TypeScript code intelligence engine for monomind",
6
6
  "main": "dist/src/index.js",
@@ -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
+ }
@@ -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, apiKey: string): Promise<Triple[] | null> {
40
+ async function callClaude(content: string): Promise<Triple[] | null> {
41
41
  try {
42
- const res = await fetch('https://api.anthropic.com/v1/messages', {
43
- method: 'POST',
44
- headers: {
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 (!apiKey || maxSections <= 0 || ctx.options.codeOnly) {
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,19 +145,11 @@ export async function generateWikiPage(
145
145
  const result = await callLLM(prompt, options.llmConfig);
146
146
  content = result.text;
147
147
  } else {
148
- const apiKey = options?.apiKey ?? process.env['ANTHROPIC_API_KEY'];
149
- if (!apiKey) {
150
- throw new Error('ANTHROPIC_API_KEY not set');
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
- const { default: Anthropic } = await import('@anthropic-ai/sdk');
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
155
  // 7. Persist to DB — prepend OKF frontmatter if not already present