modular-studio 1.0.5 → 1.1.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/README.md +122 -122
- package/dist/assets/Badge-Bsy2H_p2.js +1 -0
- package/dist/assets/GraphPanel-D4X3faxA.js +47 -0
- package/dist/assets/{Input-Bgp734xs.js → Input-Dyb88Erk.js} +1 -1
- package/dist/assets/KnowledgeTab-BccWz7Np.js +5 -0
- package/dist/assets/MemoryTab-Y_66cE01.js +16 -0
- package/dist/assets/QualificationTab-Dm9dEIpM.js +1 -0
- package/dist/assets/ReviewTab-BrfXSyyf.js +104 -0
- package/dist/assets/{Section-DoJrmytO.js → Section-68XDCFTl.js} +1 -1
- package/dist/assets/TestTab-CLKRT63X.js +42 -0
- package/dist/assets/ToolsTab-xumi9Uds.js +1 -0
- package/dist/assets/icons-CS8RUPBi.js +1 -0
- package/dist/assets/index-B2bm0161.css +1 -0
- package/dist/assets/index-C626nWuA.js +422 -0
- package/dist/assets/services-BDk6yY4o.js +369 -0
- package/dist/index.html +18 -18
- package/dist-server/bin/modular-mcp.js +1 -0
- package/dist-server/server/index.d.ts.map +1 -1
- package/dist-server/server/index.js +34 -0
- package/dist-server/server/mcp/manager.d.ts +3 -0
- package/dist-server/server/mcp/manager.d.ts.map +1 -1
- package/dist-server/server/mcp/manager.js +80 -5
- package/dist-server/server/migrations/index.d.ts +11 -0
- package/dist-server/server/migrations/index.d.ts.map +1 -0
- package/dist-server/server/migrations/index.js +57 -0
- package/dist-server/server/routes/agents.d.ts.map +1 -1
- package/dist-server/server/routes/agents.js +27 -0
- package/dist-server/server/routes/analytics.d.ts +3 -0
- package/dist-server/server/routes/analytics.d.ts.map +1 -0
- package/dist-server/server/routes/analytics.js +24 -0
- package/dist-server/server/routes/cache.d.ts +3 -0
- package/dist-server/server/routes/cache.d.ts.map +1 -0
- package/dist-server/server/routes/cache.js +55 -0
- package/dist-server/server/routes/connectors/airtable.d.ts +7 -0
- package/dist-server/server/routes/connectors/airtable.d.ts.map +1 -0
- package/dist-server/server/routes/connectors/airtable.js +119 -0
- package/dist-server/server/routes/connectors/confluence.d.ts +7 -0
- package/dist-server/server/routes/connectors/confluence.d.ts.map +1 -0
- package/dist-server/server/routes/connectors/confluence.js +176 -0
- package/dist-server/server/routes/connectors/github.d.ts +7 -0
- package/dist-server/server/routes/connectors/github.d.ts.map +1 -0
- package/dist-server/server/routes/connectors/github.js +195 -0
- package/dist-server/server/routes/connectors/gmail.d.ts +7 -0
- package/dist-server/server/routes/connectors/gmail.d.ts.map +1 -0
- package/dist-server/server/routes/connectors/gmail.js +115 -0
- package/dist-server/server/routes/connectors/google-docs.d.ts +10 -0
- package/dist-server/server/routes/connectors/google-docs.d.ts.map +1 -0
- package/dist-server/server/routes/connectors/google-docs.js +165 -0
- package/dist-server/server/routes/connectors/google-drive.d.ts +7 -0
- package/dist-server/server/routes/connectors/google-drive.d.ts.map +1 -0
- package/dist-server/server/routes/connectors/google-drive.js +163 -0
- package/dist-server/server/routes/connectors/google-sheets.d.ts +7 -0
- package/dist-server/server/routes/connectors/google-sheets.d.ts.map +1 -0
- package/dist-server/server/routes/connectors/google-sheets.js +90 -0
- package/dist-server/server/routes/connectors/hubspot.d.ts +7 -0
- package/dist-server/server/routes/connectors/hubspot.d.ts.map +1 -0
- package/dist-server/server/routes/connectors/hubspot.js +134 -0
- package/dist-server/server/routes/connectors/index.d.ts +6 -0
- package/dist-server/server/routes/connectors/index.d.ts.map +1 -0
- package/dist-server/server/routes/connectors/index.js +38 -0
- package/dist-server/server/routes/connectors/jira.d.ts +7 -0
- package/dist-server/server/routes/connectors/jira.d.ts.map +1 -0
- package/dist-server/server/routes/connectors/jira.js +151 -0
- package/dist-server/server/routes/connectors/linear.d.ts +7 -0
- package/dist-server/server/routes/connectors/linear.d.ts.map +1 -0
- package/dist-server/server/routes/connectors/linear.js +154 -0
- package/dist-server/server/routes/connectors/notion.d.ts +10 -0
- package/dist-server/server/routes/connectors/notion.d.ts.map +1 -0
- package/dist-server/server/routes/connectors/notion.js +201 -0
- package/dist-server/server/routes/connectors/plane.d.ts +10 -0
- package/dist-server/server/routes/connectors/plane.d.ts.map +1 -0
- package/dist-server/server/routes/connectors/plane.js +189 -0
- package/dist-server/server/routes/connectors/shared.d.ts +25 -0
- package/dist-server/server/routes/connectors/shared.d.ts.map +1 -0
- package/dist-server/server/routes/connectors/shared.js +202 -0
- package/dist-server/server/routes/connectors/slack.d.ts +7 -0
- package/dist-server/server/routes/connectors/slack.d.ts.map +1 -0
- package/dist-server/server/routes/connectors/slack.js +153 -0
- package/dist-server/server/routes/connectors.d.ts.map +1 -1
- package/dist-server/server/routes/connectors.js +47 -17
- package/dist-server/server/routes/cost.d.ts +3 -0
- package/dist-server/server/routes/cost.d.ts.map +1 -0
- package/dist-server/server/routes/cost.js +113 -0
- package/dist-server/server/routes/graph.d.ts +11 -0
- package/dist-server/server/routes/graph.d.ts.map +1 -0
- package/dist-server/server/routes/graph.js +213 -0
- package/dist-server/server/routes/lessons.d.ts +3 -0
- package/dist-server/server/routes/lessons.d.ts.map +1 -0
- package/dist-server/server/routes/lessons.js +160 -0
- package/dist-server/server/routes/llm.d.ts.map +1 -1
- package/dist-server/server/routes/llm.js +85 -18
- package/dist-server/server/routes/memory.d.ts.map +1 -1
- package/dist-server/server/routes/memory.js +31 -0
- package/dist-server/server/routes/metaprompt-v2.d.ts +3 -0
- package/dist-server/server/routes/metaprompt-v2.d.ts.map +1 -0
- package/dist-server/server/routes/metaprompt-v2.js +104 -0
- package/dist-server/server/routes/qualification.d.ts.map +1 -1
- package/dist-server/server/routes/qualification.js +342 -334
- package/dist-server/server/routes/repo-index.d.ts.map +1 -1
- package/dist-server/server/routes/repo-index.js +7 -0
- package/dist-server/server/routes/skills-search.d.ts.map +1 -1
- package/dist-server/server/routes/skills-search.js +192 -26
- package/dist-server/server/routes/tool-analytics.d.ts +3 -0
- package/dist-server/server/routes/tool-analytics.d.ts.map +1 -0
- package/dist-server/server/routes/tool-analytics.js +47 -0
- package/dist-server/server/services/adapters/hindsightAdapter.d.ts +28 -0
- package/dist-server/server/services/adapters/hindsightAdapter.d.ts.map +1 -0
- package/dist-server/server/services/adapters/hindsightAdapter.js +63 -0
- package/dist-server/server/services/adapters/postgresAdapter.js +30 -30
- package/dist-server/server/services/adapters/sqliteAdapter.d.ts +1 -0
- package/dist-server/server/services/adapters/sqliteAdapter.d.ts.map +1 -1
- package/dist-server/server/services/adapters/sqliteAdapter.js +66 -36
- package/dist-server/server/services/agentStore.d.ts +2 -1
- package/dist-server/server/services/agentStore.d.ts.map +1 -1
- package/dist-server/server/services/agentStore.js +2 -1
- package/dist-server/server/services/correctionDetector.d.ts +22 -0
- package/dist-server/server/services/correctionDetector.d.ts.map +1 -0
- package/dist-server/server/services/correctionDetector.js +91 -0
- package/dist-server/server/services/credentialStore.d.ts +10 -0
- package/dist-server/server/services/credentialStore.d.ts.map +1 -0
- package/dist-server/server/services/credentialStore.js +123 -0
- package/dist-server/server/services/hindsightClient.d.ts +15 -0
- package/dist-server/server/services/hindsightClient.d.ts.map +1 -0
- package/dist-server/server/services/hindsightClient.js +48 -0
- package/dist-server/server/services/lessonExtractor.d.ts +21 -0
- package/dist-server/server/services/lessonExtractor.d.ts.map +1 -0
- package/dist-server/server/services/lessonExtractor.js +92 -0
- package/dist-server/server/services/repoIndexer.d.ts +7 -1
- package/dist-server/server/services/repoIndexer.d.ts.map +1 -1
- package/dist-server/server/services/repoIndexer.js +295 -94
- package/dist-server/server/services/responseCache.d.ts +24 -0
- package/dist-server/server/services/responseCache.d.ts.map +1 -0
- package/dist-server/server/services/responseCache.js +163 -0
- package/dist-server/server/services/sqliteStore.d.ts +72 -0
- package/dist-server/server/services/sqliteStore.d.ts.map +1 -1
- package/dist-server/server/services/sqliteStore.js +291 -13
- package/dist-server/src/config.d.ts +2 -0
- package/dist-server/src/config.d.ts.map +1 -0
- package/dist-server/src/config.js +3 -0
- package/dist-server/src/graph/db.d.ts +46 -0
- package/dist-server/src/graph/db.d.ts.map +1 -0
- package/dist-server/src/graph/db.js +241 -0
- package/dist-server/src/graph/extractors/code.d.ts +12 -0
- package/dist-server/src/graph/extractors/code.d.ts.map +1 -0
- package/dist-server/src/graph/extractors/code.js +239 -0
- package/dist-server/src/graph/extractors/cross-type.d.ts +16 -0
- package/dist-server/src/graph/extractors/cross-type.d.ts.map +1 -0
- package/dist-server/src/graph/extractors/cross-type.js +67 -0
- package/dist-server/src/graph/extractors/markdown.d.ts +29 -0
- package/dist-server/src/graph/extractors/markdown.d.ts.map +1 -0
- package/dist-server/src/graph/extractors/markdown.js +224 -0
- package/dist-server/src/graph/extractors/yaml.d.ts +15 -0
- package/dist-server/src/graph/extractors/yaml.d.ts.map +1 -0
- package/dist-server/src/graph/extractors/yaml.js +104 -0
- package/dist-server/src/graph/index.d.ts +62 -0
- package/dist-server/src/graph/index.d.ts.map +1 -0
- package/dist-server/src/graph/index.js +67 -0
- package/dist-server/src/graph/packer.d.ts +19 -0
- package/dist-server/src/graph/packer.d.ts.map +1 -0
- package/dist-server/src/graph/packer.js +134 -0
- package/dist-server/src/graph/resolver.d.ts +12 -0
- package/dist-server/src/graph/resolver.d.ts.map +1 -0
- package/dist-server/src/graph/resolver.js +81 -0
- package/dist-server/src/graph/scanner.d.ts +34 -0
- package/dist-server/src/graph/scanner.d.ts.map +1 -0
- package/dist-server/src/graph/scanner.js +252 -0
- package/dist-server/src/graph/traverser.d.ts +17 -0
- package/dist-server/src/graph/traverser.d.ts.map +1 -0
- package/dist-server/src/graph/traverser.js +185 -0
- package/dist-server/src/graph/types.d.ts +117 -0
- package/dist-server/src/graph/types.d.ts.map +1 -0
- package/dist-server/src/graph/types.js +63 -0
- package/dist-server/src/metaprompt/v2/assembler.d.ts +3 -0
- package/dist-server/src/metaprompt/v2/assembler.d.ts.map +1 -0
- package/dist-server/src/metaprompt/v2/assembler.js +261 -0
- package/dist-server/src/metaprompt/v2/context-strategist.d.ts +3 -0
- package/dist-server/src/metaprompt/v2/context-strategist.d.ts.map +1 -0
- package/dist-server/src/metaprompt/v2/context-strategist.js +173 -0
- package/dist-server/src/metaprompt/v2/evaluator.d.ts +3 -0
- package/dist-server/src/metaprompt/v2/evaluator.d.ts.map +1 -0
- package/dist-server/src/metaprompt/v2/evaluator.js +281 -0
- package/dist-server/src/metaprompt/v2/index.d.ts +41 -0
- package/dist-server/src/metaprompt/v2/index.d.ts.map +1 -0
- package/dist-server/src/metaprompt/v2/index.js +90 -0
- package/dist-server/src/metaprompt/v2/parser.d.ts +3 -0
- package/dist-server/src/metaprompt/v2/parser.d.ts.map +1 -0
- package/dist-server/src/metaprompt/v2/parser.js +138 -0
- package/dist-server/src/metaprompt/v2/pattern-selector.d.ts +3 -0
- package/dist-server/src/metaprompt/v2/pattern-selector.d.ts.map +1 -0
- package/dist-server/src/metaprompt/v2/pattern-selector.js +154 -0
- package/dist-server/src/metaprompt/v2/researcher.d.ts +3 -0
- package/dist-server/src/metaprompt/v2/researcher.d.ts.map +1 -0
- package/dist-server/src/metaprompt/v2/researcher.js +194 -0
- package/dist-server/src/metaprompt/v2/tool-discovery.d.ts +74 -0
- package/dist-server/src/metaprompt/v2/tool-discovery.d.ts.map +1 -0
- package/dist-server/src/metaprompt/v2/tool-discovery.js +290 -0
- package/dist-server/src/metaprompt/v2/types.d.ts +154 -0
- package/dist-server/src/metaprompt/v2/types.d.ts.map +1 -0
- package/dist-server/src/metaprompt/v2/types.js +2 -0
- package/dist-server/src/services/contradictionDetector.js +1 -1
- package/dist-server/src/services/llmService.d.ts +61 -0
- package/dist-server/src/services/llmService.d.ts.map +1 -0
- package/dist-server/src/services/llmService.js +222 -0
- package/dist-server/src/store/knowledgeBase.d.ts +6 -1
- package/dist-server/src/store/knowledgeBase.d.ts.map +1 -1
- package/dist-server/src/store/knowledgeBase.js +0 -1
- package/dist-server/src/store/lessonStore.d.ts +26 -0
- package/dist-server/src/store/lessonStore.d.ts.map +1 -0
- package/dist-server/src/store/lessonStore.js +64 -0
- package/dist-server/src/store/mcp-registry.d.ts +29 -0
- package/dist-server/src/store/mcp-registry.d.ts.map +1 -0
- package/dist-server/src/store/mcp-registry.js +1303 -0
- package/dist-server/src/store/memoryStore.d.ts +12 -1
- package/dist-server/src/store/memoryStore.d.ts.map +1 -1
- package/dist-server/src/store/memoryStore.js +9 -0
- package/dist-server/src/types/registry.types.d.ts +13 -0
- package/dist-server/src/types/registry.types.d.ts.map +1 -0
- package/dist-server/src/types/registry.types.js +2 -0
- package/dist-server/tsconfig.server.tsbuildinfo +1 -1
- package/package.json +15 -1
- package/scripts/cleanup-worktrees.ps1 +29 -0
- package/dist/assets/Badge-22Ai0eyi.js +0 -1
- package/dist/assets/KnowledgeTab-DABxirZh.js +0 -4
- package/dist/assets/MemoryTab-DZeYElIT.js +0 -16
- package/dist/assets/QualificationTab-Dfpy3J30.js +0 -1
- package/dist/assets/ReviewTab-SD8lQuCc.js +0 -103
- package/dist/assets/TestTab-PDyMF8Fw.js +0 -33
- package/dist/assets/ToolsTab-B83qGCmG.js +0 -1
- package/dist/assets/icons-C2EV-le6.js +0 -1
- package/dist/assets/index-DkpMAxX7.css +0 -1
- package/dist/assets/index-q24ug5Qs.js +0 -143
- package/dist/assets/services-BaKotDf0.js +0 -343
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Markdown Relation Extractor
|
|
3
|
+
*
|
|
4
|
+
* Extracts: links_to, references, supersedes, continues, defined_in, depends_on
|
|
5
|
+
*/
|
|
6
|
+
// ── Link Patterns ─────────────────────────────────────────────────────────────
|
|
7
|
+
const MD_LINK_RE = /\[([^\]]*)\]\(([^)]+)\)/g;
|
|
8
|
+
const WIKI_LINK_RE = /\[\[([^\]]+)\]\]/g;
|
|
9
|
+
// ── Supersedes / Continues Patterns ───────────────────────────────────────────
|
|
10
|
+
const SUPERSEDES_RE = /(?:superseded|replaced|deprecated|updated)\s+(?:by|in)\s*:?\s*\[?([^\]\n]+)\]?/gi;
|
|
11
|
+
const CONTINUES_RE = /(?:continued\s+from|part\s+\d+\s+of|follows\s+from)\s*:?\s*\[?([^\]\n]+)\]?/gi;
|
|
12
|
+
// ── Dependency Patterns ───────────────────────────────────────────────────────
|
|
13
|
+
const DEPENDS_RE = /(?:prerequisites?|depends?\s+on|requires?|see\s+also|before\s+reading)\s*:?\s*\[?([^\]\n]+)\]?/gi;
|
|
14
|
+
// ── Definition Patterns ───────────────────────────────────────────────────────
|
|
15
|
+
const BOLD_DEF_RE = /^\*\*([^*]+)\*\*\s*[:—–-]\s*(.+)$/gm;
|
|
16
|
+
/**
|
|
17
|
+
* Resolve a relative markdown link to a file path.
|
|
18
|
+
*/
|
|
19
|
+
function resolveMarkdownLink(href, sourceDir) {
|
|
20
|
+
// Skip external links, anchors-only, mailto, etc.
|
|
21
|
+
if (/^(https?:|mailto:|#|data:)/.test(href))
|
|
22
|
+
return null;
|
|
23
|
+
if (href.startsWith('/'))
|
|
24
|
+
return null; // Absolute paths — skip
|
|
25
|
+
const [pathPart, anchor] = href.split('#');
|
|
26
|
+
if (!pathPart)
|
|
27
|
+
return anchor ? { path: '', anchor } : null;
|
|
28
|
+
const parts = sourceDir.split('/').filter(Boolean);
|
|
29
|
+
for (const segment of pathPart.split('/')) {
|
|
30
|
+
if (segment === '.')
|
|
31
|
+
continue;
|
|
32
|
+
if (segment === '..') {
|
|
33
|
+
parts.pop();
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
parts.push(segment);
|
|
37
|
+
}
|
|
38
|
+
return { path: parts.join('/'), anchor };
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Find a FileNode by path (with fuzzy matching for extensions).
|
|
42
|
+
*/
|
|
43
|
+
function findNodeByPath(path, allNodes) {
|
|
44
|
+
// Exact match
|
|
45
|
+
let match = allNodes.find(n => n.path === path);
|
|
46
|
+
if (match)
|
|
47
|
+
return match;
|
|
48
|
+
// Try with .md extension
|
|
49
|
+
if (!path.endsWith('.md')) {
|
|
50
|
+
match = allNodes.find(n => n.path === path + '.md');
|
|
51
|
+
if (match)
|
|
52
|
+
return match;
|
|
53
|
+
}
|
|
54
|
+
// Try filename match (for wiki-links)
|
|
55
|
+
const fileName = path.split('/').pop()?.toLowerCase() ?? '';
|
|
56
|
+
return allNodes.find(n => {
|
|
57
|
+
const nodeName = n.path.split('/').pop()?.toLowerCase() ?? '';
|
|
58
|
+
return nodeName === fileName || nodeName === fileName + '.md';
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Collect all defined terms across markdown files.
|
|
63
|
+
* Returns: Map<term (lowercase), { file, originalTerm }>
|
|
64
|
+
*/
|
|
65
|
+
export function collectDefinedTerms(mdNodes) {
|
|
66
|
+
const terms = new Map();
|
|
67
|
+
for (const node of mdNodes) {
|
|
68
|
+
if (node.language !== 'markdown')
|
|
69
|
+
continue;
|
|
70
|
+
// Headings become linkable terms
|
|
71
|
+
for (const sym of node.symbols) {
|
|
72
|
+
if (sym.kind === 'heading' && sym.name.length > 3) {
|
|
73
|
+
terms.set(sym.name.toLowerCase(), { file: node, term: sym.name });
|
|
74
|
+
}
|
|
75
|
+
if (sym.kind === 'definition') {
|
|
76
|
+
terms.set(sym.name.toLowerCase(), { file: node, term: sym.name });
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return terms;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Extract all relations from a markdown file.
|
|
84
|
+
*/
|
|
85
|
+
export function extractMarkdownRelations(file, allNodes, content) {
|
|
86
|
+
if (file.language !== 'markdown')
|
|
87
|
+
return [];
|
|
88
|
+
const relations = [];
|
|
89
|
+
const sourceDir = file.path.split('/').slice(0, -1).join('/');
|
|
90
|
+
// ── Explicit links: [text](path.md) ─────────────────────────────────────
|
|
91
|
+
MD_LINK_RE.lastIndex = 0;
|
|
92
|
+
let match;
|
|
93
|
+
while ((match = MD_LINK_RE.exec(content)) !== null) {
|
|
94
|
+
const href = match[2];
|
|
95
|
+
const resolved = resolveMarkdownLink(href, sourceDir);
|
|
96
|
+
if (!resolved || !resolved.path)
|
|
97
|
+
continue;
|
|
98
|
+
const target = findNodeByPath(resolved.path, allNodes);
|
|
99
|
+
if (target && target.id !== file.id) {
|
|
100
|
+
relations.push({
|
|
101
|
+
sourceFile: file.id,
|
|
102
|
+
targetFile: target.id,
|
|
103
|
+
targetSymbol: resolved.anchor,
|
|
104
|
+
kind: 'links_to',
|
|
105
|
+
weight: 1.0,
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
// ── Wiki links: [[page-name]] ────────────────────────────────────────────
|
|
110
|
+
WIKI_LINK_RE.lastIndex = 0;
|
|
111
|
+
while ((match = WIKI_LINK_RE.exec(content)) !== null) {
|
|
112
|
+
const linkName = match[1].trim();
|
|
113
|
+
const target = findNodeByPath(linkName, allNodes);
|
|
114
|
+
if (target && target.id !== file.id) {
|
|
115
|
+
relations.push({
|
|
116
|
+
sourceFile: file.id,
|
|
117
|
+
targetFile: target.id,
|
|
118
|
+
kind: 'links_to',
|
|
119
|
+
weight: 1.0,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
// ── Supersedes ───────────────────────────────────────────────────────────
|
|
124
|
+
SUPERSEDES_RE.lastIndex = 0;
|
|
125
|
+
while ((match = SUPERSEDES_RE.exec(content)) !== null) {
|
|
126
|
+
const ref = match[1].trim().replace(/\[|\]/g, '');
|
|
127
|
+
const target = findNodeByPath(ref, allNodes);
|
|
128
|
+
if (target && target.id !== file.id) {
|
|
129
|
+
relations.push({
|
|
130
|
+
sourceFile: file.id,
|
|
131
|
+
targetFile: target.id,
|
|
132
|
+
kind: 'supersedes',
|
|
133
|
+
weight: 0.9,
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
// ── Continues ────────────────────────────────────────────────────────────
|
|
138
|
+
CONTINUES_RE.lastIndex = 0;
|
|
139
|
+
while ((match = CONTINUES_RE.exec(content)) !== null) {
|
|
140
|
+
const ref = match[1].trim().replace(/\[|\]/g, '');
|
|
141
|
+
const target = findNodeByPath(ref, allNodes);
|
|
142
|
+
if (target && target.id !== file.id) {
|
|
143
|
+
relations.push({
|
|
144
|
+
sourceFile: file.id,
|
|
145
|
+
targetFile: target.id,
|
|
146
|
+
kind: 'continues',
|
|
147
|
+
weight: 0.8,
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
// ── Depends on / Prerequisites ───────────────────────────────────────────
|
|
152
|
+
DEPENDS_RE.lastIndex = 0;
|
|
153
|
+
while ((match = DEPENDS_RE.exec(content)) !== null) {
|
|
154
|
+
const ref = match[1].trim().replace(/\[|\]/g, '');
|
|
155
|
+
const target = findNodeByPath(ref, allNodes);
|
|
156
|
+
if (target && target.id !== file.id) {
|
|
157
|
+
relations.push({
|
|
158
|
+
sourceFile: file.id,
|
|
159
|
+
targetFile: target.id,
|
|
160
|
+
kind: 'depends_on',
|
|
161
|
+
weight: 0.8,
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
// ── Cross-document term references ───────────────────────────────────────
|
|
166
|
+
const definedTerms = collectDefinedTerms(allNodes.filter(n => n.id !== file.id));
|
|
167
|
+
for (const [termLower, { file: defFile, term }] of definedTerms) {
|
|
168
|
+
// Only match multi-word terms (> 2 words) or long single words (> 8 chars)
|
|
169
|
+
const wordCount = term.split(/\s+/).length;
|
|
170
|
+
if (wordCount < 2 && term.length <= 8)
|
|
171
|
+
continue;
|
|
172
|
+
// Word-boundary match, case-insensitive
|
|
173
|
+
const escaped = termLower.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
174
|
+
const re = new RegExp(`\\b${escaped}\\b`, 'i');
|
|
175
|
+
if (re.test(content)) {
|
|
176
|
+
// Don't duplicate if we already have a links_to for this file
|
|
177
|
+
const alreadyLinked = relations.some(r => r.targetFile === defFile.id && (r.kind === 'links_to' || r.kind === 'references'));
|
|
178
|
+
if (!alreadyLinked) {
|
|
179
|
+
relations.push({
|
|
180
|
+
sourceFile: file.id,
|
|
181
|
+
targetFile: defFile.id,
|
|
182
|
+
targetSymbol: term,
|
|
183
|
+
kind: 'references',
|
|
184
|
+
weight: 0.6,
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
return relations;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Extract markdown symbols (headings + defined terms) for FileNode.symbols.
|
|
193
|
+
*/
|
|
194
|
+
export function extractMarkdownSymbols(content) {
|
|
195
|
+
const symbols = [];
|
|
196
|
+
const lines = content.split('\n');
|
|
197
|
+
// Headings
|
|
198
|
+
for (let i = 0; i < lines.length; i++) {
|
|
199
|
+
const hMatch = /^(#{1,6})\s+(.+)$/.exec(lines[i]);
|
|
200
|
+
if (hMatch) {
|
|
201
|
+
symbols.push({
|
|
202
|
+
name: hMatch[2].trim(),
|
|
203
|
+
kind: 'heading',
|
|
204
|
+
lineStart: i + 1,
|
|
205
|
+
lineEnd: i + 1,
|
|
206
|
+
isExported: true,
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
// Bold definitions: **Term**: definition
|
|
211
|
+
BOLD_DEF_RE.lastIndex = 0;
|
|
212
|
+
let defMatch;
|
|
213
|
+
while ((defMatch = BOLD_DEF_RE.exec(content)) !== null) {
|
|
214
|
+
const lineNum = content.substring(0, defMatch.index).split('\n').length;
|
|
215
|
+
symbols.push({
|
|
216
|
+
name: defMatch[1].trim(),
|
|
217
|
+
kind: 'definition',
|
|
218
|
+
lineStart: lineNum,
|
|
219
|
+
lineEnd: lineNum,
|
|
220
|
+
isExported: true,
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
return symbols;
|
|
224
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* YAML / JSON Relation Extractor
|
|
3
|
+
*
|
|
4
|
+
* Extracts: configured_by
|
|
5
|
+
* Scans config files for path-like string values that reference known source files.
|
|
6
|
+
*/
|
|
7
|
+
import type { FileNode, Relation } from '../types.js';
|
|
8
|
+
/**
|
|
9
|
+
* Extract relations from a YAML or JSON config file.
|
|
10
|
+
*
|
|
11
|
+
* Creates 'configured_by' relations from the referenced code file TO the config.
|
|
12
|
+
* Weight: 0.8 for explicit path references, 0.6 for inferred ($schema).
|
|
13
|
+
*/
|
|
14
|
+
export declare function extractYamlRelations(node: FileNode, allNodes: FileNode[], content: string): Relation[];
|
|
15
|
+
//# sourceMappingURL=yaml.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"yaml.d.ts","sourceRoot":"","sources":["../../../../src/graph/extractors/yaml.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AA0CtD;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,QAAQ,EACd,QAAQ,EAAE,QAAQ,EAAE,EACpB,OAAO,EAAE,MAAM,GACd,QAAQ,EAAE,CAyDZ"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* YAML / JSON Relation Extractor
|
|
3
|
+
*
|
|
4
|
+
* Extracts: configured_by
|
|
5
|
+
* Scans config files for path-like string values that reference known source files.
|
|
6
|
+
*/
|
|
7
|
+
// ── Path Detection Patterns ───────────────────────────────────────────────────
|
|
8
|
+
// Quoted string values ending in code/doc extensions
|
|
9
|
+
const QUOTED_PATH_RE = /["']([^"'\n\r]+\.(?:ts|tsx|js|jsx|py|md))["']/g;
|
|
10
|
+
// Unquoted YAML values: `key: ./src/foo.ts`
|
|
11
|
+
const YAML_UNQUOTED_PATH_RE = /:\s+([^\s'"{\[,\n\r]+\.(?:ts|tsx|js|jsx|py|md))(?:\s*$|\s*#)/gm;
|
|
12
|
+
// JSON Schema self-reference: "$schema": "./path/to/schema.json"
|
|
13
|
+
const JSON_SCHEMA_KEY_RE = /['"]\$schema['"]\s*:\s*["']([^"']+\.json)["']/g;
|
|
14
|
+
// ── Path Normalization ────────────────────────────────────────────────────────
|
|
15
|
+
/**
|
|
16
|
+
* Normalize a raw path reference from config content to a repo-relative path.
|
|
17
|
+
* Resolves relative segments (./ and ../) against the config file's directory.
|
|
18
|
+
*/
|
|
19
|
+
function normalizePath(raw, configDir) {
|
|
20
|
+
let ref = raw.trim();
|
|
21
|
+
if (ref.startsWith('./'))
|
|
22
|
+
ref = ref.slice(2);
|
|
23
|
+
if (ref.startsWith('../')) {
|
|
24
|
+
const parts = configDir.split('/').filter(Boolean);
|
|
25
|
+
for (const seg of ref.split('/')) {
|
|
26
|
+
if (seg === '.')
|
|
27
|
+
continue;
|
|
28
|
+
if (seg === '..') {
|
|
29
|
+
parts.pop();
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
parts.push(seg);
|
|
33
|
+
}
|
|
34
|
+
return parts.join('/');
|
|
35
|
+
}
|
|
36
|
+
return ref;
|
|
37
|
+
}
|
|
38
|
+
function findNodeByNormalizedPath(normalized, allNodes) {
|
|
39
|
+
return allNodes.find(n => n.path === normalized || n.path.endsWith('/' + normalized));
|
|
40
|
+
}
|
|
41
|
+
// ── Main Extractor ────────────────────────────────────────────────────────────
|
|
42
|
+
/**
|
|
43
|
+
* Extract relations from a YAML or JSON config file.
|
|
44
|
+
*
|
|
45
|
+
* Creates 'configured_by' relations from the referenced code file TO the config.
|
|
46
|
+
* Weight: 0.8 for explicit path references, 0.6 for inferred ($schema).
|
|
47
|
+
*/
|
|
48
|
+
export function extractYamlRelations(node, allNodes, content) {
|
|
49
|
+
if (node.language !== 'yaml' && node.language !== 'json')
|
|
50
|
+
return [];
|
|
51
|
+
const relations = [];
|
|
52
|
+
const configDir = node.path.split('/').slice(0, -1).join('/');
|
|
53
|
+
const seen = new Set();
|
|
54
|
+
// ── Explicit path references ──────────────────────────────────────────────
|
|
55
|
+
const pathPatterns = [QUOTED_PATH_RE, YAML_UNQUOTED_PATH_RE];
|
|
56
|
+
for (const re of pathPatterns) {
|
|
57
|
+
re.lastIndex = 0;
|
|
58
|
+
let match;
|
|
59
|
+
while ((match = re.exec(content)) !== null) {
|
|
60
|
+
const raw = match[1];
|
|
61
|
+
if (!raw)
|
|
62
|
+
continue;
|
|
63
|
+
const normalized = normalizePath(raw, configDir);
|
|
64
|
+
const target = findNodeByNormalizedPath(normalized, allNodes);
|
|
65
|
+
if (!target || target.id === node.id)
|
|
66
|
+
continue;
|
|
67
|
+
// Relation: referenced code file is configured_by this config file
|
|
68
|
+
const key = `${target.id}→${node.id}:configured_by`;
|
|
69
|
+
if (seen.has(key))
|
|
70
|
+
continue;
|
|
71
|
+
seen.add(key);
|
|
72
|
+
relations.push({
|
|
73
|
+
sourceFile: target.id,
|
|
74
|
+
targetFile: node.id,
|
|
75
|
+
kind: 'configured_by',
|
|
76
|
+
weight: 0.8,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
// ── JSON Schema cross-references ($schema key) ────────────────────────────
|
|
81
|
+
JSON_SCHEMA_KEY_RE.lastIndex = 0;
|
|
82
|
+
let schemaMatch;
|
|
83
|
+
while ((schemaMatch = JSON_SCHEMA_KEY_RE.exec(content)) !== null) {
|
|
84
|
+
const raw = schemaMatch[1];
|
|
85
|
+
if (!raw)
|
|
86
|
+
continue;
|
|
87
|
+
const normalized = normalizePath(raw, configDir);
|
|
88
|
+
const target = findNodeByNormalizedPath(normalized, allNodes);
|
|
89
|
+
if (!target || target.id === node.id)
|
|
90
|
+
continue;
|
|
91
|
+
// Relation: this config is configured_by (validated against) the schema
|
|
92
|
+
const key = `${node.id}→${target.id}:configured_by`;
|
|
93
|
+
if (seen.has(key))
|
|
94
|
+
continue;
|
|
95
|
+
seen.add(key);
|
|
96
|
+
relations.push({
|
|
97
|
+
sourceFile: node.id,
|
|
98
|
+
targetFile: target.id,
|
|
99
|
+
kind: 'configured_by',
|
|
100
|
+
weight: 0.6,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
return relations;
|
|
104
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context Graph — Public API
|
|
3
|
+
*
|
|
4
|
+
* Unified interface: scan → query → pack.
|
|
5
|
+
*/
|
|
6
|
+
export { GraphDB } from './db.js';
|
|
7
|
+
export { fullScan, updateFiles, buildFileNode, shouldIndex, fileId, hashContent } from './scanner.js';
|
|
8
|
+
export { resolveEntryPoints } from './resolver.js';
|
|
9
|
+
export { traverseGraph, traverseForTask } from './traverser.js';
|
|
10
|
+
export { packContext } from './packer.js';
|
|
11
|
+
export { extractCodeRelations } from './extractors/code.js';
|
|
12
|
+
export { extractMarkdownRelations, extractMarkdownSymbols, collectDefinedTerms } from './extractors/markdown.js';
|
|
13
|
+
export { extractYamlRelations } from './extractors/yaml.js';
|
|
14
|
+
export { extractCrossTypeRelations } from './extractors/cross-type.js';
|
|
15
|
+
export { detectTaskType, TRAVERSAL_PRESETS } from './types.js';
|
|
16
|
+
export type { FileNode, FileLanguage, SymbolDef, SymbolKind, Relation, RelationKind, ContextGraph, TraversalConfig, TraversalResult, TraversalFile, EntryPoint, PackedContext, PackedItem, UpdateResult, ScanResult, TaskType, } from './types.js';
|
|
17
|
+
import { GraphDB } from './db.js';
|
|
18
|
+
import type { PackedContext, ScanResult, UpdateResult, TaskType } from './types.js';
|
|
19
|
+
/**
|
|
20
|
+
* High-level: scan files → build graph → query → get packed context.
|
|
21
|
+
*/
|
|
22
|
+
export declare class ContextGraphEngine {
|
|
23
|
+
private db;
|
|
24
|
+
private rootPath;
|
|
25
|
+
/**
|
|
26
|
+
* Full scan from a list of files.
|
|
27
|
+
*/
|
|
28
|
+
scan(rootPath: string, files: Array<{
|
|
29
|
+
path: string;
|
|
30
|
+
content: string;
|
|
31
|
+
mtime?: number;
|
|
32
|
+
}>): ScanResult;
|
|
33
|
+
/**
|
|
34
|
+
* Incremental update for changed files.
|
|
35
|
+
*/
|
|
36
|
+
update(changedFiles: Array<{
|
|
37
|
+
path: string;
|
|
38
|
+
content: string;
|
|
39
|
+
mtime?: number;
|
|
40
|
+
}>): UpdateResult;
|
|
41
|
+
/**
|
|
42
|
+
* Query: natural language → packed context.
|
|
43
|
+
*/
|
|
44
|
+
query(query: string, tokenBudget?: number, taskType?: TaskType): PackedContext;
|
|
45
|
+
/**
|
|
46
|
+
* Get graph stats.
|
|
47
|
+
*/
|
|
48
|
+
getStats(): {
|
|
49
|
+
nodes: number;
|
|
50
|
+
symbols: number;
|
|
51
|
+
relations: number;
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* Get the full graph for visualization.
|
|
55
|
+
*/
|
|
56
|
+
getGraph(): import("./types.js").ContextGraph;
|
|
57
|
+
/**
|
|
58
|
+
* Get DB instance for direct access.
|
|
59
|
+
*/
|
|
60
|
+
getDB(): GraphDB;
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/graph/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AACtG,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAChE,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,wBAAwB,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AACjH,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC;AACvE,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAC/D,YAAY,EACV,QAAQ,EAAE,YAAY,EAAE,SAAS,EAAE,UAAU,EAC7C,QAAQ,EAAE,YAAY,EACtB,YAAY,EACZ,eAAe,EAAE,eAAe,EAAE,aAAa,EAC/C,UAAU,EACV,aAAa,EAAE,UAAU,EACzB,YAAY,EAAE,UAAU,EACxB,QAAQ,GACT,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAKlC,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEpF;;GAEG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,EAAE,CAAiB;IAC3B,OAAO,CAAC,QAAQ,CAAM;IAEtB;;OAEG;IACH,IAAI,CACF,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,GAC9D,UAAU;IAKb;;OAEG;IACH,MAAM,CACJ,YAAY,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,GACrE,YAAY;IAIf;;OAEG;IACH,KAAK,CACH,KAAK,EAAE,MAAM,EACb,WAAW,GAAE,MAAe,EAC5B,QAAQ,CAAC,EAAE,QAAQ,GAClB,aAAa;IAOhB;;OAEG;IACH,QAAQ;;;;;IAIR;;OAEG;IACH,QAAQ;IAIR;;OAEG;IACH,KAAK;CAGN"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context Graph — Public API
|
|
3
|
+
*
|
|
4
|
+
* Unified interface: scan → query → pack.
|
|
5
|
+
*/
|
|
6
|
+
export { GraphDB } from './db.js';
|
|
7
|
+
export { fullScan, updateFiles, buildFileNode, shouldIndex, fileId, hashContent } from './scanner.js';
|
|
8
|
+
export { resolveEntryPoints } from './resolver.js';
|
|
9
|
+
export { traverseGraph, traverseForTask } from './traverser.js';
|
|
10
|
+
export { packContext } from './packer.js';
|
|
11
|
+
export { extractCodeRelations } from './extractors/code.js';
|
|
12
|
+
export { extractMarkdownRelations, extractMarkdownSymbols, collectDefinedTerms } from './extractors/markdown.js';
|
|
13
|
+
export { extractYamlRelations } from './extractors/yaml.js';
|
|
14
|
+
export { extractCrossTypeRelations } from './extractors/cross-type.js';
|
|
15
|
+
export { detectTaskType, TRAVERSAL_PRESETS } from './types.js';
|
|
16
|
+
import { GraphDB } from './db.js';
|
|
17
|
+
import { fullScan, updateFiles } from './scanner.js';
|
|
18
|
+
import { resolveEntryPoints } from './resolver.js';
|
|
19
|
+
import { traverseForTask } from './traverser.js';
|
|
20
|
+
import { packContext } from './packer.js';
|
|
21
|
+
/**
|
|
22
|
+
* High-level: scan files → build graph → query → get packed context.
|
|
23
|
+
*/
|
|
24
|
+
export class ContextGraphEngine {
|
|
25
|
+
db = new GraphDB();
|
|
26
|
+
rootPath = '';
|
|
27
|
+
/**
|
|
28
|
+
* Full scan from a list of files.
|
|
29
|
+
*/
|
|
30
|
+
scan(rootPath, files) {
|
|
31
|
+
this.rootPath = rootPath;
|
|
32
|
+
return fullScan(files, this.db);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Incremental update for changed files.
|
|
36
|
+
*/
|
|
37
|
+
update(changedFiles) {
|
|
38
|
+
return updateFiles(changedFiles, this.db);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Query: natural language → packed context.
|
|
42
|
+
*/
|
|
43
|
+
query(query, tokenBudget = 100000, taskType) {
|
|
44
|
+
const graph = this.db.toContextGraph(this.rootPath);
|
|
45
|
+
const entryPoints = resolveEntryPoints(query, graph);
|
|
46
|
+
const traversal = traverseForTask(query, entryPoints, graph, taskType);
|
|
47
|
+
return packContext(traversal, tokenBudget);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Get graph stats.
|
|
51
|
+
*/
|
|
52
|
+
getStats() {
|
|
53
|
+
return this.db.getStats();
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Get the full graph for visualization.
|
|
57
|
+
*/
|
|
58
|
+
getGraph() {
|
|
59
|
+
return this.db.toContextGraph(this.rootPath);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Get DB instance for direct access.
|
|
63
|
+
*/
|
|
64
|
+
getDB() {
|
|
65
|
+
return this.db;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Budget Packer — Relevance-weighted depth allocation
|
|
3
|
+
*
|
|
4
|
+
* Given traversal results and a token budget, decide how much of each file
|
|
5
|
+
* to include using existing depthFilter depth levels:
|
|
6
|
+
* 0 = Full, 1 = Detail, 2 = Summary, 3 = Headlines, 4 = Mention
|
|
7
|
+
*/
|
|
8
|
+
import type { TraversalResult, PackedContext } from './types.js';
|
|
9
|
+
/**
|
|
10
|
+
* Pack traversal results into a context budget.
|
|
11
|
+
*
|
|
12
|
+
* Strategy:
|
|
13
|
+
* 1. Assign initial depth based on relevance
|
|
14
|
+
* 2. If budget exceeded, demote lowest-relevance files to higher depth
|
|
15
|
+
* 3. If budget still exceeded, drop lowest-relevance files
|
|
16
|
+
* 4. If budget has room, promote highest-relevance files to lower depth
|
|
17
|
+
*/
|
|
18
|
+
export declare function packContext(traversalResult: TraversalResult, tokenBudget: number): PackedContext;
|
|
19
|
+
//# sourceMappingURL=packer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"packer.d.ts","sourceRoot":"","sources":["../../../src/graph/packer.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,aAAa,EAAwB,MAAM,YAAY,CAAC;AAmEvF;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CACzB,eAAe,EAAE,eAAe,EAChC,WAAW,EAAE,MAAM,GAClB,aAAa,CAgFf"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Budget Packer — Relevance-weighted depth allocation
|
|
3
|
+
*
|
|
4
|
+
* Given traversal results and a token budget, decide how much of each file
|
|
5
|
+
* to include using existing depthFilter depth levels:
|
|
6
|
+
* 0 = Full, 1 = Detail, 2 = Summary, 3 = Headlines, 4 = Mention
|
|
7
|
+
*/
|
|
8
|
+
// Approximate token costs per depth level (as fraction of full)
|
|
9
|
+
const DEPTH_COST_RATIOS = {
|
|
10
|
+
0: 1.0, // Full
|
|
11
|
+
1: 0.75, // Detail: signatures + docstrings
|
|
12
|
+
2: 0.50, // Summary: signatures only
|
|
13
|
+
3: 0.25, // Headlines: section names
|
|
14
|
+
4: 0.10, // Mention: file purpose only
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Estimate token cost at a given depth level.
|
|
18
|
+
*/
|
|
19
|
+
function estimateAtDepth(fileTokens, depth) {
|
|
20
|
+
const ratio = DEPTH_COST_RATIOS[depth] ?? 0.1;
|
|
21
|
+
return Math.max(10, Math.ceil(fileTokens * ratio));
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Generate content stub at a given depth level.
|
|
25
|
+
* In production, this would call depthFilter.ts — here we generate a summary.
|
|
26
|
+
*/
|
|
27
|
+
function contentAtDepth(file, depth) {
|
|
28
|
+
const symbols = file.symbols;
|
|
29
|
+
switch (depth) {
|
|
30
|
+
case 0: // Full — would return full file content
|
|
31
|
+
return `[Full content of ${file.path}]\n` +
|
|
32
|
+
symbols.map(s => `${s.kind} ${s.name}${s.signature ? s.signature : ''}`).join('\n');
|
|
33
|
+
case 1: // Detail — signatures + docstrings
|
|
34
|
+
return `// ${file.path} (detail)\n` +
|
|
35
|
+
symbols.map(s => `${s.isExported ? 'export ' : ''}${s.kind} ${s.name}${s.signature ?? ''}${s.docstring ? ` // ${s.docstring}` : ''}`).join('\n');
|
|
36
|
+
case 2: // Summary — signatures only
|
|
37
|
+
return `// ${file.path} (summary)\n` +
|
|
38
|
+
symbols.filter(s => s.isExported).map(s => `${s.kind} ${s.name}${s.signature ?? ''}`).join('\n');
|
|
39
|
+
case 3: // Headlines — section/symbol names
|
|
40
|
+
return `// ${file.path}: ` +
|
|
41
|
+
symbols.filter(s => s.isExported).map(s => s.name).join(', ');
|
|
42
|
+
case 4: // Mention — file purpose
|
|
43
|
+
return `// ${file.path} (${file.language}, ${file.tokens} tokens)`;
|
|
44
|
+
default:
|
|
45
|
+
return `// ${file.path}`;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Determine depth level based on relevance score.
|
|
50
|
+
*/
|
|
51
|
+
function relevanceToDepth(relevance) {
|
|
52
|
+
if (relevance >= 0.8)
|
|
53
|
+
return 0; // Full
|
|
54
|
+
if (relevance >= 0.6)
|
|
55
|
+
return 1; // Detail
|
|
56
|
+
if (relevance >= 0.4)
|
|
57
|
+
return 2; // Summary
|
|
58
|
+
if (relevance >= 0.2)
|
|
59
|
+
return 3; // Headlines
|
|
60
|
+
return 4; // Mention
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Pack traversal results into a context budget.
|
|
64
|
+
*
|
|
65
|
+
* Strategy:
|
|
66
|
+
* 1. Assign initial depth based on relevance
|
|
67
|
+
* 2. If budget exceeded, demote lowest-relevance files to higher depth
|
|
68
|
+
* 3. If budget still exceeded, drop lowest-relevance files
|
|
69
|
+
* 4. If budget has room, promote highest-relevance files to lower depth
|
|
70
|
+
*/
|
|
71
|
+
export function packContext(traversalResult, tokenBudget) {
|
|
72
|
+
const { files } = traversalResult;
|
|
73
|
+
if (files.length === 0) {
|
|
74
|
+
return { items: [], totalTokens: 0, budgetUtilization: 0 };
|
|
75
|
+
}
|
|
76
|
+
// Sort by relevance descending
|
|
77
|
+
const sorted = [...files].sort((a, b) => b.relevance - a.relevance);
|
|
78
|
+
const items = sorted.map(f => {
|
|
79
|
+
const depth = relevanceToDepth(f.relevance);
|
|
80
|
+
return {
|
|
81
|
+
file: f.node,
|
|
82
|
+
relevance: f.relevance,
|
|
83
|
+
depth,
|
|
84
|
+
tokens: estimateAtDepth(f.node.tokens, depth),
|
|
85
|
+
};
|
|
86
|
+
});
|
|
87
|
+
// Phase 2: Fit within budget — demote from bottom up
|
|
88
|
+
let totalTokens = items.reduce((sum, it) => sum + it.tokens, 0);
|
|
89
|
+
if (totalTokens > tokenBudget) {
|
|
90
|
+
// Demote least relevant files first
|
|
91
|
+
for (let i = items.length - 1; i >= 0 && totalTokens > tokenBudget; i--) {
|
|
92
|
+
const item = items[i];
|
|
93
|
+
while (item.depth < 4 && totalTokens > tokenBudget) {
|
|
94
|
+
const oldTokens = item.tokens;
|
|
95
|
+
item.depth++;
|
|
96
|
+
item.tokens = estimateAtDepth(item.file.tokens, item.depth);
|
|
97
|
+
totalTokens -= (oldTokens - item.tokens);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
// If still over budget, drop from bottom
|
|
101
|
+
while (items.length > 0 && totalTokens > tokenBudget) {
|
|
102
|
+
const removed = items.pop();
|
|
103
|
+
totalTokens -= removed.tokens;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
// Phase 3: If budget has room, promote top files
|
|
107
|
+
if (totalTokens < tokenBudget * 0.8) {
|
|
108
|
+
for (let i = 0; i < items.length && totalTokens < tokenBudget * 0.9; i++) {
|
|
109
|
+
const item = items[i];
|
|
110
|
+
if (item.depth > 0) {
|
|
111
|
+
const newTokens = estimateAtDepth(item.file.tokens, item.depth - 1);
|
|
112
|
+
const delta = newTokens - item.tokens;
|
|
113
|
+
if (totalTokens + delta <= tokenBudget) {
|
|
114
|
+
item.depth--;
|
|
115
|
+
item.tokens = newTokens;
|
|
116
|
+
totalTokens += delta;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
// Build final output
|
|
122
|
+
const packed = items.map(it => ({
|
|
123
|
+
file: it.file,
|
|
124
|
+
content: contentAtDepth(it.file, it.depth),
|
|
125
|
+
depth: it.depth,
|
|
126
|
+
tokens: it.tokens,
|
|
127
|
+
relevance: it.relevance,
|
|
128
|
+
}));
|
|
129
|
+
return {
|
|
130
|
+
items: packed,
|
|
131
|
+
totalTokens,
|
|
132
|
+
budgetUtilization: totalTokens / tokenBudget,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Entry Point Resolver
|
|
3
|
+
*
|
|
4
|
+
* From a natural language query, find entry points into the context graph.
|
|
5
|
+
* Three strategies: symbol match, filename match, semantic match (fallback).
|
|
6
|
+
*/
|
|
7
|
+
import type { ContextGraph, EntryPoint } from './types.js';
|
|
8
|
+
/**
|
|
9
|
+
* Resolve entry points from a query against the graph.
|
|
10
|
+
*/
|
|
11
|
+
export declare function resolveEntryPoints(query: string, graph: ContextGraph): EntryPoint[];
|
|
12
|
+
//# sourceMappingURL=resolver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolver.d.ts","sourceRoot":"","sources":["../../../src/graph/resolver.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE3D;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,YAAY,GAClB,UAAU,EAAE,CA4Ed"}
|