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,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context-intent classifier.
|
|
3
|
+
*
|
|
4
|
+
* Given the user's active context (current text in editor, chat input,
|
|
5
|
+
* doc, etc.), classify WHAT KIND of task they're doing:
|
|
6
|
+
*
|
|
7
|
+
* coding — writing/editing source code
|
|
8
|
+
* deploying — release / rollout / production push
|
|
9
|
+
* debugging — investigating an error, reading logs, fixing a bug
|
|
10
|
+
* testing — writing test cases, QA, validation
|
|
11
|
+
* documenting — readme, comments, API docs, runbooks
|
|
12
|
+
* planning — design doc, roadmap, sprint planning, architecture
|
|
13
|
+
* temporal — questions about when things happened (reuses temporal-intent)
|
|
14
|
+
* none — fallback when no strong signal
|
|
15
|
+
*
|
|
16
|
+
* This is the gate for everything downstream: which namespace to search,
|
|
17
|
+
* which kind of recall to prefer (memories vs runbooks), what confidence
|
|
18
|
+
* threshold to apply.
|
|
19
|
+
*
|
|
20
|
+
* Pure module — no I/O, no DB, no LLM. Trivially unit-testable. The
|
|
21
|
+
* patterns are tuned to be HIGH PRECISION (false positives are worse
|
|
22
|
+
* than misses for the "should I surface a suggestion?" use case).
|
|
23
|
+
*/
|
|
24
|
+
import { isTemporalQuery } from '../temporal-intent.js';
|
|
25
|
+
/**
|
|
26
|
+
* Patterns per intent kind. Order matters within an array — the first
|
|
27
|
+
* match wins for tie-breaking when two kinds score equal.
|
|
28
|
+
*
|
|
29
|
+
* Each entry has a regex + a weight 0-1. Multiple matches in the same
|
|
30
|
+
* kind compound (capped at 1.0). The kind with the highest total weight
|
|
31
|
+
* wins.
|
|
32
|
+
*/
|
|
33
|
+
const SIGNALS = {
|
|
34
|
+
coding: [
|
|
35
|
+
// Source-code markers
|
|
36
|
+
{ re: /```(?:typescript|javascript|tsx?|jsx?|python|py|go|rust|java|c\+\+|cpp|csharp|cs|ruby|rb|php|swift|kotlin|scala|sql)\b/i, w: 0.6, label: 'fenced-code-lang' },
|
|
37
|
+
{ re: /\b(?:function|const|let|var|import|export|class|interface|def|async|await)\s+[a-z_]/i, w: 0.45, label: 'language-keyword' },
|
|
38
|
+
{ re: /\b(?:return|throw|try|catch|if|else|for|while|switch)\b.*[{(]/i, w: 0.25, label: 'control-flow' },
|
|
39
|
+
{ re: /\.(?:then|catch|map|filter|reduce|forEach)\b/, w: 0.35, label: 'method-chain' },
|
|
40
|
+
{ re: /\b(?:writing|coding|implementing|refactoring|fixing|adding)\s+(?:a\s+)?(?:function|method|class|component|endpoint|api|route|handler)\b/i, w: 0.55, label: 'verb-noun' },
|
|
41
|
+
// File paths in src/, components/, lib/, etc.
|
|
42
|
+
{ re: /\b(?:src|components|lib|hooks|utils|services|controllers|routes|pages|app)\/[a-z0-9-_/]+\.(?:ts|tsx|js|jsx|py|go|rs|java|cs|rb|php|swift|kt)\b/i, w: 0.5, label: 'src-file-path' },
|
|
43
|
+
],
|
|
44
|
+
deploying: [
|
|
45
|
+
{ re: /\b(?:deploy(?:ing|ment)?|rollout|roll\s+out|release|push\s+to\s+(?:prod|production|staging|live))\b/i, w: 0.7, label: 'deploy-verb' },
|
|
46
|
+
{ re: /\b(?:vercel|netlify|fly\.io|railway|render|heroku|aws|gcp|azure|kubernetes|k8s|docker)\s+(?:deploy|push|build)/i, w: 0.6, label: 'platform-deploy' },
|
|
47
|
+
{ re: /\b(?:git\s+push|npm\s+publish|cargo\s+publish|pip\s+upload|docker\s+push)\b/i, w: 0.55, label: 'publish-cmd' },
|
|
48
|
+
{ re: /\b(?:rolling\s+back|hotfix|cab(?:\s+release)?|prod\s+(?:incident|release))\b/i, w: 0.65, label: 'release-event' },
|
|
49
|
+
{ re: /\b(?:staging|production)\s+(?:server|env|environment)\b/i, w: 0.45, label: 'env-mention' },
|
|
50
|
+
{ re: /\bblue-green\b|\bcanary\b/i, w: 0.5, label: 'deploy-strategy' },
|
|
51
|
+
],
|
|
52
|
+
debugging: [
|
|
53
|
+
{ re: /\b(?:error|exception|failure|failed|fatal|panic|stack\s+trace|traceback)\b/i, w: 0.55, label: 'error-noun' },
|
|
54
|
+
{ re: /\bfix(?:ing|ed)?\s+(?:a\s+)?(?:bug|issue|error|problem|crash)\b/i, w: 0.65, label: 'fix-verb' },
|
|
55
|
+
{ re: /\b(?:debug(?:ging)?|investigate|investigating|root\s+cause|why\s+is|what's\s+wrong)\b/i, w: 0.6, label: 'debug-verb' },
|
|
56
|
+
{ re: /\b(?:sqlstate|err_|errno|err\s+\d|http\s+(?:4|5)\d{2})\b/i, w: 0.55, label: 'error-code' },
|
|
57
|
+
{ re: /\b(?:not\s+working|broken|crashing|hangs|times?\s+out)\b/i, w: 0.45, label: 'broken-phrase' },
|
|
58
|
+
{ re: /\b(?:console\.log|print\(|debugger|breakpoint|pdb|gdb)\b/i, w: 0.4, label: 'debug-tool' },
|
|
59
|
+
],
|
|
60
|
+
testing: [
|
|
61
|
+
{ re: /\b(?:test(?:s|ing|ed)?|spec(?:s)?|describe|it\(|expect\(|assert(?:Equals?|True|False)?)\b/i, w: 0.5, label: 'test-verb' },
|
|
62
|
+
{ re: /\b(?:vitest|jest|mocha|chai|pytest|unittest|rspec|junit|xunit)\b/i, w: 0.65, label: 'test-framework' },
|
|
63
|
+
{ re: /\b(?:qa|q\.a\.|quality\s+assurance|test\s+plan|test\s+case|cr\s+coverage|cab\s+coverage|regression\s+test)\b/i, w: 0.6, label: 'qa-noun' },
|
|
64
|
+
{ re: /\b(?:writing|adding|fixing)\s+(?:a\s+)?test/i, w: 0.55, label: 'verb-test' },
|
|
65
|
+
{ re: /\b(?:should|must|expects?\s+to)\s+(?:return|throw|equal|contain|match)\b/i, w: 0.4, label: 'assertion-language' },
|
|
66
|
+
],
|
|
67
|
+
documenting: [
|
|
68
|
+
{ re: /\b(?:docu?mentation|readme|docstring|api\s+docs|user\s+guide|how-?to)\b/i, w: 0.6, label: 'doc-noun' },
|
|
69
|
+
{ re: /\bwriting\s+(?:a\s+)?(?:doc|guide|readme|tutorial|spec|specification)\b/i, w: 0.6, label: 'writing-doc' },
|
|
70
|
+
{ re: /\b(?:markdown|\.md\b|<!--|jsdoc|tsdoc|sphinx|docusaurus)\b/i, w: 0.45, label: 'doc-format' },
|
|
71
|
+
{ re: /\b(?:install(?:ation)?|setup|getting\s+started|quickstart|configure)\b/i, w: 0.4, label: 'setup-doc-topic' },
|
|
72
|
+
{ re: /^#+\s+/m, w: 0.3, label: 'md-header' },
|
|
73
|
+
],
|
|
74
|
+
planning: [
|
|
75
|
+
{ re: /\b(?:roadmap|sprint|planning|design\s+doc|rfc|architecture|adr)\b/i, w: 0.65, label: 'planning-noun' },
|
|
76
|
+
{ re: /\b(?:should\s+we|let'?s|we\s+need\s+to|next\s+up|todo|tbd|wip)\b/i, w: 0.3, label: 'planning-phrase' },
|
|
77
|
+
{ re: /\b(?:decision|decided|trade-?off|alternative(?:s)?|considered)\b/i, w: 0.45, label: 'decision-language' },
|
|
78
|
+
{ re: /\b(?:phase\s+\d|milestone|deadline|deliverable)\b/i, w: 0.5, label: 'milestone' },
|
|
79
|
+
{ re: /\b(?:goal|objective|requirement|user\s+story|acceptance\s+criteria)\b/i, w: 0.5, label: 'goal-language' },
|
|
80
|
+
],
|
|
81
|
+
};
|
|
82
|
+
/**
|
|
83
|
+
* Classify the active context. Returns the highest-scoring intent above
|
|
84
|
+
* a minimum signal floor of 0.25 (below that, it's "none").
|
|
85
|
+
*
|
|
86
|
+
* Why not multi-label: in practice the user is doing one thing at a
|
|
87
|
+
* time. A single intent keeps the downstream namespace/threshold logic
|
|
88
|
+
* deterministic. If multi-label is needed later, we can return a sorted
|
|
89
|
+
* list and let callers consume top-N.
|
|
90
|
+
*/
|
|
91
|
+
export function classifyIntent(text) {
|
|
92
|
+
if (!text || text.trim().length < 5) {
|
|
93
|
+
return { kind: 'none', confidence: 0, signals: [] };
|
|
94
|
+
}
|
|
95
|
+
// Special case: temporal queries are detected by a separate, more
|
|
96
|
+
// specific classifier we already shipped. If it fires AND no other
|
|
97
|
+
// intent scores higher, return 'temporal'.
|
|
98
|
+
const isTemporal = isTemporalQuery(text);
|
|
99
|
+
// Score each non-temporal kind.
|
|
100
|
+
const scores = {};
|
|
101
|
+
for (const [kind, patterns] of Object.entries(SIGNALS)) {
|
|
102
|
+
let total = 0;
|
|
103
|
+
const hits = [];
|
|
104
|
+
for (const p of patterns) {
|
|
105
|
+
if (p.re.test(text)) {
|
|
106
|
+
total += p.w;
|
|
107
|
+
hits.push(p.label);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
// Cap at 1.0 — diminishing returns past full signal.
|
|
111
|
+
scores[kind] = { score: Math.min(total, 1.0), signals: hits };
|
|
112
|
+
}
|
|
113
|
+
// Pick the top-scoring kind.
|
|
114
|
+
let topKind = 'none';
|
|
115
|
+
let topScore = 0;
|
|
116
|
+
let topSignals = [];
|
|
117
|
+
for (const [kind, { score, signals }] of Object.entries(scores)) {
|
|
118
|
+
if (score > topScore) {
|
|
119
|
+
topKind = kind;
|
|
120
|
+
topScore = score;
|
|
121
|
+
topSignals = signals;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
// Minimum floor: if nothing fired above 0.25, treat as 'none' unless
|
|
125
|
+
// it's a clear temporal query (which can have its own classifier hit).
|
|
126
|
+
if (topScore < 0.25) {
|
|
127
|
+
if (isTemporal) {
|
|
128
|
+
return { kind: 'temporal', confidence: 0.6, signals: ['temporal-classifier'] };
|
|
129
|
+
}
|
|
130
|
+
return { kind: 'none', confidence: 0, signals: [] };
|
|
131
|
+
}
|
|
132
|
+
// Temporal-vs-other tiebreak: if temporal fires AND another intent
|
|
133
|
+
// scores below 0.45, prefer temporal (it's a more specific signal).
|
|
134
|
+
if (isTemporal && topScore < 0.45) {
|
|
135
|
+
return { kind: 'temporal', confidence: 0.6, signals: ['temporal-classifier'] };
|
|
136
|
+
}
|
|
137
|
+
return { kind: topKind, confidence: topScore, signals: topSignals };
|
|
138
|
+
}
|
|
139
|
+
//# sourceMappingURL=intent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"intent.js","sourceRoot":"","sources":["../../../src/lib/context-engine/intent.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runbook-pattern detector.
|
|
3
|
+
*
|
|
4
|
+
* Given a chunk of active context, decide whether it looks like a
|
|
5
|
+
* sequence of repeatable operational steps that the user might want to
|
|
6
|
+
* save as a runbook. Triggers the "Do you want to create a runbook
|
|
7
|
+
* from this?" suggestion.
|
|
8
|
+
*
|
|
9
|
+
* Different from `src/runbook/fingerprint.ts` (which fingerprints
|
|
10
|
+
* INDIVIDUAL errors) and from `src/runbook/auto-extract.ts` (which
|
|
11
|
+
* LLM-extracts from past Cowork chats). This detector is REAL-TIME and
|
|
12
|
+
* regex-only — runs on every typing pause, decides "is this currently
|
|
13
|
+
* being written like a runbook?"
|
|
14
|
+
*
|
|
15
|
+
* Signals it looks for:
|
|
16
|
+
*
|
|
17
|
+
* 1. Numbered or bulleted list of 3+ items
|
|
18
|
+
* 2. Multiple fenced code blocks (commands) in sequence
|
|
19
|
+
* 3. Imperative verbs at line starts ("Run", "Click", "Open", "Copy")
|
|
20
|
+
* 4. Sequential step language ("first", "then", "next", "finally")
|
|
21
|
+
* 5. Test pattern: each step is a single-line action + optional code
|
|
22
|
+
*
|
|
23
|
+
* NEGATIVE signals that demote runbook-confidence:
|
|
24
|
+
* - Continuous prose paragraphs without lists
|
|
25
|
+
* - Code that's clearly NOT a command (function definitions, classes)
|
|
26
|
+
* - Q&A or back-and-forth dialog
|
|
27
|
+
*
|
|
28
|
+
* Pure module — no I/O. Sibling to intent.ts and entities.ts.
|
|
29
|
+
*/
|
|
30
|
+
export function detectRunbook(text) {
|
|
31
|
+
if (!text || text.trim().length < 30) {
|
|
32
|
+
return { isRunbook: false, confidence: 0, steps: [], suggestedTitle: null, signals: [] };
|
|
33
|
+
}
|
|
34
|
+
const lines = text.split(/\r?\n/);
|
|
35
|
+
const signals = [];
|
|
36
|
+
let score = 0;
|
|
37
|
+
// ─── Signal 1: numbered list of 3+ items ─────────────────────────────
|
|
38
|
+
const numberedItems = [];
|
|
39
|
+
for (let i = 0; i < lines.length; i++) {
|
|
40
|
+
const m = lines[i].match(/^\s*(\d+)[.)\]]\s+(.+)/);
|
|
41
|
+
if (m)
|
|
42
|
+
numberedItems.push({ idx: i, text: m[2].trim() });
|
|
43
|
+
}
|
|
44
|
+
if (numberedItems.length >= 3) {
|
|
45
|
+
score += 0.5;
|
|
46
|
+
signals.push(`numbered-list-${numberedItems.length}`);
|
|
47
|
+
}
|
|
48
|
+
else if (numberedItems.length === 2) {
|
|
49
|
+
score += 0.2;
|
|
50
|
+
signals.push('numbered-list-short');
|
|
51
|
+
}
|
|
52
|
+
// ─── Signal 2: bulleted list of 3+ items ─────────────────────────────
|
|
53
|
+
const bulletItems = [];
|
|
54
|
+
for (let i = 0; i < lines.length; i++) {
|
|
55
|
+
const m = lines[i].match(/^\s*[-*•]\s+(.+)/);
|
|
56
|
+
if (m)
|
|
57
|
+
bulletItems.push({ idx: i, text: m[1].trim() });
|
|
58
|
+
}
|
|
59
|
+
if (bulletItems.length >= 4) {
|
|
60
|
+
score += 0.35;
|
|
61
|
+
signals.push(`bullet-list-${bulletItems.length}`);
|
|
62
|
+
}
|
|
63
|
+
// ─── Signal 3: fenced code blocks (multiple = command sequence) ──────
|
|
64
|
+
const fencedBlocks = Array.from(text.matchAll(/```(?:bash|sh|powershell|ps1|zsh|fish|cmd|console)?\n([\s\S]*?)```/gi));
|
|
65
|
+
if (fencedBlocks.length >= 2) {
|
|
66
|
+
score += 0.4;
|
|
67
|
+
signals.push(`fenced-code-${fencedBlocks.length}`);
|
|
68
|
+
}
|
|
69
|
+
else if (fencedBlocks.length === 1 && fencedBlocks[0][1].split('\n').filter(l => l.trim()).length >= 3) {
|
|
70
|
+
score += 0.25;
|
|
71
|
+
signals.push('fenced-code-multiline');
|
|
72
|
+
}
|
|
73
|
+
// ─── Signal 4: imperative verbs at line starts ───────────────────────
|
|
74
|
+
// "Run X", "Open Y", "Click Z", "Copy ...", "Navigate to ..."
|
|
75
|
+
// The `(?:\d+[.)\]]?\s+|[-*•]\s+)?` prefix allows list-marker text
|
|
76
|
+
// BEFORE the imperative — so "1. Open Chrome" and "- Click button"
|
|
77
|
+
// both count. Without this, numbered/bulleted runbooks hide their
|
|
78
|
+
// imperatives behind list markers and score 0 on this signal.
|
|
79
|
+
const imperativeRe = /^\s*(?:\d+[.)\]]\s+|[-*•]\s+)?(?:Run|Open|Click|Copy|Paste|Navigate|Type|Press|Enter|Select|Go\s+to|Visit|Download|Upload|Install|Verify|Check|Confirm|Wait|Save|Submit|Apply|Apply|Deploy|Push|Pull|Build|Test|Edit|Add|Remove|Delete|Update|Set|Configure|Start|Stop|Restart|Login|Sign\s+in|Sign\s+up|Connect|Disconnect|Toggle|Trigger|Verify)\s+/i;
|
|
80
|
+
let imperativeCount = 0;
|
|
81
|
+
for (const line of lines) {
|
|
82
|
+
if (imperativeRe.test(line))
|
|
83
|
+
imperativeCount++;
|
|
84
|
+
}
|
|
85
|
+
if (imperativeCount >= 3) {
|
|
86
|
+
score += 0.4;
|
|
87
|
+
signals.push(`imperative-${imperativeCount}`);
|
|
88
|
+
}
|
|
89
|
+
else if (imperativeCount === 2) {
|
|
90
|
+
score += 0.15;
|
|
91
|
+
signals.push('imperative-pair');
|
|
92
|
+
}
|
|
93
|
+
// ─── Signal 5: sequential step language ──────────────────────────────
|
|
94
|
+
const sequentialPhrases = [
|
|
95
|
+
/\bfirst,?\s+\w+/i,
|
|
96
|
+
/\bthen,?\s+\w+/i,
|
|
97
|
+
/\bnext,?\s+\w+/i,
|
|
98
|
+
/\bafter\s+(?:that|this),?\s+\w+/i,
|
|
99
|
+
/\bfinally,?\s+\w+/i,
|
|
100
|
+
/\blastly,?\s+\w+/i,
|
|
101
|
+
/\bstep\s+\d+\b/i,
|
|
102
|
+
];
|
|
103
|
+
const seqHits = sequentialPhrases.filter(re => re.test(text)).length;
|
|
104
|
+
if (seqHits >= 2) {
|
|
105
|
+
score += 0.25;
|
|
106
|
+
signals.push(`sequential-${seqHits}`);
|
|
107
|
+
}
|
|
108
|
+
// ─── NEGATIVE: continuous prose ──────────────────────────────────────
|
|
109
|
+
// If the text is mostly paragraphs with NO lists, NO commands, it's
|
|
110
|
+
// probably explaining something rather than describing steps.
|
|
111
|
+
const paragraphLines = lines.filter(l => l.trim().length > 80 && !l.match(/^\s*[\d-*•]/)).length;
|
|
112
|
+
const totalNonEmpty = lines.filter(l => l.trim().length > 0).length;
|
|
113
|
+
if (totalNonEmpty > 0 && paragraphLines / totalNonEmpty > 0.7 && score < 0.5) {
|
|
114
|
+
score *= 0.5;
|
|
115
|
+
signals.push('mostly-prose-demote');
|
|
116
|
+
}
|
|
117
|
+
// ─── NEGATIVE: dialog / Q&A pattern ──────────────────────────────────
|
|
118
|
+
// "You: ... Claude: ..." or "Q: ... A: ..."
|
|
119
|
+
const dialogRe = /^(?:you|claude|gpt|user|assistant|q|a)[:>]\s+/im;
|
|
120
|
+
if (dialogRe.test(text)) {
|
|
121
|
+
score *= 0.6;
|
|
122
|
+
signals.push('dialog-demote');
|
|
123
|
+
}
|
|
124
|
+
const isRunbook = score >= 0.55;
|
|
125
|
+
// ─── Extract steps if confident enough ───────────────────────────────
|
|
126
|
+
let steps = [];
|
|
127
|
+
let suggestedTitle = null;
|
|
128
|
+
if (score >= 0.4) {
|
|
129
|
+
steps = extractSteps(lines, numberedItems, bulletItems, fencedBlocks);
|
|
130
|
+
suggestedTitle = extractTitle(text, lines, steps);
|
|
131
|
+
}
|
|
132
|
+
return {
|
|
133
|
+
isRunbook,
|
|
134
|
+
confidence: Math.min(score, 1.0),
|
|
135
|
+
steps,
|
|
136
|
+
suggestedTitle,
|
|
137
|
+
signals,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
// ─── Step extraction ──────────────────────────────────────────────────────
|
|
141
|
+
function extractSteps(lines, numbered, bullets, fenced) {
|
|
142
|
+
const out = [];
|
|
143
|
+
// Prefer numbered list as the canonical step structure.
|
|
144
|
+
const source = numbered.length >= 2 ? numbered : bullets.length >= 3 ? bullets : [];
|
|
145
|
+
for (const item of source) {
|
|
146
|
+
// Look ahead for an attached code block (line after this list item).
|
|
147
|
+
let code;
|
|
148
|
+
const nextLine = lines[item.idx + 1];
|
|
149
|
+
if (nextLine?.match(/^\s*```/)) {
|
|
150
|
+
// Walk until closing fence
|
|
151
|
+
const fenceLines = [];
|
|
152
|
+
for (let j = item.idx + 2; j < lines.length; j++) {
|
|
153
|
+
if (lines[j].match(/^\s*```/))
|
|
154
|
+
break;
|
|
155
|
+
fenceLines.push(lines[j]);
|
|
156
|
+
}
|
|
157
|
+
if (fenceLines.length > 0) {
|
|
158
|
+
code = fenceLines.join('\n').slice(0, 1000);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
// Inline code in the step text itself
|
|
163
|
+
const inlineCode = item.text.match(/`([^`]+)`/);
|
|
164
|
+
if (inlineCode)
|
|
165
|
+
code = inlineCode[1];
|
|
166
|
+
}
|
|
167
|
+
out.push({
|
|
168
|
+
text: item.text.replace(/`([^`]+)`/g, '$1').slice(0, 500),
|
|
169
|
+
...(code ? { code } : {}),
|
|
170
|
+
sourceLine: item.idx,
|
|
171
|
+
});
|
|
172
|
+
if (out.length >= 20)
|
|
173
|
+
break;
|
|
174
|
+
}
|
|
175
|
+
// If we don't have list-based steps but DO have fenced code blocks,
|
|
176
|
+
// synthesize each block as a step.
|
|
177
|
+
if (out.length === 0 && fenced.length >= 2) {
|
|
178
|
+
for (const f of fenced) {
|
|
179
|
+
out.push({
|
|
180
|
+
text: 'Run',
|
|
181
|
+
code: f[1].trim().slice(0, 500),
|
|
182
|
+
});
|
|
183
|
+
if (out.length >= 10)
|
|
184
|
+
break;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return out;
|
|
188
|
+
}
|
|
189
|
+
/** Extract a title from the text — first markdown heading, or imperative line. */
|
|
190
|
+
function extractTitle(text, lines, steps) {
|
|
191
|
+
// Markdown heading first
|
|
192
|
+
const headingMatch = text.match(/^#+\s+(.{5,120}?)$/m);
|
|
193
|
+
if (headingMatch)
|
|
194
|
+
return headingMatch[1].trim();
|
|
195
|
+
// First line if it's short + descriptive
|
|
196
|
+
const firstLine = lines.find(l => l.trim().length > 0);
|
|
197
|
+
if (firstLine && firstLine.length < 100 && !firstLine.match(/^[\d-*•]/)) {
|
|
198
|
+
return firstLine.trim();
|
|
199
|
+
}
|
|
200
|
+
// Synthesize from first step
|
|
201
|
+
if (steps.length > 0) {
|
|
202
|
+
return `${steps[0].text.slice(0, 80)}...`;
|
|
203
|
+
}
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
206
|
+
//# sourceMappingURL=runbook-detector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runbook-detector.js","sourceRoot":"","sources":["../../../src/lib/context-engine/runbook-detector.ts"],"names":[],"mappings":"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"}
|