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.
Files changed (54) hide show
  1. package/INSTALL.md +125 -0
  2. package/LICENSE +28 -21
  3. package/NOTICE +164 -0
  4. package/README.md +443 -422
  5. package/dist/cli.js +114 -26
  6. package/dist/cli.js.map +1 -1
  7. package/dist/config.js +4 -1
  8. package/dist/config.js.map +1 -1
  9. package/dist/detectors/codex.js +138 -0
  10. package/dist/detectors/codex.js.map +1 -0
  11. package/dist/detectors/index.js +2 -0
  12. package/dist/detectors/index.js.map +1 -1
  13. package/dist/detectors/json_detector.js +1 -1
  14. package/dist/lib/context-engine/confidence.js +153 -0
  15. package/dist/lib/context-engine/confidence.js.map +1 -0
  16. package/dist/lib/context-engine/entities.js +179 -0
  17. package/dist/lib/context-engine/entities.js.map +1 -0
  18. package/dist/lib/context-engine/index.js +74 -0
  19. package/dist/lib/context-engine/index.js.map +1 -0
  20. package/dist/lib/context-engine/intent.js +139 -0
  21. package/dist/lib/context-engine/intent.js.map +1 -0
  22. package/dist/lib/context-engine/runbook-detector.js +206 -0
  23. package/dist/lib/context-engine/runbook-detector.js.map +1 -0
  24. package/dist/lib/date-anchors.js +351 -0
  25. package/dist/lib/date-anchors.js.map +1 -0
  26. package/dist/lib/temporal-intent.js +98 -0
  27. package/dist/lib/temporal-intent.js.map +1 -0
  28. package/dist/runbook/auto-extract.js +415 -0
  29. package/dist/runbook/auto-extract.js.map +1 -0
  30. package/dist/runbook/capture.js +214 -0
  31. package/dist/runbook/capture.js.map +1 -0
  32. package/dist/runbook/explain.js +154 -0
  33. package/dist/runbook/explain.js.map +1 -0
  34. package/dist/runbook/fingerprint.js +128 -0
  35. package/dist/runbook/fingerprint.js.map +1 -0
  36. package/dist/runbook/search.js +76 -0
  37. package/dist/runbook/search.js.map +1 -0
  38. package/dist/runbook/types.js +15 -0
  39. package/dist/runbook/types.js.map +1 -0
  40. package/dist/setup.js +32 -4
  41. package/dist/setup.js.map +1 -1
  42. package/dist/store/entity-extractor.js +69 -2
  43. package/dist/store/entity-extractor.js.map +1 -1
  44. package/dist/store/local-db.js +33 -0
  45. package/dist/store/local-db.js.map +1 -0
  46. package/dist/store/local.js +230 -229
  47. package/dist/store/local.js.map +1 -1
  48. package/dist/store/procedural.js +185 -0
  49. package/dist/store/procedural.js.map +1 -1
  50. package/dist/store/remote.js +73 -0
  51. package/dist/store/remote.js.map +1 -1
  52. package/dist/tools.js +393 -8
  53. package/dist/tools.js.map +1 -1
  54. package/package.json +63 -55
