@kentwynn/kgraph 0.1.24 → 0.1.26
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 +20 -3
- package/dist/cli/commands/extractor.d.ts +2 -0
- package/dist/cli/commands/extractor.js +50 -0
- package/dist/cli/commands/init.d.ts +1 -1
- package/dist/cli/commands/init.js +85 -15
- package/dist/cli/commands/integrate.d.ts +1 -1
- package/dist/cli/commands/integrate.js +37 -25
- package/dist/cli/help.js +5 -0
- package/dist/cli/index.js +2 -0
- package/dist/cli/init-prompt.d.ts +13 -0
- package/dist/cli/init-prompt.js +124 -0
- package/dist/cli/init-recommendations.d.ts +31 -0
- package/dist/cli/init-recommendations.js +163 -0
- package/dist/cli/init-summary.d.ts +19 -0
- package/dist/cli/init-summary.js +147 -0
- package/dist/cognition/markdown-note-parser.js +2 -1
- package/dist/config/config.js +33 -0
- package/dist/extractors/extractor-registry.d.ts +11 -0
- package/dist/extractors/extractor-registry.js +70 -0
- package/dist/extractors/extractor-store.d.ts +10 -0
- package/dist/extractors/extractor-store.js +58 -0
- package/dist/integrations/instruction-blocks.d.ts +1 -1
- package/dist/integrations/instruction-blocks.js +18 -15
- package/dist/integrations/integration-registry.d.ts +1 -1
- package/dist/integrations/integration-registry.js +10 -10
- package/dist/integrations/integration-store.d.ts +1 -1
- package/dist/integrations/integration-store.js +20 -18
- package/dist/types/config.d.ts +9 -2
- package/dist/visualization/html-template.js +52 -18
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -100,13 +100,18 @@ 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.
|
|
103
|
+
# 3. Optional: configure deep language extractors for non-TS repos
|
|
104
|
+
kgraph extractor add jvm python
|
|
105
|
+
|
|
106
|
+
# 4. Run the normal workflow for a topic
|
|
104
107
|
kgraph "auth token refresh"
|
|
105
108
|
|
|
106
|
-
#
|
|
109
|
+
# 5. Check health if something feels off
|
|
107
110
|
kgraph doctor
|
|
108
111
|
```
|
|
109
112
|
|
|
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. When the repository contains languages that only have basic built-in extraction today, it recommends optional deep extractors and prints the exact install/configure commands.
|
|
114
|
+
|
|
110
115
|
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`.
|
|
111
116
|
|
|
112
117
|
Normal agent flow is intentionally small:
|
|
@@ -138,7 +143,7 @@ This is optional. Claude Code can use generated hook scripts for automatic captu
|
|
|
138
143
|
kgraph init
|
|
139
144
|
```
|
|
140
145
|
|
|
141
|
-
Required once per repo. Creates `.kgraph
|
|
146
|
+
Required once per repo. Creates `.kgraph/`, writes the local config, runs the first scan, and prints suggested next actions based on the detected repo languages and likely local AI tools.
|
|
142
147
|
|
|
143
148
|
```bash
|
|
144
149
|
kgraph init --integrations codex,copilot,cursor,claude-code,gemini,windsurf,cline
|
|
@@ -258,6 +263,18 @@ New integrations default to `always` mode because coding agents often under-clas
|
|
|
258
263
|
| Codex | `AGENTS.md`, `.agents/skills/kgraph/SKILL.md` |
|
|
259
264
|
| GitHub Copilot | `.github/copilot-instructions.md`, `.github/prompts/*` |
|
|
260
265
|
| 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.
|
|
261
278
|
| Claude Code | `CLAUDE.md`, `.claude/commands/*` |
|
|
262
279
|
| Gemini CLI | `GEMINI.md` |
|
|
263
280
|
| Windsurf | `.windsurf/rules/kgraph.md` |
|
|
@@ -0,0 +1,50 @@
|
|
|
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,2 +1,2 @@
|
|
|
1
|
-
import type { Command } from
|
|
1
|
+
import type { Command } from 'commander';
|
|
2
2
|
export declare function registerInitCommand(program: Command): void;
|
|
@@ -1,28 +1,95 @@
|
|
|
1
|
-
import { writeDefaultConfig } from
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
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
|
+
import { normalizeIntegrationNames } from '../../integrations/integration-registry.js';
|
|
5
|
+
import { addIntegrations } from '../../integrations/integration-store.js';
|
|
6
|
+
import { scanRepository } from '../../scanner/repo-scanner.js';
|
|
7
|
+
import { ensureWorkspace } from '../../storage/kgraph-paths.js';
|
|
8
|
+
import { readMaps, writeMaps } from '../../storage/map-store.js';
|
|
9
|
+
import { KGraphError, runCommand } from '../errors.js';
|
|
10
|
+
import { promptForInitExtractors, promptForInitIntegrations, shouldPromptForInitExtractors, shouldPromptForInitIntegrations, } from '../init-prompt.js';
|
|
11
|
+
import { detectMachineIntegrationRecommendations, recommendedExtractorsForInit, recommendedIntegrationsForInit, } from '../init-recommendations.js';
|
|
12
|
+
import { renderInitSummary } from '../init-summary.js';
|
|
6
13
|
export function registerInitCommand(program) {
|
|
7
14
|
program
|
|
8
|
-
.command(
|
|
9
|
-
.description(
|
|
10
|
-
.option(
|
|
11
|
-
.option(
|
|
12
|
-
.option(
|
|
15
|
+
.command('init')
|
|
16
|
+
.description('Initialize a .kgraph workspace')
|
|
17
|
+
.option('--integration <name>', 'Configure an AI tool integration', collectOption, [])
|
|
18
|
+
.option('--integrations <names>', 'Configure comma-separated AI tool integrations')
|
|
19
|
+
.option('--mode <mode>', 'Integration mode: always, smart, manual, or off', 'always')
|
|
13
20
|
.action((options) => runCommand(async () => {
|
|
14
21
|
const workspace = await ensureWorkspace(process.cwd());
|
|
15
22
|
const wroteConfig = await writeDefaultConfig(workspace);
|
|
16
|
-
console.log(wroteConfig
|
|
23
|
+
console.log(wroteConfig
|
|
24
|
+
? 'Initialized .kgraph workspace.'
|
|
25
|
+
: '.kgraph workspace already initialized.');
|
|
17
26
|
const names = normalizeIntegrationNames([
|
|
18
27
|
...(options.integration ?? []),
|
|
19
|
-
...(options.integrations ? [options.integrations] : [])
|
|
28
|
+
...(options.integrations ? [options.integrations] : []),
|
|
20
29
|
]);
|
|
21
30
|
if (names.length > 0) {
|
|
22
31
|
const mode = normalizeIntegrationMode(options.mode);
|
|
23
32
|
const changed = await addIntegrations(workspace, names, mode);
|
|
24
|
-
console.log(`Configured integrations: ${changed.map((item) => `${item.name}:${item.mode}`).join(
|
|
33
|
+
console.log(`Configured integrations: ${changed.map((item) => `${item.name}:${item.mode}`).join(', ')}`);
|
|
25
34
|
}
|
|
35
|
+
let config = await loadConfig(workspace);
|
|
36
|
+
const previousMaps = await readMaps(workspace);
|
|
37
|
+
const result = await scanRepository(workspace.rootPath, config, {
|
|
38
|
+
files: previousMaps.fileMap.files,
|
|
39
|
+
symbols: previousMaps.symbolMap.symbols,
|
|
40
|
+
dependencies: previousMaps.dependencyMap.dependencies,
|
|
41
|
+
relationships: previousMaps.relationshipMap.relationships,
|
|
42
|
+
warnings: [],
|
|
43
|
+
});
|
|
44
|
+
await writeMaps(workspace, result);
|
|
45
|
+
console.log(`Scanned ${result.files.length} files and ${result.symbols.length} symbols.`);
|
|
46
|
+
const detectedMachineIntegrations = await detectMachineIntegrationRecommendations();
|
|
47
|
+
let recommendedIntegrations = recommendedIntegrationsForInit({
|
|
48
|
+
configuredIntegrations: config.integrations,
|
|
49
|
+
detectedIntegrations: detectedMachineIntegrations,
|
|
50
|
+
});
|
|
51
|
+
let recommendedExtractors = recommendedExtractorsForInit({
|
|
52
|
+
files: result.files,
|
|
53
|
+
configuredExtractors: config.extractors,
|
|
54
|
+
});
|
|
55
|
+
if (shouldPromptForInitIntegrations({
|
|
56
|
+
explicitIntegrationsRequested: names.length > 0,
|
|
57
|
+
configuredIntegrations: config.integrations,
|
|
58
|
+
})) {
|
|
59
|
+
const selected = await promptForInitIntegrations(recommendedIntegrations);
|
|
60
|
+
if (selected.length > 0) {
|
|
61
|
+
const changed = await addIntegrations(workspace, selected, 'always');
|
|
62
|
+
console.log(`Configured integrations: ${changed.map((item) => `${item.name}:${item.mode}`).join(', ')}`);
|
|
63
|
+
config = await loadConfig(workspace);
|
|
64
|
+
recommendedIntegrations = recommendedIntegrationsForInit({
|
|
65
|
+
configuredIntegrations: config.integrations,
|
|
66
|
+
detectedIntegrations: detectedMachineIntegrations,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}
|
|
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
|
+
console.log('');
|
|
86
|
+
console.log(renderInitSummary({
|
|
87
|
+
files: result.files,
|
|
88
|
+
integrations: config.integrations,
|
|
89
|
+
recommendedIntegrations,
|
|
90
|
+
extractors: config.extractors,
|
|
91
|
+
recommendedExtractors,
|
|
92
|
+
}));
|
|
26
93
|
}));
|
|
27
94
|
}
|
|
28
95
|
function collectOption(value, previous) {
|
|
@@ -30,8 +97,11 @@ function collectOption(value, previous) {
|
|
|
30
97
|
return previous;
|
|
31
98
|
}
|
|
32
99
|
function normalizeIntegrationMode(value) {
|
|
33
|
-
if (value ===
|
|
100
|
+
if (value === 'smart' ||
|
|
101
|
+
value === 'always' ||
|
|
102
|
+
value === 'manual' ||
|
|
103
|
+
value === 'off') {
|
|
34
104
|
return value;
|
|
35
105
|
}
|
|
36
|
-
throw new KGraphError(
|
|
106
|
+
throw new KGraphError('--mode must be smart, always, manual, or off.');
|
|
37
107
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import type { Command } from
|
|
1
|
+
import type { Command } from 'commander';
|
|
2
2
|
export declare function registerIntegrateCommand(program: Command): void;
|
|
@@ -1,63 +1,75 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { assertWorkspace } from
|
|
4
|
-
import { KGraphError, runCommand } from
|
|
1
|
+
import { normalizeIntegrationNames } from '../../integrations/integration-registry.js';
|
|
2
|
+
import { addIntegrations, listIntegrations, removeIntegrations, setIntegrationMode, } from '../../integrations/integration-store.js';
|
|
3
|
+
import { assertWorkspace } from '../../storage/kgraph-paths.js';
|
|
4
|
+
import { KGraphError, runCommand } from '../errors.js';
|
|
5
5
|
export function registerIntegrateCommand(program) {
|
|
6
|
-
const integrate = program
|
|
7
|
-
|
|
6
|
+
const integrate = program
|
|
7
|
+
.command('integrate')
|
|
8
|
+
.description('Manage AI tool integrations');
|
|
9
|
+
integrate
|
|
10
|
+
.command('list')
|
|
11
|
+
.description('List configured integrations')
|
|
12
|
+
.action(() => runCommand(async () => {
|
|
8
13
|
const workspace = await assertWorkspace(process.cwd());
|
|
9
14
|
const integrations = await listIntegrations(workspace);
|
|
10
15
|
if (integrations.length === 0) {
|
|
11
|
-
console.log(
|
|
16
|
+
console.log('No integrations configured.');
|
|
12
17
|
return;
|
|
13
18
|
}
|
|
14
19
|
for (const integration of integrations) {
|
|
15
|
-
console.log(`${integration.name} ${integration.enabled ?
|
|
20
|
+
console.log(`${integration.name} ${integration.enabled ? 'enabled' : 'disabled'} ${integration.mode} ${integration.targetPath} ${integration.targetExists ? 'present' : 'missing'}`);
|
|
16
21
|
}
|
|
17
22
|
}));
|
|
18
23
|
integrate
|
|
19
|
-
.command(
|
|
20
|
-
.description(
|
|
21
|
-
.argument(
|
|
22
|
-
.option(
|
|
24
|
+
.command('add')
|
|
25
|
+
.description('Add AI tool integrations')
|
|
26
|
+
.argument('<names...>')
|
|
27
|
+
.option('--mode <mode>', 'always, smart, manual, or off', 'always')
|
|
23
28
|
.action((names, options) => runCommand(async () => {
|
|
24
29
|
const workspace = await assertWorkspace(process.cwd());
|
|
25
30
|
const normalized = normalizeIntegrationNames(names);
|
|
26
31
|
if (normalized.length === 0) {
|
|
27
|
-
throw new KGraphError(
|
|
32
|
+
throw new KGraphError('Provide at least one integration name.');
|
|
28
33
|
}
|
|
29
34
|
const mode = normalizeIntegrationMode(options.mode);
|
|
30
35
|
const changed = await addIntegrations(workspace, normalized, mode);
|
|
31
|
-
console.log(`Configured integrations: ${changed.map((item) => `${item.name}:${item.mode}`).join(
|
|
36
|
+
console.log(`Configured integrations: ${changed.map((item) => `${item.name}:${item.mode}`).join(', ')}`);
|
|
32
37
|
}));
|
|
33
38
|
integrate
|
|
34
|
-
.command(
|
|
35
|
-
.description(
|
|
36
|
-
.argument(
|
|
37
|
-
.requiredOption(
|
|
39
|
+
.command('set')
|
|
40
|
+
.description('Set AI tool integration mode')
|
|
41
|
+
.argument('<names...>')
|
|
42
|
+
.requiredOption('--mode <mode>', 'smart, always, manual, or off')
|
|
38
43
|
.action((names, options) => runCommand(async () => {
|
|
39
44
|
const workspace = await assertWorkspace(process.cwd());
|
|
40
45
|
const normalized = normalizeIntegrationNames(names);
|
|
41
46
|
if (normalized.length === 0) {
|
|
42
|
-
throw new KGraphError(
|
|
47
|
+
throw new KGraphError('Provide at least one integration name.');
|
|
43
48
|
}
|
|
44
49
|
const mode = normalizeIntegrationMode(options.mode);
|
|
45
50
|
const changed = await setIntegrationMode(workspace, normalized, mode);
|
|
46
|
-
console.log(`Updated integrations: ${changed.map((item) => `${item.name}:${item.mode}`).join(
|
|
51
|
+
console.log(`Updated integrations: ${changed.map((item) => `${item.name}:${item.mode}`).join(', ')}`);
|
|
47
52
|
}));
|
|
48
|
-
integrate
|
|
53
|
+
integrate
|
|
54
|
+
.command('remove')
|
|
55
|
+
.description('Remove AI tool integrations')
|
|
56
|
+
.argument('<names...>')
|
|
57
|
+
.action((names) => runCommand(async () => {
|
|
49
58
|
const workspace = await assertWorkspace(process.cwd());
|
|
50
59
|
const normalized = normalizeIntegrationNames(names);
|
|
51
60
|
if (normalized.length === 0) {
|
|
52
|
-
throw new KGraphError(
|
|
61
|
+
throw new KGraphError('Provide at least one integration name.');
|
|
53
62
|
}
|
|
54
63
|
const removed = await removeIntegrations(workspace, normalized);
|
|
55
|
-
console.log(`Removed integrations: ${removed.join(
|
|
64
|
+
console.log(`Removed integrations: ${removed.join(', ')}`);
|
|
56
65
|
}));
|
|
57
66
|
}
|
|
58
67
|
function normalizeIntegrationMode(value) {
|
|
59
|
-
if (value ===
|
|
68
|
+
if (value === 'smart' ||
|
|
69
|
+
value === 'always' ||
|
|
70
|
+
value === 'manual' ||
|
|
71
|
+
value === 'off') {
|
|
60
72
|
return value;
|
|
61
73
|
}
|
|
62
|
-
throw new KGraphError(
|
|
74
|
+
throw new KGraphError('--mode must be smart, always, manual, or off.');
|
|
63
75
|
}
|
package/dist/cli/help.js
CHANGED
|
@@ -47,6 +47,11 @@ 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
|
+
'',
|
|
50
55
|
theme.bold('Options'),
|
|
51
56
|
command('-V, --version', 'Show version'),
|
|
52
57
|
command('-h, --help', 'Show this help'),
|
package/dist/cli/index.js
CHANGED
|
@@ -5,6 +5,7 @@ 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';
|
|
8
9
|
import { registerHistoryCommand } from './commands/history.js';
|
|
9
10
|
import { registerImpactCommand } from './commands/impact.js';
|
|
10
11
|
import { registerInitCommand } from './commands/init.js';
|
|
@@ -43,6 +44,7 @@ export function createProgram() {
|
|
|
43
44
|
registerUpdateCommand(program);
|
|
44
45
|
registerContextCommand(program);
|
|
45
46
|
registerImpactCommand(program);
|
|
47
|
+
registerExtractorCommand(program);
|
|
46
48
|
registerIntegrateCommand(program);
|
|
47
49
|
registerVisualizeCommand(program);
|
|
48
50
|
registerHistoryCommand(program);
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ExtractorConfig, ExtractorName, IntegrationConfig, IntegrationName } from '../types/config.js';
|
|
2
|
+
import type { InitExtractorRecommendation, InitIntegrationRecommendation } from './init-recommendations.js';
|
|
3
|
+
export declare function shouldPromptForInitIntegrations(options: {
|
|
4
|
+
explicitIntegrationsRequested: boolean;
|
|
5
|
+
configuredIntegrations: Pick<IntegrationConfig, 'name'>[];
|
|
6
|
+
interactive?: boolean;
|
|
7
|
+
}): boolean;
|
|
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[]>;
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import * as clack from '@clack/prompts';
|
|
2
|
+
import { listExtractorAdapters } from '../extractors/extractor-registry.js';
|
|
3
|
+
import { listIntegrationAdapters } from '../integrations/integration-registry.js';
|
|
4
|
+
// --- Integration prompt ---
|
|
5
|
+
export function shouldPromptForInitIntegrations(options) {
|
|
6
|
+
const interactive = options.interactive ?? isInteractiveTerminal();
|
|
7
|
+
return (interactive &&
|
|
8
|
+
!options.explicitIntegrationsRequested &&
|
|
9
|
+
options.configuredIntegrations.length === 0);
|
|
10
|
+
}
|
|
11
|
+
export async function promptForInitIntegrations(recommendations) {
|
|
12
|
+
const recNames = recommendations.map((item) => item.name);
|
|
13
|
+
const action = await clack.select({
|
|
14
|
+
message: 'AI tool integrations',
|
|
15
|
+
options: [
|
|
16
|
+
...(recommendations.length > 0
|
|
17
|
+
? [
|
|
18
|
+
{
|
|
19
|
+
value: 'recommended',
|
|
20
|
+
label: `Use recommended (${recNames.join(', ')})`,
|
|
21
|
+
hint: recommendations
|
|
22
|
+
.map((item) => `${item.name} — ${item.reason}`)
|
|
23
|
+
.join('; '),
|
|
24
|
+
},
|
|
25
|
+
]
|
|
26
|
+
: []),
|
|
27
|
+
{ value: 'custom', label: 'Custom selection' },
|
|
28
|
+
{ value: 'skip', label: 'Skip' },
|
|
29
|
+
],
|
|
30
|
+
});
|
|
31
|
+
if (clack.isCancel(action) || action === 'skip') {
|
|
32
|
+
return [];
|
|
33
|
+
}
|
|
34
|
+
if (action === 'recommended') {
|
|
35
|
+
return recNames;
|
|
36
|
+
}
|
|
37
|
+
const recommendedNames = new Set(recNames);
|
|
38
|
+
const otherAdapters = listIntegrationAdapters().filter((adapter) => !recommendedNames.has(adapter.name));
|
|
39
|
+
const allOptions = [
|
|
40
|
+
...recommendations.map((rec) => ({
|
|
41
|
+
value: rec.name,
|
|
42
|
+
label: rec.name,
|
|
43
|
+
hint: rec.reason,
|
|
44
|
+
})),
|
|
45
|
+
...otherAdapters.map((adapter) => ({
|
|
46
|
+
value: adapter.name,
|
|
47
|
+
label: adapter.name,
|
|
48
|
+
})),
|
|
49
|
+
];
|
|
50
|
+
const selected = await clack.multiselect({
|
|
51
|
+
message: 'Select integrations (space to toggle, enter to confirm)',
|
|
52
|
+
options: allOptions,
|
|
53
|
+
required: false,
|
|
54
|
+
});
|
|
55
|
+
if (clack.isCancel(selected)) {
|
|
56
|
+
return [];
|
|
57
|
+
}
|
|
58
|
+
return selected;
|
|
59
|
+
}
|
|
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
|
+
function isInteractiveTerminal() {
|
|
123
|
+
return Boolean(process.stdin.isTTY) && Boolean(process.stdout.isTTY);
|
|
124
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { ExtractorConfig, ExtractorName, IntegrationConfig, IntegrationName } from '../types/config.js';
|
|
2
|
+
import type { RepositoryFile } from '../types/maps.js';
|
|
3
|
+
export interface InitIntegrationRecommendation {
|
|
4
|
+
name: IntegrationName;
|
|
5
|
+
reason: string;
|
|
6
|
+
}
|
|
7
|
+
export interface InitExtractorRecommendation {
|
|
8
|
+
name: ExtractorName;
|
|
9
|
+
packageName: string;
|
|
10
|
+
languages: string[];
|
|
11
|
+
}
|
|
12
|
+
interface MachineDetectionContext {
|
|
13
|
+
env?: NodeJS.ProcessEnv;
|
|
14
|
+
homeDir?: string;
|
|
15
|
+
platform?: NodeJS.Platform;
|
|
16
|
+
exists?: (targetPath: string) => Promise<boolean>;
|
|
17
|
+
readDir?: (targetPath: string) => Promise<string[]>;
|
|
18
|
+
localAppData?: string;
|
|
19
|
+
}
|
|
20
|
+
export declare function detectMachineIntegrationRecommendations(context?: MachineDetectionContext): Promise<InitIntegrationRecommendation[]>;
|
|
21
|
+
export declare function recommendedIntegrationsForInit(options: {
|
|
22
|
+
configuredIntegrations: Pick<IntegrationConfig, 'name'>[];
|
|
23
|
+
detectedIntegrations: InitIntegrationRecommendation[];
|
|
24
|
+
}): InitIntegrationRecommendation[];
|
|
25
|
+
export declare function recommendedExtractorsForInit(options: {
|
|
26
|
+
files: RepositoryFile[];
|
|
27
|
+
configuredExtractors: Pick<ExtractorConfig, 'name'>[];
|
|
28
|
+
}): InitExtractorRecommendation[];
|
|
29
|
+
export declare function integrationSetupCommand(recommendations: InitIntegrationRecommendation[]): string | undefined;
|
|
30
|
+
export declare function extractorSetupCommands(recommendations: InitExtractorRecommendation[]): string[];
|
|
31
|
+
export {};
|