@monoes/monograph 2.24.0 → 2.25.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 (116) hide show
  1. package/dist/src/embeddings/store.d.ts +38 -0
  2. package/dist/src/embeddings/store.d.ts.map +1 -0
  3. package/dist/src/embeddings/store.js +147 -0
  4. package/dist/src/embeddings/store.js.map +1 -0
  5. package/dist/src/embeddings/transformers-embedder.d.ts +19 -0
  6. package/dist/src/embeddings/transformers-embedder.d.ts.map +1 -0
  7. package/dist/src/embeddings/transformers-embedder.js +48 -0
  8. package/dist/src/embeddings/transformers-embedder.js.map +1 -0
  9. package/dist/src/index.d.ts +4 -0
  10. package/dist/src/index.d.ts.map +1 -1
  11. package/dist/src/index.js +4 -0
  12. package/dist/src/index.js.map +1 -1
  13. package/dist/src/mcp-tools/brief.d.ts +40 -0
  14. package/dist/src/mcp-tools/brief.d.ts.map +1 -0
  15. package/dist/src/mcp-tools/brief.js +65 -0
  16. package/dist/src/mcp-tools/brief.js.map +1 -0
  17. package/dist/src/mcp-tools/context.d.ts +9 -0
  18. package/dist/src/mcp-tools/context.d.ts.map +1 -1
  19. package/dist/src/mcp-tools/context.js +20 -0
  20. package/dist/src/mcp-tools/context.js.map +1 -1
  21. package/dist/src/mcp-tools/impact.d.ts +9 -0
  22. package/dist/src/mcp-tools/impact.d.ts.map +1 -1
  23. package/dist/src/mcp-tools/impact.js +37 -0
  24. package/dist/src/mcp-tools/impact.js.map +1 -1
  25. package/dist/src/mcp-tools/index.d.ts +1 -0
  26. package/dist/src/mcp-tools/index.d.ts.map +1 -1
  27. package/dist/src/mcp-tools/index.js +1 -0
  28. package/dist/src/mcp-tools/index.js.map +1 -1
  29. package/dist/src/pipeline/manifest.d.ts +34 -0
  30. package/dist/src/pipeline/manifest.d.ts.map +1 -0
  31. package/dist/src/pipeline/manifest.js +38 -0
  32. package/dist/src/pipeline/manifest.js.map +1 -0
  33. package/dist/src/pipeline/orchestrator.d.ts.map +1 -1
  34. package/dist/src/pipeline/orchestrator.js +18 -33
  35. package/dist/src/pipeline/orchestrator.js.map +1 -1
  36. package/dist/src/pipeline/phases/agent-registry.d.ts +33 -0
  37. package/dist/src/pipeline/phases/agent-registry.d.ts.map +1 -0
  38. package/dist/src/pipeline/phases/agent-registry.js +153 -0
  39. package/dist/src/pipeline/phases/agent-registry.js.map +1 -0
  40. package/dist/src/pipeline/phases/communities.d.ts.map +1 -1
  41. package/dist/src/pipeline/phases/communities.js +21 -16
  42. package/dist/src/pipeline/phases/communities.js.map +1 -1
  43. package/dist/src/pipeline/phases/cross-file.d.ts.map +1 -1
  44. package/dist/src/pipeline/phases/cross-file.js +8 -2
  45. package/dist/src/pipeline/phases/cross-file.js.map +1 -1
  46. package/dist/src/pipeline/phases/god-nodes.d.ts +8 -0
  47. package/dist/src/pipeline/phases/god-nodes.d.ts.map +1 -1
  48. package/dist/src/pipeline/phases/god-nodes.js +8 -0
  49. package/dist/src/pipeline/phases/god-nodes.js.map +1 -1
  50. package/dist/src/pipeline/phases/parse.d.ts.map +1 -1
  51. package/dist/src/pipeline/phases/parse.js +18 -2
  52. package/dist/src/pipeline/phases/parse.js.map +1 -1
  53. package/dist/src/pipeline/phases/processes.d.ts.map +1 -1
  54. package/dist/src/pipeline/phases/processes.js +7 -0
  55. package/dist/src/pipeline/phases/processes.js.map +1 -1
  56. package/dist/src/pipeline/phases/stale-sweep.d.ts +21 -0
  57. package/dist/src/pipeline/phases/stale-sweep.d.ts.map +1 -0
  58. package/dist/src/pipeline/phases/stale-sweep.js +49 -0
  59. package/dist/src/pipeline/phases/stale-sweep.js.map +1 -0
  60. package/dist/src/pipeline/phases/summaries.d.ts +20 -0
  61. package/dist/src/pipeline/phases/summaries.d.ts.map +1 -0
  62. package/dist/src/pipeline/phases/summaries.js +176 -0
  63. package/dist/src/pipeline/phases/summaries.js.map +1 -0
  64. package/dist/src/pipeline/phases/tests-edges.d.ts +16 -0
  65. package/dist/src/pipeline/phases/tests-edges.d.ts.map +1 -0
  66. package/dist/src/pipeline/phases/tests-edges.js +44 -0
  67. package/dist/src/pipeline/phases/tests-edges.js.map +1 -0
  68. package/dist/src/pipeline/runner.d.ts.map +1 -1
  69. package/dist/src/pipeline/runner.js +5 -0
  70. package/dist/src/pipeline/runner.js.map +1 -1
  71. package/dist/src/search/eval.d.ts +44 -0
  72. package/dist/src/search/eval.d.ts.map +1 -0
  73. package/dist/src/search/eval.js +35 -0
  74. package/dist/src/search/eval.js.map +1 -0
  75. package/dist/src/search/fused-search.d.ts +22 -0
  76. package/dist/src/search/fused-search.d.ts.map +1 -0
  77. package/dist/src/search/fused-search.js +103 -0
  78. package/dist/src/search/fused-search.js.map +1 -0
  79. package/dist/src/search/hybrid-query.d.ts +29 -2
  80. package/dist/src/search/hybrid-query.d.ts.map +1 -1
  81. package/dist/src/search/hybrid-query.js +111 -2
  82. package/dist/src/search/hybrid-query.js.map +1 -1
  83. package/dist/src/staleness/git-staleness.d.ts.map +1 -1
  84. package/dist/src/staleness/git-staleness.js +2 -0
  85. package/dist/src/staleness/git-staleness.js.map +1 -1
  86. package/dist/src/storage/db.d.ts.map +1 -1
  87. package/dist/src/storage/db.js +3 -2
  88. package/dist/src/storage/db.js.map +1 -1
  89. package/dist/src/storage/fts-store-hybrid.d.ts +2 -2
  90. package/dist/src/storage/fts-store-hybrid.d.ts.map +1 -1
  91. package/dist/src/storage/fts-store-hybrid.js +4 -3
  92. package/dist/src/storage/fts-store-hybrid.js.map +1 -1
  93. package/dist/src/storage/fts-store-search.d.ts +14 -1
  94. package/dist/src/storage/fts-store-search.d.ts.map +1 -1
  95. package/dist/src/storage/fts-store-search.js +25 -8
  96. package/dist/src/storage/fts-store-search.js.map +1 -1
  97. package/dist/src/storage/fts-store-terms.d.ts +1 -0
  98. package/dist/src/storage/fts-store-terms.d.ts.map +1 -1
  99. package/dist/src/storage/fts-store-terms.js +1 -1
  100. package/dist/src/storage/fts-store-terms.js.map +1 -1
  101. package/dist/src/storage/node-store.d.ts.map +1 -1
  102. package/dist/src/storage/node-store.js +23 -9
  103. package/dist/src/storage/node-store.js.map +1 -1
  104. package/dist/src/storage/schema.d.ts +14 -2
  105. package/dist/src/storage/schema.d.ts.map +1 -1
  106. package/dist/src/storage/schema.js +22 -5
  107. package/dist/src/storage/schema.js.map +1 -1
  108. package/dist/src/storage/summary-search.d.ts +13 -0
  109. package/dist/src/storage/summary-search.d.ts.map +1 -0
  110. package/dist/src/storage/summary-search.js +58 -0
  111. package/dist/src/storage/summary-search.js.map +1 -0
  112. package/dist/src/types.d.ts +2 -2
  113. package/dist/src/types.d.ts.map +1 -1
  114. package/dist/src/types.js.map +1 -1
  115. package/dist/tsconfig.tsbuildinfo +1 -1
  116. package/package.json +1 -1