@@ -0,0 +1,138 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
2
+ import { homedir } from 'node:os';
3
+ import { dirname, join } from 'node:path';
4
+ /**
5
+ * Codex stores MCP servers in ~/.codex/config.toml:
6
+ *
7
+ * [mcp_servers.<name>]
8
+ * command = "..."
9
+ * args = [...]
10
+ *
11
+ * [mcp_servers.<name>.env]
12
+ * KEY = "value"
13
+ *
14
+ * This detector edits only the mnueron block and leaves the rest of the TOML
15
+ * untouched. It intentionally avoids a full TOML parser dependency.
16
+ */
17
+ export class CodexDetector {
18
+ id = 'codex';
19
+ displayName = 'Codex';
20
+ configPath() {
21
+ return join(homedir(), '.codex', 'config.toml');
22
+ }
23
+ status() {
24
+ const path = this.configPath();
25
+ const dir = dirname(path);
26
+ const installed = existsSync(dir) || existsSync(path);
27
+ const configExists = existsSync(path);
28
+ let alreadyConfigured = false;
29
+ if (configExists) {
30
+ try {
31
+ const raw = readFileSync(path, 'utf8');
32
+ alreadyConfigured = hasServerBlock(raw, 'mnueron');
33
+ }
34
+ catch {
35
+ alreadyConfigured = false;
36
+ }
37
+ }
38
+ return {
39
+ id: this.id,
40
+ displayName: this.displayName,
41
+ installed,
42
+ configPath: path,
43
+ configExists,
44
+ alreadyConfigured,
45
+ note: 'writes ~/.codex/config.toml',
46
+ };
47
+ }
48
+ install(serverName, entry) {
49
+ const path = this.configPath();
50
+ mkdirSync(dirname(path), { recursive: true });
51
+ const codexEntry = {
52
+ ...entry,
53
+ command: entry.command === 'node' ? process.execPath : entry.command,
54
+ };
55
+ let raw = '';
56
+ if (existsSync(path)) {
57
+ try {
58
+ raw = readFileSync(path, 'utf8');
59
+ }
60
+ catch (e) {
61
+ return { ok: false, changed: false, message: `failed to read ${path}: ${e?.message ?? e}` };
62
+ }
63
+ }
64
+ const before = raw;
65
+ const withoutOld = removeServerBlock(raw, serverName).trimEnd();
66
+ const next = `${withoutOld}${withoutOld ? '\n\n' : ''}${formatServerBlock(serverName, codexEntry)}\n`;
67
+ const changed = before !== next;
68
+ writeFileSync(path, next);
69
+ return {
70
+ ok: true,
71
+ changed,
72
+ configPath: path,
73
+ message: changed
74
+ ? (hasServerBlock(before, serverName) ? 'updated existing entry' : 'added')
75
+ : 'already up to date',
76
+ };
77
+ }
78
+ uninstall(serverName) {
79
+ const path = this.configPath();
80
+ if (!existsSync(path))
81
+ return { ok: true, removed: false, message: 'nothing to remove' };
82
+ try {
83
+ const raw = readFileSync(path, 'utf8');
84
+ const next = removeServerBlock(raw, serverName).trimEnd() + '\n';
85
+ const removed = raw !== next;
86
+ if (removed)
87
+ writeFileSync(path, next);
88
+ return { ok: true, removed, message: removed ? 'removed' : 'not registered' };
89
+ }
90
+ catch (e) {
91
+ return { ok: false, removed: false, message: `failed: ${e?.message ?? e}` };
92
+ }
93
+ }
94
+ }
95
+ function hasServerBlock(raw, serverName) {
96
+ return new RegExp(`^\\[mcp_servers\\.${escapeRegExp(serverName)}\\]`, 'm').test(raw);
97
+ }
98
+ function removeServerBlock(raw, serverName) {
99
+ const lines = raw.split(/\r?\n/);
100
+ const out = [];
101
+ let skipping = false;
102
+ for (const line of lines) {
103
+ const header = line.match(/^\s*\[([^\]]+)\]\s*$/);
104
+ if (header) {
105
+ const section = header[1].trim();
106
+ if (section === `mcp_servers.${serverName}` || section === `mcp_servers.${serverName}.env`) {
107
+ skipping = true;
108
+ continue;
109
+ }
110
+ skipping = false;
111
+ }
112
+ if (!skipping)
113
+ out.push(line);
114
+ }
115
+ return out.join('\n');
116
+ }
117
+ function formatServerBlock(serverName, entry) {
118
+ const lines = [];
119
+ lines.push(`[mcp_servers.${serverName}]`);
120
+ lines.push(`command = ${tomlString(entry.command)}`);
121
+ lines.push(`args = [${entry.args.map(tomlString).join(', ')}]`);
122
+ lines.push('startup_timeout_sec = 120');
123
+ if (entry.env && Object.keys(entry.env).length > 0) {
124
+ lines.push('');
125
+ lines.push(`[mcp_servers.${serverName}.env]`);
126
+ for (const [key, value] of Object.entries(entry.env)) {
127
+ lines.push(`${key} = ${tomlString(value)}`);
128
+ }
129
+ }
130
+ return lines.join('\n');
131
+ }
132
+ function tomlString(value) {
133
+ return JSON.stringify(value);
134
+ }
135
+ function escapeRegExp(value) {
136
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
137
+ }
138
+ //# sourceMappingURL=codex.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codex.js","sourceRoot":"","sources":["../../src/detectors/codex.ts"],"names":[],"mappings":"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"}
@@ -8,6 +8,7 @@ import { ZedDetector } from './zed.js';
8
8
  import { AiderDetector } from './aider.js';
9
9
  import { GooseDetector } from './goose.js';
10
10
  import { OpenCodeDetector } from './opencode.js';
