@kentwynn/kgraph 0.1.26 → 0.2.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 +3 -18
- package/dist/cli/commands/context.d.ts +2 -2
- package/dist/cli/commands/context.js +82 -23
- package/dist/cli/commands/init.js +2 -25
- package/dist/cli/commands/workflow.js +2 -2
- package/dist/cli/help.d.ts +1 -0
- package/dist/cli/help.js +4 -6
- package/dist/cli/index.js +0 -2
- package/dist/cli/init-prompt.d.ts +2 -7
- package/dist/cli/init-prompt.js +0 -63
- package/dist/cli/init-recommendations.d.ts +1 -12
- package/dist/cli/init-recommendations.js +0 -23
- package/dist/cli/init-summary.d.ts +2 -4
- package/dist/cli/init-summary.js +10 -35
- package/dist/config/config.js +0 -33
- package/dist/context/context-query.js +23 -0
- package/dist/scanner/c-symbol-extractor.d.ts +1 -1
- package/dist/scanner/c-symbol-extractor.js +108 -65
- package/dist/scanner/csharp-symbol-extractor.d.ts +1 -1
- package/dist/scanner/csharp-symbol-extractor.js +93 -67
- package/dist/scanner/go-symbol-extractor.d.ts +1 -1
- package/dist/scanner/go-symbol-extractor.js +75 -60
- package/dist/scanner/jvm-symbol-extractor.d.ts +1 -1
- package/dist/scanner/jvm-symbol-extractor.js +139 -71
- package/dist/scanner/python-symbol-extractor.d.ts +1 -1
- package/dist/scanner/python-symbol-extractor.js +92 -71
- package/dist/scanner/repo-scanner.js +65 -8
- package/dist/scanner/rust-symbol-extractor.d.ts +1 -1
- package/dist/scanner/rust-symbol-extractor.js +94 -89
- package/dist/scanner/tree-sitter-parser.d.ts +5 -0
- package/dist/scanner/tree-sitter-parser.js +55 -0
- package/dist/types/cognition.d.ts +3 -2
- package/dist/types/config.d.ts +0 -7
- package/dist/types/maps.d.ts +6 -5
- package/package.json +10 -1
- package/dist/cli/commands/extractor.d.ts +0 -2
- package/dist/cli/commands/extractor.js +0 -50
- package/dist/extractors/extractor-registry.d.ts +0 -11
- package/dist/extractors/extractor-registry.js +0 -70
- package/dist/extractors/extractor-store.d.ts +0 -10
- package/dist/extractors/extractor-store.js +0 -58
|
@@ -1,38 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { parseSource } from './tree-sitter-parser.js';
|
|
2
|
+
export async function extractRustSymbols(sourceText, filePath) {
|
|
3
3
|
const symbols = [];
|
|
4
4
|
const dependencies = [];
|
|
5
5
|
const relationships = [];
|
|
6
6
|
const warnings = [];
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
.join('#');
|
|
14
|
-
// Rust: pub = exported
|
|
15
|
-
symbols.push({
|
|
16
|
-
id,
|
|
17
|
-
name,
|
|
18
|
-
kind,
|
|
19
|
-
filePath,
|
|
20
|
-
startLine: lineNum,
|
|
21
|
-
endLine: lineNum,
|
|
22
|
-
exported: false, // set by caller
|
|
23
|
-
parentName,
|
|
24
|
-
});
|
|
25
|
-
relationships.push({
|
|
26
|
-
sourceType: 'file',
|
|
27
|
-
sourceId: filePath,
|
|
28
|
-
targetType: 'symbol',
|
|
29
|
-
targetId: id,
|
|
30
|
-
relationshipType: 'contains',
|
|
31
|
-
confidence: 'high',
|
|
32
|
-
});
|
|
33
|
-
};
|
|
34
|
-
const addSymbolExported = (name, kind, lineNum, exported, parentName) => {
|
|
35
|
-
const id = [filePath, kind, parentName, name, lineNum]
|
|
7
|
+
if (!sourceText.trim()) {
|
|
8
|
+
return { symbols, dependencies, relationships, warnings };
|
|
9
|
+
}
|
|
10
|
+
const tree = await parseSource(sourceText, 'rust');
|
|
11
|
+
const addSymbol = (name, kind, startLine, endLine, exported, parentName) => {
|
|
12
|
+
const id = [filePath, kind, parentName, name, startLine]
|
|
36
13
|
.filter(Boolean)
|
|
37
14
|
.join('#');
|
|
38
15
|
symbols.push({
|
|
@@ -40,8 +17,8 @@ export function extractRustSymbols(sourceText, filePath) {
|
|
|
40
17
|
name,
|
|
41
18
|
kind,
|
|
42
19
|
filePath,
|
|
43
|
-
startLine
|
|
44
|
-
endLine
|
|
20
|
+
startLine,
|
|
21
|
+
endLine,
|
|
45
22
|
exported,
|
|
46
23
|
parentName,
|
|
47
24
|
});
|
|
@@ -54,66 +31,94 @@ export function extractRustSymbols(sourceText, filePath) {
|
|
|
54
31
|
confidence: 'high',
|
|
55
32
|
});
|
|
56
33
|
};
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
while (implStack.length > 0 &&
|
|
68
|
-
braceDepth < implStack[implStack.length - 1].braceDepth) {
|
|
69
|
-
implStack.pop();
|
|
70
|
-
}
|
|
71
|
-
// use statement: use crate::path or use external::path
|
|
72
|
-
const useMatch = trimmed.match(/^use\s+([\w:]+)/);
|
|
73
|
-
if (useMatch) {
|
|
74
|
-
const specifier = useMatch[1];
|
|
75
|
-
const kind = specifier.startsWith('crate::') ||
|
|
76
|
-
specifier.startsWith('super::') ||
|
|
77
|
-
specifier.startsWith('self::')
|
|
78
|
-
? 'local'
|
|
79
|
-
: 'package';
|
|
80
|
-
dependencies.push({ fromFile: filePath, specifier, kind });
|
|
81
|
-
relationships.push({
|
|
82
|
-
sourceType: 'file',
|
|
83
|
-
sourceId: filePath,
|
|
84
|
-
targetType: kind === 'local' ? 'file' : 'package',
|
|
85
|
-
targetId: specifier,
|
|
86
|
-
relationshipType: 'import',
|
|
87
|
-
confidence: 'high',
|
|
88
|
-
});
|
|
89
|
-
continue;
|
|
90
|
-
}
|
|
91
|
-
// impl block: impl TypeName or impl Trait for TypeName
|
|
92
|
-
const implMatch = trimmed.match(/^impl(?:<[^>]*>)?\s+(?:\w+\s+for\s+)?(\w+)/);
|
|
93
|
-
if (implMatch) {
|
|
94
|
-
implStack.push({ typeName: implMatch[1], braceDepth });
|
|
95
|
-
continue;
|
|
96
|
-
}
|
|
97
|
-
// struct / enum / trait definition
|
|
98
|
-
const typeMatch = trimmed.match(/^(pub\s+)?(?:struct|enum|trait)\s+(\w+)/);
|
|
99
|
-
if (typeMatch) {
|
|
100
|
-
addSymbolExported(typeMatch[2], 'class', lineNum, !!typeMatch[1]);
|
|
101
|
-
continue;
|
|
34
|
+
function hasPub(node) {
|
|
35
|
+
return node.namedChildren.some((c) => c.type === 'visibility_modifier');
|
|
36
|
+
}
|
|
37
|
+
function extractUseSpecifier(node) {
|
|
38
|
+
// use_declaration has a child tree of scoped_identifier / scoped_use_list / use_wildcard
|
|
39
|
+
// We want the text without 'use' and ';'
|
|
40
|
+
for (const child of node.namedChildren) {
|
|
41
|
+
if (child.type !== 'visibility_modifier') {
|
|
42
|
+
return child.text;
|
|
43
|
+
}
|
|
102
44
|
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
45
|
+
return '';
|
|
46
|
+
}
|
|
47
|
+
function walk(node, implTypeName) {
|
|
48
|
+
switch (node.type) {
|
|
49
|
+
case 'use_declaration': {
|
|
50
|
+
const specifier = extractUseSpecifier(node);
|
|
51
|
+
if (specifier) {
|
|
52
|
+
const kind = specifier.startsWith('crate::') ||
|
|
53
|
+
specifier.startsWith('super::') ||
|
|
54
|
+
specifier.startsWith('self::')
|
|
55
|
+
? 'local'
|
|
56
|
+
: 'package';
|
|
57
|
+
dependencies.push({
|
|
58
|
+
fromFile: filePath,
|
|
59
|
+
specifier,
|
|
60
|
+
kind,
|
|
61
|
+
});
|
|
62
|
+
relationships.push({
|
|
63
|
+
sourceType: 'file',
|
|
64
|
+
sourceId: filePath,
|
|
65
|
+
targetType: kind === 'local' ? 'file' : 'package',
|
|
66
|
+
targetId: specifier,
|
|
67
|
+
relationshipType: 'import',
|
|
68
|
+
confidence: 'high',
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
case 'struct_item':
|
|
74
|
+
case 'enum_item':
|
|
75
|
+
case 'trait_item': {
|
|
76
|
+
const nameNode = node.childForFieldName('name');
|
|
77
|
+
if (nameNode) {
|
|
78
|
+
addSymbol(nameNode.text, 'class', node.startPosition.row + 1, node.endPosition.row + 1, hasPub(node));
|
|
79
|
+
}
|
|
80
|
+
return;
|
|
111
81
|
}
|
|
112
|
-
|
|
113
|
-
|
|
82
|
+
case 'impl_item': {
|
|
83
|
+
// impl TypeName { ... } or impl Trait for TypeName { ... }
|
|
84
|
+
const typeNode = node.childForFieldName('type');
|
|
85
|
+
const typeName = typeNode?.type === 'type_identifier' ? typeNode.text : typeNode?.text;
|
|
86
|
+
const body = node.childForFieldName('body') ??
|
|
87
|
+
node.namedChildren.find((c) => c.type === 'declaration_list');
|
|
88
|
+
if (body && typeName) {
|
|
89
|
+
for (const child of body.namedChildren) {
|
|
90
|
+
walk(child, typeName);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return;
|
|
114
94
|
}
|
|
115
|
-
|
|
95
|
+
case 'function_item': {
|
|
96
|
+
const nameNode = node.childForFieldName('name');
|
|
97
|
+
if (nameNode) {
|
|
98
|
+
const exported = hasPub(node);
|
|
99
|
+
if (implTypeName) {
|
|
100
|
+
addSymbol(nameNode.text, 'method', node.startPosition.row + 1, node.endPosition.row + 1, exported, implTypeName);
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
addSymbol(nameNode.text, 'function', node.startPosition.row + 1, node.endPosition.row + 1, exported);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
case 'function_signature_item': {
|
|
109
|
+
// trait method signatures
|
|
110
|
+
const nameNode = node.childForFieldName('name');
|
|
111
|
+
if (nameNode && implTypeName) {
|
|
112
|
+
addSymbol(nameNode.text, 'method', node.startPosition.row + 1, node.endPosition.row + 1, false, implTypeName);
|
|
113
|
+
}
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
for (const child of node.namedChildren) {
|
|
118
|
+
walk(child, implTypeName);
|
|
116
119
|
}
|
|
117
120
|
}
|
|
121
|
+
walk(tree.rootNode);
|
|
122
|
+
tree.delete();
|
|
118
123
|
return { symbols, dependencies, relationships, warnings };
|
|
119
124
|
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { Language, Tree } from 'web-tree-sitter';
|
|
2
|
+
type GrammarKey = 'python' | 'java' | 'kotlin' | 'go' | 'rust' | 'c' | 'cpp' | 'c_sharp';
|
|
3
|
+
export declare function loadLanguage(grammarKey: GrammarKey): Promise<Language>;
|
|
4
|
+
export declare function parseSource(sourceText: string, grammarKey: GrammarKey): Promise<Tree>;
|
|
5
|
+
export {};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { createRequire } from 'node:module';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { Language, Parser } from 'web-tree-sitter';
|
|
4
|
+
const require = createRequire(import.meta.url);
|
|
5
|
+
let initPromise = null;
|
|
6
|
+
const languageCache = new Map();
|
|
7
|
+
const GRAMMAR_PACKAGES = {
|
|
8
|
+
python: { pkg: 'tree-sitter-python', wasm: 'tree-sitter-python.wasm' },
|
|
9
|
+
java: { pkg: 'tree-sitter-java', wasm: 'tree-sitter-java.wasm' },
|
|
10
|
+
kotlin: {
|
|
11
|
+
pkg: '@tree-sitter-grammars/tree-sitter-kotlin',
|
|
12
|
+
wasm: 'tree-sitter-kotlin.wasm',
|
|
13
|
+
},
|
|
14
|
+
go: { pkg: 'tree-sitter-go', wasm: 'tree-sitter-go.wasm' },
|
|
15
|
+
rust: { pkg: 'tree-sitter-rust', wasm: 'tree-sitter-rust.wasm' },
|
|
16
|
+
c: { pkg: 'tree-sitter-c', wasm: 'tree-sitter-c.wasm' },
|
|
17
|
+
cpp: { pkg: 'tree-sitter-cpp', wasm: 'tree-sitter-cpp.wasm' },
|
|
18
|
+
c_sharp: {
|
|
19
|
+
pkg: 'tree-sitter-c-sharp',
|
|
20
|
+
wasm: 'tree-sitter-c_sharp.wasm',
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
async function ensureInit() {
|
|
24
|
+
if (!initPromise) {
|
|
25
|
+
initPromise = Parser.init();
|
|
26
|
+
}
|
|
27
|
+
return initPromise;
|
|
28
|
+
}
|
|
29
|
+
function resolveWasmPath(grammarKey) {
|
|
30
|
+
const { pkg, wasm } = GRAMMAR_PACKAGES[grammarKey];
|
|
31
|
+
const pkgDir = path.dirname(require.resolve(`${pkg}/package.json`));
|
|
32
|
+
return path.join(pkgDir, wasm);
|
|
33
|
+
}
|
|
34
|
+
export async function loadLanguage(grammarKey) {
|
|
35
|
+
const cached = languageCache.get(grammarKey);
|
|
36
|
+
if (cached)
|
|
37
|
+
return cached;
|
|
38
|
+
await ensureInit();
|
|
39
|
+
const wasmPath = resolveWasmPath(grammarKey);
|
|
40
|
+
const language = await Language.load(wasmPath);
|
|
41
|
+
languageCache.set(grammarKey, language);
|
|
42
|
+
return language;
|
|
43
|
+
}
|
|
44
|
+
export async function parseSource(sourceText, grammarKey) {
|
|
45
|
+
await ensureInit();
|
|
46
|
+
const language = await loadLanguage(grammarKey);
|
|
47
|
+
const parser = new Parser();
|
|
48
|
+
parser.setLanguage(language);
|
|
49
|
+
const tree = parser.parse(sourceText);
|
|
50
|
+
parser.delete();
|
|
51
|
+
if (!tree) {
|
|
52
|
+
throw new Error(`Failed to parse source with grammar ${grammarKey}`);
|
|
53
|
+
}
|
|
54
|
+
return tree;
|
|
55
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { CodeSymbol, Relationship, RepositoryFile } from
|
|
2
|
-
export type ReferenceStatus =
|
|
1
|
+
import type { CodeSymbol, Relationship, RepositoryFile } from './maps.js';
|
|
2
|
+
export type ReferenceStatus = 'current' | 'stale' | 'unresolved' | 'mixed';
|
|
3
3
|
export interface ParsedCognitionNote {
|
|
4
4
|
title: string;
|
|
5
5
|
domain?: string;
|
|
@@ -38,6 +38,7 @@ export interface ContextResponse {
|
|
|
38
38
|
relevantSymbols: RankedItem<CodeSymbol>[];
|
|
39
39
|
relevantCognition: RankedItem<CognitionNote>[];
|
|
40
40
|
relationships: Relationship[];
|
|
41
|
+
nearbySymbols?: CodeSymbol[];
|
|
41
42
|
staleReferences: string[];
|
|
42
43
|
warnings: string[];
|
|
43
44
|
}
|
package/dist/types/config.d.ts
CHANGED
|
@@ -7,7 +7,6 @@ export interface KGraphConfig {
|
|
|
7
7
|
maxContextItems: number;
|
|
8
8
|
domainHints: Record<string, DomainHint>;
|
|
9
9
|
integrations: IntegrationConfig[];
|
|
10
|
-
extractors: ExtractorConfig[];
|
|
11
10
|
}
|
|
12
11
|
export interface DomainHint {
|
|
13
12
|
paths?: string[];
|
|
@@ -15,18 +14,12 @@ export interface DomainHint {
|
|
|
15
14
|
}
|
|
16
15
|
export type IntegrationName = 'claude-code' | 'cline' | 'codex' | 'copilot' | 'cursor' | 'gemini' | 'windsurf';
|
|
17
16
|
export type IntegrationMode = 'smart' | 'always' | 'manual' | 'off';
|
|
18
|
-
export type ExtractorName = 'c-family' | 'csharp' | 'go' | 'jvm' | 'python' | 'rust';
|
|
19
17
|
export interface IntegrationConfig {
|
|
20
18
|
name: IntegrationName;
|
|
21
19
|
enabled: boolean;
|
|
22
20
|
mode: IntegrationMode;
|
|
23
21
|
targetPath: string;
|
|
24
22
|
}
|
|
25
|
-
export interface ExtractorConfig {
|
|
26
|
-
name: ExtractorName;
|
|
27
|
-
enabled: boolean;
|
|
28
|
-
packageName: string;
|
|
29
|
-
}
|
|
30
23
|
export interface KGraphWorkspace {
|
|
31
24
|
rootPath: string;
|
|
32
25
|
kgraphPath: string;
|
package/dist/types/maps.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
export type ScanStatus =
|
|
2
|
-
export type DependencyKind =
|
|
3
|
-
export type SymbolKind =
|
|
4
|
-
export type RelationshipType =
|
|
1
|
+
export type ScanStatus = 'mapped' | 'generic' | 'failed';
|
|
2
|
+
export type DependencyKind = 'local' | 'package' | 'unknown';
|
|
3
|
+
export type SymbolKind = 'function' | 'class' | 'method' | 'type' | 'interface' | 'export' | 'import';
|
|
4
|
+
export type RelationshipType = 'import' | 'contains' | 'symbol-contains' | 'calls' | 'mentions' | 'belongs-to-domain' | 'stale-reference' | 'moved-from';
|
|
5
5
|
export interface RepositoryFile {
|
|
6
6
|
id: string;
|
|
7
7
|
path: string;
|
|
@@ -36,7 +36,7 @@ export interface Relationship {
|
|
|
36
36
|
targetType: string;
|
|
37
37
|
targetId: string;
|
|
38
38
|
relationshipType: RelationshipType;
|
|
39
|
-
confidence:
|
|
39
|
+
confidence: 'high' | 'medium' | 'low';
|
|
40
40
|
}
|
|
41
41
|
export interface FileMap {
|
|
42
42
|
generatedAt: string;
|
|
@@ -60,4 +60,5 @@ export interface ScanResult {
|
|
|
60
60
|
dependencies: Dependency[];
|
|
61
61
|
relationships: Relationship[];
|
|
62
62
|
warnings: string[];
|
|
63
|
+
skippedFiles?: number;
|
|
63
64
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kentwynn/kgraph",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Persistent repo intelligence for AI coding assistants.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -40,11 +40,20 @@
|
|
|
40
40
|
"homepage": "https://github.com/kentwynn/KGraph#readme",
|
|
41
41
|
"dependencies": {
|
|
42
42
|
"@clack/prompts": "^1.3.0",
|
|
43
|
+
"@tree-sitter-grammars/tree-sitter-kotlin": "^1.1.0",
|
|
43
44
|
"chalk": "^5.6.2",
|
|
44
45
|
"commander": "^12.1.0",
|
|
45
46
|
"fast-glob": "^3.3.2",
|
|
46
47
|
"figlet": "^1.11.0",
|
|
48
|
+
"tree-sitter-c": "^0.24.1",
|
|
49
|
+
"tree-sitter-c-sharp": "^0.23.5",
|
|
50
|
+
"tree-sitter-cpp": "^0.23.4",
|
|
51
|
+
"tree-sitter-go": "^0.25.0",
|
|
52
|
+
"tree-sitter-java": "^0.23.5",
|
|
53
|
+
"tree-sitter-python": "^0.25.0",
|
|
54
|
+
"tree-sitter-rust": "^0.24.0",
|
|
47
55
|
"typescript": "^5.9.3",
|
|
56
|
+
"web-tree-sitter": "^0.26.8",
|
|
48
57
|
"yaml": "^2.5.1"
|
|
49
58
|
},
|
|
50
59
|
"devDependencies": {
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import { installCommandForExtractors, normalizeExtractorNames, } from '../../extractors/extractor-registry.js';
|
|
2
|
-
import { addExtractors, listExtractors, removeExtractors, } from '../../extractors/extractor-store.js';
|
|
3
|
-
import { assertWorkspace } from '../../storage/kgraph-paths.js';
|
|
4
|
-
import { KGraphError, runCommand } from '../errors.js';
|
|
5
|
-
export function registerExtractorCommand(program) {
|
|
6
|
-
const extractor = program
|
|
7
|
-
.command('extractor')
|
|
8
|
-
.description('Manage optional deep language extractors');
|
|
9
|
-
extractor
|
|
10
|
-
.command('list')
|
|
11
|
-
.description('List configured optional extractors')
|
|
12
|
-
.action(() => runCommand(async () => {
|
|
13
|
-
const workspace = await assertWorkspace(process.cwd());
|
|
14
|
-
const extractors = await listExtractors(workspace);
|
|
15
|
-
if (extractors.length === 0) {
|
|
16
|
-
console.log('No extractors configured.');
|
|
17
|
-
return;
|
|
18
|
-
}
|
|
19
|
-
for (const item of extractors) {
|
|
20
|
-
console.log(`${item.name} ${item.enabled ? 'enabled' : 'disabled'} ${item.packageName} ${item.packageInstalled ? 'present' : 'missing'}`);
|
|
21
|
-
}
|
|
22
|
-
}));
|
|
23
|
-
extractor
|
|
24
|
-
.command('add')
|
|
25
|
-
.description('Configure optional deep extractors')
|
|
26
|
-
.argument('<names...>')
|
|
27
|
-
.action((names) => runCommand(async () => {
|
|
28
|
-
const workspace = await assertWorkspace(process.cwd());
|
|
29
|
-
const normalized = normalizeExtractorNames(names);
|
|
30
|
-
if (normalized.length === 0) {
|
|
31
|
-
throw new KGraphError('Provide at least one extractor name.');
|
|
32
|
-
}
|
|
33
|
-
const changed = await addExtractors(workspace, normalized);
|
|
34
|
-
console.log(`Configured extractors: ${changed.map((item) => item.name).join(', ')}`);
|
|
35
|
-
console.log(`Install packages: ${installCommandForExtractors(changed.map((item) => item.packageName))}`);
|
|
36
|
-
}));
|
|
37
|
-
extractor
|
|
38
|
-
.command('remove')
|
|
39
|
-
.description('Remove optional deep extractors')
|
|
40
|
-
.argument('<names...>')
|
|
41
|
-
.action((names) => runCommand(async () => {
|
|
42
|
-
const workspace = await assertWorkspace(process.cwd());
|
|
43
|
-
const normalized = normalizeExtractorNames(names);
|
|
44
|
-
if (normalized.length === 0) {
|
|
45
|
-
throw new KGraphError('Provide at least one extractor name.');
|
|
46
|
-
}
|
|
47
|
-
const removed = await removeExtractors(workspace, normalized);
|
|
48
|
-
console.log(`Removed extractors: ${removed.join(', ')}`);
|
|
49
|
-
}));
|
|
50
|
-
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import type { ExtractorName } from '../types/config.js';
|
|
2
|
-
export interface ExtractorAdapter {
|
|
3
|
-
name: ExtractorName;
|
|
4
|
-
label: string;
|
|
5
|
-
packageName: string;
|
|
6
|
-
languages: string[];
|
|
7
|
-
}
|
|
8
|
-
export declare function listExtractorAdapters(): ExtractorAdapter[];
|
|
9
|
-
export declare function getExtractorAdapter(name: string): ExtractorAdapter;
|
|
10
|
-
export declare function normalizeExtractorNames(values: string[] | undefined): ExtractorName[];
|
|
11
|
-
export declare function installCommandForExtractors(packageNames: string[]): string;
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
const ADAPTERS = [
|
|
2
|
-
{
|
|
3
|
-
name: 'python',
|
|
4
|
-
label: 'Python deep extractor',
|
|
5
|
-
packageName: '@kentwynn/kgraph-extractor-python',
|
|
6
|
-
languages: ['python'],
|
|
7
|
-
},
|
|
8
|
-
{
|
|
9
|
-
name: 'jvm',
|
|
10
|
-
label: 'JVM deep extractor',
|
|
11
|
-
packageName: '@kentwynn/kgraph-extractor-jvm',
|
|
12
|
-
languages: ['java', 'kotlin'],
|
|
13
|
-
},
|
|
14
|
-
{
|
|
15
|
-
name: 'go',
|
|
16
|
-
label: 'Go deep extractor',
|
|
17
|
-
packageName: '@kentwynn/kgraph-extractor-go',
|
|
18
|
-
languages: ['go'],
|
|
19
|
-
},
|
|
20
|
-
{
|
|
21
|
-
name: 'rust',
|
|
22
|
-
label: 'Rust deep extractor',
|
|
23
|
-
packageName: '@kentwynn/kgraph-extractor-rust',
|
|
24
|
-
languages: ['rust'],
|
|
25
|
-
},
|
|
26
|
-
{
|
|
27
|
-
name: 'c-family',
|
|
28
|
-
label: 'C/C++ deep extractor',
|
|
29
|
-
packageName: '@kentwynn/kgraph-extractor-c-family',
|
|
30
|
-
languages: ['c', 'cpp'],
|
|
31
|
-
},
|
|
32
|
-
{
|
|
33
|
-
name: 'csharp',
|
|
34
|
-
label: 'C# deep extractor',
|
|
35
|
-
packageName: '@kentwynn/kgraph-extractor-csharp',
|
|
36
|
-
languages: ['csharp'],
|
|
37
|
-
},
|
|
38
|
-
].sort((left, right) => left.name.localeCompare(right.name));
|
|
39
|
-
export function listExtractorAdapters() {
|
|
40
|
-
return ADAPTERS;
|
|
41
|
-
}
|
|
42
|
-
export function getExtractorAdapter(name) {
|
|
43
|
-
const adapter = ADAPTERS.find((item) => item.name === name);
|
|
44
|
-
if (!adapter) {
|
|
45
|
-
throw new Error(`Unsupported extractor "${name}". Supported extractors: ${ADAPTERS.map((item) => item.name).join(', ')}`);
|
|
46
|
-
}
|
|
47
|
-
return adapter;
|
|
48
|
-
}
|
|
49
|
-
export function normalizeExtractorNames(values) {
|
|
50
|
-
if (!values || values.length === 0) {
|
|
51
|
-
return [];
|
|
52
|
-
}
|
|
53
|
-
const names = [];
|
|
54
|
-
const seen = new Set();
|
|
55
|
-
for (const value of values) {
|
|
56
|
-
for (const raw of value.split(/[\s,]+/)) {
|
|
57
|
-
const name = raw.trim();
|
|
58
|
-
if (!name || seen.has(name)) {
|
|
59
|
-
continue;
|
|
60
|
-
}
|
|
61
|
-
const adapter = getExtractorAdapter(name);
|
|
62
|
-
seen.add(adapter.name);
|
|
63
|
-
names.push(adapter.name);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
return names;
|
|
67
|
-
}
|
|
68
|
-
export function installCommandForExtractors(packageNames) {
|
|
69
|
-
return `npm install -D ${packageNames.join(' ')}`;
|
|
70
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import type { ExtractorConfig, ExtractorName, KGraphWorkspace } from '../types/config.js';
|
|
2
|
-
export interface ExtractorStatus {
|
|
3
|
-
name: ExtractorName;
|
|
4
|
-
enabled: boolean;
|
|
5
|
-
packageName: string;
|
|
6
|
-
packageInstalled: boolean;
|
|
7
|
-
}
|
|
8
|
-
export declare function listExtractors(workspace: KGraphWorkspace): Promise<ExtractorStatus[]>;
|
|
9
|
-
export declare function addExtractors(workspace: KGraphWorkspace, names: ExtractorName[]): Promise<ExtractorConfig[]>;
|
|
10
|
-
export declare function removeExtractors(workspace: KGraphWorkspace, names: ExtractorName[]): Promise<ExtractorName[]>;
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import { readFile } from 'node:fs/promises';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import { loadConfig, saveConfig } from '../config/config.js';
|
|
4
|
-
import { pathExists } from '../storage/kgraph-paths.js';
|
|
5
|
-
import { getExtractorAdapter } from './extractor-registry.js';
|
|
6
|
-
export async function listExtractors(workspace) {
|
|
7
|
-
const config = await loadConfig(workspace);
|
|
8
|
-
const statuses = await Promise.all(config.extractors.map(async (extractor) => ({
|
|
9
|
-
name: extractor.name,
|
|
10
|
-
enabled: extractor.enabled,
|
|
11
|
-
packageName: extractor.packageName,
|
|
12
|
-
packageInstalled: await isExtractorInstalled(workspace.rootPath, extractor.packageName),
|
|
13
|
-
})));
|
|
14
|
-
return statuses.sort((left, right) => left.name.localeCompare(right.name));
|
|
15
|
-
}
|
|
16
|
-
export async function addExtractors(workspace, names) {
|
|
17
|
-
const config = await loadConfig(workspace);
|
|
18
|
-
const byName = new Map(config.extractors.map((extractor) => [extractor.name, extractor]));
|
|
19
|
-
const changed = [];
|
|
20
|
-
for (const name of names) {
|
|
21
|
-
const adapter = getExtractorAdapter(name);
|
|
22
|
-
const next = {
|
|
23
|
-
name: adapter.name,
|
|
24
|
-
enabled: true,
|
|
25
|
-
packageName: adapter.packageName,
|
|
26
|
-
};
|
|
27
|
-
byName.set(adapter.name, next);
|
|
28
|
-
changed.push(next);
|
|
29
|
-
}
|
|
30
|
-
config.extractors = [...byName.values()].sort((left, right) => left.name.localeCompare(right.name));
|
|
31
|
-
await saveConfig(workspace, config);
|
|
32
|
-
return changed;
|
|
33
|
-
}
|
|
34
|
-
export async function removeExtractors(workspace, names) {
|
|
35
|
-
const config = await loadConfig(workspace);
|
|
36
|
-
const removeNames = new Set(names);
|
|
37
|
-
config.extractors = config.extractors.filter((extractor) => !removeNames.has(extractor.name));
|
|
38
|
-
await saveConfig(workspace, config);
|
|
39
|
-
return [...removeNames].sort((left, right) => left.localeCompare(right));
|
|
40
|
-
}
|
|
41
|
-
async function isExtractorInstalled(rootPath, packageName) {
|
|
42
|
-
const packageJsonPath = path.join(rootPath, 'package.json');
|
|
43
|
-
if (await pathExists(packageJsonPath)) {
|
|
44
|
-
try {
|
|
45
|
-
const raw = await readFile(packageJsonPath, 'utf8');
|
|
46
|
-
const pkg = JSON.parse(raw);
|
|
47
|
-
if (pkg.dependencies?.[packageName] ||
|
|
48
|
-
pkg.devDependencies?.[packageName] ||
|
|
49
|
-
pkg.optionalDependencies?.[packageName]) {
|
|
50
|
-
return true;
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
catch {
|
|
54
|
-
return false;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
return pathExists(path.join(rootPath, 'node_modules', ...packageName.split('/'), 'package.json'));
|
|
58
|
-
}
|