mnueron 0.4.0 → 0.6.0
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/INSTALL.md +125 -0
- package/LICENSE +28 -21
- package/NOTICE +164 -0
- package/README.md +443 -422
- package/dist/cli.js +114 -26
- package/dist/cli.js.map +1 -1
- package/dist/config.js +4 -1
- package/dist/config.js.map +1 -1
- package/dist/detectors/codex.js +138 -0
- package/dist/detectors/codex.js.map +1 -0
- package/dist/detectors/index.js +2 -0
- package/dist/detectors/index.js.map +1 -1
- package/dist/detectors/json_detector.js +1 -1
- package/dist/lib/context-engine/confidence.js +153 -0
- package/dist/lib/context-engine/confidence.js.map +1 -0
- package/dist/lib/context-engine/entities.js +179 -0
- package/dist/lib/context-engine/entities.js.map +1 -0
- package/dist/lib/context-engine/index.js +74 -0
- package/dist/lib/context-engine/index.js.map +1 -0
- package/dist/lib/context-engine/intent.js +139 -0
- package/dist/lib/context-engine/intent.js.map +1 -0
- package/dist/lib/context-engine/runbook-detector.js +206 -0
- package/dist/lib/context-engine/runbook-detector.js.map +1 -0
- package/dist/lib/date-anchors.js +351 -0
- package/dist/lib/date-anchors.js.map +1 -0
- package/dist/lib/temporal-intent.js +98 -0
- package/dist/lib/temporal-intent.js.map +1 -0
- package/dist/runbook/auto-extract.js +415 -0
- package/dist/runbook/auto-extract.js.map +1 -0
- package/dist/runbook/capture.js +214 -0
- package/dist/runbook/capture.js.map +1 -0
- package/dist/runbook/explain.js +154 -0
- package/dist/runbook/explain.js.map +1 -0
- package/dist/runbook/fingerprint.js +128 -0
- package/dist/runbook/fingerprint.js.map +1 -0
- package/dist/runbook/search.js +76 -0
- package/dist/runbook/search.js.map +1 -0
- package/dist/runbook/types.js +15 -0
- package/dist/runbook/types.js.map +1 -0
- package/dist/setup.js +32 -4
- package/dist/setup.js.map +1 -1
- package/dist/store/entity-extractor.js +69 -2
- package/dist/store/entity-extractor.js.map +1 -1
- package/dist/store/local-db.js +33 -0
- package/dist/store/local-db.js.map +1 -0
- package/dist/store/local.js +230 -229
- package/dist/store/local.js.map +1 -1
- package/dist/store/procedural.js +185 -0
- package/dist/store/procedural.js.map +1 -1
- package/dist/store/remote.js +73 -0
- package/dist/store/remote.js.map +1 -1
- package/dist/tools.js +393 -8
- package/dist/tools.js.map +1 -1
- package/package.json +63 -55
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `mnueron runbook auto-extract` — read recent Cowork sessions and
|
|
3
|
+
* propose runbooks for any error→fix patterns mnueron already saw the
|
|
4
|
+
* AI solve in chat.
|
|
5
|
+
*
|
|
6
|
+
* The premise: every fix the user worked through with Claude is already
|
|
7
|
+
* in the local DB (via the `mnueron-cowork-daily-sync` scheduled task
|
|
8
|
+
* that runs every 4 hours). Asking the user to retype that into the
|
|
9
|
+
* `runbook capture` wizard is busywork. Instead, scan the recent
|
|
10
|
+
* memories, find error→fix pairs, hand them to an LLM to format as
|
|
11
|
+
* runbooks, and present a review UI with one-tap save.
|
|
12
|
+
*
|
|
13
|
+
* Pipeline:
|
|
14
|
+
* 1. Query memories where namespace LIKE 'claude-cowork%' since N hours ago
|
|
15
|
+
* 2. Group by source_ref (one session = one source_ref)
|
|
16
|
+
* 3. Walk each session chronologically — find error-shaped chunks
|
|
17
|
+
* 4. Auto-verify: a candidate counts as "fixed" if the next 1-2
|
|
18
|
+
* chunks don't share the same error fingerprint
|
|
19
|
+
* 5. LLM-extract each candidate into a structured runbook draft
|
|
20
|
+
* 6. Review UI: [s]ave / [e]dit-in-wizard / [k]ip per draft
|
|
21
|
+
* 7. Save approved drafts (with dedup against existing fingerprints)
|
|
22
|
+
*
|
|
23
|
+
* Cost: ~$0.001-0.005 per pass with Haiku, depending on session length.
|
|
24
|
+
* Dramatically cheaper than re-deriving the same fix two months later.
|
|
25
|
+
*/
|
|
26
|
+
import { createInterface } from 'node:readline';
|
|
27
|
+
import { randomUUID } from 'node:crypto';
|
|
28
|
+
import { platform } from 'node:os';
|
|
29
|
+
import { loadConfig } from '../config.js';
|
|
30
|
+
import { openLocalDb } from '../store/local-db.js';
|
|
31
|
+
import { redact } from '../store/redactor.js';
|
|
32
|
+
import { fingerprintError } from './fingerprint.js';
|
|
33
|
+
const ANTHROPIC_MODEL = 'claude-haiku-4-5';
|
|
34
|
+
const OPENAI_MODEL = 'gpt-4o-mini';
|
|
35
|
+
const TIMEOUT_MS = 30000;
|
|
36
|
+
const MAX_EXTRACT_CHARS = 6000; // truncate per-candidate LLM context
|
|
37
|
+
export async function cmdRunbookAutoExtract(args) {
|
|
38
|
+
const opts = parseArgs(args);
|
|
39
|
+
const cfg = loadConfig();
|
|
40
|
+
const db = openLocalDb(cfg.dbPath);
|
|
41
|
+
console.log('');
|
|
42
|
+
console.log(`Scanning ${opts.namespace}* memories from the last ${opts.sinceHours}h...`);
|
|
43
|
+
// 1. Pull recent memories in scope
|
|
44
|
+
const sinceMs = Date.now() - opts.sinceHours * 60 * 60 * 1000;
|
|
45
|
+
const rows = db
|
|
46
|
+
.prepare(`SELECT id, namespace, content, source, source_ref, created_at, meta_json
|
|
47
|
+
FROM memories
|
|
48
|
+
WHERE namespace LIKE ?
|
|
49
|
+
AND created_at > ?
|
|
50
|
+
ORDER BY source_ref, created_at
|
|
51
|
+
LIMIT 1000`)
|
|
52
|
+
.all(`${opts.namespace}%`, sinceMs);
|
|
53
|
+
if (rows.length === 0) {
|
|
54
|
+
console.log('');
|
|
55
|
+
console.log(`No memories found in last ${opts.sinceHours}h.`);
|
|
56
|
+
console.log('');
|
|
57
|
+
console.log(`Verify Cowork import is working:`);
|
|
58
|
+
console.log(` npx tsx src/cli.ts import --claude-cowork --probe`);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
console.log(`Loaded ${rows.length} memory chunks across ${countDistinct(rows.map((r) => r.source_ref))} session(s).`);
|
|
62
|
+
// 2. Group by source_ref (one session = one source_ref)
|
|
63
|
+
const sessions = new Map();
|
|
64
|
+
for (const r of rows) {
|
|
65
|
+
const k = r.source_ref ?? `(no-ref-${r.id})`;
|
|
66
|
+
const list = sessions.get(k) ?? [];
|
|
67
|
+
list.push(r);
|
|
68
|
+
sessions.set(k, list);
|
|
69
|
+
}
|
|
70
|
+
// 3. Find candidate error→fix patterns
|
|
71
|
+
const candidates = [];
|
|
72
|
+
for (const session of sessions.values()) {
|
|
73
|
+
for (let i = 0; i < session.length; i++) {
|
|
74
|
+
const content = session[i].content;
|
|
75
|
+
if (!isErrorShaped(content))
|
|
76
|
+
continue;
|
|
77
|
+
const fp = fingerprintError(content);
|
|
78
|
+
// Auto-verify: next 1-2 chunks should NOT repeat the same fingerprint
|
|
79
|
+
const next = session.slice(i + 1, i + 3);
|
|
80
|
+
const stillFailing = next.some((t) => isErrorShaped(t.content) && fingerprintError(t.content).hash === fp.hash);
|
|
81
|
+
candidates.push({ session, errorIdx: i, fingerprint: fp, autoVerified: !stillFailing });
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
console.log(`Found ${candidates.length} candidate error→fix pattern(s).`);
|
|
85
|
+
// Dedup candidates against existing runbooks AND against each other
|
|
86
|
+
const existingHashes = new Set(db
|
|
87
|
+
.prepare(`SELECT error_fingerprints FROM procedural_memories WHERE error_fingerprints IS NOT NULL`)
|
|
88
|
+
.all()
|
|
89
|
+
.flatMap((r) => {
|
|
90
|
+
try {
|
|
91
|
+
const v = JSON.parse(r.error_fingerprints);
|
|
92
|
+
return Array.isArray(v) ? v : [];
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
return [];
|
|
96
|
+
}
|
|
97
|
+
}));
|
|
98
|
+
const seenInPass = new Set();
|
|
99
|
+
const novel = [];
|
|
100
|
+
for (const c of candidates) {
|
|
101
|
+
if (existingHashes.has(c.fingerprint.hash))
|
|
102
|
+
continue;
|
|
103
|
+
if (seenInPass.has(c.fingerprint.hash))
|
|
104
|
+
continue;
|
|
105
|
+
seenInPass.add(c.fingerprint.hash);
|
|
106
|
+
novel.push(c);
|
|
107
|
+
if (novel.length >= opts.limit)
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
console.log(`${novel.length} new pattern(s) to extract (existing matches dedupped).`);
|
|
111
|
+
if (novel.length === 0) {
|
|
112
|
+
console.log('');
|
|
113
|
+
console.log('Either you already have runbooks for everything, or no errors were detected.');
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
// 4. LLM-extract each candidate
|
|
117
|
+
console.log('');
|
|
118
|
+
console.log('Extracting drafts via LLM (this can take 30-60s)...');
|
|
119
|
+
const drafts = [];
|
|
120
|
+
for (let i = 0; i < novel.length; i++) {
|
|
121
|
+
const c = novel[i];
|
|
122
|
+
process.stdout.write(` [${i + 1}/${novel.length}] ${c.fingerprint.hash}... `);
|
|
123
|
+
try {
|
|
124
|
+
const draft = await extractDraft(c);
|
|
125
|
+
if (draft) {
|
|
126
|
+
drafts.push({ draft, candidate: c });
|
|
127
|
+
process.stdout.write(`✓ "${draft.name}"\n`);
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
process.stdout.write('skipped (no fix found)\n');
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
catch (e) {
|
|
134
|
+
process.stdout.write(`error: ${e.message}\n`);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
if (drafts.length === 0) {
|
|
138
|
+
console.log('');
|
|
139
|
+
console.log('LLM extraction returned no usable runbooks. Try again later when more chat history accumulates.');
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
// 5. Review UI (unless --auto-save)
|
|
143
|
+
if (opts.dryRun) {
|
|
144
|
+
console.log('');
|
|
145
|
+
console.log(`Would save ${drafts.length} runbook(s):`);
|
|
146
|
+
for (const { draft } of drafts) {
|
|
147
|
+
console.log(` • [${draft.tool ?? 'unknown'}] ${draft.name} — ${draft.summary}`);
|
|
148
|
+
}
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
let savedCount = 0;
|
|
152
|
+
let skippedCount = 0;
|
|
153
|
+
if (opts.autoSave) {
|
|
154
|
+
for (const { draft, candidate } of drafts) {
|
|
155
|
+
saveDraft(db, draft, candidate, opts.targetNs);
|
|
156
|
+
savedCount++;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
161
|
+
try {
|
|
162
|
+
console.log('');
|
|
163
|
+
console.log(`Review ${drafts.length} draft runbook(s):`);
|
|
164
|
+
console.log('');
|
|
165
|
+
for (const [i, { draft, candidate }] of drafts.entries()) {
|
|
166
|
+
console.log(`── Draft ${i + 1}/${drafts.length} ─────────────────────────────────`);
|
|
167
|
+
console.log(` name: ${draft.name}`);
|
|
168
|
+
console.log(` summary: ${draft.summary}`);
|
|
169
|
+
console.log(` tool: ${draft.tool ?? '(unknown)'}`);
|
|
170
|
+
console.log(` fingerprint: ${draft.fingerprintHash}`);
|
|
171
|
+
console.log(` verified: ${draft.fixWorked ? 'yes (auto)' : 'no'}`);
|
|
172
|
+
console.log(` command: ${draft.failingCommand.slice(0, 80)}`);
|
|
173
|
+
console.log(` steps (${draft.steps.length}):`);
|
|
174
|
+
for (const [si, s] of draft.steps.entries()) {
|
|
175
|
+
const code = s.code ? ` → ${s.code.slice(0, 60)}` : '';
|
|
176
|
+
console.log(` ${si + 1}. ${s.step}${code}`);
|
|
177
|
+
}
|
|
178
|
+
const choice = (await ask(rl, ' [s]ave / [k]ip / [a]ll remaining ? ')).trim().toLowerCase();
|
|
179
|
+
if (choice === 'a') {
|
|
180
|
+
for (const remaining of drafts.slice(i)) {
|
|
181
|
+
saveDraft(db, remaining.draft, remaining.candidate, opts.targetNs);
|
|
182
|
+
savedCount++;
|
|
183
|
+
}
|
|
184
|
+
break;
|
|
185
|
+
}
|
|
186
|
+
else if (choice === 'k' || choice === 'skip') {
|
|
187
|
+
skippedCount++;
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
// Default = save
|
|
191
|
+
saveDraft(db, draft, candidate, opts.targetNs);
|
|
192
|
+
savedCount++;
|
|
193
|
+
}
|
|
194
|
+
console.log('');
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
finally {
|
|
198
|
+
rl.close();
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
console.log('');
|
|
202
|
+
console.log(`Done — saved ${savedCount}, skipped ${skippedCount} of ${drafts.length}.`);
|
|
203
|
+
if (savedCount > 0) {
|
|
204
|
+
console.log(`Try: mnueron runbook list --ns ${opts.targetNs}`);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
// ── Helpers ─────────────────────────────────────────────────────────────────
|
|
208
|
+
function parseArgs(args) {
|
|
209
|
+
const out = {
|
|
210
|
+
sinceHours: 24,
|
|
211
|
+
namespace: 'claude-cowork',
|
|
212
|
+
limit: 10,
|
|
213
|
+
dryRun: false,
|
|
214
|
+
autoSave: false,
|
|
215
|
+
targetNs: 'mnueron',
|
|
216
|
+
};
|
|
217
|
+
for (let i = 0; i < args.length; i++) {
|
|
218
|
+
const a = args[i];
|
|
219
|
+
if (a === '--since' && args[i + 1])
|
|
220
|
+
out.sinceHours = Math.max(1, Number(args[++i]) || 24);
|
|
221
|
+
else if (a === '--from-ns' && args[i + 1])
|
|
222
|
+
out.namespace = args[++i];
|
|
223
|
+
else if (a === '--ns' && args[i + 1])
|
|
224
|
+
out.targetNs = args[++i];
|
|
225
|
+
else if (a === '--limit' && args[i + 1])
|
|
226
|
+
out.limit = Math.max(1, Number(args[++i]) || 10);
|
|
227
|
+
else if (a === '--dry-run')
|
|
228
|
+
out.dryRun = true;
|
|
229
|
+
else if (a === '--auto-save')
|
|
230
|
+
out.autoSave = true;
|
|
231
|
+
else if (a === '--help' || a === '-h') {
|
|
232
|
+
console.log(`mnueron runbook auto-extract — generate runbooks from recent Cowork chats
|
|
233
|
+
|
|
234
|
+
--since <hours> How far back to scan (default 24)
|
|
235
|
+
--from-ns <name> Source namespace prefix (default 'claude-cowork')
|
|
236
|
+
--ns <name> Where to save the runbooks (default 'mnueron')
|
|
237
|
+
--limit <n> Max drafts to extract per pass (default 10)
|
|
238
|
+
--dry-run List candidates without LLM extraction or save
|
|
239
|
+
--auto-save Skip review UI; save all extracted drafts
|
|
240
|
+
-h, --help Show this help`);
|
|
241
|
+
process.exit(0);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
return out;
|
|
245
|
+
}
|
|
246
|
+
/** Heuristic: does this content look like an error/stderr block? */
|
|
247
|
+
function isErrorShaped(content) {
|
|
248
|
+
// Skip very short or very long chunks — likely not error pastes
|
|
249
|
+
if (!content || content.length < 30 || content.length > 8000)
|
|
250
|
+
return false;
|
|
251
|
+
return (/\b(error|fatal|failed?|exception|panic|sqlstate)\b[: \t]/i.test(content) ||
|
|
252
|
+
/^\s*[+>]\s*.*\n.*(~~|error|fail)/im.test(content) ||
|
|
253
|
+
/\btraceback\b|\bcannot find\b|\bis not recognized\b|\bnot a valid\b/i.test(content));
|
|
254
|
+
}
|
|
255
|
+
function countDistinct(arr) {
|
|
256
|
+
return new Set(arr.filter((x) => x != null)).size;
|
|
257
|
+
}
|
|
258
|
+
function ask(rl, prompt) {
|
|
259
|
+
return new Promise((resolve) => rl.question(prompt, (a) => resolve(a)));
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Call an LLM (Haiku preferred, gpt-4o-mini fallback) with a focused
|
|
263
|
+
* extraction prompt. Returns null if the LLM judges the candidate too
|
|
264
|
+
* weak to formalize.
|
|
265
|
+
*/
|
|
266
|
+
async function extractDraft(c) {
|
|
267
|
+
// Build LLM context: 1 chunk before error + error + 2 chunks after
|
|
268
|
+
const slice = c.session.slice(Math.max(0, c.errorIdx - 1), c.errorIdx + 3);
|
|
269
|
+
const transcript = slice
|
|
270
|
+
.map((r, i) => {
|
|
271
|
+
const role = r.source === 'cowork-user' || /^user\b/.test(r.source) ? 'USER' : 'ASSISTANT';
|
|
272
|
+
const marker = i === Math.min(c.errorIdx, 1) ? ' (← error chunk)' : '';
|
|
273
|
+
return `### ${role}${marker}\n${redact(r.content).content.slice(0, 1500)}`;
|
|
274
|
+
})
|
|
275
|
+
.join('\n\n');
|
|
276
|
+
const truncated = transcript.length > MAX_EXTRACT_CHARS
|
|
277
|
+
? transcript.slice(0, MAX_EXTRACT_CHARS) + '\n... [truncated]'
|
|
278
|
+
: transcript;
|
|
279
|
+
const prompt = `You are extracting a "runbook" from a chat transcript between a user and an AI assistant. The user hit an error and the assistant proposed a fix.
|
|
280
|
+
|
|
281
|
+
Return STRICT JSON only, no prose. If the transcript doesn't actually contain an error+fix pair (or if the fix isn't concrete enough to act on), return {"skip": true}.
|
|
282
|
+
|
|
283
|
+
Schema:
|
|
284
|
+
{
|
|
285
|
+
"name": "short kebab-case name like 'fix-git-index-lock'",
|
|
286
|
+
"summary": "one sentence — what this runbook fixes",
|
|
287
|
+
"failingCommand": "the command the user ran that produced the error",
|
|
288
|
+
"errorText": "the key error message (≤200 chars; copy from the transcript)",
|
|
289
|
+
"steps": [
|
|
290
|
+
{"step": "imperative description", "code": "optional exact command/snippet"}
|
|
291
|
+
],
|
|
292
|
+
"fixWorked": true | false,
|
|
293
|
+
"tool": "git" | "npm" | "supabase" | "postgres" | "typescript" | "powershell" | "docker" | "kubectl" | "python" | "node" | null
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
Transcript:
|
|
297
|
+
${truncated}
|
|
298
|
+
|
|
299
|
+
Return JSON now:`;
|
|
300
|
+
const json = await callLLM(prompt);
|
|
301
|
+
if (!json)
|
|
302
|
+
return null;
|
|
303
|
+
let parsed;
|
|
304
|
+
try {
|
|
305
|
+
parsed = JSON.parse(json);
|
|
306
|
+
}
|
|
307
|
+
catch {
|
|
308
|
+
return null;
|
|
309
|
+
}
|
|
310
|
+
if (parsed.skip === true)
|
|
311
|
+
return null;
|
|
312
|
+
if (!parsed.name || !parsed.steps || !Array.isArray(parsed.steps) || parsed.steps.length === 0) {
|
|
313
|
+
return null;
|
|
314
|
+
}
|
|
315
|
+
const steps = parsed.steps
|
|
316
|
+
.filter((s) => s && typeof s.step === 'string')
|
|
317
|
+
.map((s) => ({
|
|
318
|
+
step: String(s.step).slice(0, 500),
|
|
319
|
+
...(typeof s.code === 'string' ? { code: s.code.slice(0, 1000) } : {}),
|
|
320
|
+
}))
|
|
321
|
+
.slice(0, 30);
|
|
322
|
+
if (steps.length === 0)
|
|
323
|
+
return null;
|
|
324
|
+
return {
|
|
325
|
+
name: String(parsed.name).toLowerCase().replace(/[^a-z0-9-]+/g, '-').slice(0, 64),
|
|
326
|
+
summary: String(parsed.summary ?? '').slice(0, 300),
|
|
327
|
+
failingCommand: String(parsed.failingCommand ?? '').slice(0, 500),
|
|
328
|
+
errorText: c.fingerprint.redactedOriginal.slice(0, 4000),
|
|
329
|
+
steps,
|
|
330
|
+
// Trust LLM judgment, but if our auto-verify says yes too, bias to yes
|
|
331
|
+
fixWorked: Boolean(parsed.fixWorked) || c.autoVerified,
|
|
332
|
+
fingerprintHash: c.fingerprint.hash,
|
|
333
|
+
tool: parsed.tool ?? c.fingerprint.tool ?? undefined,
|
|
334
|
+
sourceSessionRef: c.session[c.errorIdx].source_ref,
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
/** Save a draft to procedural_memories, dedup by fingerprint. */
|
|
338
|
+
function saveDraft(db, draft, _candidate, targetNs) {
|
|
339
|
+
// One last dedup check inside the same pass
|
|
340
|
+
const existing = db
|
|
341
|
+
.prepare(`SELECT id FROM procedural_memories WHERE error_fingerprints LIKE ? LIMIT 1`)
|
|
342
|
+
.get(`%"${draft.fingerprintHash}"%`);
|
|
343
|
+
if (existing)
|
|
344
|
+
return; // someone else won the race; skip silently
|
|
345
|
+
const now = Date.now();
|
|
346
|
+
db.prepare(`INSERT INTO procedural_memories
|
|
347
|
+
(id, namespace, name, summary, steps_json, tools_json, last_used_at, use_count, created_at,
|
|
348
|
+
trigger_phrases, error_fingerprints, verified, verified_at, os, tool,
|
|
349
|
+
success_count, failure_count, error_text, failing_command)
|
|
350
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, 0, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(randomUUID(), targetNs, draft.name, draft.summary || `Fix for ${draft.tool ?? 'an unknown'} error`, JSON.stringify(draft.steps), JSON.stringify(draft.tool ? [draft.tool] : []), now, now, JSON.stringify([]), JSON.stringify([draft.fingerprintHash]), draft.fixWorked ? 1 : 0, draft.fixWorked ? now : null, platform(), draft.tool ?? null, draft.fixWorked ? 1 : 0, 0, draft.errorText, draft.failingCommand);
|
|
351
|
+
}
|
|
352
|
+
/**
|
|
353
|
+
* LLM call with Haiku preferred / OpenAI fallback. Mirrors the pattern
|
|
354
|
+
* in store/procedural.ts:extractProcedural so behavior + key handling
|
|
355
|
+
* stay consistent across the codebase.
|
|
356
|
+
*/
|
|
357
|
+
async function callLLM(prompt) {
|
|
358
|
+
const anthropicKey = process.env.ANTHROPIC_API_KEY;
|
|
359
|
+
const openaiKey = process.env.OPENAI_API_KEY;
|
|
360
|
+
if (!anthropicKey && !openaiKey) {
|
|
361
|
+
throw new Error('auto-extract requires ANTHROPIC_API_KEY or OPENAI_API_KEY env var (no LLM key configured)');
|
|
362
|
+
}
|
|
363
|
+
const controller = new AbortController();
|
|
364
|
+
const timer = setTimeout(() => controller.abort(), TIMEOUT_MS);
|
|
365
|
+
try {
|
|
366
|
+
if (anthropicKey) {
|
|
367
|
+
const resp = await fetch('https://api.anthropic.com/v1/messages', {
|
|
368
|
+
method: 'POST',
|
|
369
|
+
headers: {
|
|
370
|
+
'content-type': 'application/json',
|
|
371
|
+
'x-api-key': anthropicKey,
|
|
372
|
+
'anthropic-version': '2023-06-01',
|
|
373
|
+
},
|
|
374
|
+
body: JSON.stringify({
|
|
375
|
+
model: ANTHROPIC_MODEL,
|
|
376
|
+
max_tokens: 1500,
|
|
377
|
+
temperature: 0,
|
|
378
|
+
messages: [{ role: 'user', content: prompt }],
|
|
379
|
+
}),
|
|
380
|
+
signal: controller.signal,
|
|
381
|
+
});
|
|
382
|
+
if (!resp.ok)
|
|
383
|
+
throw new Error(`anthropic ${resp.status}`);
|
|
384
|
+
const data = (await resp.json());
|
|
385
|
+
const text = data.content?.[0]?.text ?? '';
|
|
386
|
+
return extractJsonBlob(text);
|
|
387
|
+
}
|
|
388
|
+
const resp = await fetch('https://api.openai.com/v1/chat/completions', {
|
|
389
|
+
method: 'POST',
|
|
390
|
+
headers: { 'content-type': 'application/json', authorization: `Bearer ${openaiKey}` },
|
|
391
|
+
body: JSON.stringify({
|
|
392
|
+
model: OPENAI_MODEL,
|
|
393
|
+
max_tokens: 1500,
|
|
394
|
+
temperature: 0,
|
|
395
|
+
messages: [{ role: 'user', content: prompt }],
|
|
396
|
+
response_format: { type: 'json_object' },
|
|
397
|
+
}),
|
|
398
|
+
signal: controller.signal,
|
|
399
|
+
});
|
|
400
|
+
if (!resp.ok)
|
|
401
|
+
throw new Error(`openai ${resp.status}`);
|
|
402
|
+
const data = (await resp.json());
|
|
403
|
+
const text = data.choices?.[0]?.message?.content ?? '';
|
|
404
|
+
return extractJsonBlob(text);
|
|
405
|
+
}
|
|
406
|
+
finally {
|
|
407
|
+
clearTimeout(timer);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
/** Pull the first {...} JSON blob out of a possibly chatty LLM response. */
|
|
411
|
+
function extractJsonBlob(text) {
|
|
412
|
+
const match = text.match(/\{[\s\S]*\}/);
|
|
413
|
+
return match ? match[0] : null;
|
|
414
|
+
}
|
|
415
|
+
//# sourceMappingURL=auto-extract.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auto-extract.js","sourceRoot":"","sources":["../../src/runbook/auto-extract.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `mnueron runbook capture` — interactive wizard for saving a runbook
|
|
3
|
+
* after the user has solved a problem.
|
|
4
|
+
*
|
|
5
|
+
* Five-prompt sequence:
|
|
6
|
+
* 1. The command that was failing
|
|
7
|
+
* 2. The error text (gets redacted automatically; user sees what was scrubbed)
|
|
8
|
+
* 3. The fix steps (multi-line, one step per line; blank line to end)
|
|
9
|
+
* 4. Did the fix actually work? (yes → verified; no → save as a failed-attempt note)
|
|
10
|
+
* 5. Save as a new runbook? (with dedup-prompt if a fingerprint matches existing)
|
|
11
|
+
*
|
|
12
|
+
* Storage:
|
|
13
|
+
* - Fingerprint of the (redacted) error is computed and stored on the
|
|
14
|
+
* runbook so future `mnueron explain-error` matches it.
|
|
15
|
+
* - Steps land in steps_json. Failing command + redacted error in the
|
|
16
|
+
* new failing_command + error_text columns.
|
|
17
|
+
* - `verified` is set to true only when the user confirmed fix worked.
|
|
18
|
+
* - Dedup: if an existing runbook already has this fingerprint, we
|
|
19
|
+
* default to UPDATING (append step, bump counters) rather than
|
|
20
|
+
* creating a duplicate row.
|
|
21
|
+
*/
|
|
22
|
+
import { createInterface } from 'node:readline';
|
|
23
|
+
import { randomUUID } from 'node:crypto';
|
|
24
|
+
import { platform } from 'node:os';
|
|
25
|
+
import { loadConfig } from '../config.js';
|
|
26
|
+
import { openLocalDb } from '../store/local-db.js';
|
|
27
|
+
import { recordRunbookOutcome } from '../store/procedural.js';
|
|
28
|
+
import { fingerprintError } from './fingerprint.js';
|
|
29
|
+
export async function cmdRunbookCapture(args) {
|
|
30
|
+
const opts = parseArgs(args);
|
|
31
|
+
const cfg = loadConfig();
|
|
32
|
+
const db = openLocalDb(cfg.dbPath);
|
|
33
|
+
// ensureProceduralSchema is called inside openLocalDb — no need to call here
|
|
34
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
35
|
+
try {
|
|
36
|
+
console.log('');
|
|
37
|
+
console.log('mnueron runbook capture');
|
|
38
|
+
console.log('───────────────────────');
|
|
39
|
+
console.log('Five questions. Press Enter for blank to skip optional ones.');
|
|
40
|
+
console.log('');
|
|
41
|
+
const failingCommand = await ask(rl, '1. What command were you running?\n > ');
|
|
42
|
+
if (!failingCommand.trim()) {
|
|
43
|
+
console.error('A failing command is required.');
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
console.log('');
|
|
47
|
+
console.log('2. What error did you hit? (paste multi-line, blank line to end)');
|
|
48
|
+
const errorText = await askMultiLine(rl);
|
|
49
|
+
if (!errorText.trim()) {
|
|
50
|
+
console.error('Error text is required.');
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
const fp = fingerprintError(errorText);
|
|
54
|
+
console.log('');
|
|
55
|
+
console.log(` Fingerprint: ${fp.hash}${fp.tool ? ` (tool: ${fp.tool})` : ''}`);
|
|
56
|
+
if (fp.redactedCount > 0) {
|
|
57
|
+
console.log(` Redacted ${fp.redactedCount} secret${fp.redactedCount > 1 ? 's' : ''}: ${fp.redactedKinds.join(', ')} — they won't be stored.`);
|
|
58
|
+
}
|
|
59
|
+
// Dedup probe
|
|
60
|
+
const dedup = db
|
|
61
|
+
.prepare(`SELECT id, name, summary FROM procedural_memories
|
|
62
|
+
WHERE error_fingerprints LIKE ?
|
|
63
|
+
LIMIT 1`)
|
|
64
|
+
.get(`%"${fp.hash}"%`);
|
|
65
|
+
if (dedup) {
|
|
66
|
+
console.log('');
|
|
67
|
+
console.log(` ℹ Existing runbook matches this fingerprint: "${dedup.name}"`);
|
|
68
|
+
console.log(` ${dedup.summary}`);
|
|
69
|
+
const choice = await ask(rl, ' Update it instead of creating new? [Y/n] ');
|
|
70
|
+
if (choice.trim().toLowerCase() !== 'n') {
|
|
71
|
+
await updateExisting(rl, dedup.id, fp, failingCommand);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
console.log('');
|
|
76
|
+
console.log('3. What fix did you apply? (one step per line, blank line to end)');
|
|
77
|
+
const steps = await askSteps(rl);
|
|
78
|
+
if (steps.length === 0) {
|
|
79
|
+
console.error('At least one step is required.');
|
|
80
|
+
process.exit(1);
|
|
81
|
+
}
|
|
82
|
+
console.log('');
|
|
83
|
+
const fixWorked = await askYesNo(rl, '4. Did this fix actually resolve the issue?', true);
|
|
84
|
+
console.log('');
|
|
85
|
+
const defaultName = suggestName(fp, failingCommand);
|
|
86
|
+
const nameInput = await ask(rl, `5. Name for this runbook [${defaultName}]: `);
|
|
87
|
+
const name = nameInput.trim() || defaultName;
|
|
88
|
+
const summary = await ask(rl, ` One-line summary [${suggestSummary(fp, failingCommand)}]: `);
|
|
89
|
+
const namespace = opts.namespace ?? cfg.defaultNamespace ?? 'mnueron';
|
|
90
|
+
// Final confirmation
|
|
91
|
+
console.log('');
|
|
92
|
+
console.log('About to save:');
|
|
93
|
+
console.log(` namespace: ${namespace}`);
|
|
94
|
+
console.log(` name: ${name}`);
|
|
95
|
+
console.log(` summary: ${summary || suggestSummary(fp, failingCommand)}`);
|
|
96
|
+
console.log(` tool: ${fp.tool ?? '(unknown)'}`);
|
|
97
|
+
console.log(` fingerprint:${fp.hash}`);
|
|
98
|
+
console.log(` verified: ${fixWorked ? 'yes' : 'no — saved as attempted fix'}`);
|
|
99
|
+
console.log(` steps:`);
|
|
100
|
+
for (const [i, s] of steps.entries()) {
|
|
101
|
+
console.log(` ${i + 1}. ${s.step}${s.code ? ` → ${s.code.slice(0, 60)}` : ''}`);
|
|
102
|
+
}
|
|
103
|
+
const proceed = await askYesNo(rl, 'Save? [Y/n]', true);
|
|
104
|
+
if (!proceed) {
|
|
105
|
+
console.log('Cancelled.');
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
const id = randomUUID();
|
|
109
|
+
const now = Date.now();
|
|
110
|
+
db.prepare(`INSERT INTO procedural_memories
|
|
111
|
+
(id, namespace, name, summary, steps_json, tools_json, last_used_at, use_count, created_at,
|
|
112
|
+
trigger_phrases, error_fingerprints, verified, verified_at, os, tool,
|
|
113
|
+
success_count, failure_count, error_text, failing_command)
|
|
114
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, 1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(id, namespace, name, summary || suggestSummary(fp, failingCommand), JSON.stringify(steps), JSON.stringify(fp.tool ? [fp.tool] : []), now, now, JSON.stringify([]), // trigger_phrases empty for MVP — LLM-extracted in Phase 2
|
|
115
|
+
JSON.stringify([fp.hash]), fixWorked ? 1 : 0, fixWorked ? now : null, platform(), fp.tool ?? null, fixWorked ? 1 : 0, fixWorked ? 0 : 1, fp.redactedOriginal, failingCommand);
|
|
116
|
+
console.log('');
|
|
117
|
+
console.log(`✓ Saved runbook ${id.slice(0, 8)} — "${name}"`);
|
|
118
|
+
if (fixWorked) {
|
|
119
|
+
console.log(' Marked verified. Future explain-error matches will surface this.');
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
console.log(' Saved as attempted fix (failure_count=1). Capture again when you find the real fix.');
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
finally {
|
|
126
|
+
rl.close();
|
|
127
|
+
}
|
|
128
|
+
async function updateExisting(rl, runbookId, fp, failingCommand) {
|
|
129
|
+
console.log('');
|
|
130
|
+
console.log('3. What additional fix step did you apply? (blank line when done)');
|
|
131
|
+
const steps = await askSteps(rl);
|
|
132
|
+
console.log('');
|
|
133
|
+
const fixWorked = await askYesNo(rl, '4. Did this attempt resolve the issue?', true);
|
|
134
|
+
recordRunbookOutcome(db, runbookId, {
|
|
135
|
+
fingerprintHash: fp.hash,
|
|
136
|
+
outcome: fixWorked ? 'success' : 'failure',
|
|
137
|
+
verified: fixWorked ? true : undefined,
|
|
138
|
+
os: platform(),
|
|
139
|
+
tool: fp.tool,
|
|
140
|
+
failingCommand,
|
|
141
|
+
errorText: fp.redactedOriginal,
|
|
142
|
+
extraSteps: steps.length > 0 ? steps : undefined,
|
|
143
|
+
});
|
|
144
|
+
console.log('');
|
|
145
|
+
console.log(`✓ Updated runbook ${runbookId.slice(0, 8)}.`);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
function parseArgs(args) {
|
|
149
|
+
const out = {};
|
|
150
|
+
for (let i = 0; i < args.length; i++) {
|
|
151
|
+
const a = args[i];
|
|
152
|
+
if (a === '--ns' || a === '--namespace')
|
|
153
|
+
out.namespace = args[++i];
|
|
154
|
+
else if (a === '--help' || a === '-h') {
|
|
155
|
+
console.log(`mnueron runbook capture — save a runbook after fixing an issue.
|
|
156
|
+
|
|
157
|
+
--ns <name> Target namespace (default from config, falls back to "mnueron")
|
|
158
|
+
-h, --help Show this help`);
|
|
159
|
+
process.exit(0);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return out;
|
|
163
|
+
}
|
|
164
|
+
function ask(rl, prompt) {
|
|
165
|
+
return new Promise((resolve) => rl.question(prompt, (a) => resolve(a)));
|
|
166
|
+
}
|
|
167
|
+
async function askMultiLine(rl) {
|
|
168
|
+
const lines = [];
|
|
169
|
+
while (true) {
|
|
170
|
+
const line = await ask(rl, ' ');
|
|
171
|
+
if (line === '')
|
|
172
|
+
break;
|
|
173
|
+
lines.push(line);
|
|
174
|
+
}
|
|
175
|
+
return lines.join('\n');
|
|
176
|
+
}
|
|
177
|
+
async function askSteps(rl) {
|
|
178
|
+
const steps = [];
|
|
179
|
+
let n = 1;
|
|
180
|
+
while (true) {
|
|
181
|
+
const line = await ask(rl, ` ${n}. `);
|
|
182
|
+
if (line.trim() === '')
|
|
183
|
+
break;
|
|
184
|
+
// Support "description → code" or "description: code"
|
|
185
|
+
const sepMatch = line.match(/^(.+?)\s*(?:→|->|:)\s*(.+)$/);
|
|
186
|
+
if (sepMatch) {
|
|
187
|
+
steps.push({ step: sepMatch[1].trim(), code: sepMatch[2].trim() });
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
steps.push({ step: line.trim() });
|
|
191
|
+
}
|
|
192
|
+
n++;
|
|
193
|
+
}
|
|
194
|
+
return steps;
|
|
195
|
+
}
|
|
196
|
+
async function askYesNo(rl, prompt, defaultYes) {
|
|
197
|
+
const ans = await ask(rl, prompt + ' ');
|
|
198
|
+
if (!ans.trim())
|
|
199
|
+
return defaultYes;
|
|
200
|
+
return /^y(es)?$/i.test(ans.trim());
|
|
201
|
+
}
|
|
202
|
+
function suggestName(fp, failingCommand) {
|
|
203
|
+
// Heuristic: "fix-<tool>-<first-word-of-command>"
|
|
204
|
+
const cmd = failingCommand.trim().split(/\s+/)[0] || 'cmd';
|
|
205
|
+
const tool = fp.tool ?? cmd;
|
|
206
|
+
return `fix-${tool}-${fp.hash.slice(0, 6)}`;
|
|
207
|
+
}
|
|
208
|
+
function suggestSummary(fp, failingCommand) {
|
|
209
|
+
const cmd = failingCommand.trim().split(/\s+/).slice(0, 2).join(' ');
|
|
210
|
+
return fp.tool
|
|
211
|
+
? `Fix ${fp.tool} error when running '${cmd}'`
|
|
212
|
+
: `Fix error when running '${cmd}'`;
|
|
213
|
+
}
|
|
214
|
+
//# sourceMappingURL=capture.js.map
|