@grafema/cli 0.2.11 → 0.3.0-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.
- package/dist/cli.js +13 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/analyze.d.ts.map +1 -1
- package/dist/commands/analyze.js +2 -4
- package/dist/commands/analyze.js.map +1 -1
- package/dist/commands/analyzeAction.d.ts +5 -3
- package/dist/commands/analyzeAction.d.ts.map +1 -1
- package/dist/commands/analyzeAction.js +109 -151
- package/dist/commands/analyzeAction.js.map +1 -1
- package/dist/commands/check.d.ts +1 -1
- package/dist/commands/check.js +4 -4
- package/dist/commands/check.js.map +1 -1
- package/dist/commands/context.js +2 -2
- package/dist/commands/context.js.map +1 -1
- package/dist/commands/coverage.js +2 -2
- package/dist/commands/coverage.js.map +1 -1
- package/dist/commands/describe.d.ts +13 -0
- package/dist/commands/describe.d.ts.map +1 -0
- package/dist/commands/describe.js +131 -0
- package/dist/commands/describe.js.map +1 -0
- package/dist/commands/doctor/checks.d.ts +6 -1
- package/dist/commands/doctor/checks.d.ts.map +1 -1
- package/dist/commands/doctor/checks.js +128 -13
- package/dist/commands/doctor/checks.js.map +1 -1
- package/dist/commands/doctor.d.ts +10 -9
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +12 -10
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/explain.js +2 -2
- package/dist/commands/explain.js.map +1 -1
- package/dist/commands/file.js +2 -2
- package/dist/commands/file.js.map +1 -1
- package/dist/commands/get.js +2 -2
- package/dist/commands/get.js.map +1 -1
- package/dist/commands/git-ingest.d.ts +6 -0
- package/dist/commands/git-ingest.d.ts.map +1 -0
- package/dist/commands/git-ingest.js +46 -0
- package/dist/commands/git-ingest.js.map +1 -0
- package/dist/commands/impact.d.ts.map +1 -1
- package/dist/commands/impact.js +276 -50
- package/dist/commands/impact.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +20 -22
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/ls.js +2 -2
- package/dist/commands/ls.js.map +1 -1
- package/dist/commands/overview.js +2 -2
- package/dist/commands/overview.js.map +1 -1
- package/dist/commands/query.d.ts +1 -1
- package/dist/commands/query.d.ts.map +1 -1
- package/dist/commands/query.js +169 -7
- package/dist/commands/query.js.map +1 -1
- package/dist/commands/schema.js +2 -2
- package/dist/commands/schema.js.map +1 -1
- package/dist/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +122 -76
- package/dist/commands/server.js.map +1 -1
- package/dist/commands/stats.js +2 -2
- package/dist/commands/stats.js.map +1 -1
- package/dist/commands/tldr.d.ts +12 -0
- package/dist/commands/tldr.d.ts.map +1 -0
- package/dist/commands/tldr.js +81 -0
- package/dist/commands/tldr.js.map +1 -0
- package/dist/commands/trace.d.ts +1 -1
- package/dist/commands/trace.d.ts.map +1 -1
- package/dist/commands/trace.js +17 -133
- package/dist/commands/trace.js.map +1 -1
- package/dist/commands/types.js +2 -2
- package/dist/commands/types.js.map +1 -1
- package/dist/commands/who.d.ts +12 -0
- package/dist/commands/who.d.ts.map +1 -0
- package/dist/commands/who.js +184 -0
- package/dist/commands/who.js.map +1 -0
- package/dist/commands/why.d.ts +12 -0
- package/dist/commands/why.d.ts.map +1 -0
- package/dist/commands/why.js +118 -0
- package/dist/commands/why.js.map +1 -0
- package/dist/commands/wtf.d.ts +12 -0
- package/dist/commands/wtf.d.ts.map +1 -0
- package/dist/commands/wtf.js +117 -0
- package/dist/commands/wtf.js.map +1 -0
- package/dist/plugins/builtinPlugins.d.ts +1 -9
- package/dist/plugins/builtinPlugins.d.ts.map +1 -1
- package/dist/plugins/builtinPlugins.js +2 -67
- package/dist/plugins/builtinPlugins.js.map +1 -1
- package/dist/plugins/pluginLoader.d.ts +1 -15
- package/dist/plugins/pluginLoader.d.ts.map +1 -1
- package/dist/plugins/pluginLoader.js +2 -100
- package/dist/plugins/pluginLoader.js.map +1 -1
- package/dist/plugins/pluginResolver.js +3 -3
- package/dist/utils/progressRenderer.d.ts +15 -1
- package/dist/utils/progressRenderer.d.ts.map +1 -1
- package/dist/utils/progressRenderer.js +19 -3
- package/dist/utils/progressRenderer.js.map +1 -1
- package/dist/utils/queryHints.d.ts +6 -0
- package/dist/utils/queryHints.d.ts.map +1 -0
- package/dist/utils/queryHints.js +36 -0
- package/dist/utils/queryHints.js.map +1 -0
- package/package.json +4 -4
- package/skills/grafema-codebase-analysis/SKILL.md +1 -1
- package/src/cli.ts +14 -0
- package/src/commands/analyze.ts +2 -4
- package/src/commands/analyzeAction.ts +122 -168
- package/src/commands/check.ts +5 -5
- package/src/commands/context.ts +3 -3
- package/src/commands/coverage.ts +2 -2
- package/src/commands/describe.ts +160 -0
- package/src/commands/doctor/checks.ts +153 -10
- package/src/commands/doctor.ts +13 -9
- package/src/commands/explain.ts +2 -2
- package/src/commands/explore.tsx +2 -2
- package/src/commands/file.ts +3 -3
- package/src/commands/get.ts +2 -2
- package/src/commands/git-ingest.ts +49 -0
- package/src/commands/impact.ts +318 -55
- package/src/commands/init.ts +20 -22
- package/src/commands/ls.ts +2 -2
- package/src/commands/overview.ts +2 -2
- package/src/commands/query.ts +197 -7
- package/src/commands/schema.ts +2 -2
- package/src/commands/server.ts +136 -84
- package/src/commands/stats.ts +2 -2
- package/src/commands/tldr.ts +103 -0
- package/src/commands/trace.ts +19 -161
- package/src/commands/types.ts +2 -2
- package/src/commands/who.ts +215 -0
- package/src/commands/why.ts +134 -0
- package/src/commands/wtf.ts +140 -0
- package/src/plugins/builtinPlugins.ts +1 -108
- package/src/plugins/pluginLoader.ts +1 -123
- package/src/plugins/pluginResolver.js +3 -3
- package/src/utils/progressRenderer.ts +34 -4
- package/src/utils/queryHints.ts +46 -0
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Describe command — Render compact DSL notation for a graph node
|
|
3
|
+
*
|
|
4
|
+
* Resolves target by: semantic ID → file path (MODULE) → node name.
|
|
5
|
+
* Calls extractSubgraph() + renderNotation() from @grafema/util.
|
|
6
|
+
*
|
|
7
|
+
* Output is DSL notation using archetype-grouped operators:
|
|
8
|
+
* o- dependency, > call/flow, < read/input, => write,
|
|
9
|
+
* >x exception, ~>> event, ?| guard, |= governance
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { Command } from 'commander';
|
|
13
|
+
import { resolve, join } from 'path';
|
|
14
|
+
import { existsSync } from 'fs';
|
|
15
|
+
import {
|
|
16
|
+
RFDBServerBackend,
|
|
17
|
+
renderNotation,
|
|
18
|
+
extractSubgraph,
|
|
19
|
+
PERSPECTIVES,
|
|
20
|
+
} from '@grafema/util';
|
|
21
|
+
import type { DescribeOptions } from '@grafema/util';
|
|
22
|
+
import { exitWithError } from '../utils/errorFormatter.js';
|
|
23
|
+
import { Spinner } from '../utils/spinner.js';
|
|
24
|
+
|
|
25
|
+
interface DescribeCommandOptions {
|
|
26
|
+
project: string;
|
|
27
|
+
depth: string;
|
|
28
|
+
perspective?: string;
|
|
29
|
+
budget?: string;
|
|
30
|
+
json?: boolean;
|
|
31
|
+
locations?: boolean;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export const describeCommand = new Command('describe')
|
|
35
|
+
.description('Render compact DSL notation for a graph node')
|
|
36
|
+
.argument('<target>', 'Semantic ID, file path, or node name')
|
|
37
|
+
.option('-p, --project <path>', 'Project path', '.')
|
|
38
|
+
.option('-d, --depth <level>', 'LOD: 0=names, 1=edges, 2=nested+fold, 3=nested (exact)', '1')
|
|
39
|
+
.option(
|
|
40
|
+
'--perspective <name>',
|
|
41
|
+
`Perspective preset: ${Object.keys(PERSPECTIVES).join(', ')}`,
|
|
42
|
+
)
|
|
43
|
+
.option('-b, --budget <n>', 'Max items before summarization (default 7)')
|
|
44
|
+
.option('-j, --json', 'Output as JSON for scripting')
|
|
45
|
+
.option('-l, --locations', 'Include file:line locations')
|
|
46
|
+
.addHelpText('after', `
|
|
47
|
+
Perspectives:
|
|
48
|
+
security writes + exceptions
|
|
49
|
+
data flow out/in + writes
|
|
50
|
+
errors exceptions only
|
|
51
|
+
api flow out + publishes + depends
|
|
52
|
+
events publishes only
|
|
53
|
+
|
|
54
|
+
LOD levels:
|
|
55
|
+
0 Node names only (minimal)
|
|
56
|
+
1 Node + edges (default)
|
|
57
|
+
2 Node + edges + nested children (folded)
|
|
58
|
+
3 Node + edges + nested children (exact, no folding)
|
|
59
|
+
|
|
60
|
+
Examples:
|
|
61
|
+
grafema describe "src/app.ts->FUNCTION->main"
|
|
62
|
+
grafema describe src/app.ts
|
|
63
|
+
grafema describe handleRequest
|
|
64
|
+
grafema describe handleRequest --perspective security
|
|
65
|
+
grafema describe MyClass -d 2 --locations
|
|
66
|
+
grafema describe handleRequest --json
|
|
67
|
+
`)
|
|
68
|
+
.action(async (target: string, options: DescribeCommandOptions) => {
|
|
69
|
+
const projectPath = resolve(options.project);
|
|
70
|
+
const grafemaDir = join(projectPath, '.grafema');
|
|
71
|
+
const dbPath = join(grafemaDir, 'graph.rfdb');
|
|
72
|
+
|
|
73
|
+
if (!existsSync(dbPath)) {
|
|
74
|
+
exitWithError('No graph database found', ['Run: grafema analyze']);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const depth = parseInt(options.depth, 10);
|
|
78
|
+
if (isNaN(depth) || depth < 0 || depth > 3) {
|
|
79
|
+
exitWithError('Invalid depth', ['Use 0, 1, 2, or 3']);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (options.perspective && !PERSPECTIVES[options.perspective]) {
|
|
83
|
+
exitWithError(`Unknown perspective: "${options.perspective}"`, [
|
|
84
|
+
`Available: ${Object.keys(PERSPECTIVES).join(', ')}`,
|
|
85
|
+
]);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const backend = new RFDBServerBackend({ dbPath, clientName: 'cli' });
|
|
89
|
+
await backend.connect();
|
|
90
|
+
|
|
91
|
+
const spinner = new Spinner('Resolving target...');
|
|
92
|
+
spinner.start();
|
|
93
|
+
|
|
94
|
+
try {
|
|
95
|
+
// Step 1: Resolve target → node
|
|
96
|
+
let node = await backend.getNode(target);
|
|
97
|
+
|
|
98
|
+
if (!node) {
|
|
99
|
+
for await (const n of backend.queryNodes({ file: target, type: 'MODULE' })) {
|
|
100
|
+
node = n;
|
|
101
|
+
break;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
if (!node) {
|
|
105
|
+
for await (const n of backend.queryNodes({ name: target })) {
|
|
106
|
+
node = n;
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (!node) {
|
|
112
|
+
spinner.stop();
|
|
113
|
+
exitWithError(`Target not found: "${target}"`, [
|
|
114
|
+
'Use: grafema query "<name>" to find available nodes',
|
|
115
|
+
'Try: semantic ID, file path, or node name',
|
|
116
|
+
]);
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Step 2: Extract subgraph
|
|
121
|
+
const subgraph = await extractSubgraph(backend, node.id, depth);
|
|
122
|
+
|
|
123
|
+
// Step 3: Build options
|
|
124
|
+
const describeOptions: DescribeOptions = {
|
|
125
|
+
depth,
|
|
126
|
+
includeLocations: options.locations ?? depth >= 2,
|
|
127
|
+
};
|
|
128
|
+
if (options.perspective && PERSPECTIVES[options.perspective]) {
|
|
129
|
+
describeOptions.archetypeFilter = PERSPECTIVES[options.perspective];
|
|
130
|
+
}
|
|
131
|
+
if (options.budget) {
|
|
132
|
+
describeOptions.budget = parseInt(options.budget, 10);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Step 4: Render
|
|
136
|
+
const notation = renderNotation(subgraph, describeOptions);
|
|
137
|
+
|
|
138
|
+
spinner.stop();
|
|
139
|
+
|
|
140
|
+
if (options.json) {
|
|
141
|
+
console.log(JSON.stringify({
|
|
142
|
+
target: node.id,
|
|
143
|
+
dsl: notation || `[${node.type}] ${node.name ?? node.id}\nNo relationships found at depth=${depth}.`,
|
|
144
|
+
subgraph: {
|
|
145
|
+
rootNodes: subgraph.rootNodes.length,
|
|
146
|
+
edges: subgraph.edges.length,
|
|
147
|
+
nodes: subgraph.nodeMap.size,
|
|
148
|
+
},
|
|
149
|
+
}, null, 2));
|
|
150
|
+
} else if (notation.trim()) {
|
|
151
|
+
console.log(notation);
|
|
152
|
+
} else {
|
|
153
|
+
console.log(`[${node.type}] ${node.name ?? node.id}`);
|
|
154
|
+
console.log(`No relationships found at depth=${depth}.`);
|
|
155
|
+
}
|
|
156
|
+
} finally {
|
|
157
|
+
spinner.stop();
|
|
158
|
+
await backend.close();
|
|
159
|
+
}
|
|
160
|
+
});
|
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
* Diagnostic check functions for `grafema doctor` command - REG-214
|
|
3
3
|
*
|
|
4
4
|
* Checks are organized in levels:
|
|
5
|
-
* - Level 1: Prerequisites (fail-fast) - checkGrafemaInitialized, checkServerStatus
|
|
5
|
+
* - Level 1: Prerequisites (fail-fast) - checkBinaries, checkGrafemaInitialized, checkServerStatus
|
|
6
6
|
* - Level 2: Configuration - checkConfigValidity, checkEntrypoints
|
|
7
7
|
* - Level 3: Graph Health - checkDatabaseExists, checkGraphStats, checkConnectivity, checkFreshness
|
|
8
8
|
* - Level 4: Informational - checkVersions
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import { existsSync, readFileSync, statSync } from 'fs';
|
|
12
|
-
import { join, dirname } from 'path';
|
|
12
|
+
import { join, dirname, delimiter } from 'path';
|
|
13
13
|
import { fileURLToPath } from 'url';
|
|
14
14
|
import { createRequire } from 'module';
|
|
15
15
|
import {
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
RFDBClient,
|
|
18
18
|
loadConfig,
|
|
19
19
|
GraphFreshnessChecker,
|
|
20
|
-
} from '@grafema/
|
|
20
|
+
} from '@grafema/util';
|
|
21
21
|
import type { DoctorCheckResult } from './types.js';
|
|
22
22
|
|
|
23
23
|
const __filename = fileURLToPath(import.meta.url);
|
|
@@ -38,13 +38,156 @@ const VALID_PLUGIN_NAMES = new Set([
|
|
|
38
38
|
'RustFFIEnricher',
|
|
39
39
|
// Validation
|
|
40
40
|
'CallResolverValidator', 'EvalBanValidator', 'SQLInjectionValidator', 'ShadowingDetector',
|
|
41
|
-
'GraphConnectivityValidator', 'DataFlowValidator',
|
|
41
|
+
'GraphConnectivityValidator', 'DataFlowValidator',
|
|
42
42
|
]);
|
|
43
43
|
|
|
44
44
|
// =============================================================================
|
|
45
45
|
// Level 1: Prerequisites (fail-fast)
|
|
46
46
|
// =============================================================================
|
|
47
47
|
|
|
48
|
+
/**
|
|
49
|
+
* Binary lookup config — mirrors packages/grafema/src/binaries.ts but inlined
|
|
50
|
+
* to avoid cross-package dependency (CLI does not depend on the `grafema` pkg).
|
|
51
|
+
*/
|
|
52
|
+
type BinaryName = 'rfdb-server' | 'grafema-orchestrator';
|
|
53
|
+
|
|
54
|
+
interface BinaryLookupResult {
|
|
55
|
+
name: BinaryName;
|
|
56
|
+
path: string | null;
|
|
57
|
+
source: string | null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function findBinaryForDoctor(binaryName: BinaryName): BinaryLookupResult {
|
|
61
|
+
const envVars: Record<BinaryName, string> = {
|
|
62
|
+
'rfdb-server': 'GRAFEMA_RFDB_SERVER',
|
|
63
|
+
'grafema-orchestrator': 'GRAFEMA_ORCHESTRATOR',
|
|
64
|
+
};
|
|
65
|
+
const monorepoPackages: Record<BinaryName, string> = {
|
|
66
|
+
'rfdb-server': 'rfdb-server',
|
|
67
|
+
'grafema-orchestrator': 'grafema-orchestrator',
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const envVar = envVars[binaryName];
|
|
71
|
+
const monorepoPkg = monorepoPackages[binaryName];
|
|
72
|
+
|
|
73
|
+
// 1. Environment variable
|
|
74
|
+
const envPath = process.env[envVar];
|
|
75
|
+
if (envPath && existsSync(envPath)) {
|
|
76
|
+
return { name: binaryName, path: envPath, source: `$${envVar}` };
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// 2. Monorepo development builds (release, then debug)
|
|
80
|
+
// Walk up from this file to find monorepo root
|
|
81
|
+
let dir = __dirname;
|
|
82
|
+
let monorepoRoot: string | null = null;
|
|
83
|
+
for (let i = 0; i < 8; i++) {
|
|
84
|
+
if (existsSync(join(dir, 'packages', 'util')) && existsSync(join(dir, 'pnpm-workspace.yaml'))) {
|
|
85
|
+
monorepoRoot = dir;
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
88
|
+
const parent = dirname(dir);
|
|
89
|
+
if (parent === dir) break;
|
|
90
|
+
dir = parent;
|
|
91
|
+
}
|
|
92
|
+
if (!monorepoRoot) {
|
|
93
|
+
const envRoot = process.env.GRAFEMA_ROOT;
|
|
94
|
+
if (envRoot && existsSync(join(envRoot, 'packages', 'util'))) {
|
|
95
|
+
monorepoRoot = envRoot;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (monorepoRoot) {
|
|
100
|
+
for (const profile of ['release', 'debug'] as const) {
|
|
101
|
+
const p = join(monorepoRoot, 'packages', monorepoPkg, 'target', profile, binaryName);
|
|
102
|
+
if (existsSync(p)) {
|
|
103
|
+
return { name: binaryName, path: p, source: `monorepo (${profile})` };
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// 3. System PATH
|
|
109
|
+
const pathDirs = (process.env.PATH || '').split(delimiter);
|
|
110
|
+
for (const pathDir of pathDirs) {
|
|
111
|
+
if (!pathDir) continue;
|
|
112
|
+
const p = join(pathDir, binaryName);
|
|
113
|
+
if (existsSync(p)) {
|
|
114
|
+
return { name: binaryName, path: p, source: 'PATH' };
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// 4. ~/.local/bin
|
|
119
|
+
const home = process.env.HOME || process.env.USERPROFILE || '';
|
|
120
|
+
if (home) {
|
|
121
|
+
const p = join(home, '.local', 'bin', binaryName);
|
|
122
|
+
if (existsSync(p)) {
|
|
123
|
+
return { name: binaryName, path: p, source: '~/.local/bin' };
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return { name: binaryName, path: null, source: null };
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Check if native binaries (rfdb-server, grafema-orchestrator) are findable.
|
|
132
|
+
* FAIL if both missing, WARN if one missing, PASS if both found.
|
|
133
|
+
*/
|
|
134
|
+
export async function checkBinaries(): Promise<DoctorCheckResult> {
|
|
135
|
+
const rfdb = findBinaryForDoctor('rfdb-server');
|
|
136
|
+
const orchestrator = findBinaryForDoctor('grafema-orchestrator');
|
|
137
|
+
|
|
138
|
+
const found: string[] = [];
|
|
139
|
+
const missing: string[] = [];
|
|
140
|
+
|
|
141
|
+
if (rfdb.path) {
|
|
142
|
+
found.push(`rfdb-server (${rfdb.source})`);
|
|
143
|
+
} else {
|
|
144
|
+
missing.push('rfdb-server');
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (orchestrator.path) {
|
|
148
|
+
found.push(`grafema-orchestrator (${orchestrator.source})`);
|
|
149
|
+
} else {
|
|
150
|
+
missing.push('grafema-orchestrator');
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (missing.length === 0) {
|
|
154
|
+
return {
|
|
155
|
+
name: 'binaries',
|
|
156
|
+
status: 'pass',
|
|
157
|
+
message: `Binaries: ${found.join(', ')}`,
|
|
158
|
+
details: {
|
|
159
|
+
rfdbServer: rfdb.path,
|
|
160
|
+
orchestrator: orchestrator.path,
|
|
161
|
+
},
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (missing.length === 2) {
|
|
166
|
+
return {
|
|
167
|
+
name: 'binaries',
|
|
168
|
+
status: 'fail',
|
|
169
|
+
message: 'Native binaries not found: rfdb-server, grafema-orchestrator',
|
|
170
|
+
recommendation:
|
|
171
|
+
'Install: npm install grafema, or build from source: cd packages/<name> && cargo build --release, or set GRAFEMA_RFDB_SERVER / GRAFEMA_ORCHESTRATOR env vars',
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// One found, one missing
|
|
176
|
+
return {
|
|
177
|
+
name: 'binaries',
|
|
178
|
+
status: 'warn',
|
|
179
|
+
message: `Missing binary: ${missing[0]} (found: ${found[0]})`,
|
|
180
|
+
recommendation:
|
|
181
|
+
missing[0] === 'rfdb-server'
|
|
182
|
+
? 'Set GRAFEMA_RFDB_SERVER env var or build: cd packages/rfdb-server && cargo build --release'
|
|
183
|
+
: 'Set GRAFEMA_ORCHESTRATOR env var or build: cd packages/grafema-orchestrator && cargo build --release',
|
|
184
|
+
details: {
|
|
185
|
+
rfdbServer: rfdb.path,
|
|
186
|
+
orchestrator: orchestrator.path,
|
|
187
|
+
},
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
|
|
48
191
|
/**
|
|
49
192
|
* Check if .grafema directory exists with config file.
|
|
50
193
|
* FAIL if not initialized.
|
|
@@ -103,7 +246,7 @@ export async function checkServerStatus(
|
|
|
103
246
|
};
|
|
104
247
|
}
|
|
105
248
|
|
|
106
|
-
const client = new RFDBClient(socketPath);
|
|
249
|
+
const client = new RFDBClient(socketPath, 'cli');
|
|
107
250
|
client.on('error', () => {}); // Suppress error events
|
|
108
251
|
|
|
109
252
|
try {
|
|
@@ -330,7 +473,7 @@ export async function checkGraphStats(
|
|
|
330
473
|
};
|
|
331
474
|
}
|
|
332
475
|
|
|
333
|
-
const backend = new RFDBServerBackend({ dbPath });
|
|
476
|
+
const backend = new RFDBServerBackend({ dbPath, clientName: 'cli' });
|
|
334
477
|
try {
|
|
335
478
|
await backend.connect();
|
|
336
479
|
const stats = await backend.getStats();
|
|
@@ -387,7 +530,7 @@ export async function checkConnectivity(
|
|
|
387
530
|
};
|
|
388
531
|
}
|
|
389
532
|
|
|
390
|
-
const backend = new RFDBServerBackend({ dbPath });
|
|
533
|
+
const backend = new RFDBServerBackend({ dbPath, clientName: 'cli' });
|
|
391
534
|
try {
|
|
392
535
|
await backend.connect();
|
|
393
536
|
|
|
@@ -523,7 +666,7 @@ export async function checkFreshness(
|
|
|
523
666
|
};
|
|
524
667
|
}
|
|
525
668
|
|
|
526
|
-
const backend = new RFDBServerBackend({ dbPath });
|
|
669
|
+
const backend = new RFDBServerBackend({ dbPath, clientName: 'cli' });
|
|
527
670
|
try {
|
|
528
671
|
await backend.connect();
|
|
529
672
|
const freshnessChecker = new GraphFreshnessChecker();
|
|
@@ -584,7 +727,7 @@ export async function checkVersions(
|
|
|
584
727
|
// Read core version
|
|
585
728
|
try {
|
|
586
729
|
const require = createRequire(import.meta.url);
|
|
587
|
-
const corePkgPath = require.resolve('@grafema/
|
|
730
|
+
const corePkgPath = require.resolve('@grafema/util/package.json');
|
|
588
731
|
const corePkg = JSON.parse(readFileSync(corePkgPath, 'utf-8'));
|
|
589
732
|
coreVersion = corePkg.version;
|
|
590
733
|
} catch {
|
|
@@ -594,7 +737,7 @@ export async function checkVersions(
|
|
|
594
737
|
// Get RFDB version from server if running
|
|
595
738
|
const socketPath = join(projectPath, '.grafema', 'rfdb.sock');
|
|
596
739
|
if (existsSync(socketPath)) {
|
|
597
|
-
const client = new RFDBClient(socketPath);
|
|
740
|
+
const client = new RFDBClient(socketPath, 'cli');
|
|
598
741
|
client.on('error', () => {});
|
|
599
742
|
try {
|
|
600
743
|
await client.connect();
|
package/src/commands/doctor.ts
CHANGED
|
@@ -2,20 +2,22 @@
|
|
|
2
2
|
* Doctor command - Diagnose Grafema setup issues
|
|
3
3
|
*
|
|
4
4
|
* Checks (in order):
|
|
5
|
-
* 1.
|
|
6
|
-
* 2.
|
|
7
|
-
* 3.
|
|
8
|
-
* 4.
|
|
9
|
-
* 5.
|
|
10
|
-
* 6.
|
|
11
|
-
* 7. Graph
|
|
12
|
-
* 8. Graph
|
|
13
|
-
* 9.
|
|
5
|
+
* 1. Binaries (rfdb-server, grafema-orchestrator)
|
|
6
|
+
* 2. Initialization (.grafema directory, config file)
|
|
7
|
+
* 3. Config validity (syntax, plugin names)
|
|
8
|
+
* 4. Entrypoints (service paths exist)
|
|
9
|
+
* 5. Server status (RFDB server running)
|
|
10
|
+
* 6. Database exists and has data
|
|
11
|
+
* 7. Graph statistics
|
|
12
|
+
* 8. Graph connectivity
|
|
13
|
+
* 9. Graph freshness
|
|
14
|
+
* 10. Version information
|
|
14
15
|
*/
|
|
15
16
|
|
|
16
17
|
import { Command } from 'commander';
|
|
17
18
|
import { resolve } from 'path';
|
|
18
19
|
import {
|
|
20
|
+
checkBinaries,
|
|
19
21
|
checkGrafemaInitialized,
|
|
20
22
|
checkServerStatus,
|
|
21
23
|
checkConfigValidity,
|
|
@@ -47,6 +49,8 @@ Examples:
|
|
|
47
49
|
const checks: DoctorCheckResult[] = [];
|
|
48
50
|
|
|
49
51
|
// Level 1: Prerequisites (fail-fast)
|
|
52
|
+
checks.push(await checkBinaries());
|
|
53
|
+
|
|
50
54
|
const initCheck = await checkGrafemaInitialized(projectPath);
|
|
51
55
|
checks.push(initCheck);
|
|
52
56
|
|
package/src/commands/explain.ts
CHANGED
|
@@ -16,7 +16,7 @@ import { Command } from 'commander';
|
|
|
16
16
|
import { resolve, join, relative, normalize } from 'path';
|
|
17
17
|
import { existsSync, realpathSync } from 'fs';
|
|
18
18
|
import { toRelativeDisplay } from '../utils/pathUtils.js';
|
|
19
|
-
import { RFDBServerBackend, FileExplainer, type EnhancedNode } from '@grafema/
|
|
19
|
+
import { RFDBServerBackend, FileExplainer, type EnhancedNode } from '@grafema/util';
|
|
20
20
|
import { exitWithError } from '../utils/errorFormatter.js';
|
|
21
21
|
|
|
22
22
|
interface ExplainOptions {
|
|
@@ -81,7 +81,7 @@ If a file shows NOT_ANALYZED:
|
|
|
81
81
|
// Keep relative path for display
|
|
82
82
|
const relativeFilePath = relative(projectPath, absoluteFilePath);
|
|
83
83
|
|
|
84
|
-
const backend = new RFDBServerBackend({ dbPath });
|
|
84
|
+
const backend = new RFDBServerBackend({ dbPath, clientName: 'cli' });
|
|
85
85
|
await backend.connect();
|
|
86
86
|
|
|
87
87
|
try {
|
package/src/commands/explore.tsx
CHANGED
|
@@ -15,7 +15,7 @@ import { existsSync } from 'fs';
|
|
|
15
15
|
import { execSync } from 'child_process';
|
|
16
16
|
import React, { useState, useEffect } from 'react';
|
|
17
17
|
import { render, Box, Text, useInput, useApp } from 'ink';
|
|
18
|
-
import { RFDBServerBackend, findContainingFunction as findContainingFunctionCore, findCallsInFunction as findCallsInFunctionCore } from '@grafema/
|
|
18
|
+
import { RFDBServerBackend, findContainingFunction as findContainingFunctionCore, findCallsInFunction as findCallsInFunctionCore } from '@grafema/util';
|
|
19
19
|
import { getCodePreview, formatCodePreview } from '../utils/codePreview.js';
|
|
20
20
|
import { exitWithError } from '../utils/errorFormatter.js';
|
|
21
21
|
|
|
@@ -816,7 +816,7 @@ async function getCallees(backend: RFDBServerBackend, nodeId: string, limit: num
|
|
|
816
816
|
const seen = new Set<string>();
|
|
817
817
|
|
|
818
818
|
try {
|
|
819
|
-
// Use shared utility from @grafema/
|
|
819
|
+
// Use shared utility from @grafema/util
|
|
820
820
|
const calls = await findCallsInFunctionCore(backend, nodeId);
|
|
821
821
|
|
|
822
822
|
for (const call of calls) {
|
package/src/commands/file.ts
CHANGED
|
@@ -14,8 +14,8 @@
|
|
|
14
14
|
import { Command } from 'commander';
|
|
15
15
|
import { resolve, join, relative, normalize } from 'path';
|
|
16
16
|
import { existsSync, realpathSync } from 'fs';
|
|
17
|
-
import { RFDBServerBackend, FileOverview } from '@grafema/
|
|
18
|
-
import type { FileOverviewResult, FunctionOverview } from '@grafema/
|
|
17
|
+
import { RFDBServerBackend, FileOverview } from '@grafema/util';
|
|
18
|
+
import type { FileOverviewResult, FunctionOverview } from '@grafema/util';
|
|
19
19
|
import { exitWithError } from '../utils/errorFormatter.js';
|
|
20
20
|
import { Spinner } from '../utils/spinner.js';
|
|
21
21
|
|
|
@@ -79,7 +79,7 @@ Use 'grafema context <id>' to dive deeper into any specific entity.
|
|
|
79
79
|
const absoluteFilePath = realpathSync(resolvedPath);
|
|
80
80
|
const relativeFilePath = relative(projectPath, absoluteFilePath);
|
|
81
81
|
|
|
82
|
-
const backend = new RFDBServerBackend({ dbPath });
|
|
82
|
+
const backend = new RFDBServerBackend({ dbPath, clientName: 'cli' });
|
|
83
83
|
await backend.connect();
|
|
84
84
|
|
|
85
85
|
const spinner = new Spinner('Loading file overview...');
|
package/src/commands/get.ts
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
import { Command } from 'commander';
|
|
10
10
|
import { resolve, join } from 'path';
|
|
11
11
|
import { existsSync } from 'fs';
|
|
12
|
-
import { RFDBServerBackend } from '@grafema/
|
|
12
|
+
import { RFDBServerBackend } from '@grafema/util';
|
|
13
13
|
import { formatNodeDisplay } from '../utils/formatNode.js';
|
|
14
14
|
import { exitWithError } from '../utils/errorFormatter.js';
|
|
15
15
|
import { Spinner } from '../utils/spinner.js';
|
|
@@ -64,7 +64,7 @@ Examples:
|
|
|
64
64
|
exitWithError('No graph database found', ['Run: grafema analyze']);
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
const backend = new RFDBServerBackend({ dbPath });
|
|
67
|
+
const backend = new RFDBServerBackend({ dbPath, clientName: 'cli' });
|
|
68
68
|
await backend.connect();
|
|
69
69
|
|
|
70
70
|
const spinner = new Spinner('Querying graph...');
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Git Ingest command - ingest git history into knowledge layer
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { Command } from 'commander';
|
|
6
|
+
import { resolve, join } from 'path';
|
|
7
|
+
import { GitIngest } from '@grafema/util';
|
|
8
|
+
|
|
9
|
+
export const gitIngestCommand = new Command('git-ingest')
|
|
10
|
+
.description('Ingest git history into the knowledge layer')
|
|
11
|
+
.argument('[path]', 'Repository path', '.')
|
|
12
|
+
.option('--full', 'Full re-ingest (rebuilds derived/)')
|
|
13
|
+
.option('--since <date>', 'Ingest from date (ISO format)')
|
|
14
|
+
.option('--branch <branch>', 'Ingest specific branch')
|
|
15
|
+
.addHelpText('after', `
|
|
16
|
+
Examples:
|
|
17
|
+
grafema git-ingest Incremental ingest from last cursor
|
|
18
|
+
grafema git-ingest --full Full re-ingest of all history
|
|
19
|
+
grafema git-ingest --full ./my-repo Full ingest of specific repo
|
|
20
|
+
grafema git-ingest --branch develop Ingest specific branch
|
|
21
|
+
grafema git-ingest --since 2025-01-01 Ingest from specific date
|
|
22
|
+
`)
|
|
23
|
+
.action(async (path: string, options: { full?: boolean; since?: string; branch?: string }) => {
|
|
24
|
+
const repoPath = resolve(path);
|
|
25
|
+
const knowledgeDir = join(repoPath, '.grafema', 'knowledge');
|
|
26
|
+
|
|
27
|
+
const ingest = new GitIngest(knowledgeDir);
|
|
28
|
+
|
|
29
|
+
console.log(`Ingesting git history from ${repoPath}...`);
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
let result;
|
|
33
|
+
if (options.full || options.since) {
|
|
34
|
+
result = await ingest.ingestFull(repoPath, options.branch, options.since);
|
|
35
|
+
} else {
|
|
36
|
+
result = await ingest.ingestIncremental(repoPath, options.branch);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
console.log('Git ingest complete:');
|
|
40
|
+
console.log(` Commits: ${result.commits}`);
|
|
41
|
+
console.log(` Authors: ${result.authors}`);
|
|
42
|
+
console.log(` Files changed: ${result.filesChanged}`);
|
|
43
|
+
console.log(`\nData written to ${knowledgeDir}/derived/`);
|
|
44
|
+
} catch (error) {
|
|
45
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
46
|
+
console.error(`Error: ${message}`);
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
});
|