@grafema/cli 0.2.12-beta → 0.3.1-beta

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.
Files changed (131) hide show
  1. package/dist/cli.js +13 -0
  2. package/dist/cli.js.map +1 -1
  3. package/dist/commands/analyze.d.ts.map +1 -1
  4. package/dist/commands/analyze.js +2 -4
  5. package/dist/commands/analyze.js.map +1 -1
  6. package/dist/commands/analyzeAction.d.ts +5 -3
  7. package/dist/commands/analyzeAction.d.ts.map +1 -1
  8. package/dist/commands/analyzeAction.js +109 -151
  9. package/dist/commands/analyzeAction.js.map +1 -1
  10. package/dist/commands/check.d.ts +1 -1
  11. package/dist/commands/check.js +4 -4
  12. package/dist/commands/check.js.map +1 -1
  13. package/dist/commands/context.js +2 -2
  14. package/dist/commands/context.js.map +1 -1
  15. package/dist/commands/coverage.js +2 -2
  16. package/dist/commands/coverage.js.map +1 -1
  17. package/dist/commands/describe.d.ts +13 -0
  18. package/dist/commands/describe.d.ts.map +1 -0
  19. package/dist/commands/describe.js +131 -0
  20. package/dist/commands/describe.js.map +1 -0
  21. package/dist/commands/doctor/checks.d.ts +6 -1
  22. package/dist/commands/doctor/checks.d.ts.map +1 -1
  23. package/dist/commands/doctor/checks.js +128 -13
  24. package/dist/commands/doctor/checks.js.map +1 -1
  25. package/dist/commands/doctor.d.ts +10 -9
  26. package/dist/commands/doctor.d.ts.map +1 -1
  27. package/dist/commands/doctor.js +12 -10
  28. package/dist/commands/doctor.js.map +1 -1
  29. package/dist/commands/explain.js +2 -2
  30. package/dist/commands/explain.js.map +1 -1
  31. package/dist/commands/file.js +2 -2
  32. package/dist/commands/file.js.map +1 -1
  33. package/dist/commands/get.js +2 -2
  34. package/dist/commands/get.js.map +1 -1
  35. package/dist/commands/git-ingest.d.ts +6 -0
  36. package/dist/commands/git-ingest.d.ts.map +1 -0
  37. package/dist/commands/git-ingest.js +46 -0
  38. package/dist/commands/git-ingest.js.map +1 -0
  39. package/dist/commands/impact.d.ts.map +1 -1
  40. package/dist/commands/impact.js +276 -50
  41. package/dist/commands/impact.js.map +1 -1
  42. package/dist/commands/init.d.ts.map +1 -1
  43. package/dist/commands/init.js +20 -22
  44. package/dist/commands/init.js.map +1 -1
  45. package/dist/commands/ls.js +2 -2
  46. package/dist/commands/ls.js.map +1 -1
  47. package/dist/commands/overview.js +2 -2
  48. package/dist/commands/overview.js.map +1 -1
  49. package/dist/commands/query.d.ts +1 -1
  50. package/dist/commands/query.d.ts.map +1 -1
  51. package/dist/commands/query.js +169 -7
  52. package/dist/commands/query.js.map +1 -1
  53. package/dist/commands/schema.js +2 -2
  54. package/dist/commands/schema.js.map +1 -1
  55. package/dist/commands/server.js +11 -6
  56. package/dist/commands/server.js.map +1 -1
  57. package/dist/commands/stats.js +2 -2
  58. package/dist/commands/stats.js.map +1 -1
  59. package/dist/commands/tldr.d.ts +12 -0
  60. package/dist/commands/tldr.d.ts.map +1 -0
  61. package/dist/commands/tldr.js +81 -0
  62. package/dist/commands/tldr.js.map +1 -0
  63. package/dist/commands/trace.d.ts +1 -1
  64. package/dist/commands/trace.d.ts.map +1 -1
  65. package/dist/commands/trace.js +17 -133
  66. package/dist/commands/trace.js.map +1 -1
  67. package/dist/commands/types.js +2 -2
  68. package/dist/commands/types.js.map +1 -1
  69. package/dist/commands/who.d.ts +12 -0
  70. package/dist/commands/who.d.ts.map +1 -0
  71. package/dist/commands/who.js +184 -0
  72. package/dist/commands/who.js.map +1 -0
  73. package/dist/commands/why.d.ts +12 -0
  74. package/dist/commands/why.d.ts.map +1 -0
  75. package/dist/commands/why.js +118 -0
  76. package/dist/commands/why.js.map +1 -0
  77. package/dist/commands/wtf.d.ts +12 -0
  78. package/dist/commands/wtf.d.ts.map +1 -0
  79. package/dist/commands/wtf.js +117 -0
  80. package/dist/commands/wtf.js.map +1 -0
  81. package/dist/plugins/builtinPlugins.d.ts +1 -9
  82. package/dist/plugins/builtinPlugins.d.ts.map +1 -1
  83. package/dist/plugins/builtinPlugins.js +2 -67
  84. package/dist/plugins/builtinPlugins.js.map +1 -1
  85. package/dist/plugins/pluginLoader.d.ts +1 -15
  86. package/dist/plugins/pluginLoader.d.ts.map +1 -1
  87. package/dist/plugins/pluginLoader.js +2 -100
  88. package/dist/plugins/pluginLoader.js.map +1 -1
  89. package/dist/plugins/pluginResolver.js +3 -3
  90. package/dist/utils/progressRenderer.d.ts +15 -1
  91. package/dist/utils/progressRenderer.d.ts.map +1 -1
  92. package/dist/utils/progressRenderer.js.map +1 -1
  93. package/dist/utils/queryHints.d.ts +6 -0
  94. package/dist/utils/queryHints.d.ts.map +1 -0
  95. package/dist/utils/queryHints.js +36 -0
  96. package/dist/utils/queryHints.js.map +1 -0
  97. package/package.json +4 -4
  98. package/skills/grafema-codebase-analysis/SKILL.md +1 -1
  99. package/src/cli.ts +14 -0
  100. package/src/commands/analyze.ts +2 -4
  101. package/src/commands/analyzeAction.ts +122 -168
  102. package/src/commands/check.ts +5 -5
  103. package/src/commands/context.ts +3 -3
  104. package/src/commands/coverage.ts +2 -2
  105. package/src/commands/describe.ts +160 -0
  106. package/src/commands/doctor/checks.ts +153 -10
  107. package/src/commands/doctor.ts +13 -9
  108. package/src/commands/explain.ts +2 -2
  109. package/src/commands/explore.tsx +2 -2
  110. package/src/commands/file.ts +3 -3
  111. package/src/commands/get.ts +2 -2
  112. package/src/commands/git-ingest.ts +49 -0
  113. package/src/commands/impact.ts +318 -55
  114. package/src/commands/init.ts +20 -22
  115. package/src/commands/ls.ts +2 -2
  116. package/src/commands/overview.ts +2 -2
  117. package/src/commands/query.ts +197 -7
  118. package/src/commands/schema.ts +2 -2
  119. package/src/commands/server.ts +12 -6
  120. package/src/commands/stats.ts +2 -2
  121. package/src/commands/tldr.ts +103 -0
  122. package/src/commands/trace.ts +19 -161
  123. package/src/commands/types.ts +2 -2
  124. package/src/commands/who.ts +215 -0
  125. package/src/commands/why.ts +134 -0
  126. package/src/commands/wtf.ts +140 -0
  127. package/src/plugins/builtinPlugins.ts +1 -108
  128. package/src/plugins/pluginLoader.ts +1 -123
  129. package/src/plugins/pluginResolver.js +3 -3
  130. package/src/utils/progressRenderer.ts +15 -1
  131. package/src/utils/queryHints.ts +46 -0