@@ -0,0 +1,176 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { basename, dirname, join } from 'node:path';
3
+ import { CODE_SCOPE_LABELS } from '../../search/hybrid-query.js';
4
+ import { definitionText } from './agent-registry.js';
5
+ const MAX_COMMENT_CHARS = 300;
6
+ const HEADER_SCAN_LINES = 40;
7
+ /** "pickAgents" / "pick_agents" / "pick-agents.ts" → "pick Agents" words. */
8
+ export function splitWords(identifier) {
9
+ return identifier
10
+ .replace(/\.[A-Za-z0-9]+$/, '')
11
+ .replace(/([a-z0-9])([A-Z])/g, '$1 $2')
12
+ .replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2')
13
+ .replace(/[_\-./\\]+/g, ' ')
14
+ .trim();
15
+ }
16
+ function cleanCommentLine(line) {
17
+ return line
18
+ .trim()
19
+ .replace(/^\/\*\*?|\*\/$/g, '')
20
+ .replace(/^\/\/\/?!?|^#+|^\*+/g, '')
21
+ .replace(/@\w+\s*(\{[^}]*\})?/g, ' ')
22
+ .trim();
23
+ }
24
+ const isCommentLine = (l) => /^(\/\/|\/\*|\*|#(?!\[)|--|""")/.test(l.trim());
25
+ /**
26
+ * The comment block directly above a 1-based `startLine` (JSDoc, `//`, `///`,
27
+ * `#`), skipping decorator/attribute lines; for a Python-style `def ...:` with
28
+ * no comment above, the docstring that follows it.
29
+ */
30
+ export function extractLeadingComment(lines, startLine) {
31
+ let i = startLine - 2;
32
+ while (i >= 0 && /^\s*(@|#\[)/.test(lines[i]))
33
+ i--;
34
+ const block = [];
35
+ while (i >= 0 && lines[i].trim() !== '' && isCommentLine(lines[i])) {
36
+ block.unshift(lines[i]);
37
+ i--;
38
+ }
39
+ let text = block.map(cleanCommentLine).filter(Boolean).join(' ');
40
+ if (!text) {
41
+ const def = lines[startLine - 1] ?? '';
42
+ const next = (lines[startLine] ?? '').trim();
43
+ const m = def.trimEnd().endsWith(':') && /^[rRuU]?("""|''')/.exec(next);
44
+ if (m) {
45
+ const quote = m[1];
46
+ const parts = [];
47
+ for (let j = startLine; j < Math.min(lines.length, startLine + 8); j++) {
48
+ const part = lines[j].trim().replace(/^[rRuU]?("""|''')/, '');
49
+ const end = part.indexOf(quote);
50
+ parts.push(end === -1 ? part : part.slice(0, end));
51
+ if (end !== -1)
52
+ break;
53
+ }
54
+ text = parts.join(' ');
55
+ }
56
+ }
57
+ return text.replace(/\s+/g, ' ').trim().slice(0, MAX_COMMENT_CHARS);
58
+ }
59
+ /** The first comment block near the top of a file (its stated purpose). */
60
+ export function extractFileHeader(lines) {
61
+ const head = lines.slice(0, HEADER_SCAN_LINES);
62
+ let i = 0;
63
+ while (i < head.length && (head[i].trim() === '' || head[i].startsWith('#!')))
64
+ i++;
65
+ const block = [];
66
+ while (i < head.length && isCommentLine(head[i]))
67
+ block.push(head[i++]);
68
+ return block
69
+ .map(cleanCommentLine)
70
+ .filter(Boolean)
71
+ .join(' ')
72
+ .replace(/\s+/g, ' ')
73
+ .trim()
74
+ .slice(0, MAX_COMMENT_CHARS);
75
+ }
76
+ const stripMarkdown = (s) => s
77
+ .replace(/<!--[\s\S]*?-->/g, ' ')
78
+ .replace(/!?\[([^\]]*)\]\([^)]*\)/g, '$1')
79
+ .replace(/[`*_>#|]/g, ' ')
80
+ .replace(/\s+/g, ' ')
81
+ .trim();
82
+ /** A markdown document's title and opening paragraph — what it says it is about. */
83
+ export function extractMarkdownLead(lines) {
84
+ let i = 0;
85
+ if (lines[0]?.trim() === '---') {
86
+ i = lines.findIndex((l, n) => n > 0 && l.trim() === '---') + 1 || 0;
87
+ }
88
+ let title = '';
89
+ while (i < lines.length && !title) {
90
+ const m = /^#{1,3}\s+(.+)/.exec(lines[i]);
91
+ if (m)
92
+ title = m[1];
93
+ i++;
94
+ }
95
+ const para = [];
96
+ for (; i < lines.length && para.length < 6; i++) {
97
+ const l = lines[i].trim();
98
+ if (/^#{1,6}\s/.test(l) || l.startsWith('```') || l.startsWith('|')) {
99
+ if (para.length)
100
+ break;
101
+ continue;
102
+ }
103
+ if (l)
104
+ para.push(l);
105
+ else if (para.length)
106
+ break;
107
+ }
108
+ return stripMarkdown(`${title}. ${para.join(' ')}`).slice(0, MAX_COMMENT_CHARS);
109
+ }
110
+ /** Words a person would use to describe a symbol: its name, location and comment. */
111
+ export function buildSummaryText(name, filePath, comment) {
112
+ const where = filePath
113
+ ? `${splitWords(basename(filePath))} ${splitWords(basename(dirname(filePath)))}`
114
+ : '';
115
+ return `${splitWords(name)} ${comment} ${where}`.replace(/\s+/g, ' ').trim();
116
+ }
117
+ /** Rebuilds `node_summaries_fts` for every code symbol and file in the graph. */
118
+ export const summariesPhase = {
119
+ name: 'summaries',
120
+ deps: ['stale-sweep', 'agent-registry'],
121
+ async execute(ctx) {
122
+ const db = ctx.db;
123
+ const labels = [...CODE_SCOPE_LABELS, 'Document'];
124
+ const placeholders = labels.map(() => '?').join(',');
125
+ const rows = db
126
+ .prepare(`SELECT id, label, name, file_path, start_line FROM nodes
127
+ WHERE label IN (${placeholders}) AND file_path IS NOT NULL ORDER BY file_path`)
128
+ .all(...labels);
129
+ db.exec('DELETE FROM node_summaries_fts');
130
+ const insert = db.prepare('INSERT INTO node_summaries_fts (node_id, text) VALUES (?, ?)');
131
+ let summarized = 0;
132
+ // Agent and skill definitions describe themselves in frontmatter.
133
+ const defs = db
134
+ .prepare("SELECT id, name, properties FROM nodes WHERE label IN ('Agent', 'Skill')")
135
+ .all();
136
+ for (const d of defs) {
137
+ let props = {};
138
+ try {
139
+ props = d.properties ? JSON.parse(d.properties) : {};
140
+ }
141
+ catch {
142
+ /* malformed properties: index the name alone */
143
+ }
144
+ insert.run(d.id, definitionText(props, splitWords(d.name)));
145
+ summarized++;
146
+ }
147
+ let currentFile = '';
148
+ let lines = [];
149
+ for (const r of rows) {
150
+ if (r.file_path !== currentFile) {
151
+ currentFile = r.file_path;
152
+ try {
153
+ lines = readFileSync(join(ctx.repoPath, r.file_path), 'utf8').split('\n');
154
+ }
155
+ catch {
156
+ lines = [];
157
+ }
158
+ }
159
+ const isDoc = r.label === 'Document' || /\.(md|mdx)$/i.test(r.file_path);
160
+ const comment = isDoc
161
+ ? extractMarkdownLead(lines)
162
+ : r.label === 'File'
163
+ ? extractFileHeader(lines)
164
+ : r.start_line
165
+ ? extractLeadingComment(lines, r.start_line)
166
+ : '';
167
+ const text = buildSummaryText(r.label === 'File' || r.label === 'Document' ? basename(r.file_path) : r.name, r.file_path, comment);
168
+ if (text) {
169
+ insert.run(r.id, text);
170
+ summarized++;
171
+ }
172
+ }
173
+ return { summarized };
174
+ },
175
+ };
176
+ //# sourceMappingURL=summaries.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"summaries.js","sourceRoot":"","sources":["../../../../src/pipeline/phases/summaries.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import type { PipelinePhase } from '../types.js';
2
+ /** SQL predicate: `col` is the path of a test file. */
3
+ export declare function testPathSql(col: string): string;
4
+ /**
5
+ * Derives `TESTS` edges — test File → the source it exercises — so an agent can
6
+ * ask "what covers this?" and "what should I run after changing it?":
7
+ * - a test file that IMPORTS a non-test file tests that file (INFERRED: an
8
+ * import shows the test touches the file, not that it asserts anything);
9
+ * - a test-file function that CALLS a non-test symbol tests that symbol.
10
+ * Always rebuilt from scratch from the committed IMPORTS/CALLS edges, so it can
11
+ * never drift from them.
12
+ */
13
+ export declare const testsEdgesPhase: PipelinePhase<{
14
+ edges: number;
15
+ }>;
16
+ //# sourceMappingURL=tests-edges.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tests-edges.d.ts","sourceRoot":"","sources":["../../../../src/pipeline/phases/tests-edges.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjD,uDAAuD;AACvD,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,eAAe,EAAE,aAAa,CAAC;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAkC5D,CAAC"}
@@ -0,0 +1,44 @@
1
+ /** SQL predicate: `col` is the path of a test file. */
2
+ export function testPathSql(col) {
3
+ return `(${col} LIKE '%.test.%' OR ${col} LIKE '%.spec.%' OR ${col} LIKE '%/__tests__/%' OR ${col} LIKE '%/tests/%' OR ${col} LIKE '%/test/%' OR ${col} LIKE '%\\_test.%' ESCAPE '\\' OR ${col} LIKE 'test\\_%' ESCAPE '\\')`;
4
+ }
5
+ /**
6
+ * Derives `TESTS` edges — test File → the source it exercises — so an agent can
7
+ * ask "what covers this?" and "what should I run after changing it?":
8
+ * - a test file that IMPORTS a non-test file tests that file (INFERRED: an
9
+ * import shows the test touches the file, not that it asserts anything);
10
+ * - a test-file function that CALLS a non-test symbol tests that symbol.
11
+ * Always rebuilt from scratch from the committed IMPORTS/CALLS edges, so it can
12
+ * never drift from them.
13
+ */
14
+ export const testsEdgesPhase = {
15
+ name: 'tests-edges',
16
+ deps: ['stale-sweep'],
17
+ async execute(ctx) {
18
+ const db = ctx.db;
19
+ db.exec("DELETE FROM edges WHERE relation = 'TESTS'");
20
+ db.exec(`
21
+ INSERT OR IGNORE INTO edges (id, source_id, target_id, relation, confidence, confidence_score, reason)
22
+ SELECT 'tests:' || s.id || '>' || t.id, s.id, t.id, 'TESTS', 'INFERRED', 0.7,
23
+ 'test file imports this file'
24
+ FROM edges e
25
+ JOIN nodes s ON s.id = e.source_id AND s.label = 'File'
26
+ JOIN nodes t ON t.id = e.target_id AND t.label = 'File'
27
+ WHERE e.relation = 'IMPORTS'
28
+ AND ${testPathSql('s.file_path')} AND NOT ${testPathSql('t.file_path')}`);
29
+ // Resolves the calling function to its test File node by path.
30
+ db.exec(`
31
+ INSERT OR IGNORE INTO edges (id, source_id, target_id, relation, confidence, confidence_score, reason)
32
+ SELECT 'tests:' || f.id || '>' || t.id, f.id, t.id, 'TESTS', 'INFERRED', 0.9,
33
+ 'test code calls this symbol'
34
+ FROM edges e
35
+ JOIN nodes s ON s.id = e.source_id
36
+ JOIN nodes f ON f.label = 'File' AND f.file_path = s.file_path
37
+ JOIN nodes t ON t.id = e.target_id
38
+ WHERE e.relation = 'CALLS' AND t.label != 'File'
39
+ AND ${testPathSql('s.file_path')} AND NOT ${testPathSql('t.file_path')}`);
40
+ const { n } = db.prepare("SELECT COUNT(*) AS n FROM edges WHERE relation = 'TESTS'").get();
41
+ return { edges: n };
42
+ },
43
+ };
44
+ //# sourceMappingURL=tests-edges.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tests-edges.js","sourceRoot":"","sources":["../../../../src/pipeline/phases/tests-edges.ts"],"names":[],"mappings":"AAEA,uDAAuD;AACvD,MAAM,UAAU,WAAW,CAAC,GAAW;IACrC,OAAO,IAAI,GAAG,uBAAuB,GAAG,uBAAuB,GAAG,4BAA4B,GAAG,wBAAwB,GAAG,uBAAuB,GAAG,qCAAqC,GAAG,+BAA+B,CAAC;AAChO,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,eAAe,GAAqC;IAC/D,IAAI,EAAE,aAAa;IACnB,IAAI,EAAE,CAAC,aAAa,CAAC;IACrB,KAAK,CAAC,OAAO,CAAC,GAAG;QACf,MAAM,EAAE,GAAG,GAAG,CAAC,EAAE,CAAC;QAClB,EAAE,CAAC,IAAI,CAAC,4CAA4C,CAAC,CAAC;QAEtD,EAAE,CAAC,IAAI,CAAC;;;;;;;;cAQE,WAAW,CAAC,aAAa,CAAC,YAAY,WAAW,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;QAE9E,+DAA+D;QAC/D,EAAE,CAAC,IAAI,CAAC;;;;;;;;;cASE,WAAW,CAAC,aAAa,CAAC,YAAY,WAAW,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;QAE9E,MAAM,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,0DAA0D,CAAC,CAAC,GAAG,EAEvF,CAAC;QACF,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;IACtB,CAAC;CACF,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../../src/pipeline/runner.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEjE,qBAAa,cAAc;IACzB,wFAAwF;IACxF,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAW;IACvC,iCAAiC;IACjC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAsC;IAE/D,YAAY,MAAM,EAAE,aAAa,CAAC,OAAO,CAAC,EAAE,EAG3C;IAEK,GAAG,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAoC7D;CACF"}
1
+ {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../../src/pipeline/runner.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEjE,qBAAa,cAAc;IACzB,wFAAwF;IACxF,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAW;IACvC,iCAAiC;IACjC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAsC;IAE/D,YAAY,MAAM,EAAE,aAAa,CAAC,OAAO,CAAC,EAAE,EAG3C;IAEK,GAAG,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAyC7D;CACF"}
@@ -20,9 +20,14 @@ export class PipelineRunner {
20
20
  // Wait for all dep phases to finish
21
21
  await Promise.all(phase.deps.map((dep) => getOrCreatePromise(dep)));
22
22
  ctx.onProgress?.({ phase: phase.name });
23
+ const startedAt = Date.now();
23
24
  try {
24
25
  const output = await phase.execute(ctx, outputs);
25
26
  outputs.set(phase.name, output);
27
+ ctx.onProgress?.({
28
+ phase: phase.name,
29
+ message: `done in ${((Date.now() - startedAt) / 1000).toFixed(1)}s`,
30
+ });
26
31
  }
27
32
  catch (err) {
28
33
  const msg = err instanceof Error ? err.message : String(err);
@@ -1 +1 @@
1
- {"version":3,"file":"runner.js","sourceRoot":"","sources":["../../../src/pipeline/runner.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"runner.js","sourceRoot":"","sources":["../../../src/pipeline/runner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Retrieval evaluation: run gold queries through {@link searchGraph} and report
3
+ * how often, and how high, the expected node shows up.
4
+ *
5
+ * A case passes at rank r when any hit's name or file path contains one of its
6
+ * `expect` strings (case-insensitive). Substring matching keeps gold files
7
+ * stable across line-number and id-scheme changes.
8
+ */
9
+ import type Database from 'better-sqlite3';
10
+ import type { Embedder } from '../embeddings/store.js';
11
+ import type { GraphSearchOptions } from './hybrid-query.js';
12
+ export interface EvalCase {
13
+ query: string;
14
+ /** Any hit whose name or filePath contains one of these counts as found. */
15
+ expect: string[];
16
+ /** Free-form bucket for the report: exact, ambiguous, natural, doc, dependency. */
17
+ kind?: string;
18
+ scope?: GraphSearchOptions['scope'];
19
+ }
20
+ export interface EvalCaseResult {
21
+ query: string;
22
+ kind: string;
23
+ /** 1-based rank of the first expected hit, or null when absent from the top k. */
24
+ rank: number | null;
25
+ }
26
+ export interface EvalReport {
27
+ k: number;
28
+ cases: number;
29
+ recallAtK: number;
30
+ /** Mean reciprocal rank: 1/rank per case, 0 when absent. */
31
+ mrr: number;
32
+ byKind: Record<string, {
33
+ cases: number;
34
+ recallAtK: number;
35
+ mrr: number;
36
+ }>;
37
+ results: EvalCaseResult[];
38
+ }
39
+ export declare function evaluateRetrieval(db: Database.Database, cases: EvalCase[], options?: {
40
+ k?: number;
41
+ search?: Partial<GraphSearchOptions>;
42
+ embedder?: Embedder | null;
43
+ }): Promise<EvalReport>;
44
+ //# sourceMappingURL=eval.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"eval.d.ts","sourceRoot":"","sources":["../../../src/search/eval.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAC3C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAEvD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAE5D,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,4EAA4E;IAC5E,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,mFAAmF;IACnF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,kBAAkB,CAAC,OAAO,CAAC,CAAC;CACrC;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,kFAAkF;IAClF,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CACrB;AAED,MAAM,WAAW,UAAU;IACzB,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,4DAA4D;IAC5D,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC1E,OAAO,EAAE,cAAc,EAAE,CAAC;CAC3B;AAED,wBAAsB,iBAAiB,CACrC,EAAE,EAAE,QAAQ,CAAC,QAAQ,EACrB,KAAK,EAAE,QAAQ,EAAE,EACjB,OAAO,GAAE;IAAE,CAAC,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAC,kBAAkB,CAAC,CAAC;IAAC,QAAQ,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAA;CAAO,GAC7F,OAAO,CAAC,UAAU,CAAC,CA6BrB"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Retrieval evaluation: run gold queries through {@link searchGraph} and report
3
+ * how often, and how high, the expected node shows up.
4
+ *
5
+ * A case passes at rank r when any hit's name or file path contains one of its
6
+ * `expect` strings (case-insensitive). Substring matching keeps gold files
7
+ * stable across line-number and id-scheme changes.
8
+ */
9
+ import { searchGraphFused } from './fused-search.js';
10
+ export async function evaluateRetrieval(db, cases, options = {}) {
11
+ const k = options.k ?? 10;
12
+ const results = [];
13
+ for (const c of cases) {
14
+ const hits = await searchGraphFused(db, c.query, {
15
+ ...options.search,
16
+ scope: c.scope ?? options.search?.scope,
17
+ limit: k,
18
+ embedder: options.embedder,
19
+ });
20
+ const wanted = c.expect.map((e) => e.toLowerCase());
21
+ const idx = hits.findIndex((h) => wanted.some((w) => h.name.toLowerCase().includes(w) || (h.filePath ?? '').toLowerCase().includes(w)));
22
+ results.push({ query: c.query, kind: c.kind ?? 'general', rank: idx === -1 ? null : idx + 1 });
23
+ }
24
+ const summarize = (rs) => ({
25
+ cases: rs.length,
26
+ recallAtK: rs.length ? rs.filter((r) => r.rank !== null).length / rs.length : 0,
27
+ mrr: rs.length ? rs.reduce((sum, r) => sum + (r.rank ? 1 / r.rank : 0), 0) / rs.length : 0,
28
+ });
29
+ const byKind = {};
30
+ for (const kind of new Set(results.map((r) => r.kind))) {
31
+ byKind[kind] = summarize(results.filter((r) => r.kind === kind));
32
+ }
33
+ return { k, ...summarize(results), byKind, results };
34
+ }
35
+ //# sourceMappingURL=eval.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"eval.js","sourceRoot":"","sources":["../../../src/search/eval.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AA6BrD,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,EAAqB,EACrB,KAAiB,EACjB,OAAO,GAAqF,EAAE;IAE9F,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC;IAC1B,MAAM,OAAO,GAAqB,EAAE,CAAC;IACrC,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,MAAM,IAAI,GAAG,MAAM,gBAAgB,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,EAAE;YAC/C,GAAG,OAAO,CAAC,MAAM;YACjB,KAAK,EAAE,CAAC,CAAC,KAAK,IAAI,OAAO,CAAC,MAAM,EAAE,KAAK;YACvC,KAAK,EAAE,CAAC;YACR,QAAQ,EAAE,OAAO,CAAC,QAAQ;SAC3B,CAAC,CAAC;QACH,MAAM,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;QACpD,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAC/B,MAAM,CAAC,IAAI,CACT,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,CACxF,CACF,CAAC;QACF,OAAO,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,IAAI,SAAS,EAAE,IAAI,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,EAAE,CAAC,CAAC;IACjG,CAAC;IAED,MAAM,SAAS,GAAG,CAAC,EAAoB,EAAE,EAAE,CAAC,CAAC;QAC3C,KAAK,EAAE,EAAE,CAAC,MAAM;QAChB,SAAS,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC,MAAM,GAAG,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QAC/E,GAAG,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;KAC3F,CAAC,CAAC;IACH,MAAM,MAAM,GAAyB,EAAE,CAAC;IACxC,KAAK,MAAM,IAAI,IAAI,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;QACvD,MAAM,CAAC,IAAI,CAAC,GAAG,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC;IACnE,CAAC;IACD,OAAO,EAAE,CAAC,EAAE,GAAG,SAAS,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;AACvD,CAAC"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Lexical + semantic retrieval, fused.
3
+ *
4
+ * Lexical search (names, stemmed summaries) is exact and fast but only finds
5
+ * what shares words with the query. Embeddings find "which agent should handle
6
+ * a task" -> `pickAgents` but blur exact identifiers. Reciprocal-rank fusion
7
+ * keeps the strengths of both; the weights follow the query's style, and an
8
+ * exact name match is always pinned first.
9
+ *
10
+ * Degrades to plain lexical search when no embedder is supplied, nothing has
11
+ * been embedded yet, or the query only concerns documentation.
12
+ */
13
+ import type Database from 'better-sqlite3';
14
+ import { type Embedder } from '../embeddings/store.js';
15
+ import { type GraphSearchOptions, type GraphSearchResult } from './hybrid-query.js';
16
+ export interface FusedSearchOptions extends GraphSearchOptions {
17
+ embedder?: Embedder | null;
18
+ }
19
+ /** A single token (`UserService`, `agent-pick`, `fts-store.ts`) is a name, not a question. */
20
+ export declare function isIdentifierQuery(query: string): boolean;
21
+ export declare function searchGraphFused(db: Database.Database, query: string, options?: FusedSearchOptions): Promise<GraphSearchResult[]>;
22
+ //# sourceMappingURL=fused-search.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fused-search.d.ts","sourceRoot":"","sources":["../../../src/search/fused-search.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,KAAK,QAAQ,EAAoC,MAAM,wBAAwB,CAAC;AACzF,OAAO,EAGL,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EAKvB,MAAM,mBAAmB,CAAC;AAS3B,MAAM,WAAW,kBAAmB,SAAQ,kBAAkB;IAC5D,QAAQ,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAC;CAC5B;AAED,8FAA8F;AAC9F,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAExD;AAUD,wBAAsB,gBAAgB,CACpC,EAAE,EAAE,QAAQ,CAAC,QAAQ,EACrB,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC,iBAAiB,EAAE,CAAC,CA0E9B"}
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Lexical + semantic retrieval, fused.
3
+ *
4
+ * Lexical search (names, stemmed summaries) is exact and fast but only finds
5
+ * what shares words with the query. Embeddings find "which agent should handle
6
+ * a task" -> `pickAgents` but blur exact identifiers. Reciprocal-rank fusion
7
+ * keeps the strengths of both; the weights follow the query's style, and an
8
+ * exact name match is always pinned first.
9
+ *
10
+ * Degrades to plain lexical search when no embedder is supplied, nothing has
11
+ * been embedded yet, or the query only concerns documentation.
12
+ */
13
+ import { semanticSearch } from '../embeddings/store.js';
14
+ import { AGENT_SCOPE_LABELS, CODE_SCOPE_LABELS, searchGraph, TEST_FACTOR, TEST_INTENT, TEST_PATH, } from './hybrid-query.js';
15
+ const RRF_K = 60;
16
+ /** How many of keyword search's top hits are never dropped by fusion. */
17
+ const PROTECTED_LEXICAL = 2;
18
+ /** [lexical, semantic] weights. */
19
+ const WEIGHTS_IDENTIFIER = [2, 1];
20
+ const WEIGHTS_NATURAL = [1, 2];
21
+ /** A single token (`UserService`, `agent-pick`, `fts-store.ts`) is a name, not a question. */
22
+ export function isIdentifierQuery(query) {
23
+ return !/\s/.test(query.trim());
24
+ }
25
+ function semanticLabels(options) {
26
+ if (options.label)
27
+ return options.label;
28
+ if (options.scope === 'code')
29
+ return CODE_SCOPE_LABELS;
30
+ if (options.scope === 'agents')
31
+ return AGENT_SCOPE_LABELS;
32
+ if (options.scope === 'docs')
33
+ return ['Document'];
34
+ return undefined;
35
+ }
36
+ export async function searchGraphFused(db, query, options = {}) {
37
+ const { embedder, ...base } = options;
38
+ const limit = base.limit ?? 20;
39
+ const q = (query ?? '').trim();
40
+ if (limit <= 0 || !q)
41
+ return [];
42
+ const pool = Math.max(limit * 5, 50);
43
+ const lexical = searchGraph(db, q, { ...base, limit: pool });
44
+ if (!embedder)
45
+ return lexical.slice(0, limit);
46
+ let sem = [];
47
+ try {
48
+ sem = semanticSearch(db, await embedder.embedQuery(q), pool, semanticLabels(base));
49
+ }
50
+ catch {
51
+ return lexical.slice(0, limit);
52
+ }
53
+ if (sem.length === 0)
54
+ return lexical.slice(0, limit);
55
+ const [wl, ws] = isIdentifierQuery(q) ? WEIGHTS_IDENTIFIER : WEIGHTS_NATURAL;
56
+ const wantsTests = TEST_INTENT.test(q);
57
+ const fused = new Map();
58
+ const add = (hit, rank, weight) => {
59
+ const penalty = !wantsTests && hit.filePath && TEST_PATH.test(hit.filePath) ? TEST_FACTOR : 1;
60
+ const entry = fused.get(hit.id) ?? { hit, score: 0 };
61
+ entry.score += (weight * penalty) / (RRF_K + rank + 1);
62
+ fused.set(hit.id, entry);
63
+ };
64
+ lexical.forEach((hit, i) => add(hit, i, wl));
65
+ sem.forEach((s, i) => add({
66
+ id: s.id,
67
+ name: s.name,
68
+ normLabel: s.normLabel,
69
+ filePath: s.filePath,
70
+ label: s.label,
71
+ relevance: 0,
72
+ startLine: s.startLine,
73
+ endLine: s.endLine,
74
+ matchStrategy: 'semantic',
75
+ }, i, ws));
76
+ const ceiling = (wl + ws) / (RRF_K + 1);
77
+ const ranked = [...fused.values()]
78
+ .sort((a, b) => b.score - a.score)
79
+ .map(({ hit, score }) => ({ ...hit, relevance: score / ceiling }));
80
+ // An exact name match is what the caller typed; nothing the fusion adds outranks it.
81
+ const lower = q.toLowerCase();
82
+ const exact = ranked.filter((r) => r.name.toLowerCase() === lower);
83
+ const rest = ranked.filter((r) => r.name.toLowerCase() !== lower);
84
+ const final = [...exact, ...rest].slice(0, limit);
85
+ // Keyword search's two best hits stay in the results even when the semantic lane crowds
86
+ // them out (a sentence that contains a real name — "share a meeting" -> shareRoutes —
87
+ // is exactly where a keyword hit is right and the embedding is only roughly so).
88
+ const present = new Set(final.map((r) => r.id));
89
+ const protectedHits = lexical.slice(0, PROTECTED_LEXICAL).filter((h) => !present.has(h.id));
90
+ for (const hit of protectedHits) {
91
+ if (final.length >= limit) {
92
+ if (final.length <= exact.length)
93
+ break; // never evict an exact name match
94
+ final.pop();
95
+ }
96
+ final.push({
97
+ ...(ranked.find((r) => r.id === hit.id) ?? hit),
98
+ relevance: final.at(-1)?.relevance ?? 0,
99
+ });
100
+ }
101
+ return final;
102
+ }
103
+ //# sourceMappingURL=fused-search.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fused-search.js","sourceRoot":"","sources":["../../../src/search/fused-search.ts"],"names":[],"mappings":"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"}
@@ -30,9 +30,32 @@ import type { RankedResult } from './rrf.js';
30
30
  * wants pure lexical ranking with no heuristic bonuses.
31
31
  */
32
32
  export type RetrievalMode = 'bm25' | 'hybrid';
33
+ /**
34
+ * Which part of the graph a search covers.
35
+ *
36
+ * - `code` — symbols, files, routes and tools.
37
+ * - `docs` — markdown sections, documents and concepts.
38
+ * - `agents` — agent and skill definitions.
39
+ * - `all` — everything, ranked purely by relevance.
40
+ * - `auto` (default) — code first, with documentation filling any slots the
41
+ * code left empty; a query that is plainly about documentation flips that
42
+ * order. Doc sections outnumber code symbols roughly 1:2 in this repo, so
43
+ * without this an abstract task query is answered entirely by headings.
44
+ */
45
+ export type SearchScope = 'code' | 'docs' | 'agents' | 'all' | 'auto';
46
+ export declare const CODE_SCOPE_LABELS: readonly string[];
47
+ export declare const DOC_SCOPE_LABELS: readonly string[];
48
+ export declare const AGENT_SCOPE_LABELS: readonly string[];
49
+ /** True when the query is asking which agent or skill to use. */
50
+ export declare function hasAgentIntent(query: string): boolean;
51
+ /** True when the query is plainly asking about documentation rather than code. */
52
+ export declare function hasDocIntent(query: string): boolean;
33
53
  export interface GraphSearchOptions {
34
54
  limit?: number;
35
- label?: string;
55
+ /** Exact node label (or labels). Overrides `scope`. */
56
+ label?: string | readonly string[];
57
+ /** Part of the graph to search (default: `auto`). */
58
+ scope?: SearchScope;
36
59
  /** Retrieval strategy (default: `hybrid`). */
37
60
  mode?: RetrievalMode;
38
61
  }
@@ -49,7 +72,7 @@ export interface GraphSearchResult {
49
72
  /** Last line of the symbol in its source file (1-based, null if unknown). */
50
73
  endLine: number | null;
51
74
  /** Which retrieval strategy surfaced this row. */
52
- matchStrategy: 'fts' | 'like' | 'fuzzy';
75
+ matchStrategy: 'fts' | 'like' | 'fuzzy' | 'semantic';
53
76
  }
54
77
  /**
55
78
  * Run a lexical search over the graph and return results under the
@@ -68,6 +91,10 @@ export interface GraphSearchResult {
68
91
  * would break the LIKE fallback against a capitalized name.
69
92
  */
70
93
  export declare function searchGraph(db: Database.Database, query: string, options?: GraphSearchOptions): GraphSearchResult[];
94
+ /** Tests restate the code they exercise; they rank below it unless asked for. */
95
+ export declare const TEST_PATH: RegExp;
96
+ export declare const TEST_INTENT: RegExp;
97
+ export declare const TEST_FACTOR = 0.6;
71
98
  export interface BM25QueryOptions {
72
99
  limit?: number;
73
100
  label?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"hybrid-query.d.ts","sourceRoot":"","sources":["../../../src/search/hybrid-query.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAE3C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAE7C;;;;;;;;GAQG;AACH,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,QAAQ,CAAC;AAE9C,MAAM,WAAW,kBAAkB;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,8CAA8C;IAC9C,IAAI,CAAC,EAAE,aAAa,CAAC;CACtB;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,yEAAyE;IACzE,SAAS,EAAE,MAAM,CAAC;IAClB,8EAA8E;IAC9E,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,6EAA6E;IAC7E,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,kDAAkD;IAClD,aAAa,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,CAAC;CACzC;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,WAAW,CACzB,EAAE,EAAE,QAAQ,CAAC,QAAQ,EACrB,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,kBAAuB,GAC/B,iBAAiB,EAAE,CAqCrB;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,uIAAuI;IACvI,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,yGAAyG;AACzG,MAAM,MAAM,kBAAkB,GAAG,gBAAgB,CAAC;AAElD,MAAM,WAAW,UAAW,SAAQ,YAAY;IAC9C,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,0EAA0E;IAC1E,KAAK,EAAE,MAAM,CAAC;IACd,8EAA8E;IAC9E,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,6EAA6E;IAC7E,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED,wGAAwG;AACxG,MAAM,MAAM,YAAY,GAAG,UAAU,CAAC;AAEtC;;;;;;GAMG;AACH,wBAAsB,SAAS,CAC7B,EAAE,EAAE,QAAQ,CAAC,QAAQ,EACrB,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,UAAU,EAAE,CAAC,CAYvB;AAED;;;;;GAKG;AACH,eAAO,MAAM,WAAW,kBAAY,CAAC"}
1
+ {"version":3,"file":"hybrid-query.d.ts","sourceRoot":"","sources":["../../../src/search/hybrid-query.ts"],"names":[],"mappings":"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"}