opentology 0.3.7 → 0.3.9
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/dist/lib/deep-scan-triples.d.ts +7 -2
- package/dist/lib/deep-scan-triples.js +23 -6
- package/dist/lib/deep-scanner-treesitter.d.ts +1 -0
- package/dist/lib/deep-scanner-ts.d.ts +1 -0
- package/dist/lib/deep-scanner-ts.js +1 -0
- package/dist/lib/deep-scanner.d.ts +8 -0
- package/dist/lib/deep-scanner.js +16 -0
- package/dist/lib/extractors/go.d.ts +1 -0
- package/dist/lib/extractors/go.js +1 -0
- package/dist/lib/extractors/java.d.ts +1 -0
- package/dist/lib/extractors/java.js +1 -0
- package/dist/lib/extractors/python.d.ts +1 -0
- package/dist/lib/extractors/python.js +1 -0
- package/dist/lib/extractors/rust.d.ts +1 -0
- package/dist/lib/extractors/rust.js +1 -0
- package/dist/lib/extractors/swift.d.ts +1 -0
- package/dist/lib/extractors/swift.js +1 -0
- package/dist/lib/language-extractor.d.ts +4 -0
- package/dist/mcp/server.js +48 -4
- package/package.json +1 -1
|
@@ -8,8 +8,13 @@ export declare function batchTriples(triples: string[], batchSize?: number): str
|
|
|
8
8
|
import type { StoreAdapter as FullStoreAdapter } from './store-adapter.js';
|
|
9
9
|
type StoreAdapter = Pick<FullStoreAdapter, 'sparqlUpdate'>;
|
|
10
10
|
export declare function deleteExistingSymbols(adapter: StoreAdapter, graphUri: string, modulePaths: string[]): Promise<void>;
|
|
11
|
-
export
|
|
11
|
+
export interface PushResult {
|
|
12
12
|
triplesInserted: number;
|
|
13
|
+
triplesFailed: number;
|
|
13
14
|
batchCount: number;
|
|
14
|
-
|
|
15
|
+
batchesFailed: number;
|
|
16
|
+
errors: string[];
|
|
17
|
+
retryHint: string | null;
|
|
18
|
+
}
|
|
19
|
+
export declare function pushSymbolTriples(adapter: StoreAdapter, graphUri: string, result: DeepScanResult): Promise<PushResult>;
|
|
15
20
|
export {};
|
|
@@ -84,7 +84,7 @@ export function generateSymbolTriples(result) {
|
|
|
84
84
|
return triples;
|
|
85
85
|
}
|
|
86
86
|
// ── Batching ────────────────────────────────────────────────────
|
|
87
|
-
export function batchTriples(triples, batchSize =
|
|
87
|
+
export function batchTriples(triples, batchSize = 25) {
|
|
88
88
|
const batches = [];
|
|
89
89
|
for (let i = 0; i < triples.length; i += batchSize) {
|
|
90
90
|
batches.push(triples.slice(i, i + batchSize));
|
|
@@ -110,11 +110,28 @@ export async function pushSymbolTriples(adapter, graphUri, result) {
|
|
|
110
110
|
modulePaths.add(f.filePath);
|
|
111
111
|
// Delete existing symbols for these modules
|
|
112
112
|
await deleteExistingSymbols(adapter, graphUri, [...modulePaths]);
|
|
113
|
-
// Generate and batch-insert
|
|
113
|
+
// Generate and batch-insert with per-batch error handling
|
|
114
114
|
const triples = generateSymbolTriples(result);
|
|
115
|
-
const batches = batchTriples(triples,
|
|
116
|
-
|
|
117
|
-
|
|
115
|
+
const batches = batchTriples(triples, 25);
|
|
116
|
+
let inserted = 0;
|
|
117
|
+
let failed = 0;
|
|
118
|
+
let batchesFailed = 0;
|
|
119
|
+
const errors = [];
|
|
120
|
+
for (let i = 0; i < batches.length; i++) {
|
|
121
|
+
const batch = batches[i];
|
|
122
|
+
try {
|
|
123
|
+
await adapter.sparqlUpdate(`INSERT DATA { GRAPH <${graphUri}> {\n${batch.join('\n')}\n} }`);
|
|
124
|
+
inserted += batch.length;
|
|
125
|
+
}
|
|
126
|
+
catch (err) {
|
|
127
|
+
batchesFailed++;
|
|
128
|
+
failed += batch.length;
|
|
129
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
130
|
+
errors.push(`Batch ${i + 1}/${batches.length} failed (${batch.length} triples): ${msg}`);
|
|
131
|
+
}
|
|
118
132
|
}
|
|
119
|
-
|
|
133
|
+
const retryHint = batchesFailed > 0
|
|
134
|
+
? `${batchesFailed}/${batches.length} batches failed (${failed} triples lost). Re-run context_scan with depth="symbol" to retry. If failures persist, try reducing maxSymbols or disabling includeMethodCalls.`
|
|
135
|
+
: null;
|
|
136
|
+
return { triplesInserted: inserted, triplesFailed: failed, batchCount: batches.length, batchesFailed, errors, retryHint };
|
|
120
137
|
}
|
|
@@ -12,6 +12,7 @@ export type { TSNode, TSTree, TSLanguage };
|
|
|
12
12
|
export declare abstract class TreeSitterExtractor implements LanguageExtractor {
|
|
13
13
|
abstract readonly language: string;
|
|
14
14
|
abstract readonly extensions: string[];
|
|
15
|
+
abstract readonly dependencyModel: import('./language-extractor.js').DependencyModel;
|
|
15
16
|
/** WASM file name, e.g. 'tree-sitter-python.wasm' */
|
|
16
17
|
protected abstract readonly wasmName: string;
|
|
17
18
|
protected lang: TSLanguage | null;
|
|
@@ -6,6 +6,7 @@ import type { LanguageExtractor, ExtractedSymbols } from './language-extractor.j
|
|
|
6
6
|
export declare class TypeScriptExtractor implements LanguageExtractor {
|
|
7
7
|
readonly language = "typescript";
|
|
8
8
|
readonly extensions: string[];
|
|
9
|
+
readonly dependencyModel: "file-based";
|
|
9
10
|
private tsMorph;
|
|
10
11
|
isAvailable(): Promise<boolean>;
|
|
11
12
|
extract(_filePaths: string[], rootDir: string, options: {
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* Deep scanner — language-agnostic orchestrator for symbol-level codebase analysis.
|
|
3
3
|
* Delegates to LanguageExtractor implementations (ts-morph, tree-sitter, etc.).
|
|
4
4
|
*/
|
|
5
|
+
import type { DependencyModel } from './language-extractor.js';
|
|
5
6
|
export interface DeepScanOptions {
|
|
6
7
|
maxFiles?: number;
|
|
7
8
|
maxSymbols?: number;
|
|
@@ -48,6 +49,12 @@ export interface MethodCallInfo {
|
|
|
48
49
|
caller: string;
|
|
49
50
|
callee: string;
|
|
50
51
|
}
|
|
52
|
+
export interface LanguageHint {
|
|
53
|
+
language: string;
|
|
54
|
+
dependencyModel: DependencyModel;
|
|
55
|
+
moduleScanApplicable: boolean;
|
|
56
|
+
recommendation: string;
|
|
57
|
+
}
|
|
51
58
|
export interface UnsupportedFileGroup {
|
|
52
59
|
extension: string;
|
|
53
60
|
language: string;
|
|
@@ -61,6 +68,7 @@ export interface DeepScanResult {
|
|
|
61
68
|
functions: FunctionInfo[];
|
|
62
69
|
methodCalls: MethodCallInfo[];
|
|
63
70
|
unsupportedFiles: UnsupportedFileGroup[];
|
|
71
|
+
languageHints: LanguageHint[];
|
|
64
72
|
fileCount: number;
|
|
65
73
|
symbolCount: number;
|
|
66
74
|
scanDurationMs: number;
|
package/dist/lib/deep-scanner.js
CHANGED
|
@@ -103,6 +103,20 @@ async function discoverUnsupportedFiles(rootDir, supportedExtensions) {
|
|
|
103
103
|
.filter(g => g.count > 0)
|
|
104
104
|
.sort((a, b) => b.count - a.count);
|
|
105
105
|
}
|
|
106
|
+
// ── Language hint generation ────────────────────────────────────
|
|
107
|
+
const DEPENDENCY_MODEL_RECOMMENDATIONS = {
|
|
108
|
+
'file-based': 'Module-level dependency graph (depth="module") is applicable.',
|
|
109
|
+
'package-based': 'This language uses package-based imports — module-level scan is not applicable. Use depth="symbol" for class/method/call-level analysis.',
|
|
110
|
+
'framework-based': 'This language uses framework-level imports — module-level scan is not applicable. Use depth="symbol" for class/method/call-level analysis.',
|
|
111
|
+
};
|
|
112
|
+
function buildLanguageHints(extractors) {
|
|
113
|
+
return extractors.map(ext => ({
|
|
114
|
+
language: ext.language,
|
|
115
|
+
dependencyModel: ext.dependencyModel,
|
|
116
|
+
moduleScanApplicable: ext.dependencyModel === 'file-based',
|
|
117
|
+
recommendation: DEPENDENCY_MODEL_RECOMMENDATIONS[ext.dependencyModel],
|
|
118
|
+
}));
|
|
119
|
+
}
|
|
106
120
|
// ── Main entry point ────────────────────────────────────────────
|
|
107
121
|
export async function deepScan(rootDir, options) {
|
|
108
122
|
const maxFiles = options?.maxFiles ?? 500;
|
|
@@ -144,6 +158,7 @@ export async function deepScan(rootDir, options) {
|
|
|
144
158
|
functions: [],
|
|
145
159
|
methodCalls: [],
|
|
146
160
|
unsupportedFiles: [],
|
|
161
|
+
languageHints: buildLanguageHints(available),
|
|
147
162
|
fileCount: total,
|
|
148
163
|
symbolCount: 0,
|
|
149
164
|
scanDurationMs: Date.now() - start,
|
|
@@ -218,6 +233,7 @@ export async function deepScan(rootDir, options) {
|
|
|
218
233
|
functions,
|
|
219
234
|
methodCalls,
|
|
220
235
|
unsupportedFiles,
|
|
236
|
+
languageHints: buildLanguageHints(available),
|
|
221
237
|
fileCount: files.length,
|
|
222
238
|
symbolCount,
|
|
223
239
|
scanDurationMs: Date.now() - start,
|
|
@@ -8,6 +8,7 @@ import type { ExtractedSymbols } from '../language-extractor.js';
|
|
|
8
8
|
export declare class GoExtractor extends TreeSitterExtractor {
|
|
9
9
|
readonly language = "go";
|
|
10
10
|
readonly extensions: string[];
|
|
11
|
+
readonly dependencyModel: "package-based";
|
|
11
12
|
protected readonly wasmName = "tree-sitter-go.wasm";
|
|
12
13
|
protected extractFromTree(tree: TSTree, relPath: string, _source: string, includeMethodCalls: boolean): ExtractedSymbols;
|
|
13
14
|
private extractStructFields;
|
|
@@ -8,6 +8,7 @@ export class GoExtractor extends TreeSitterExtractor {
|
|
|
8
8
|
super(...arguments);
|
|
9
9
|
this.language = 'go';
|
|
10
10
|
this.extensions = ['.go'];
|
|
11
|
+
this.dependencyModel = 'package-based';
|
|
11
12
|
this.wasmName = 'tree-sitter-go.wasm';
|
|
12
13
|
}
|
|
13
14
|
extractFromTree(tree, relPath, _source, includeMethodCalls) {
|
|
@@ -8,6 +8,7 @@ import type { ExtractedSymbols } from '../language-extractor.js';
|
|
|
8
8
|
export declare class JavaExtractor extends TreeSitterExtractor {
|
|
9
9
|
readonly language = "java";
|
|
10
10
|
readonly extensions: string[];
|
|
11
|
+
readonly dependencyModel: "package-based";
|
|
11
12
|
protected readonly wasmName = "tree-sitter-java.wasm";
|
|
12
13
|
protected extractFromTree(tree: TSTree, relPath: string, _source: string, includeMethodCalls: boolean): ExtractedSymbols;
|
|
13
14
|
private getSuperclass;
|
|
@@ -8,6 +8,7 @@ export class JavaExtractor extends TreeSitterExtractor {
|
|
|
8
8
|
super(...arguments);
|
|
9
9
|
this.language = 'java';
|
|
10
10
|
this.extensions = ['.java'];
|
|
11
|
+
this.dependencyModel = 'package-based';
|
|
11
12
|
this.wasmName = 'tree-sitter-java.wasm';
|
|
12
13
|
}
|
|
13
14
|
extractFromTree(tree, relPath, _source, includeMethodCalls) {
|
|
@@ -8,6 +8,7 @@ import type { ExtractedSymbols } from '../language-extractor.js';
|
|
|
8
8
|
export declare class PythonExtractor extends TreeSitterExtractor {
|
|
9
9
|
readonly language = "python";
|
|
10
10
|
readonly extensions: string[];
|
|
11
|
+
readonly dependencyModel: "file-based";
|
|
11
12
|
protected readonly wasmName = "tree-sitter-python.wasm";
|
|
12
13
|
protected extractFromTree(tree: TSTree, relPath: string, _source: string, includeMethodCalls: boolean): ExtractedSymbols;
|
|
13
14
|
private getBaseClasses;
|
|
@@ -9,6 +9,7 @@ export class PythonExtractor extends TreeSitterExtractor {
|
|
|
9
9
|
super(...arguments);
|
|
10
10
|
this.language = 'python';
|
|
11
11
|
this.extensions = ['.py'];
|
|
12
|
+
this.dependencyModel = 'file-based';
|
|
12
13
|
this.wasmName = 'tree-sitter-python.wasm';
|
|
13
14
|
}
|
|
14
15
|
extractFromTree(tree, relPath, _source, includeMethodCalls) {
|
|
@@ -8,6 +8,7 @@ import type { ExtractedSymbols } from '../language-extractor.js';
|
|
|
8
8
|
export declare class RustExtractor extends TreeSitterExtractor {
|
|
9
9
|
readonly language = "rust";
|
|
10
10
|
readonly extensions: string[];
|
|
11
|
+
readonly dependencyModel: "file-based";
|
|
11
12
|
protected readonly wasmName = "tree-sitter-rust.wasm";
|
|
12
13
|
protected extractFromTree(tree: TSTree, relPath: string, _source: string, includeMethodCalls: boolean): ExtractedSymbols;
|
|
13
14
|
private extractParams;
|
|
@@ -8,6 +8,7 @@ export class RustExtractor extends TreeSitterExtractor {
|
|
|
8
8
|
super(...arguments);
|
|
9
9
|
this.language = 'rust';
|
|
10
10
|
this.extensions = ['.rs'];
|
|
11
|
+
this.dependencyModel = 'file-based';
|
|
11
12
|
this.wasmName = 'tree-sitter-rust.wasm';
|
|
12
13
|
}
|
|
13
14
|
extractFromTree(tree, relPath, _source, includeMethodCalls) {
|
|
@@ -8,6 +8,7 @@ import type { ExtractedSymbols } from '../language-extractor.js';
|
|
|
8
8
|
export declare class SwiftExtractor extends TreeSitterExtractor {
|
|
9
9
|
readonly language = "swift";
|
|
10
10
|
readonly extensions: string[];
|
|
11
|
+
readonly dependencyModel: "framework-based";
|
|
11
12
|
protected readonly wasmName = "tree-sitter-swift.wasm";
|
|
12
13
|
protected extractFromTree(tree: TSTree, relPath: string, _source: string, includeMethodCalls: boolean): ExtractedSymbols;
|
|
13
14
|
private extractClassLike;
|
|
@@ -8,6 +8,7 @@ export class SwiftExtractor extends TreeSitterExtractor {
|
|
|
8
8
|
super(...arguments);
|
|
9
9
|
this.language = 'swift';
|
|
10
10
|
this.extensions = ['.swift'];
|
|
11
|
+
this.dependencyModel = 'framework-based';
|
|
11
12
|
this.wasmName = 'tree-sitter-swift.wasm';
|
|
12
13
|
}
|
|
13
14
|
extractFromTree(tree, relPath, _source, includeMethodCalls) {
|
|
@@ -9,11 +9,15 @@ export interface ExtractedSymbols {
|
|
|
9
9
|
functions: FunctionInfo[];
|
|
10
10
|
methodCalls: MethodCallInfo[];
|
|
11
11
|
}
|
|
12
|
+
/** How a language resolves inter-file dependencies. */
|
|
13
|
+
export type DependencyModel = 'file-based' | 'package-based' | 'framework-based';
|
|
12
14
|
export interface LanguageExtractor {
|
|
13
15
|
/** Language identifier, e.g. 'typescript', 'python', 'go' */
|
|
14
16
|
readonly language: string;
|
|
15
17
|
/** File extensions handled by this extractor, e.g. ['.ts', '.tsx'] */
|
|
16
18
|
readonly extensions: string[];
|
|
19
|
+
/** How this language resolves dependencies between source files. */
|
|
20
|
+
readonly dependencyModel: DependencyModel;
|
|
17
21
|
/** Check whether the extractor's dependencies are available at runtime. */
|
|
18
22
|
isAvailable(): Promise<boolean>;
|
|
19
23
|
/**
|
package/dist/mcp/server.js
CHANGED
|
@@ -381,12 +381,16 @@ async function handleContextScan(args) {
|
|
|
381
381
|
// Auto-push triples server-side
|
|
382
382
|
let pushStats = null;
|
|
383
383
|
let moduleStats = null;
|
|
384
|
+
const pushWarnings = [];
|
|
384
385
|
try {
|
|
385
386
|
const config = loadConfig();
|
|
386
387
|
const contextUri = `${config.graphUri}/context`;
|
|
387
388
|
const adapter = await createReadyAdapter(config);
|
|
388
389
|
// Push symbol triples
|
|
389
390
|
pushStats = await pushSymbolTriples(adapter, contextUri, scanResult);
|
|
391
|
+
if (pushStats.errors.length > 0) {
|
|
392
|
+
pushWarnings.push(...pushStats.errors);
|
|
393
|
+
}
|
|
390
394
|
// Also push module dependency graph (fixes #64)
|
|
391
395
|
const snapshot = await scanCodebase(process.cwd());
|
|
392
396
|
if (snapshot.dependencyGraph && snapshot.dependencyGraph.modules.length > 0) {
|
|
@@ -405,14 +409,20 @@ async function handleContextScan(args) {
|
|
|
405
409
|
}
|
|
406
410
|
await persistGraph(adapter, config, contextUri);
|
|
407
411
|
}
|
|
408
|
-
catch {
|
|
409
|
-
|
|
412
|
+
catch (err) {
|
|
413
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
414
|
+
pushWarnings.push(`Push failed: ${msg}`);
|
|
410
415
|
}
|
|
411
416
|
const hints = [];
|
|
412
|
-
if (pushStats)
|
|
413
|
-
hints.push(`Symbol triples: ${pushStats.triplesInserted}
|
|
417
|
+
if (pushStats) {
|
|
418
|
+
hints.push(`Symbol triples: ${pushStats.triplesInserted} inserted, ${pushStats.triplesFailed} failed, ${pushStats.batchCount} batches`);
|
|
419
|
+
if (pushStats.retryHint)
|
|
420
|
+
hints.push(pushStats.retryHint);
|
|
421
|
+
}
|
|
414
422
|
if (moduleStats)
|
|
415
423
|
hints.push(`Module triples: ${moduleStats.modules} modules, ${moduleStats.edges} edges`);
|
|
424
|
+
if (pushWarnings.length > 0)
|
|
425
|
+
hints.push(...pushWarnings);
|
|
416
426
|
// Build compact summary — do NOT return full symbol arrays (#66)
|
|
417
427
|
const compact = {
|
|
418
428
|
deepScanAvailable: true,
|
|
@@ -424,6 +434,7 @@ async function handleContextScan(args) {
|
|
|
424
434
|
files: scanResult.fileCount,
|
|
425
435
|
symbols: scanResult.symbolCount,
|
|
426
436
|
},
|
|
437
|
+
languageHints: scanResult.languageHints,
|
|
427
438
|
scanDurationMs: scanResult.scanDurationMs,
|
|
428
439
|
capped: scanResult.capped,
|
|
429
440
|
warnings: scanResult.warnings,
|
|
@@ -512,9 +523,42 @@ async function handleContextScan(args) {
|
|
|
512
523
|
catch {
|
|
513
524
|
// Non-fatal: module triple push is best-effort
|
|
514
525
|
}
|
|
526
|
+
// Generate language hints for module scan — helps LLM understand when module scan isn't applicable
|
|
527
|
+
const moduleLanguageHints = [];
|
|
528
|
+
const fileExtensionMap = {
|
|
529
|
+
'.ts': { lang: 'typescript', model: 'file-based' },
|
|
530
|
+
'.tsx': { lang: 'typescript', model: 'file-based' },
|
|
531
|
+
'.js': { lang: 'javascript', model: 'file-based' },
|
|
532
|
+
'.jsx': { lang: 'javascript', model: 'file-based' },
|
|
533
|
+
'.py': { lang: 'python', model: 'file-based' },
|
|
534
|
+
'.rs': { lang: 'rust', model: 'file-based' },
|
|
535
|
+
'.go': { lang: 'go', model: 'package-based' },
|
|
536
|
+
'.java': { lang: 'java', model: 'package-based' },
|
|
537
|
+
'.swift': { lang: 'swift', model: 'framework-based' },
|
|
538
|
+
};
|
|
539
|
+
if (snapshot.dependencyGraph) {
|
|
540
|
+
const detectedLangs = new Set();
|
|
541
|
+
for (const mod of snapshot.dependencyGraph.modules) {
|
|
542
|
+
const ext = '.' + mod.split('.').pop();
|
|
543
|
+
const info = fileExtensionMap[ext];
|
|
544
|
+
if (info && !detectedLangs.has(info.lang)) {
|
|
545
|
+
detectedLangs.add(info.lang);
|
|
546
|
+
const applicable = info.model === 'file-based';
|
|
547
|
+
moduleLanguageHints.push({
|
|
548
|
+
language: info.lang,
|
|
549
|
+
dependencyModel: info.model,
|
|
550
|
+
moduleScanApplicable: applicable,
|
|
551
|
+
recommendation: applicable
|
|
552
|
+
? 'Module-level dependency graph (depth="module") is applicable.'
|
|
553
|
+
: `This language uses ${info.model} imports — module-level scan is not applicable. Use depth="symbol" for class/method/call-level analysis.`,
|
|
554
|
+
});
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
}
|
|
515
558
|
return {
|
|
516
559
|
codebaseSnapshot: snapshot,
|
|
517
560
|
moduleStats,
|
|
561
|
+
languageHints: moduleLanguageHints.length > 0 ? moduleLanguageHints : undefined,
|
|
518
562
|
_hint: moduleStats
|
|
519
563
|
? `Module triples auto-pushed: ${moduleStats.modules} modules, ${moduleStats.edges} edges. Query with: SELECT ?m WHERE { ?m a otx:Module }`
|
|
520
564
|
: 'No dependency graph auto-extracted or push failed. Inspect key source files manually.',
|