@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
package/README.md
CHANGED
|
@@ -100,17 +100,14 @@ kgraph init
|
|
|
100
100
|
# 2. Optional: connect AI tools so they know the KGraph workflow
|
|
101
101
|
kgraph integrate add codex copilot cursor claude-code gemini windsurf cline
|
|
102
102
|
|
|
103
|
-
# 3.
|
|
104
|
-
kgraph extractor add jvm python
|
|
105
|
-
|
|
106
|
-
# 4. Run the normal workflow for a topic
|
|
103
|
+
# 3. Run the normal workflow for a topic
|
|
107
104
|
kgraph "auth token refresh"
|
|
108
105
|
|
|
109
|
-
#
|
|
106
|
+
# 4. Check health if something feels off
|
|
110
107
|
kgraph doctor
|
|
111
108
|
```
|
|
112
109
|
|
|
113
|
-
`kgraph init` now scans once, then prints relevant next steps. When KGraph can detect likely AI tools on the machine, it recommends matching integrations.
|
|
110
|
+
`kgraph init` now scans once, then prints relevant next steps. When KGraph can detect likely AI tools on the machine, it recommends matching integrations.
|
|
114
111
|
|
|
115
112
|
After useful AI work, assistants save durable runtime-capture notes into `.kgraph/inbox/`. These notes are not project documentation; they are KGraph input files that the next `kgraph` run processes automatically. You can also process them directly with `kgraph update`.
|
|
116
113
|
|
|
@@ -263,18 +260,6 @@ New integrations default to `always` mode because coding agents often under-clas
|
|
|
263
260
|
| Codex | `AGENTS.md`, `.agents/skills/kgraph/SKILL.md` |
|
|
264
261
|
| GitHub Copilot | `.github/copilot-instructions.md`, `.github/prompts/*` |
|
|
265
262
|
| Cursor | `.cursor/rules/kgraph.mdc` |
|
|
266
|
-
|
|
267
|
-
## Optional Deep Extractors
|
|
268
|
-
|
|
269
|
-
KGraph ships with built-in extractors for several languages, but TypeScript and JavaScript still have the deepest built-in analysis today. For languages such as Java, Kotlin, Python, Go, Rust, C/C++, and C#, `kgraph init` can recommend optional deep extractors when those languages are detected in the repository.
|
|
270
|
-
|
|
271
|
-
```bash
|
|
272
|
-
kgraph extractor list
|
|
273
|
-
kgraph extractor add jvm python
|
|
274
|
-
kgraph extractor remove jvm
|
|
275
|
-
```
|
|
276
|
-
|
|
277
|
-
`extractor add` writes extractor configuration into `.kgraph/config.yaml` and prints the exact `npm install -D ...` command for the matching optional packages. This is explicit on purpose: KGraph recommends install commands by default rather than silently changing package-manager state.
|
|
278
263
|
| Claude Code | `CLAUDE.md`, `.claude/commands/*` |
|
|
279
264
|
| Gemini CLI | `GEMINI.md` |
|
|
280
265
|
| Windsurf | `.windsurf/rules/kgraph.md` |
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Command } from
|
|
2
|
-
import type { ContextResponse } from
|
|
1
|
+
import type { Command } from 'commander';
|
|
2
|
+
import type { ContextResponse } from '../../types/cognition.js';
|
|
3
3
|
export declare function registerContextCommand(program: Command): void;
|
|
4
4
|
export declare function renderContextMarkdown(response: ContextResponse): string;
|
|
@@ -1,43 +1,102 @@
|
|
|
1
|
-
import { loadConfig } from
|
|
2
|
-
import { queryContext } from
|
|
3
|
-
import { assertWorkspace } from
|
|
4
|
-
import { mapsExist, readMaps } from
|
|
5
|
-
import { KGraphError, runCommand } from
|
|
1
|
+
import { loadConfig } from '../../config/config.js';
|
|
2
|
+
import { queryContext } from '../../context/context-query.js';
|
|
3
|
+
import { assertWorkspace } from '../../storage/kgraph-paths.js';
|
|
4
|
+
import { mapsExist, readMaps } from '../../storage/map-store.js';
|
|
5
|
+
import { KGraphError, runCommand } from '../errors.js';
|
|
6
6
|
export function registerContextCommand(program) {
|
|
7
7
|
program
|
|
8
|
-
.command(
|
|
9
|
-
.description(
|
|
10
|
-
.option(
|
|
8
|
+
.command('context <query>')
|
|
9
|
+
.description('Return compact repo context for a query')
|
|
10
|
+
.option('--json', 'Print JSON output')
|
|
11
11
|
.action((query, options) => runCommand(async () => {
|
|
12
12
|
if (!query.trim()) {
|
|
13
|
-
throw new KGraphError(
|
|
13
|
+
throw new KGraphError('Query cannot be empty.');
|
|
14
14
|
}
|
|
15
15
|
const workspace = await assertWorkspace(process.cwd());
|
|
16
16
|
if (!(await mapsExist(workspace))) {
|
|
17
|
-
throw new KGraphError(
|
|
17
|
+
throw new KGraphError('KGraph maps are missing. Run `kgraph scan` first.');
|
|
18
18
|
}
|
|
19
19
|
const config = await loadConfig(workspace);
|
|
20
20
|
const maps = await readMaps(workspace);
|
|
21
21
|
const response = await queryContext(workspace, config, maps, query);
|
|
22
|
-
console.log(options.json
|
|
22
|
+
console.log(options.json
|
|
23
|
+
? JSON.stringify(response, null, 2)
|
|
24
|
+
: renderContextMarkdown(response));
|
|
23
25
|
}));
|
|
24
26
|
}
|
|
25
27
|
export function renderContextMarkdown(response) {
|
|
26
28
|
const lines = [`# KGraph Context`, ``, `Query: ${response.query}`, ``];
|
|
27
|
-
lines.push(
|
|
28
|
-
lines.push(...formatList(response.matchedDomains.map((item) => `- ${item.item.name} (${item.reasons.join(
|
|
29
|
-
lines.push(
|
|
30
|
-
lines.push(...formatList(response.relevantFiles.map((item) =>
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
29
|
+
lines.push('## Matched Domains', '');
|
|
30
|
+
lines.push(...formatList(response.matchedDomains.map((item) => `- ${item.item.name} (${item.reasons.join(', ')})`)));
|
|
31
|
+
lines.push('', '## Relevant Files', '');
|
|
32
|
+
lines.push(...formatList(response.relevantFiles.map((item) => {
|
|
33
|
+
const f = item.item;
|
|
34
|
+
const meta = [
|
|
35
|
+
f.language,
|
|
36
|
+
f.tokenEstimate ? `~${f.tokenEstimate} tokens` : '',
|
|
37
|
+
]
|
|
38
|
+
.filter(Boolean)
|
|
39
|
+
.join(', ');
|
|
40
|
+
return `- ${f.path}${meta ? ` [${meta}]` : ''}`;
|
|
41
|
+
})));
|
|
42
|
+
lines.push('', '## Relevant Symbols', '');
|
|
43
|
+
lines.push(...formatList(response.relevantSymbols.map((item) => {
|
|
44
|
+
const s = item.item;
|
|
45
|
+
const kindInfo = [s.kind, s.parentName].filter(Boolean).join(', ');
|
|
46
|
+
const lineRange = s.startLine != null && s.endLine != null
|
|
47
|
+
? `:${s.startLine}-${s.endLine}`
|
|
48
|
+
: '';
|
|
49
|
+
return `- ${s.name} (${kindInfo}) in ${s.filePath}${lineRange}`;
|
|
50
|
+
})));
|
|
51
|
+
lines.push('', '## Relevant Cognition', '');
|
|
34
52
|
lines.push(...formatList(response.relevantCognition.map((item) => `- ${item.item.title} [${item.item.referencesStatus}]`)));
|
|
35
|
-
lines.push(
|
|
36
|
-
lines.push(...
|
|
37
|
-
lines.push(
|
|
53
|
+
lines.push('', '## Relationships', '');
|
|
54
|
+
lines.push(...formatGroupedRelationships(response.relationships));
|
|
55
|
+
lines.push('', '## Nearby Symbols (1-hop imports)', '');
|
|
56
|
+
lines.push(...formatList((response.nearbySymbols ?? []).map((s) => {
|
|
57
|
+
const kindInfo = [s.kind, s.parentName].filter(Boolean).join(', ');
|
|
58
|
+
const lineRange = s.startLine != null && s.endLine != null
|
|
59
|
+
? `:${s.startLine}-${s.endLine}`
|
|
60
|
+
: '';
|
|
61
|
+
return `- ${s.name} (${kindInfo}) in ${s.filePath}${lineRange}`;
|
|
62
|
+
})));
|
|
63
|
+
lines.push('', '## Stale References', '');
|
|
38
64
|
lines.push(...formatList(response.staleReferences.map((ref) => `- ${ref}`)));
|
|
39
|
-
return lines.join(
|
|
65
|
+
return lines.join('\n');
|
|
66
|
+
}
|
|
67
|
+
function formatGroupedRelationships(relationships) {
|
|
68
|
+
const imports = relationships.filter((r) => r.relationshipType === 'import');
|
|
69
|
+
const calls = relationships.filter((r) => r.relationshipType === 'calls');
|
|
70
|
+
const contains = relationships.filter((r) => r.relationshipType === 'symbol-contains');
|
|
71
|
+
const other = relationships.filter((r) => r.relationshipType !== 'import' &&
|
|
72
|
+
r.relationshipType !== 'calls' &&
|
|
73
|
+
r.relationshipType !== 'symbol-contains' &&
|
|
74
|
+
r.relationshipType !== 'mentions' &&
|
|
75
|
+
r.relationshipType !== 'belongs-to-domain' &&
|
|
76
|
+
r.relationshipType !== 'stale-reference');
|
|
77
|
+
const lines = [];
|
|
78
|
+
if (imports.length > 0) {
|
|
79
|
+
lines.push('Imports:');
|
|
80
|
+
for (const r of imports)
|
|
81
|
+
lines.push(` ${r.sourceId} → ${r.targetId}`);
|
|
82
|
+
}
|
|
83
|
+
if (calls.length > 0) {
|
|
84
|
+
lines.push('Calls:');
|
|
85
|
+
for (const r of calls)
|
|
86
|
+
lines.push(` ${r.sourceId} → ${r.targetId}`);
|
|
87
|
+
}
|
|
88
|
+
if (contains.length > 0) {
|
|
89
|
+
lines.push('Contains:');
|
|
90
|
+
for (const r of contains)
|
|
91
|
+
lines.push(` ${r.sourceId} contains ${r.targetId}`);
|
|
92
|
+
}
|
|
93
|
+
if (other.length > 0) {
|
|
94
|
+
lines.push('Other:');
|
|
95
|
+
for (const r of other)
|
|
96
|
+
lines.push(` ${r.sourceId} ${r.relationshipType} ${r.targetId}`);
|
|
97
|
+
}
|
|
98
|
+
return lines.length > 0 ? lines : ['- None'];
|
|
40
99
|
}
|
|
41
100
|
function formatList(items) {
|
|
42
|
-
return items.length > 0 ? items : [
|
|
101
|
+
return items.length > 0 ? items : ['- None'];
|
|
43
102
|
}
|
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
import { loadConfig, writeDefaultConfig } from '../../config/config.js';
|
|
2
|
-
import { installCommandForExtractors } from '../../extractors/extractor-registry.js';
|
|
3
|
-
import { addExtractors } from '../../extractors/extractor-store.js';
|
|
4
2
|
import { normalizeIntegrationNames } from '../../integrations/integration-registry.js';
|
|
5
3
|
import { addIntegrations } from '../../integrations/integration-store.js';
|
|
6
4
|
import { scanRepository } from '../../scanner/repo-scanner.js';
|
|
7
5
|
import { ensureWorkspace } from '../../storage/kgraph-paths.js';
|
|
8
6
|
import { readMaps, writeMaps } from '../../storage/map-store.js';
|
|
9
7
|
import { KGraphError, runCommand } from '../errors.js';
|
|
10
|
-
import {
|
|
11
|
-
import { detectMachineIntegrationRecommendations,
|
|
8
|
+
import { promptForInitIntegrations, shouldPromptForInitIntegrations, } from '../init-prompt.js';
|
|
9
|
+
import { detectMachineIntegrationRecommendations, recommendedIntegrationsForInit, } from '../init-recommendations.js';
|
|
12
10
|
import { renderInitSummary } from '../init-summary.js';
|
|
13
11
|
export function registerInitCommand(program) {
|
|
14
12
|
program
|
|
@@ -48,10 +46,6 @@ export function registerInitCommand(program) {
|
|
|
48
46
|
configuredIntegrations: config.integrations,
|
|
49
47
|
detectedIntegrations: detectedMachineIntegrations,
|
|
50
48
|
});
|
|
51
|
-
let recommendedExtractors = recommendedExtractorsForInit({
|
|
52
|
-
files: result.files,
|
|
53
|
-
configuredExtractors: config.extractors,
|
|
54
|
-
});
|
|
55
49
|
if (shouldPromptForInitIntegrations({
|
|
56
50
|
explicitIntegrationsRequested: names.length > 0,
|
|
57
51
|
configuredIntegrations: config.integrations,
|
|
@@ -67,28 +61,11 @@ export function registerInitCommand(program) {
|
|
|
67
61
|
});
|
|
68
62
|
}
|
|
69
63
|
}
|
|
70
|
-
if (shouldPromptForInitExtractors({
|
|
71
|
-
configuredExtractors: config.extractors,
|
|
72
|
-
})) {
|
|
73
|
-
const selected = await promptForInitExtractors(recommendedExtractors);
|
|
74
|
-
if (selected.length > 0) {
|
|
75
|
-
const changed = await addExtractors(workspace, selected);
|
|
76
|
-
console.log(`Configured extractors: ${changed.map((item) => item.name).join(', ')}`);
|
|
77
|
-
console.log(`Install packages: ${installCommandForExtractors(changed.map((item) => item.packageName))}`);
|
|
78
|
-
config = await loadConfig(workspace);
|
|
79
|
-
recommendedExtractors = recommendedExtractorsForInit({
|
|
80
|
-
files: result.files,
|
|
81
|
-
configuredExtractors: config.extractors,
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
64
|
console.log('');
|
|
86
65
|
console.log(renderInitSummary({
|
|
87
66
|
files: result.files,
|
|
88
67
|
integrations: config.integrations,
|
|
89
68
|
recommendedIntegrations,
|
|
90
|
-
extractors: config.extractors,
|
|
91
|
-
recommendedExtractors,
|
|
92
69
|
}));
|
|
93
70
|
}));
|
|
94
71
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { updateCognition } from '../../cognition/cognition-updater.js';
|
|
2
|
-
import { refreshCognitionReferenceStatuses } from '../../cognition/cognition-updater.js';
|
|
1
|
+
import { refreshCognitionReferenceStatuses, updateCognition, } from '../../cognition/cognition-updater.js';
|
|
3
2
|
import { loadConfig } from '../../config/config.js';
|
|
4
3
|
import { queryContext } from '../../context/context-query.js';
|
|
5
4
|
import { scanRepository } from '../../scanner/repo-scanner.js';
|
|
@@ -35,6 +34,7 @@ export async function runDefaultWorkflow(query) {
|
|
|
35
34
|
console.log(renderWorkflowBanner({
|
|
36
35
|
files: scan.files.length,
|
|
37
36
|
symbols: scan.symbols.length,
|
|
37
|
+
skippedFiles: scan.skippedFiles,
|
|
38
38
|
cognitionNotes: update.processed.length,
|
|
39
39
|
integrations: config.integrations.map((integration) => ({
|
|
40
40
|
name: integration.name,
|
package/dist/cli/help.d.ts
CHANGED
package/dist/cli/help.js
CHANGED
|
@@ -47,11 +47,6 @@ export function renderRootHelp(useColor = supportsColor()) {
|
|
|
47
47
|
command('integrate remove cursor', 'Remove KGraph-managed instruction blocks'),
|
|
48
48
|
command('--mode smart|always|manual|off', 'Control automatic KGraph involvement per integration'),
|
|
49
49
|
'',
|
|
50
|
-
theme.bold('Extractors'),
|
|
51
|
-
command('extractor list', 'Show configured optional deep extractors'),
|
|
52
|
-
command('extractor add jvm python', 'Configure optional deep extractors and print install commands'),
|
|
53
|
-
command('extractor remove jvm', 'Remove extractor configuration'),
|
|
54
|
-
'',
|
|
55
50
|
theme.bold('Options'),
|
|
56
51
|
command('-V, --version', 'Show version'),
|
|
57
52
|
command('-h, --help', 'Show this help'),
|
|
@@ -82,7 +77,10 @@ export function renderWorkflowBanner(stats, useColor = supportsColor()) {
|
|
|
82
77
|
` ${theme.bold('KGraph')} ${theme.dim('repo intelligence refreshed')}`,
|
|
83
78
|
'',
|
|
84
79
|
theme.bold('Refresh Complete'),
|
|
85
|
-
command('files', String(stats.files)
|
|
80
|
+
command('files', String(stats.files) +
|
|
81
|
+
(stats.skippedFiles
|
|
82
|
+
? ` (${stats.skippedFiles} unchanged, skipped)`
|
|
83
|
+
: '')),
|
|
86
84
|
command('symbols', String(stats.symbols)),
|
|
87
85
|
command('cognition notes processed', String(stats.cognitionNotes)),
|
|
88
86
|
command('integration modes', integrationLine),
|
package/dist/cli/index.js
CHANGED
|
@@ -5,7 +5,6 @@ import { createRequire } from 'node:module';
|
|
|
5
5
|
import { fileURLToPath } from 'node:url';
|
|
6
6
|
import { registerContextCommand } from './commands/context.js';
|
|
7
7
|
import { registerDoctorCommand } from './commands/doctor.js';
|
|
8
|
-
import { registerExtractorCommand } from './commands/extractor.js';
|
|
9
8
|
import { registerHistoryCommand } from './commands/history.js';
|
|
10
9
|
import { registerImpactCommand } from './commands/impact.js';
|
|
11
10
|
import { registerInitCommand } from './commands/init.js';
|
|
@@ -44,7 +43,6 @@ export function createProgram() {
|
|
|
44
43
|
registerUpdateCommand(program);
|
|
45
44
|
registerContextCommand(program);
|
|
46
45
|
registerImpactCommand(program);
|
|
47
|
-
registerExtractorCommand(program);
|
|
48
46
|
registerIntegrateCommand(program);
|
|
49
47
|
registerVisualizeCommand(program);
|
|
50
48
|
registerHistoryCommand(program);
|
|
@@ -1,13 +1,8 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type {
|
|
1
|
+
import type { IntegrationConfig, IntegrationName } from '../types/config.js';
|
|
2
|
+
import type { InitIntegrationRecommendation } from './init-recommendations.js';
|
|
3
3
|
export declare function shouldPromptForInitIntegrations(options: {
|
|
4
4
|
explicitIntegrationsRequested: boolean;
|
|
5
5
|
configuredIntegrations: Pick<IntegrationConfig, 'name'>[];
|
|
6
6
|
interactive?: boolean;
|
|
7
7
|
}): boolean;
|
|
8
8
|
export declare function promptForInitIntegrations(recommendations: InitIntegrationRecommendation[]): Promise<IntegrationName[]>;
|
|
9
|
-
export declare function shouldPromptForInitExtractors(options: {
|
|
10
|
-
configuredExtractors: Pick<ExtractorConfig, 'name'>[];
|
|
11
|
-
interactive?: boolean;
|
|
12
|
-
}): boolean;
|
|
13
|
-
export declare function promptForInitExtractors(recommendations: InitExtractorRecommendation[]): Promise<ExtractorName[]>;
|
package/dist/cli/init-prompt.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import * as clack from '@clack/prompts';
|
|
2
|
-
import { listExtractorAdapters } from '../extractors/extractor-registry.js';
|
|
3
2
|
import { listIntegrationAdapters } from '../integrations/integration-registry.js';
|
|
4
3
|
// --- Integration prompt ---
|
|
5
4
|
export function shouldPromptForInitIntegrations(options) {
|
|
@@ -57,68 +56,6 @@ export async function promptForInitIntegrations(recommendations) {
|
|
|
57
56
|
}
|
|
58
57
|
return selected;
|
|
59
58
|
}
|
|
60
|
-
// --- Extractor prompt ---
|
|
61
|
-
export function shouldPromptForInitExtractors(options) {
|
|
62
|
-
const interactive = options.interactive ?? isInteractiveTerminal();
|
|
63
|
-
return interactive && options.configuredExtractors.length === 0;
|
|
64
|
-
}
|
|
65
|
-
export async function promptForInitExtractors(recommendations) {
|
|
66
|
-
const recNames = recommendations.map((item) => item.name);
|
|
67
|
-
const hasRecommendations = recommendations.length > 0;
|
|
68
|
-
const action = await clack.select({
|
|
69
|
-
message: 'Optional deep language extractors',
|
|
70
|
-
options: [
|
|
71
|
-
...(hasRecommendations
|
|
72
|
-
? [
|
|
73
|
-
{
|
|
74
|
-
value: 'recommended',
|
|
75
|
-
label: `Use recommended (${recNames.join(', ')})`,
|
|
76
|
-
hint: recommendations
|
|
77
|
-
.map((item) => `${item.name} for ${item.languages.join(', ')}`)
|
|
78
|
-
.join('; '),
|
|
79
|
-
},
|
|
80
|
-
]
|
|
81
|
-
: []),
|
|
82
|
-
{
|
|
83
|
-
value: 'custom',
|
|
84
|
-
label: 'Custom selection',
|
|
85
|
-
hint: hasRecommendations
|
|
86
|
-
? undefined
|
|
87
|
-
: 'no language-specific extractors detected; pick manually',
|
|
88
|
-
},
|
|
89
|
-
{ value: 'skip', label: 'Skip' },
|
|
90
|
-
],
|
|
91
|
-
});
|
|
92
|
-
if (clack.isCancel(action) || action === 'skip') {
|
|
93
|
-
return [];
|
|
94
|
-
}
|
|
95
|
-
if (action === 'recommended') {
|
|
96
|
-
return recNames;
|
|
97
|
-
}
|
|
98
|
-
const recommendedNames = new Set(recNames);
|
|
99
|
-
const otherAdapters = listExtractorAdapters().filter((adapter) => !recommendedNames.has(adapter.name));
|
|
100
|
-
const allOptions = [
|
|
101
|
-
...recommendations.map((rec) => ({
|
|
102
|
-
value: rec.name,
|
|
103
|
-
label: rec.name,
|
|
104
|
-
hint: `${rec.languages.join(', ')} — ${rec.packageName} (recommended)`,
|
|
105
|
-
})),
|
|
106
|
-
...otherAdapters.map((adapter) => ({
|
|
107
|
-
value: adapter.name,
|
|
108
|
-
label: adapter.name,
|
|
109
|
-
hint: `${adapter.languages.join(', ')} — ${adapter.packageName}`,
|
|
110
|
-
})),
|
|
111
|
-
];
|
|
112
|
-
const selected = await clack.multiselect({
|
|
113
|
-
message: 'Select extractors (space to toggle, enter to confirm)',
|
|
114
|
-
options: allOptions,
|
|
115
|
-
required: false,
|
|
116
|
-
});
|
|
117
|
-
if (clack.isCancel(selected)) {
|
|
118
|
-
return [];
|
|
119
|
-
}
|
|
120
|
-
return selected;
|
|
121
|
-
}
|
|
122
59
|
function isInteractiveTerminal() {
|
|
123
60
|
return Boolean(process.stdin.isTTY) && Boolean(process.stdout.isTTY);
|
|
124
61
|
}
|
|
@@ -1,14 +1,8 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type { RepositoryFile } from '../types/maps.js';
|
|
1
|
+
import type { IntegrationConfig, IntegrationName } from '../types/config.js';
|
|
3
2
|
export interface InitIntegrationRecommendation {
|
|
4
3
|
name: IntegrationName;
|
|
5
4
|
reason: string;
|
|
6
5
|
}
|
|
7
|
-
export interface InitExtractorRecommendation {
|
|
8
|
-
name: ExtractorName;
|
|
9
|
-
packageName: string;
|
|
10
|
-
languages: string[];
|
|
11
|
-
}
|
|
12
6
|
interface MachineDetectionContext {
|
|
13
7
|
env?: NodeJS.ProcessEnv;
|
|
14
8
|
homeDir?: string;
|
|
@@ -22,10 +16,5 @@ export declare function recommendedIntegrationsForInit(options: {
|
|
|
22
16
|
configuredIntegrations: Pick<IntegrationConfig, 'name'>[];
|
|
23
17
|
detectedIntegrations: InitIntegrationRecommendation[];
|
|
24
18
|
}): InitIntegrationRecommendation[];
|
|
25
|
-
export declare function recommendedExtractorsForInit(options: {
|
|
26
|
-
files: RepositoryFile[];
|
|
27
|
-
configuredExtractors: Pick<ExtractorConfig, 'name'>[];
|
|
28
|
-
}): InitExtractorRecommendation[];
|
|
29
19
|
export declare function integrationSetupCommand(recommendations: InitIntegrationRecommendation[]): string | undefined;
|
|
30
|
-
export declare function extractorSetupCommands(recommendations: InitExtractorRecommendation[]): string[];
|
|
31
20
|
export {};
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { readdir } from 'node:fs/promises';
|
|
2
2
|
import os from 'node:os';
|
|
3
3
|
import path from 'node:path';
|
|
4
|
-
import { installCommandForExtractors, listExtractorAdapters, } from '../extractors/extractor-registry.js';
|
|
5
4
|
import { pathExists } from '../storage/kgraph-paths.js';
|
|
6
5
|
export async function detectMachineIntegrationRecommendations(context = {}) {
|
|
7
6
|
const env = context.env ?? process.env;
|
|
@@ -40,34 +39,12 @@ export function recommendedIntegrationsForInit(options) {
|
|
|
40
39
|
const configured = new Set(options.configuredIntegrations.map((item) => item.name));
|
|
41
40
|
return options.detectedIntegrations.filter((item) => !configured.has(item.name));
|
|
42
41
|
}
|
|
43
|
-
export function recommendedExtractorsForInit(options) {
|
|
44
|
-
const configured = new Set(options.configuredExtractors.map((item) => item.name));
|
|
45
|
-
const detectedLanguages = new Set(options.files.map((file) => file.language));
|
|
46
|
-
return listExtractorAdapters()
|
|
47
|
-
.filter((adapter) => !configured.has(adapter.name))
|
|
48
|
-
.map((adapter) => ({
|
|
49
|
-
name: adapter.name,
|
|
50
|
-
packageName: adapter.packageName,
|
|
51
|
-
languages: adapter.languages.filter((language) => detectedLanguages.has(language)),
|
|
52
|
-
}))
|
|
53
|
-
.filter((adapter) => adapter.languages.length > 0)
|
|
54
|
-
.sort((left, right) => left.name.localeCompare(right.name));
|
|
55
|
-
}
|
|
56
42
|
export function integrationSetupCommand(recommendations) {
|
|
57
43
|
if (recommendations.length === 0) {
|
|
58
44
|
return undefined;
|
|
59
45
|
}
|
|
60
46
|
return `kgraph integrate add ${recommendations.map((item) => item.name).join(' ')}`;
|
|
61
47
|
}
|
|
62
|
-
export function extractorSetupCommands(recommendations) {
|
|
63
|
-
if (recommendations.length === 0) {
|
|
64
|
-
return [];
|
|
65
|
-
}
|
|
66
|
-
return [
|
|
67
|
-
`kgraph extractor add ${recommendations.map((item) => item.name).join(' ')}`,
|
|
68
|
-
installCommandForExtractors(recommendations.map((item) => item.packageName)),
|
|
69
|
-
];
|
|
70
|
-
}
|
|
71
48
|
async function hasVsCodeExtension(prefixes, context) {
|
|
72
49
|
const exists = context.exists ?? pathExists;
|
|
73
50
|
const readDir = context.readDir ?? defaultReadDir;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { IntegrationConfig } from '../types/config.js';
|
|
2
2
|
import type { RepositoryFile } from '../types/maps.js';
|
|
3
|
-
import { type
|
|
3
|
+
import { type InitIntegrationRecommendation } from './init-recommendations.js';
|
|
4
4
|
type CoverageLevel = 'deep' | 'basic' | 'generic';
|
|
5
5
|
export interface InitLanguageSummary {
|
|
6
6
|
language: string;
|
|
@@ -13,7 +13,5 @@ export declare function renderInitSummary(options: {
|
|
|
13
13
|
files: RepositoryFile[];
|
|
14
14
|
integrations: Pick<IntegrationConfig, 'name' | 'enabled' | 'mode'>[];
|
|
15
15
|
recommendedIntegrations: InitIntegrationRecommendation[];
|
|
16
|
-
extractors: Pick<ExtractorConfig, 'name' | 'enabled' | 'packageName'>[];
|
|
17
|
-
recommendedExtractors: InitExtractorRecommendation[];
|
|
18
16
|
}): string;
|
|
19
17
|
export {};
|
package/dist/cli/init-summary.js
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { integrationSetupCommand, } from './init-recommendations.js';
|
|
2
2
|
const LANGUAGE_PRESENTATION = {
|
|
3
3
|
javascript: { label: 'JavaScript', coverage: 'deep' },
|
|
4
4
|
javascriptreact: { label: 'JavaScript', coverage: 'deep' },
|
|
5
5
|
typescript: { label: 'TypeScript', coverage: 'deep' },
|
|
6
6
|
typescriptreact: { label: 'TypeScript', coverage: 'deep' },
|
|
7
|
-
python: { label: 'Python', coverage: '
|
|
8
|
-
go: { label: 'Go', coverage: '
|
|
9
|
-
rust: { label: 'Rust', coverage: '
|
|
10
|
-
java: { label: 'Java', coverage: '
|
|
11
|
-
kotlin: { label: 'Kotlin', coverage: '
|
|
12
|
-
c: { label: 'C', coverage: '
|
|
13
|
-
cpp: { label: 'C++', coverage: '
|
|
14
|
-
csharp: { label: 'C#', coverage: '
|
|
7
|
+
python: { label: 'Python', coverage: 'deep' },
|
|
8
|
+
go: { label: 'Go', coverage: 'deep' },
|
|
9
|
+
rust: { label: 'Rust', coverage: 'deep' },
|
|
10
|
+
java: { label: 'Java', coverage: 'deep' },
|
|
11
|
+
kotlin: { label: 'Kotlin', coverage: 'deep' },
|
|
12
|
+
c: { label: 'C', coverage: 'deep' },
|
|
13
|
+
cpp: { label: 'C++', coverage: 'deep' },
|
|
14
|
+
csharp: { label: 'C#', coverage: 'deep' },
|
|
15
15
|
yaml: { label: 'YAML', coverage: 'generic' },
|
|
16
16
|
json: { label: 'JSON', coverage: 'generic' },
|
|
17
17
|
toml: { label: 'TOML', coverage: 'generic' },
|
|
@@ -50,12 +50,6 @@ export function summarizeInitLanguages(files) {
|
|
|
50
50
|
}
|
|
51
51
|
export function renderInitSummary(options) {
|
|
52
52
|
const languages = summarizeInitLanguages(options.files);
|
|
53
|
-
const recommendedExtractorByLanguage = new Map();
|
|
54
|
-
for (const extractor of options.recommendedExtractors) {
|
|
55
|
-
for (const language of extractor.languages) {
|
|
56
|
-
recommendedExtractorByLanguage.set(language, extractor.name);
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
53
|
const lines = ['KGraph Init Summary', ''];
|
|
60
54
|
lines.push('AI integrations');
|
|
61
55
|
if (options.recommendedIntegrations.length > 0) {
|
|
@@ -76,23 +70,7 @@ export function renderInitSummary(options) {
|
|
|
76
70
|
}
|
|
77
71
|
else {
|
|
78
72
|
for (const language of languages) {
|
|
79
|
-
|
|
80
|
-
lines.push(` ${language.label}: ${formatFileCount(language.fileCount)}, ${coverageDescription(language.coverage)}${recommendedExtractor ? `; recommended extractor: ${recommendedExtractor}` : ''}`);
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
lines.push('');
|
|
84
|
-
lines.push('Optional extractors');
|
|
85
|
-
if (options.extractors.length === 0) {
|
|
86
|
-
lines.push(' configured: none');
|
|
87
|
-
}
|
|
88
|
-
else {
|
|
89
|
-
for (const extractor of options.extractors) {
|
|
90
|
-
lines.push(` configured: ${extractor.name}: ${extractor.enabled ? extractor.packageName : 'off'}`);
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
if (options.recommendedExtractors.length > 0) {
|
|
94
|
-
for (const extractor of options.recommendedExtractors) {
|
|
95
|
-
lines.push(` recommended: ${extractor.name} for ${extractor.languages.map(humanizeLanguage).join(', ')} (${extractor.packageName})`);
|
|
73
|
+
lines.push(` ${language.label}: ${formatFileCount(language.fileCount)}, ${coverageDescription(language.coverage)}`);
|
|
96
74
|
}
|
|
97
75
|
}
|
|
98
76
|
lines.push('');
|
|
@@ -105,9 +83,6 @@ export function renderInitSummary(options) {
|
|
|
105
83
|
else if (options.integrations.length === 0) {
|
|
106
84
|
lines.push(' kgraph integrate add <agent> Optional: connect an AI tool');
|
|
107
85
|
}
|
|
108
|
-
for (const command of extractorSetupCommands(options.recommendedExtractors)) {
|
|
109
|
-
lines.push(` ${command}`);
|
|
110
|
-
}
|
|
111
86
|
lines.push(' kgraph doctor Check workspace health');
|
|
112
87
|
return lines.join('\n');
|
|
113
88
|
}
|
package/dist/config/config.js
CHANGED
|
@@ -71,7 +71,6 @@ export const DEFAULT_CONFIG = {
|
|
|
71
71
|
maxContextItems: 8,
|
|
72
72
|
domainHints: {},
|
|
73
73
|
integrations: [],
|
|
74
|
-
extractors: [],
|
|
75
74
|
};
|
|
76
75
|
export async function writeDefaultConfig(workspace) {
|
|
77
76
|
if (await pathExists(workspace.configPath)) {
|
|
@@ -115,7 +114,6 @@ export function normalizeConfig(config) {
|
|
|
115
114
|
? config.domainHints
|
|
116
115
|
: {},
|
|
117
116
|
integrations: normalizeIntegrations(config.integrations),
|
|
118
|
-
extractors: normalizeExtractors(config.extractors),
|
|
119
117
|
};
|
|
120
118
|
}
|
|
121
119
|
function mergeUnique(base, extra) {
|
|
@@ -163,34 +161,3 @@ function normalizeIntegrationMode(value) {
|
|
|
163
161
|
? value
|
|
164
162
|
: 'smart';
|
|
165
163
|
}
|
|
166
|
-
function normalizeExtractors(value) {
|
|
167
|
-
if (!Array.isArray(value)) {
|
|
168
|
-
return [];
|
|
169
|
-
}
|
|
170
|
-
const seen = new Set();
|
|
171
|
-
const extractors = [];
|
|
172
|
-
for (const item of value) {
|
|
173
|
-
if (!item || typeof item !== 'object') {
|
|
174
|
-
continue;
|
|
175
|
-
}
|
|
176
|
-
const candidate = item;
|
|
177
|
-
if (typeof candidate.name !== 'string' ||
|
|
178
|
-
typeof candidate.packageName !== 'string' ||
|
|
179
|
-
seen.has(candidate.name)) {
|
|
180
|
-
continue;
|
|
181
|
-
}
|
|
182
|
-
if (!isExtractorName(candidate.name)) {
|
|
183
|
-
continue;
|
|
184
|
-
}
|
|
185
|
-
seen.add(candidate.name);
|
|
186
|
-
extractors.push({
|
|
187
|
-
name: candidate.name,
|
|
188
|
-
enabled: candidate.enabled !== false,
|
|
189
|
-
packageName: candidate.packageName,
|
|
190
|
-
});
|
|
191
|
-
}
|
|
192
|
-
return extractors;
|
|
193
|
-
}
|
|
194
|
-
function isExtractorName(value) {
|
|
195
|
-
return ['c-family', 'csharp', 'go', 'jvm', 'python', 'rust'].includes(value);
|
|
196
|
-
}
|
|
@@ -73,6 +73,28 @@ export async function queryContext(workspace, config, maps, query) {
|
|
|
73
73
|
.filter((s) => !symbolNames.has(s))
|
|
74
74
|
.map((ref) => `${note.title}: ${ref}`),
|
|
75
75
|
]);
|
|
76
|
+
// Collect nearby symbols: exported symbols from files 1-hop imported by matched files
|
|
77
|
+
const matchedFilePaths = new Set([
|
|
78
|
+
...relevantFiles.map((f) => f.item.path),
|
|
79
|
+
...relevantSymbols.map((s) => s.item.filePath),
|
|
80
|
+
]);
|
|
81
|
+
const matchedSymbolIds = new Set(relevantSymbols.map((s) => s.item.id));
|
|
82
|
+
const importedFilePaths = new Set();
|
|
83
|
+
for (const dep of maps.dependencyMap.dependencies) {
|
|
84
|
+
if (dep.kind === 'local' &&
|
|
85
|
+
dep.resolvedFile &&
|
|
86
|
+
matchedFilePaths.has(dep.fromFile)) {
|
|
87
|
+
importedFilePaths.add(dep.resolvedFile);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
// Remove files already in the matched set
|
|
91
|
+
for (const p of matchedFilePaths)
|
|
92
|
+
importedFilePaths.delete(p);
|
|
93
|
+
const nearbySymbols = maps.symbolMap.symbols
|
|
94
|
+
.filter((s) => s.exported &&
|
|
95
|
+
importedFilePaths.has(s.filePath) &&
|
|
96
|
+
!matchedSymbolIds.has(s.id))
|
|
97
|
+
.slice(0, max);
|
|
76
98
|
return {
|
|
77
99
|
query,
|
|
78
100
|
matchedDomains,
|
|
@@ -80,6 +102,7 @@ export async function queryContext(workspace, config, maps, query) {
|
|
|
80
102
|
relevantSymbols,
|
|
81
103
|
relevantCognition,
|
|
82
104
|
relationships: relationships.slice(0, max),
|
|
105
|
+
nearbySymbols,
|
|
83
106
|
staleReferences,
|
|
84
107
|
warnings: [],
|
|
85
108
|
};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import type { SymbolExtractionResult } from './ts-symbol-extractor.js';
|
|
2
|
-
export declare function extractCSymbols(sourceText: string, filePath: string): SymbolExtractionResult
|
|
2
|
+
export declare function extractCSymbols(sourceText: string, filePath: string): Promise<SymbolExtractionResult>;
|