11
+ import { CodexDetector } from './codex.js';
11
12
  /**
12
13
  * Order matters for the setup wizard output. The first five (Claude-family +
13
14
  * Cursor + Windsurf + Cline) are the original tier-1 tools. The v0.2.7
@@ -17,6 +18,7 @@ export function allDetectors() {
17
18
  return [
18
19
  new ClaudeDesktopDetector(),
19
20
  new ClaudeCodeDetector(),
21
+ new CodexDetector(),
20
22
  new CursorDetector(),
21
23
  new WindsurfDetector(),
22
24
  new ClineDetector(),
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/detectors/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC7D,uDAAuD;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAEjD;;;;GAIG;AACH,MAAM,UAAU,YAAY;IAC1B,OAAO;QACL,IAAI,qBAAqB,EAAE;QAC3B,IAAI,kBAAkB,EAAE;QACxB,IAAI,cAAc,EAAE;QACpB,IAAI,gBAAgB,EAAE;QACtB,IAAI,aAAa,EAAE;QACnB,IAAI,gBAAgB,EAAE;QACtB,IAAI,WAAW,EAAE;QACjB,IAAI,aAAa,EAAE;QACnB,IAAI,gBAAgB,EAAE;QACtB,IAAI,aAAa,EAAE;KACpB,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/detectors/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC7D,uDAAuD;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE3C;;;;GAIG;AACH,MAAM,UAAU,YAAY;IAC1B,OAAO;QACL,IAAI,qBAAqB,EAAE;QAC3B,IAAI,kBAAkB,EAAE;QACxB,IAAI,aAAa,EAAE;QACnB,IAAI,cAAc,EAAE;QACpB,IAAI,gBAAgB,EAAE;QACtB,IAAI,aAAa,EAAE;QACnB,IAAI,gBAAgB,EAAE;QACtB,IAAI,WAAW,EAAE;QACjB,IAAI,aAAa,EAAE;QACnB,IAAI,gBAAgB,EAAE;QACtB,IAAI,aAAa,EAAE;KACpB,CAAC;AACJ,CAAC"}
@@ -89,7 +89,7 @@ export class JsonMcpDetector {
89
89
  }
90
90
  lastServerName;
91
91
  serverNameInConfig() {
92
- return this.lastServerName ?? 'engrama';
92
+ return this.lastServerName ?? 'mnueron';
93
93
  }
94
94
  }
95
95
  //# sourceMappingURL=json_detector.js.map
@@ -0,0 +1,153 @@
1
+ /**
2
+ * Confidence scoring + threshold logic for the Context Engine.
3
+ *
4
+ * The recall engine searches mnueron memory and returns candidates with
5
+ * raw hybrid-search scores (BM25 + cosine). Those scores are good for
6
+ * RANKING within a result set, but they're not directly interpretable
7
+ * as "should I surface this to the user?"
8
+ *
9
+ * This module converts the raw signals (intent classification,
10
+ * entity matches, raw recall score) into a single confidence value
11
+ * 0..1, and gates surfacing decisions against a conservative threshold.
12
+ *
13
+ * Conservative default 0.75 — only surface high-confidence matches.
14
+ * Users CAN explicitly lower it for "show me anything you've got" mode.
15
+ */
16
+ export const DEFAULT_CONFIG = {
17
+ threshold: 0.75,
18
+ maxSuggestions: 3,
19
+ preferRunbooks: true,
20
+ };
21
+ /**
22
+ * Score a candidate by combining intent fit, entity overlap, raw search
23
+ * score, and verified-runbook prior.
24
+ *
25
+ * The formula:
26
+ *
27
+ * confidence = 0.45 * raw_score
28
+ * + 0.20 * intent_fit
29
+ * + 0.20 * entity_overlap
30
+ * + 0.15 * verified_prior
31
+ *
32
+ * Weights tuned for HIGH PRECISION. Boost factors:
33
+ * - Verified runbook with >0 successes: +0.10
34
+ * - Verified runbook with >0 failures: -0.05 (proven not always working)
35
+ */
36
+ export function scoreCandidate(candidate, intent, entities, config = DEFAULT_CONFIG) {
37
+ const reasons = [];
38
+ // ─── Raw search score (0.45 weight) ──────────────────────────────────
39
+ // BM25+cosine RRF scores from LocalProvider are roughly [0..1].
40
+ // For runbooks (from procedural_memories) we treat them as already
41
+ // normalized.
42
+ const rawComponent = Math.max(0, Math.min(1, candidate.rawScore)) * 0.45;
43
+ reasons.push(`raw=${(rawComponent / 0.45).toFixed(2)}`);
44
+ // ─── Intent fit (0.20 weight) ────────────────────────────────────────
45
+ // High when the intent strongly matches the candidate kind. E.g.,
46
+ // 'deploying' intent + runbook candidate = good fit. 'debugging' intent
47
+ // + runbook with `tool=git` and matching error fingerprint = great fit.
48
+ let intentFit = 0;
49
+ if (intent.kind === 'none') {
50
+ intentFit = 0.3; // no penalty for unclassified intent
51
+ }
52
+ else if (candidate.kind === 'runbook') {
53
+ if (intent.kind === 'deploying' || intent.kind === 'debugging')
54
+ intentFit = 1.0;
55
+ else if (intent.kind === 'testing' || intent.kind === 'coding')
56
+ intentFit = 0.7;
57
+ else
58
+ intentFit = 0.5;
59
+ }
60
+ else {
61
+ // memory candidate
62
+ intentFit = 0.6;
63
+ }
64
+ const intentComponent = intentFit * 0.20;
65
+ reasons.push(`intent[${intent.kind}]=${intentFit.toFixed(2)}`);
66
+ // ─── Entity overlap (0.20 weight) ────────────────────────────────────
67
+ // How many query entities appear in the candidate content?
68
+ const candidateLower = (candidate.content ?? '').toLowerCase();
69
+ let overlap = 0;
70
+ let possibleOverlap = 0;
71
+ for (const tech of entities.technologies) {
72
+ possibleOverlap++;
73
+ if (candidateLower.includes(tech))
74
+ overlap++;
75
+ }
76
+ for (const file of entities.files) {
77
+ possibleOverlap++;
78
+ if (candidateLower.includes(file.toLowerCase()))
79
+ overlap++;
80
+ }
81
+ if (entities.project) {
82
+ possibleOverlap++;
83
+ if (candidateLower.includes(entities.project.toLowerCase()))
84
+ overlap++;
85
+ }
86
+ const overlapRatio = possibleOverlap > 0 ? overlap / possibleOverlap : 0.5;
87
+ const overlapComponent = overlapRatio * 0.20;
88
+ reasons.push(`entities=${overlap}/${possibleOverlap}`);
89
+ // ─── Verified prior (0.15 weight) ────────────────────────────────────
90
+ // A verified runbook with successful uses is more confident than an
91
+ // unverified draft.
92
+ let verifiedPrior = 0;
93
+ if (candidate.kind === 'runbook') {
94
+ const successes = candidate.successCount ?? 0;
95
+ const failures = candidate.failureCount ?? 0;
96
+ if (candidate.verified)
97
+ verifiedPrior += 0.5;
98
+ if (successes > 0)
99
+ verifiedPrior += Math.min(0.5, successes * 0.1);
100
+ if (failures > 0)
101
+ verifiedPrior -= Math.min(0.3, failures * 0.05);
102
+ verifiedPrior = Math.max(0, Math.min(1, verifiedPrior));
103
+ }
104
+ else {
105
+ verifiedPrior = 0.3; // memories don't have verified state; neutral
106
+ }
107
+ const verifiedComponent = verifiedPrior * 0.15;
108
+ reasons.push(`verified=${verifiedPrior.toFixed(2)}`);
109
+ let confidence = rawComponent + intentComponent + overlapComponent + verifiedComponent;
110
+ confidence = Math.max(0, Math.min(1, confidence));
111
+ return {
112
+ ...candidate,
113
+ confidence,
114
+ reason: reasons.join(' '),
115
+ };
116
+ }
117
+ /**
118
+ * Filter ranked candidates by threshold and rank. Returns at most
119
+ * `config.maxSuggestions` candidates above `config.threshold`.
120
+ */
121
+ export function filterByConfidence(scored, config = DEFAULT_CONFIG) {
122
+ let threshold = config.threshold;
123
+ // If runbook preference enabled and we have runbook candidates,
124
+ // lower threshold for them by 0.1 (runbooks tend to be more
125
+ // actionable than memories — worth surfacing slightly earlier).
126
+ const surviving = scored.filter(s => {
127
+ const effective = config.preferRunbooks && s.kind === 'runbook'
128
+ ? Math.max(0.5, threshold - 0.1)
129
+ : threshold;
130
+ return s.confidence >= effective;
131
+ });
132
+ surviving.sort((a, b) => b.confidence - a.confidence);
133
+ return surviving.slice(0, config.maxSuggestions);
134
+ }
135
+ /**
136
+ * Decide whether to surface ANY suggestion. Used by the suggestion
137
+ * service to short-circuit when no candidate clears the bar.
138
+ *
139
+ * Returns the surfaced list (possibly empty).
140
+ */
141
+ export function gateSurfacing(candidates, intent, entities, runbookDetection, config = DEFAULT_CONFIG) {
142
+ // If intent is 'none' and no entities of substance, don't bother.
143
+ if (intent.kind === 'none' &&
144
+ entities.files.length === 0 &&
145
+ entities.commands.length === 0 &&
146
+ entities.errors.length === 0 &&
147
+ !runbookDetection?.isRunbook) {
148
+ return [];
149
+ }
150
+ const scored = candidates.map(c => scoreCandidate(c, intent, entities, config));
151
+ return filterByConfidence(scored, config);
152
+ }
153
+ //# sourceMappingURL=confidence.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"confidence.js","sourceRoot":"","sources":["../../../src/lib/context-engine/confidence.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,179 @@
1
+ /**
2
+ * Active-context entity extractor.
3
+ *
4
+ * Different from `src/store/entity-extractor.ts` — that one runs an LLM
5
+ * call on saved memories to find people/orgs/products for the knowledge
6
+ * graph. THIS extractor is regex-only, runs in <5ms, and pulls the
7
+ * DEVELOPMENT-context entities the recall engine needs:
8
+ *
9
+ * project — namespace/repo name (from cwd or explicit mention)
10
+ * files — referenced source files
11
+ * commands — shell commands (git, npm, supabase, curl, etc.)
12
+ * errors — error messages, codes, stack-trace prefixes
13
+ * technologies — frameworks, services, languages mentioned
14
+ *
15
+ * These feed the recall engine's namespace + filter selection:
16
+ * - project name → search namespace=`repo:<project>` first
17
+ * - files mentioned → boost memories that reference the same files
18
+ * - errors → check procedural_memories for matching fingerprints
19
+ * - technologies → expand search vocabulary
20
+ *
21
+ * Pure module — no I/O, no LLM. Same shape as date-anchors.ts.
22
+ */
23
+ import { basename, dirname } from 'node:path';
24
+ const TECH_VOCAB = [
25
+ // Languages
26
+ 'typescript', 'javascript', 'python', 'go', 'rust', 'java', 'c#', 'ruby', 'php', 'swift', 'kotlin',
27
+ // Frameworks
28
+ 'react', 'next.js', 'next', 'vue', 'svelte', 'angular', 'express', 'fastify', 'nestjs', 'django',
29
+ 'flask', 'fastapi', 'rails', 'spring', '.net', 'dotnet',
30
+ // Cloud / infra
31
+ 'aws', 'gcp', 'azure', 'vercel', 'netlify', 'cloudflare', 'fly.io', 'railway', 'heroku', 'render',
32
+ 'kubernetes', 'k8s', 'docker', 'terraform', 'ansible',
33
+ // Databases
34
+ 'postgres', 'postgresql', 'mysql', 'sqlite', 'redis', 'mongodb', 'dynamodb', 'supabase', 'planetscale',
35
+ 'prisma', 'drizzle',
36
+ // AI / ML
37
+ 'openai', 'anthropic', 'claude', 'gpt-4', 'gpt-4o', 'haiku', 'sonnet', 'opus', 'gemini', 'llama',
38
+ 'mistral', 'embeddings', 'pgvector', 'sqlite-vec', 'langchain', 'llamaindex',
39
+ // Payment / billing
40
+ 'stripe', 'paypal', 'lemon squeezy', 'paddle',
41
+ // Auth / identity
42
+ 'oauth', 'jwt', 'session', 'cookie', 'sso', 'saml',
43
+ // MCP / mnueron-specific
44
+ 'mcp', 'mnueron', 'cowork', 'claude desktop', 'cursor', 'windsurf', 'cline',
45
+ // Build / package
46
+ 'npm', 'pnpm', 'yarn', 'cargo', 'pip', 'composer', 'maven', 'gradle',
47
+ // Test / CI
48
+ 'vitest', 'jest', 'mocha', 'pytest', 'github actions', 'circleci',
49
+ ];
50
+ /**
51
+ * Extract development entities from active context.
52
+ *
53
+ * @param text The active context — typically the last 500-2000 chars of
54
+ * what the user has been writing/typing/talking-about.
55
+ * @param opts.cwd If provided, the working directory. Used to infer
56
+ * `project` when not explicitly mentioned.
57
+ * @param opts.explicitProject If the caller already knows the project
58
+ * (e.g. from MCP-side namespace hints),
59
+ * skip cwd inference.
60
+ */
61
+ export function extractEntities(text, opts = {}) {
62
+ if (!text) {
63
+ return {
64
+ project: opts.explicitProject ?? inferProjectFromCwd(opts.cwd),
65
+ files: [],
66
+ commands: [],
67
+ errors: [],
68
+ technologies: [],
69
+ tags: [],
70
+ };
71
+ }
72
+ const lower = text.toLowerCase();
73
+ // ─── Files ────────────────────────────────────────────────────────────
74
+ // Looks for path-like tokens: src/foo/bar.ts, ./components/X.tsx, etc.
75
+ // Quoted paths in errors ('C:/foo/.git/index.lock') already captured
76
+ // by the path regex.
77
+ const fileRe = /(?<!\w)((?:[a-z0-9_-]+\/){0,5}[a-z0-9_.-]+\.(?:ts|tsx|js|jsx|py|go|rs|java|cs|rb|php|swift|kt|sql|md|yaml|yml|json|sh|ps1|html|css))(?!\w)/gi;
78
+ const files = Array.from(new Set(Array.from(text.matchAll(fileRe)).map(m => m[1]).filter(f => !f.startsWith('node_modules/') && f.length < 200))).slice(0, 15);
79
+ // ─── Commands ─────────────────────────────────────────────────────────
80
+ // Recognize lines that LOOK like shell commands: starts with a known
81
+ // CLI verb. Captures from fenced code blocks too.
82
+ const cmdRe = /(?:^|\n|`{1,3}\s*|\$\s+)((?:git|npm|pnpm|yarn|node|npx|tsx|python|pip|cargo|rustc|go|docker|kubectl|terraform|ansible|aws|gcloud|az|supabase|stripe|vercel|netlify|gh|mnueron|curl|wget|psql|mysql|sqlite3|redis-cli|jq|sed|awk|grep|find|ls|cd|rm|cp|mv|mkdir|chmod|chown|sudo|systemctl|service|kill|ps|top|netstat|ssh|scp|rsync|tar|zip|unzip|cmake|make|gradle|mvn|bundle|gem)\s+[^\n`]{1,200})/g;
83
+ const commands = Array.from(new Set(Array.from(text.matchAll(cmdRe)).map(m => m[1].trim()))).slice(0, 10);
84
+ // ─── Errors ───────────────────────────────────────────────────────────
85
+ // Catches lines that look like error output. Conservative — we don't
86
+ // want to mark every "failed" mention as an error entity.
87
+ const errorPatterns = [
88
+ /(?:^|\n)\s*(?:ERROR|FATAL|Error|Exception|Traceback)[: ][^\n]{5,200}/g,
89
+ /\b(?:HTTP|status)\s+(?:4|5)\d{2}\b/g,
90
+ /\bSQLSTATE\s+\d{5}\b/gi,
91
+ /\bcannot find (?:module|file)\b[^\n]{0,100}/gi,
92
+ /\b(?:is not recognized|not found|undefined|null pointer|access denied|permission denied)\b/gi,
93
+ ];
94
+ const errors = [];
95
+ for (const re of errorPatterns) {
96
+ for (const m of text.matchAll(re)) {
97
+ errors.push(m[0].trim());
98
+ if (errors.length >= 5)
99
+ break;
100
+ }
101
+ if (errors.length >= 5)
102
+ break;
103
+ }
104
+ // ─── Technologies ─────────────────────────────────────────────────────
105
+ // Conservative vocabulary match. Avoids false positives on words like
106
+ // "Go" by requiring word boundaries (so "Go" only matches as a token).
107
+ const technologies = TECH_VOCAB.filter(t => {
108
+ // Special handling for short tokens to avoid false positives
109
+ if (t.length <= 2) {
110
+ // "go", "k8s" — require word boundary on both sides + lowercase context
111
+ return new RegExp(`\\b${escapeRe(t)}\\b`).test(lower);
112
+ }
113
+ return lower.includes(t);
114
+ }).slice(0, 15);
115
+ // ─── Project ──────────────────────────────────────────────────────────
116
+ const project = opts.explicitProject ?? extractProjectFromText(text) ?? inferProjectFromCwd(opts.cwd);
117
+ // ─── Tags ─────────────────────────────────────────────────────────────
118
+ // Pull noun phrases from the first 200 chars as candidate tags.
119
+ // Useful for stamping captured runbooks with relevant labels.
120
+ const tags = Array.from(new Set([
121
+ ...technologies.slice(0, 5),
122
+ ...(project ? [`project:${project}`] : []),
123
+ ...files.slice(0, 3).map(f => `file:${basename(f)}`),
124
+ ])).slice(0, 10);
125
+ return {
126
+ project,
127
+ files,
128
+ commands,
129
+ errors,
130
+ technologies,
131
+ tags,
132
+ };
133
+ }
134
+ // ─── Helpers ──────────────────────────────────────────────────────────────
135
+ /** Pull "in the foo repo" / "repo: foo" / "namespace=repo:foo" from text. */
136
+ function extractProjectFromText(text) {
137
+ // Order from most-specific-phrase to least. The "in the X repo" pattern
138
+ // comes FIRST because it's the most natural English phrasing and the
139
+ // most unambiguous about which word is the project name.
140
+ const patterns = [
141
+ // "in the mnueron repo" / "in mnueron project" / "in the foo codebase"
142
+ /\bin\s+(?:the\s+)?([a-zA-Z0-9._-]{2,60})\s+(?:repo|project|codebase)\b/i,
143
+ // "namespace=repo:mnueron" / "namespace: repo:foo"
144
+ /\bnamespace[=:\s]+["`']?(repo:[a-zA-Z0-9._-]+)["`']?/i,
145
+ // "project: mnueron" / "repo: foo" — REQUIRES `:` or `=` (NOT bare whitespace)
146
+ // to avoid matching "repo working" or "project plan" as "working" or "plan".
147
+ /\b(?:project|repo|repository)\s*[:=]\s*["`']?([a-zA-Z0-9._-]{2,60})["`']?\b/i,
148
+ ];
149
+ for (const re of patterns) {
150
+ const m = re.exec(text);
151
+ if (m)
152
+ return m[1].replace(/^repo:/, '');
153
+ }
154
+ return null;
155
+ }
156
+ /** Last dir name of cwd, e.g. "/home/me/projects/mnueron" → "mnueron". */
157
+ function inferProjectFromCwd(cwd) {
158
+ if (!cwd)
159
+ return null;
160
+ try {
161
+ let name = basename(cwd);
162
+ // If cwd ends in `/src` or similar generic, walk up.
163
+ while (['src', 'lib', 'app', 'packages', 'frontend', 'backend'].includes(name)) {
164
+ const parent = dirname(cwd);
165
+ if (parent === cwd)
166
+ break;
167
+ cwd = parent;
168
+ name = basename(cwd);
169
+ }
170
+ return name || null;
171
+ }
172
+ catch {
173
+ return null;
174
+ }
175
+ }
176
+ function escapeRe(s) {
177
+ return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
178
+ }
179
+ //# sourceMappingURL=entities.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entities.js","sourceRoot":"","sources":["../../../src/lib/context-engine/entities.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Context Engine — top-level orchestrator.
3
+ *
4
+ * Single entrypoint that takes active context text + optional cwd and
5
+ * returns a complete `ContextSignal` ready to be passed to the recall
6
+ * engine.
7
+ *
8
+ * Combines:
9
+ * - intent.classifyIntent()
10
+ * - entities.extractEntities()
11
+ * - runbook-detector.detectRunbook()
12
+ *
13
+ * Used by the MCP tools (`recall_assist`, `runbook_suggest`) and any
14
+ * future client (VS Code extension, browser ext suggestion watcher,
15
+ * dashboard live-demo box).
16
+ */
17
+ import { classifyIntent } from './intent.js';
18
+ import { extractEntities } from './entities.js';
19
+ import { detectRunbook } from './runbook-detector.js';
20
+ export { DEFAULT_CONFIG, scoreCandidate, filterByConfidence, gateSurfacing, } from './confidence.js';
21
+ /**
22
+ * Analyze active context. Pure function — no I/O.
23
+ *
24
+ * Returns a ContextSignal that the recall engine can use to decide:
25
+ * - What namespace(s) to search
26
+ * - Whether to look at procedural_memories (runbooks) vs memories
27
+ * - What confidence threshold to apply
28
+ * - Whether to offer the user to save THIS text as a runbook
29
+ */
30
+ export function analyzeContext(text, opts = {}) {
31
+ const intent = classifyIntent(text);
32
+ const entities = extractEntities(text, {
33
+ cwd: opts.cwd,
34
+ explicitProject: opts.project,
35
+ });
36
+ const runbookDetection = detectRunbook(text);
37
+ // ─── Namespace hints ──────────────────────────────────────────────────
38
+ // Project namespace convention (from migration 040): `repo:<name>`.
39
+ // If we have a project, prefer searching that namespace first; fall
40
+ // back to broader namespaces if no hits.
41
+ const namespaceHints = [];
42
+ if (opts.namespaceHints?.length) {
43
+ namespaceHints.push(...opts.namespaceHints);
44
+ }
45
+ if (entities.project) {
46
+ namespaceHints.push(`repo:${entities.project}`);
47
+ namespaceHints.push(`project:${entities.project}`);
48
+ }
49
+ // For runbook-shaped queries, also include the 'mnueron' namespace
50
+ // (the default for `runbook capture` saves).
51
+ if (runbookDetection.isRunbook || intent.kind === 'deploying' || intent.kind === 'debugging') {
52
+ namespaceHints.push('mnueron');
53
+ }
54
+ // Always include 'default' as a fallback.
55
+ if (!namespaceHints.includes('default'))
56
+ namespaceHints.push('default');
57
+ // ─── Worth searching? ────────────────────────────────────────────────
58
+ // We skip the recall query entirely if the signal is so weak there's
59
+ // no useful filter. Saves DB queries on every keystroke debounce.
60
+ const worthSearching = intent.kind !== 'none' ||
61
+ entities.files.length > 0 ||
62
+ entities.commands.length > 0 ||
63
+ entities.errors.length > 0 ||
64
+ runbookDetection.confidence > 0.4 ||
65
+ (entities.technologies.length > 0 && entities.technologies.length >= 2);
66
+ return {
67
+ intent,
68
+ entities,
69
+ runbookDetection,
70
+ namespaceHints,
71
+ worthSearching,
72
+ };
73
+ }
74
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/lib/context-engine/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,cAAc,EAAsB,MAAM,aAAa,CAAC;AACjE,OAAO,EAAE,eAAe,EAAwB,MAAM,eAAe,CAAC;AACtE,OAAO,EAAE,aAAa,EAAyB,MAAM,uBAAuB,CAAC;AAU7E,OAAO,EACL,cAAc,EACd,cAAc,EACd,kBAAkB,EAClB,aAAa,GACd,MAAM,iBAAiB,CAAC;AA8BzB;;;;;;;;GAQG;AACH,MAAM,UAAU,cAAc,CAAC,IAAY,EAAE,OAAuB,EAAE;IACpE,MAAM,MAAM,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC;IACpC,MAAM,QAAQ,GAAG,eAAe,CAAC,IAAI,EAAE;QACrC,GAAG,EAAE,IAAI,CAAC,GAAG;QACb,eAAe,EAAE,IAAI,CAAC,OAAO;KAC9B,CAAC,CAAC;IACH,MAAM,gBAAgB,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;IAE7C,yEAAyE;IACzE,oEAAoE;IACpE,oEAAoE;IACpE,yCAAyC;IACzC,MAAM,cAAc,GAAa,EAAE,CAAC;IACpC,IAAI,IAAI,CAAC,cAAc,EAAE,MAAM,EAAE,CAAC;QAChC,cAAc,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,cAAc,CAAC,CAAC;IAC9C,CAAC;IACD,IAAI,QAAQ,CAAC,OAAO,EAAE,CAAC;QACrB,cAAc,CAAC,IAAI,CAAC,QAAQ,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC;QAChD,cAAc,CAAC,IAAI,CAAC,WAAW,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC;IACrD,CAAC;IACD,mEAAmE;IACnE,6CAA6C;IAC7C,IAAI,gBAAgB,CAAC,SAAS,IAAI,MAAM,CAAC,IAAI,KAAK,WAAW,IAAI,MAAM,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;QAC7F,cAAc,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACjC,CAAC;IACD,0CAA0C;IAC1C,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,SAAS,CAAC;QAAE,cAAc,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAExE,wEAAwE;IACxE,qEAAqE;IACrE,kEAAkE;IAClE,MAAM,cAAc,GAClB,MAAM,CAAC,IAAI,KAAK,MAAM;QACtB,QAAQ,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC;QACzB,QAAQ,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC;QAC5B,QAAQ,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC;QAC1B,gBAAgB,CAAC,UAAU,GAAG,GAAG;QACjC,CAAC,QAAQ,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC,IAAI,QAAQ,CAAC,YAAY,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC;IAE1E,OAAO;QACL,MAAM;QACN,QAAQ;QACR,gBAAgB;QAChB,cAAc;QACd,cAAc;KACf,CAAC;AACJ,CAAC"}