@@ -0,0 +1,140 @@
1
+ /**
2
+ * wtf command — "Where does this come from?"
3
+ *
4
+ * Backward dataflow trace with arrow-formatted output.
5
+ *
6
+ * Usage:
7
+ * grafema wtf req.user # Backward trace
8
+ * grafema wtf config.apiKey # Where does this value originate?
9
+ */
10
+
11
+ import { Command } from 'commander';
12
+ import { resolve, join } from 'path';
13
+ import { existsSync } from 'fs';
14
+ import {
15
+ RFDBServerBackend,
16
+ traceDataflow,
17
+ renderTraceNarrative,
18
+ } from '@grafema/util';
19
+ import type { DataflowBackend } from '@grafema/util';
20
+ import { exitWithError } from '../utils/errorFormatter.js';
21
+ import { Spinner } from '../utils/spinner.js';
22
+
23
+ interface WtfCommandOptions {
24
+ project: string;
25
+ depth: string;
26
+ json?: boolean;
27
+ }
28
+
29
+ export const wtfCommand = new Command('wtf')
30
+ .description('Where does this come from? — backward dataflow trace')
31
+ .argument('<symbol>', 'Variable, constant, or parameter name to trace')
32
+ .option('-p, --project <path>', 'Project path', '.')
33
+ .option('-d, --depth <n>', 'Max trace depth', '10')
34
+ .option('-j, --json', 'Output as JSON')
35
+ .addHelpText('after', `
36
+ Examples:
37
+ grafema wtf req.user Trace where req.user comes from
38
+ grafema wtf config.apiKey Where does this value originate?
39
+ grafema wtf userId --depth 5 Limit trace depth
40
+ grafema wtf token --json Output as JSON
41
+ `)
42
+ .action(async (symbol: string, options: WtfCommandOptions) => {
43
+ const projectPath = resolve(options.project);
44
+ const grafemaDir = join(projectPath, '.grafema');
45
+ const dbPath = join(grafemaDir, 'graph.rfdb');
46
+
47
+ if (!existsSync(dbPath)) {
48
+ exitWithError('No graph database found', ['Run: grafema analyze']);
49
+ }
50
+
51
+ const maxDepth = parseInt(options.depth, 10);
52
+ if (isNaN(maxDepth) || maxDepth < 1) {
53
+ exitWithError('Invalid depth', ['Provide a positive integer']);
54
+ }
55
+
56
+ const backend = new RFDBServerBackend({ dbPath, clientName: 'cli' });
57
+ await backend.connect();
58
+
59
+ const spinner = new Spinner('Searching for symbol...');
60
+ spinner.start();
61
+
62
+ try {
63
+ // Find the node: search VARIABLE, CONSTANT, PARAMETER by name (case-insensitive)
64
+ // Also try PROPERTY_ACCESS by matching method part after last dot
65
+ const lowerSymbol = symbol.toLowerCase();
66
+ // If symbol has a dot, extract the part after the last dot for method matching
67
+ const dotIndex = symbol.lastIndexOf('.');
68
+ const methodPart = dotIndex >= 0 ? symbol.substring(dotIndex + 1).toLowerCase() : null;
69
+
70
+ type FoundNode = { id: string; type: string; name: string; file: string; line?: number };
71
+ let found: FoundNode | null = null;
72
+
73
+ for (const nodeType of ['VARIABLE', 'CONSTANT', 'PARAMETER'] as const) {
74
+ for await (const n of backend.queryNodes({ type: nodeType })) {
75
+ const name = (n.name || '').toLowerCase();
76
+ if (name === lowerSymbol) {
77
+ found = { id: n.id, type: n.type || nodeType, name: n.name || '', file: n.file || '', line: n.line };
78
+ break;
79
+ }
80
+ }
81
+ if (found) break;
82
+ }
83
+
84
+ // Try PROPERTY_ACCESS if not found and symbol has a dot
85
+ if (!found && methodPart) {
86
+ for await (const n of backend.queryNodes({ type: 'PROPERTY_ACCESS' as any })) {
87
+ const name = (n.name || '').toLowerCase();
88
+ // Match by full name or by the part after last dot
89
+ const nameMethodPart = name.lastIndexOf('.') >= 0
90
+ ? name.substring(name.lastIndexOf('.') + 1)
91
+ : name;
92
+ if (name === lowerSymbol || nameMethodPart === methodPart) {
93
+ found = { id: n.id, type: n.type || 'PROPERTY_ACCESS', name: n.name || '', file: n.file || '', line: n.line };
94
+ break;
95
+ }
96
+ }
97
+ }
98
+
99
+ if (!found) {
100
+ spinner.stop();
101
+ exitWithError(`Symbol not found: "${symbol}"`, [
102
+ 'Check the symbol name and try again',
103
+ 'Use: grafema query "<name>" to search available nodes',
104
+ ]);
105
+ return;
106
+ }
107
+
108
+ spinner.stop();
109
+
110
+ // Cast backend to DataflowBackend
111
+ const dfDb = backend as unknown as DataflowBackend;
112
+
113
+ // Trace backward
114
+ const results = await traceDataflow(dfDb, found.id, {
115
+ direction: 'backward',
116
+ maxDepth,
117
+ });
118
+
119
+ if (options.json) {
120
+ console.log(JSON.stringify({
121
+ symbol: found.name,
122
+ node: found,
123
+ results: results.map(r => ({
124
+ direction: r.direction,
125
+ startNode: r.startNode,
126
+ reached: r.reached,
127
+ totalReached: r.totalReached,
128
+ })),
129
+ }, null, 2));
130
+ } else {
131
+ console.log(`${found.name} (${found.type}) — ${found.file}${found.line ? ':' + found.line : ''}`);
132
+ console.log('');
133
+ const narrative = renderTraceNarrative(results, found.name, { detail: 'normal' });
134
+ console.log(narrative);
135
+ }
136
+ } finally {
137
+ spinner.stop();
138
+ await backend.close();
139
+ }
140
+ });
@@ -1,108 +1 @@
1
- /**
2
- * Built-in plugin registry — maps plugin names to factory functions.
3
- *
4
- * Each entry creates a fresh plugin instance. Plugin names match the class names
5
- * and are referenced by name in .grafema/config.yaml under phases:
6
- * discovery, indexing, analysis, enrichment, validation.
7
- */
8
-
9
- import type { Plugin } from '@grafema/core';
10
- import {
11
- // Discovery
12
- SimpleProjectDiscovery,
13
- MonorepoServiceDiscovery,
14
- WorkspaceDiscovery,
15
- // Indexing
16
- JSModuleIndexer,
17
- RustModuleIndexer,
18
- // Analysis
19
- JSASTAnalyzer,
20
- ExpressRouteAnalyzer,
21
- ExpressResponseAnalyzer,
22
- NestJSRouteAnalyzer,
23
- SocketIOAnalyzer,
24
- DatabaseAnalyzer,
25
- FetchAnalyzer,
26
- ServiceLayerAnalyzer,
27
- ReactAnalyzer,
28
- RustAnalyzer,
29
- // Enrichment
30
- MethodCallResolver,
31
- ArgumentParameterLinker,
32
- AliasTracker,
33
- ValueDomainAnalyzer,
34
- MountPointResolver,
35
- ExpressHandlerLinker,
36
- PrefixEvaluator,
37
- InstanceOfResolver,
38
- ImportExportLinker,
39
- FunctionCallResolver,
40
- HTTPConnectionEnricher,
41
- ConfigRoutingMapBuilder,
42
- ServiceConnectionEnricher,
43
- RustFFIEnricher,
44
- RejectionPropagationEnricher,
45
- CallbackCallResolver,
46
- // Validation
47
- CallResolverValidator,
48
- EvalBanValidator,
49
- SQLInjectionValidator,
50
- AwaitInLoopValidator,
51
- ShadowingDetector,
52
- GraphConnectivityValidator,
53
- DataFlowValidator,
54
- TypeScriptDeadCodeValidator,
55
- BrokenImportValidator,
56
- UnconnectedRouteValidator,
57
- PackageCoverageValidator,
58
- } from '@grafema/core';
59
-
60
- export const BUILTIN_PLUGINS: Record<string, () => Plugin> = {
61
- // Discovery
62
- SimpleProjectDiscovery: () => new SimpleProjectDiscovery() as Plugin,
63
- MonorepoServiceDiscovery: () => new MonorepoServiceDiscovery() as Plugin,
64
- WorkspaceDiscovery: () => new WorkspaceDiscovery() as Plugin,
65
- // Indexing
66
- JSModuleIndexer: () => new JSModuleIndexer() as Plugin,
67
- RustModuleIndexer: () => new RustModuleIndexer() as Plugin,
68
- // Analysis
69
- JSASTAnalyzer: () => new JSASTAnalyzer() as Plugin,
70
- ExpressRouteAnalyzer: () => new ExpressRouteAnalyzer() as Plugin,
71
- ExpressResponseAnalyzer: () => new ExpressResponseAnalyzer() as Plugin,
72
- NestJSRouteAnalyzer: () => new NestJSRouteAnalyzer() as Plugin,
73
- SocketIOAnalyzer: () => new SocketIOAnalyzer() as Plugin,
74
- DatabaseAnalyzer: () => new DatabaseAnalyzer() as Plugin,
75
- FetchAnalyzer: () => new FetchAnalyzer() as Plugin,
76
- ServiceLayerAnalyzer: () => new ServiceLayerAnalyzer() as Plugin,
77
- ReactAnalyzer: () => new ReactAnalyzer() as Plugin,
78
- RustAnalyzer: () => new RustAnalyzer() as Plugin,
79
- // Enrichment
80
- MethodCallResolver: () => new MethodCallResolver() as Plugin,
81
- ArgumentParameterLinker: () => new ArgumentParameterLinker() as Plugin,
82
- AliasTracker: () => new AliasTracker() as Plugin,
83
- ValueDomainAnalyzer: () => new ValueDomainAnalyzer() as Plugin,
84
- MountPointResolver: () => new MountPointResolver() as Plugin,
85
- ExpressHandlerLinker: () => new ExpressHandlerLinker() as Plugin,
86
- PrefixEvaluator: () => new PrefixEvaluator() as Plugin,
87
- InstanceOfResolver: () => new InstanceOfResolver() as Plugin,
88
- ImportExportLinker: () => new ImportExportLinker() as Plugin,
89
- FunctionCallResolver: () => new FunctionCallResolver() as Plugin,
90
- HTTPConnectionEnricher: () => new HTTPConnectionEnricher() as Plugin,
91
- ConfigRoutingMapBuilder: () => new ConfigRoutingMapBuilder() as Plugin,
92
- ServiceConnectionEnricher: () => new ServiceConnectionEnricher() as Plugin,
93
- RustFFIEnricher: () => new RustFFIEnricher() as Plugin,
94
- RejectionPropagationEnricher: () => new RejectionPropagationEnricher() as Plugin,
95
- CallbackCallResolver: () => new CallbackCallResolver() as Plugin,
96
- // Validation
97
- CallResolverValidator: () => new CallResolverValidator() as Plugin,
98
- EvalBanValidator: () => new EvalBanValidator() as Plugin,
99
- SQLInjectionValidator: () => new SQLInjectionValidator() as Plugin,
100
- AwaitInLoopValidator: () => new AwaitInLoopValidator() as Plugin,
101
- ShadowingDetector: () => new ShadowingDetector() as Plugin,
102
- GraphConnectivityValidator: () => new GraphConnectivityValidator() as Plugin,
103
- DataFlowValidator: () => new DataFlowValidator() as Plugin,
104
- TypeScriptDeadCodeValidator: () => new TypeScriptDeadCodeValidator() as Plugin,
105
- BrokenImportValidator: () => new BrokenImportValidator() as Plugin,
106
- UnconnectedRouteValidator: () => new UnconnectedRouteValidator() as Plugin,
107
- PackageCoverageValidator: () => new PackageCoverageValidator() as Plugin,
108
- };
1
+ // Deleted: plugin registration moved to grafema-orchestrator (Rust).
@@ -1,123 +1 @@
1
- /**
2
- * Plugin loading — resolves built-in and custom plugins from config.
3
- *
4
- * Handles:
5
- * - ESM resolve hook for custom plugin @grafema/* imports
6
- * - Loading custom plugins from .grafema/plugins/
7
- * - Creating plugin instances from config phases
8
- */
9
-
10
- import { join } from 'path';
11
- import { existsSync, readdirSync } from 'fs';
12
- import { pathToFileURL } from 'url';
13
- import { register } from 'node:module';
14
- import type { Plugin, GrafemaConfig } from '@grafema/core';
15
- import { BUILTIN_PLUGINS } from './builtinPlugins.js';
16
-
17
- /**
18
- * Register ESM resolve hook so custom plugins can import @grafema/* packages.
19
- *
20
- * Plugins in .grafema/plugins/ do `import { Plugin } from '@grafema/core'`,
21
- * but @grafema/core isn't in the target project's node_modules/.
22
- * This hook redirects those imports to the CLI's bundled packages.
23
- *
24
- * Uses module.register() (stable Node.js 20.6+ API).
25
- * Safe to call multiple times — subsequent calls add redundant hooks
26
- * that short-circuit on the same specifiers.
27
- */
28
- let pluginResolverRegistered = false;
29
-
30
- export function registerPluginResolver(): void {
31
- if (pluginResolverRegistered) return;
32
- pluginResolverRegistered = true;
33
-
34
- const grafemaPackages: Record<string, string> = {};
35
- for (const pkg of ['@grafema/core', '@grafema/types']) {
36
- try {
37
- grafemaPackages[pkg] = import.meta.resolve(pkg);
38
- } catch {
39
- // Package not available from CLI context — skip
40
- }
41
- }
42
-
43
- register(
44
- new URL('./pluginResolver.js', import.meta.url),
45
- { data: { grafemaPackages } },
46
- );
47
- }
48
-
49
- /**
50
- * Load custom plugins from .grafema/plugins/ directory
51
- */
52
- export async function loadCustomPlugins(
53
- projectPath: string,
54
- log: (msg: string) => void
55
- ): Promise<Record<string, () => Plugin>> {
56
- const pluginsDir = join(projectPath, '.grafema', 'plugins');
57
- if (!existsSync(pluginsDir)) {
58
- return {};
59
- }
60
-
61
- // Ensure @grafema/* imports resolve for custom plugins (REG-380)
62
- registerPluginResolver();
63
-
64
- const customPlugins: Record<string, () => Plugin> = {};
65
-
66
- try {
67
- const files = readdirSync(pluginsDir).filter(
68
- (f) => f.endsWith('.js') || f.endsWith('.mjs') || f.endsWith('.cjs')
69
- );
70
-
71
- for (const file of files) {
72
- try {
73
- const pluginPath = join(pluginsDir, file);
74
- const pluginUrl = pathToFileURL(pluginPath).href;
75
- const module = await import(pluginUrl);
76
-
77
- const PluginClass = module.default || module[file.replace(/\.[cm]?js$/, '')];
78
- if (PluginClass && typeof PluginClass === 'function') {
79
- const pluginName = PluginClass.name || file.replace(/\.[cm]?js$/, '');
80
- customPlugins[pluginName] = () => {
81
- const instance = new PluginClass() as Plugin;
82
- instance.config.sourceFile = pluginPath;
83
- return instance;
84
- };
85
- log(`Loaded custom plugin: ${pluginName}`);
86
- }
87
- } catch (err) {
88
- const message = err instanceof Error ? err.message : String(err);
89
- console.warn(`Failed to load plugin ${file}: ${message}`);
90
- }
91
- }
92
- } catch (err) {
93
- const message = err instanceof Error ? err.message : String(err);
94
- console.warn(`Error loading custom plugins: ${message}`);
95
- }
96
-
97
- return customPlugins;
98
- }
99
-
100
- export function createPlugins(
101
- config: GrafemaConfig['plugins'],
102
- customPlugins: Record<string, () => Plugin> = {},
103
- verbose: boolean = false
104
- ): Plugin[] {
105
- const plugins: Plugin[] = [];
106
- const phases: (keyof GrafemaConfig['plugins'])[] = ['discovery', 'indexing', 'analysis', 'enrichment', 'validation'];
107
-
108
- for (const phase of phases) {
109
- const names = config[phase] || [];
110
- for (const name of names) {
111
- // Check built-in first, then custom
112
- const factory = BUILTIN_PLUGINS[name] || customPlugins[name];
113
- if (factory) {
114
- plugins.push(factory());
115
- } else if (verbose) {
116
- // Only show plugin warning in verbose mode
117
- console.warn(`Plugin not found: ${name} (skipping). Check .grafema/config.yaml or add to .grafema/plugins/`);
118
- }
119
- }
120
- }
121
-
122
- return plugins;
123
- }
1
+ // Deleted: plugin loading moved to grafema-orchestrator (Rust).
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * ESM resolve hook for custom Grafema plugins.
3
3
  *
4
- * Allows plugins in .grafema/plugins/ to `import { Plugin } from '@grafema/core'`
5
- * without requiring @grafema/core in the target project's node_modules/.
4
+ * Allows plugins in .grafema/plugins/ to `import { Plugin } from '@grafema/util'`
5
+ * without requiring @grafema/util in the target project's node_modules/.
6
6
  *
7
7
  * The hook maps @grafema/* bare specifiers to the actual package URLs
8
8
  * within the CLI's dependency tree.
@@ -26,7 +26,7 @@ export function initialize(data) {
26
26
  * Resolve hook — intercepts bare specifier imports for @grafema/* packages
27
27
  * and redirects them to the CLI's bundled versions.
28
28
  *
29
- * Only exact package name matches are handled (e.g. '@grafema/core').
29
+ * Only exact package name matches are handled (e.g. '@grafema/util').
30
30
  * All other specifiers pass through to the default resolver.
31
31
  */
32
32
  export function resolve(specifier, context, next) {
@@ -15,7 +15,21 @@
15
15
  * ```
16
16
  */
17
17
 
18
- import type { ProgressInfo } from '@grafema/core';
18
+ /**
19
+ * Progress information from the analysis pipeline.
20
+ * Defined locally to avoid dependency on @grafema/util.
21
+ */
22
+ export interface ProgressInfo {
23
+ phase: string;
24
+ currentPlugin?: string;
25
+ message?: string;
26
+ servicesDiscovered?: number;
27
+ servicesAnalyzed?: number;
28
+ totalServices?: number;
29
+ totalFiles?: number;
30
+ processedFiles?: number;
31
+ currentService?: string;
32
+ }
19
33
 
20
34
  /**
21
35
  * Options for creating a ProgressRenderer instance.
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Query hint utilities for the CLI raw query path.
3
+ *
4
+ * Note: extractQueriedTypes() is intentionally duplicated from packages/mcp/src/utils.ts.
5
+ * The CLI cannot import @grafema/mcp (dependency direction). If the Datalog syntax changes,
6
+ * both copies must be updated.
7
+ */
8
+ import { levenshtein } from '@grafema/util';
9
+
10
+ export function extractQueriedTypes(query: string): { nodeTypes: string[]; edgeTypes: string[] } {
11
+ const nodeTypes: string[] = [];
12
+ const edgeTypes: string[] = [];
13
+
14
+ // Match node(VAR, "TYPE") — only working node predicate.
15
+ // type(VAR, "TYPE") is excluded: Rust evaluator has no "type" branch.
16
+ const nodeRegex = /\bnode\([^,)]+,\s*"([^"]+)"\)/g;
17
+ let m: RegExpExecArray | null;
18
+ while ((m = nodeRegex.exec(query)) !== null) {
19
+ nodeTypes.push(m[1]);
20
+ }
21
+
22
+ const edgeRegex = /\b(?:edge|incoming)\([^,)]+,\s*[^,)]+,\s*"([^"]+)"\)/g;
23
+ while ((m = edgeRegex.exec(query)) !== null) {
24
+ edgeTypes.push(m[1]);
25
+ }
26
+
27
+ return { nodeTypes, edgeTypes };
28
+ }
29
+
30
+ export function findSimilarTypes(
31
+ queriedType: string,
32
+ availableTypes: string[],
33
+ maxDistance: number = 2
34
+ ): string[] {
35
+ const queriedLower = queriedType.toLowerCase();
36
+ const similar: string[] = [];
37
+
38
+ for (const type of availableTypes) {
39
+ const dist = levenshtein(queriedLower, type.toLowerCase());
40
+ if (dist <= maxDistance && (dist > 0 || queriedType !== type)) {
41
+ similar.push(type);
42
+ }
43
+ }
44
+
45
+ return similar;
46
+ }