@grafema/cli 0.1.1-alpha → 0.2.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 +10 -0
- package/dist/commands/analyze.d.ts.map +1 -1
- package/dist/commands/analyze.js +28 -7
- package/dist/commands/check.d.ts +6 -0
- package/dist/commands/check.d.ts.map +1 -1
- package/dist/commands/check.js +177 -1
- package/dist/commands/coverage.d.ts.map +1 -1
- package/dist/commands/coverage.js +7 -0
- package/dist/commands/doctor/checks.d.ts +55 -0
- package/dist/commands/doctor/checks.d.ts.map +1 -0
- package/dist/commands/doctor/checks.js +534 -0
- package/dist/commands/doctor/output.d.ts +20 -0
- package/dist/commands/doctor/output.d.ts.map +1 -0
- package/dist/commands/doctor/output.js +94 -0
- package/dist/commands/doctor/types.d.ts +42 -0
- package/dist/commands/doctor/types.d.ts.map +1 -0
- package/dist/commands/doctor/types.js +4 -0
- package/dist/commands/doctor.d.ts +17 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +80 -0
- package/dist/commands/explain.d.ts +16 -0
- package/dist/commands/explain.d.ts.map +1 -0
- package/dist/commands/explain.js +145 -0
- package/dist/commands/explore.d.ts +7 -1
- package/dist/commands/explore.d.ts.map +1 -1
- package/dist/commands/explore.js +204 -85
- package/dist/commands/get.d.ts.map +1 -1
- package/dist/commands/get.js +16 -4
- package/dist/commands/impact.d.ts.map +1 -1
- package/dist/commands/impact.js +48 -50
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +93 -15
- package/dist/commands/ls.d.ts +14 -0
- package/dist/commands/ls.d.ts.map +1 -0
- package/dist/commands/ls.js +132 -0
- package/dist/commands/overview.d.ts.map +1 -1
- package/dist/commands/overview.js +15 -2
- package/dist/commands/query.d.ts +98 -0
- package/dist/commands/query.d.ts.map +1 -1
- package/dist/commands/query.js +549 -136
- package/dist/commands/schema.d.ts +13 -0
- package/dist/commands/schema.d.ts.map +1 -0
- package/dist/commands/schema.js +279 -0
- package/dist/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +13 -6
- package/dist/commands/stats.d.ts.map +1 -1
- package/dist/commands/stats.js +7 -0
- package/dist/commands/trace.d.ts +73 -0
- package/dist/commands/trace.d.ts.map +1 -1
- package/dist/commands/trace.js +500 -5
- package/dist/commands/types.d.ts +12 -0
- package/dist/commands/types.d.ts.map +1 -0
- package/dist/commands/types.js +79 -0
- package/dist/utils/formatNode.d.ts +13 -0
- package/dist/utils/formatNode.d.ts.map +1 -1
- package/dist/utils/formatNode.js +35 -2
- package/package.json +3 -3
- package/src/cli.ts +10 -0
- package/src/commands/analyze.ts +31 -5
- package/src/commands/check.ts +201 -0
- package/src/commands/coverage.ts +7 -0
- package/src/commands/doctor/checks.ts +612 -0
- package/src/commands/doctor/output.ts +115 -0
- package/src/commands/doctor/types.ts +45 -0
- package/src/commands/doctor.ts +106 -0
- package/src/commands/explain.ts +173 -0
- package/src/commands/explore.tsx +247 -97
- package/src/commands/get.ts +20 -6
- package/src/commands/impact.ts +55 -61
- package/src/commands/init.ts +101 -14
- package/src/commands/ls.ts +166 -0
- package/src/commands/overview.ts +15 -2
- package/src/commands/query.ts +643 -149
- package/src/commands/schema.ts +345 -0
- package/src/commands/server.ts +13 -6
- package/src/commands/stats.ts +7 -0
- package/src/commands/trace.ts +647 -6
- package/src/commands/types.ts +94 -0
- package/src/utils/formatNode.ts +42 -2
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Schema command - Export code schemas
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* grafema schema export --interface ConfigSchema
|
|
6
|
+
* grafema schema export --interface ConfigSchema --format yaml
|
|
7
|
+
* grafema schema export --interface ConfigSchema --file src/config/types.ts
|
|
8
|
+
* grafema schema export --graph
|
|
9
|
+
* grafema schema export --graph --format yaml
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { Command } from 'commander';
|
|
13
|
+
import { resolve, join, relative } from 'path';
|
|
14
|
+
import { existsSync, writeFileSync } from 'fs';
|
|
15
|
+
import {
|
|
16
|
+
RFDBServerBackend,
|
|
17
|
+
InterfaceSchemaExtractor,
|
|
18
|
+
GraphSchemaExtractor,
|
|
19
|
+
type InterfaceSchema,
|
|
20
|
+
type GraphSchema,
|
|
21
|
+
type NodeTypeSchema,
|
|
22
|
+
type EdgeTypeSchema,
|
|
23
|
+
} from '@grafema/core';
|
|
24
|
+
import { exitWithError } from '../utils/errorFormatter.js';
|
|
25
|
+
|
|
26
|
+
interface ExportOptions {
|
|
27
|
+
project: string;
|
|
28
|
+
interface?: string;
|
|
29
|
+
graph?: boolean;
|
|
30
|
+
all?: boolean;
|
|
31
|
+
file?: string;
|
|
32
|
+
format: 'json' | 'yaml' | 'markdown';
|
|
33
|
+
output?: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// ============================================================================
|
|
37
|
+
// Interface Schema Formatters
|
|
38
|
+
// ============================================================================
|
|
39
|
+
|
|
40
|
+
function formatInterfaceJson(schema: InterfaceSchema): string {
|
|
41
|
+
return JSON.stringify(schema, null, 2);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function formatInterfaceYaml(schema: InterfaceSchema): string {
|
|
45
|
+
const lines: string[] = [];
|
|
46
|
+
|
|
47
|
+
lines.push(`$schema: ${schema.$schema}`);
|
|
48
|
+
lines.push(`name: ${schema.name}`);
|
|
49
|
+
lines.push('source:');
|
|
50
|
+
lines.push(` file: ${schema.source.file}`);
|
|
51
|
+
lines.push(` line: ${schema.source.line}`);
|
|
52
|
+
lines.push(` column: ${schema.source.column}`);
|
|
53
|
+
|
|
54
|
+
if (schema.typeParameters && schema.typeParameters.length > 0) {
|
|
55
|
+
lines.push('typeParameters:');
|
|
56
|
+
for (const param of schema.typeParameters) {
|
|
57
|
+
lines.push(` - "${param}"`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
lines.push('properties:');
|
|
62
|
+
for (const [name, prop] of Object.entries(schema.properties)) {
|
|
63
|
+
lines.push(` ${name}:`);
|
|
64
|
+
lines.push(` type: "${prop.type}"`);
|
|
65
|
+
lines.push(` required: ${prop.required}`);
|
|
66
|
+
lines.push(` readonly: ${prop.readonly}`);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (schema.extends.length > 0) {
|
|
70
|
+
lines.push('extends:');
|
|
71
|
+
for (const ext of schema.extends) {
|
|
72
|
+
lines.push(` - ${ext}`);
|
|
73
|
+
}
|
|
74
|
+
} else {
|
|
75
|
+
lines.push('extends: []');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
lines.push(`checksum: ${schema.checksum}`);
|
|
79
|
+
|
|
80
|
+
return lines.join('\n');
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function formatInterfaceMarkdown(schema: InterfaceSchema, projectPath: string): string {
|
|
84
|
+
const lines: string[] = [];
|
|
85
|
+
const relPath = relative(projectPath, schema.source.file);
|
|
86
|
+
|
|
87
|
+
lines.push(`# Interface: ${schema.name}`);
|
|
88
|
+
lines.push('');
|
|
89
|
+
|
|
90
|
+
if (schema.typeParameters && schema.typeParameters.length > 0) {
|
|
91
|
+
lines.push(`**Type Parameters:** \`<${schema.typeParameters.join(', ')}>\``);
|
|
92
|
+
lines.push('');
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
lines.push(`**Source:** \`${relPath}:${schema.source.line}\``);
|
|
96
|
+
lines.push('');
|
|
97
|
+
|
|
98
|
+
if (schema.extends.length > 0) {
|
|
99
|
+
lines.push(`**Extends:** ${schema.extends.map(e => `\`${e}\``).join(', ')}`);
|
|
100
|
+
lines.push('');
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
lines.push('## Properties');
|
|
104
|
+
lines.push('');
|
|
105
|
+
lines.push('| Name | Type | Required | Readonly |');
|
|
106
|
+
lines.push('|------|------|----------|----------|');
|
|
107
|
+
|
|
108
|
+
for (const [name, prop] of Object.entries(schema.properties)) {
|
|
109
|
+
const required = prop.required ? 'Yes' : 'No';
|
|
110
|
+
const readonly = prop.readonly ? 'Yes' : 'No';
|
|
111
|
+
lines.push(`| \`${name}\` | \`${prop.type}\` | ${required} | ${readonly} |`);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
lines.push('');
|
|
115
|
+
lines.push('---');
|
|
116
|
+
lines.push('');
|
|
117
|
+
lines.push(`*Checksum: \`${schema.checksum}\`*`);
|
|
118
|
+
|
|
119
|
+
return lines.join('\n');
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Check if schema has method properties (type='function')
|
|
124
|
+
* Used to show Phase 1 limitation warning
|
|
125
|
+
*/
|
|
126
|
+
function hasMethodProperties(schema: InterfaceSchema): boolean {
|
|
127
|
+
return Object.values(schema.properties).some(p => p.type === 'function');
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// ============================================================================
|
|
131
|
+
// Graph Schema Formatters
|
|
132
|
+
// ============================================================================
|
|
133
|
+
|
|
134
|
+
function formatGraphJson(schema: GraphSchema): string {
|
|
135
|
+
return JSON.stringify(schema, null, 2);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function formatGraphYaml(schema: GraphSchema): string {
|
|
139
|
+
const lines: string[] = [];
|
|
140
|
+
|
|
141
|
+
lines.push(`$schema: ${schema.$schema}`);
|
|
142
|
+
lines.push(`extractedAt: ${schema.extractedAt}`);
|
|
143
|
+
lines.push('');
|
|
144
|
+
|
|
145
|
+
lines.push('statistics:');
|
|
146
|
+
lines.push(` totalNodes: ${schema.statistics.totalNodes}`);
|
|
147
|
+
lines.push(` totalEdges: ${schema.statistics.totalEdges}`);
|
|
148
|
+
lines.push(` nodeTypeCount: ${schema.statistics.nodeTypeCount}`);
|
|
149
|
+
lines.push(` edgeTypeCount: ${schema.statistics.edgeTypeCount}`);
|
|
150
|
+
lines.push('');
|
|
151
|
+
|
|
152
|
+
lines.push('nodeTypes:');
|
|
153
|
+
for (const [type, info] of Object.entries(schema.nodeTypes) as [string, NodeTypeSchema][]) {
|
|
154
|
+
if (info.count > 0) {
|
|
155
|
+
lines.push(` ${type}:`);
|
|
156
|
+
lines.push(` category: ${info.category}`);
|
|
157
|
+
if (info.namespace) {
|
|
158
|
+
lines.push(` namespace: ${info.namespace}`);
|
|
159
|
+
}
|
|
160
|
+
lines.push(` count: ${info.count}`);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
lines.push('');
|
|
164
|
+
|
|
165
|
+
lines.push('edgeTypes:');
|
|
166
|
+
for (const [type, info] of Object.entries(schema.edgeTypes) as [string, EdgeTypeSchema][]) {
|
|
167
|
+
if (info.count > 0) {
|
|
168
|
+
lines.push(` ${type}:`);
|
|
169
|
+
lines.push(` count: ${info.count}`);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
lines.push('');
|
|
173
|
+
|
|
174
|
+
lines.push(`checksum: ${schema.checksum}`);
|
|
175
|
+
|
|
176
|
+
return lines.join('\n');
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function formatGraphMarkdown(schema: GraphSchema): string {
|
|
180
|
+
const lines: string[] = [];
|
|
181
|
+
|
|
182
|
+
lines.push('# Graph Schema');
|
|
183
|
+
lines.push('');
|
|
184
|
+
lines.push(`**Extracted:** ${schema.extractedAt}`);
|
|
185
|
+
lines.push('');
|
|
186
|
+
|
|
187
|
+
lines.push('## Statistics');
|
|
188
|
+
lines.push('');
|
|
189
|
+
lines.push(`- Total Nodes: ${schema.statistics.totalNodes}`);
|
|
190
|
+
lines.push(`- Total Edges: ${schema.statistics.totalEdges}`);
|
|
191
|
+
lines.push(`- Node Types: ${schema.statistics.nodeTypeCount}`);
|
|
192
|
+
lines.push(`- Edge Types: ${schema.statistics.edgeTypeCount}`);
|
|
193
|
+
lines.push('');
|
|
194
|
+
|
|
195
|
+
lines.push('## Node Types');
|
|
196
|
+
lines.push('');
|
|
197
|
+
lines.push('| Type | Category | Count |');
|
|
198
|
+
lines.push('|------|----------|-------|');
|
|
199
|
+
|
|
200
|
+
for (const [type, info] of Object.entries(schema.nodeTypes) as [string, NodeTypeSchema][]) {
|
|
201
|
+
if (info.count > 0) {
|
|
202
|
+
const cat = info.namespace ? `${info.category} (${info.namespace})` : info.category;
|
|
203
|
+
lines.push(`| \`${type}\` | ${cat} | ${info.count} |`);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
lines.push('');
|
|
207
|
+
|
|
208
|
+
lines.push('## Edge Types');
|
|
209
|
+
lines.push('');
|
|
210
|
+
lines.push('| Type | Count |');
|
|
211
|
+
lines.push('|------|-------|');
|
|
212
|
+
|
|
213
|
+
for (const [type, info] of Object.entries(schema.edgeTypes) as [string, EdgeTypeSchema][]) {
|
|
214
|
+
if (info.count > 0) {
|
|
215
|
+
lines.push(`| \`${type}\` | ${info.count} |`);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
lines.push('');
|
|
219
|
+
|
|
220
|
+
lines.push('---');
|
|
221
|
+
lines.push('');
|
|
222
|
+
lines.push(`*Checksum: \`${schema.checksum}\`*`);
|
|
223
|
+
|
|
224
|
+
return lines.join('\n');
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// ============================================================================
|
|
228
|
+
// Command
|
|
229
|
+
// ============================================================================
|
|
230
|
+
|
|
231
|
+
const exportSubcommand = new Command('export')
|
|
232
|
+
.description('Export interface or graph schema')
|
|
233
|
+
.option('--interface <name>', 'Interface name to export')
|
|
234
|
+
.option('--graph', 'Export graph node/edge type schema')
|
|
235
|
+
.option('--all', 'Include all defined types, not just used ones (with --graph)')
|
|
236
|
+
.option('--file <path>', 'File path filter (for multiple interfaces with same name)')
|
|
237
|
+
.option('-f, --format <type>', 'Output format: json, yaml, markdown', 'json')
|
|
238
|
+
.option('-p, --project <path>', 'Project path', '.')
|
|
239
|
+
.option('-o, --output <file>', 'Output file (default: stdout)')
|
|
240
|
+
.action(async (options: ExportOptions) => {
|
|
241
|
+
// Validate: must have either --interface or --graph
|
|
242
|
+
if (!options.interface && !options.graph) {
|
|
243
|
+
exitWithError('Must specify either --interface <name> or --graph', [
|
|
244
|
+
'Examples:',
|
|
245
|
+
' grafema schema export --interface ConfigSchema',
|
|
246
|
+
' grafema schema export --graph',
|
|
247
|
+
]);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
if (options.interface && options.graph) {
|
|
251
|
+
exitWithError('Cannot specify both --interface and --graph', [
|
|
252
|
+
'Use one at a time.',
|
|
253
|
+
]);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const projectPath = resolve(options.project);
|
|
257
|
+
const grafemaDir = join(projectPath, '.grafema');
|
|
258
|
+
const dbPath = join(grafemaDir, 'graph.rfdb');
|
|
259
|
+
|
|
260
|
+
if (!existsSync(dbPath)) {
|
|
261
|
+
exitWithError('No graph database found', ['Run: grafema analyze']);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const backend = new RFDBServerBackend({ dbPath });
|
|
265
|
+
await backend.connect();
|
|
266
|
+
|
|
267
|
+
try {
|
|
268
|
+
if (options.graph) {
|
|
269
|
+
// Graph schema export
|
|
270
|
+
const extractor = new GraphSchemaExtractor(backend);
|
|
271
|
+
const schema = await extractor.extract({ includeAll: options.all });
|
|
272
|
+
|
|
273
|
+
let output: string;
|
|
274
|
+
switch (options.format) {
|
|
275
|
+
case 'yaml':
|
|
276
|
+
output = formatGraphYaml(schema);
|
|
277
|
+
break;
|
|
278
|
+
case 'markdown':
|
|
279
|
+
output = formatGraphMarkdown(schema);
|
|
280
|
+
break;
|
|
281
|
+
case 'json':
|
|
282
|
+
default:
|
|
283
|
+
output = formatGraphJson(schema);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
if (options.output) {
|
|
287
|
+
writeFileSync(resolve(options.output), output + '\n');
|
|
288
|
+
console.log(`Graph schema written to ${options.output}`);
|
|
289
|
+
} else {
|
|
290
|
+
console.log(output);
|
|
291
|
+
}
|
|
292
|
+
} else {
|
|
293
|
+
// Interface schema export
|
|
294
|
+
const extractor = new InterfaceSchemaExtractor(backend);
|
|
295
|
+
const schema = await extractor.extract(options.interface!, {
|
|
296
|
+
file: options.file,
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
if (!schema) {
|
|
300
|
+
exitWithError(`Interface not found: ${options.interface}`, [
|
|
301
|
+
'Use "grafema query interface <name>" to search',
|
|
302
|
+
]);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// Phase 1 limitation warning for methods
|
|
306
|
+
if (hasMethodProperties(schema)) {
|
|
307
|
+
console.warn(
|
|
308
|
+
'Note: Method signatures are shown as "function" type. ' +
|
|
309
|
+
'Full signatures planned for v2.'
|
|
310
|
+
);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
let output: string;
|
|
314
|
+
switch (options.format) {
|
|
315
|
+
case 'yaml':
|
|
316
|
+
output = formatInterfaceYaml(schema);
|
|
317
|
+
break;
|
|
318
|
+
case 'markdown':
|
|
319
|
+
output = formatInterfaceMarkdown(schema, projectPath);
|
|
320
|
+
break;
|
|
321
|
+
case 'json':
|
|
322
|
+
default:
|
|
323
|
+
output = formatInterfaceJson(schema);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
if (options.output) {
|
|
327
|
+
writeFileSync(resolve(options.output), output + '\n');
|
|
328
|
+
console.log(`Schema written to ${options.output}`);
|
|
329
|
+
} else {
|
|
330
|
+
console.log(output);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
} catch (error) {
|
|
334
|
+
if (error instanceof Error) {
|
|
335
|
+
exitWithError(error.message);
|
|
336
|
+
}
|
|
337
|
+
throw error;
|
|
338
|
+
} finally {
|
|
339
|
+
await backend.close();
|
|
340
|
+
}
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
export const schemaCommand = new Command('schema')
|
|
344
|
+
.description('Extract and manage code schemas')
|
|
345
|
+
.addCommand(exportSubcommand);
|
package/src/commands/server.ts
CHANGED
|
@@ -22,8 +22,8 @@ const __dirname = dirname(__filename);
|
|
|
22
22
|
/**
|
|
23
23
|
* Find RFDB server binary in order of preference:
|
|
24
24
|
* 1. @grafema/rfdb npm package
|
|
25
|
-
* 2.
|
|
26
|
-
* 3.
|
|
25
|
+
* 2. packages/rfdb-server/target/release (monorepo development)
|
|
26
|
+
* 3. packages/rfdb-server/target/debug
|
|
27
27
|
*/
|
|
28
28
|
function findServerBinary(): string | null {
|
|
29
29
|
// 1. Check @grafema/rfdb npm package
|
|
@@ -50,16 +50,16 @@ function findServerBinary(): string | null {
|
|
|
50
50
|
// @grafema/rfdb not installed
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
// 2. Check
|
|
53
|
+
// 2. Check packages/rfdb-server in monorepo
|
|
54
54
|
// From packages/cli/dist/commands -> project root is 4 levels up
|
|
55
55
|
const projectRoot = join(__dirname, '../../../..');
|
|
56
|
-
const releaseBinary = join(projectRoot, '
|
|
56
|
+
const releaseBinary = join(projectRoot, 'packages/rfdb-server/target/release/rfdb-server');
|
|
57
57
|
if (existsSync(releaseBinary)) {
|
|
58
58
|
return releaseBinary;
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
// 3. Check debug build
|
|
62
|
-
const debugBinary = join(projectRoot, '
|
|
62
|
+
const debugBinary = join(projectRoot, 'packages/rfdb-server/target/debug/rfdb-server');
|
|
63
63
|
if (existsSync(debugBinary)) {
|
|
64
64
|
return debugBinary;
|
|
65
65
|
}
|
|
@@ -103,7 +103,14 @@ function getProjectPaths(projectPath: string) {
|
|
|
103
103
|
|
|
104
104
|
// Create main server command with subcommands
|
|
105
105
|
export const serverCommand = new Command('server')
|
|
106
|
-
.description('Manage RFDB server lifecycle')
|
|
106
|
+
.description('Manage RFDB server lifecycle')
|
|
107
|
+
.addHelpText('after', `
|
|
108
|
+
Examples:
|
|
109
|
+
grafema server start Start the RFDB server (detached)
|
|
110
|
+
grafema server stop Stop the running server
|
|
111
|
+
grafema server status Check if server is running
|
|
112
|
+
grafema server status --json Server status as JSON
|
|
113
|
+
`);
|
|
107
114
|
|
|
108
115
|
// grafema server start
|
|
109
116
|
serverCommand
|
package/src/commands/stats.ts
CHANGED
|
@@ -13,6 +13,13 @@ export const statsCommand = new Command('stats')
|
|
|
13
13
|
.option('-p, --project <path>', 'Project path', '.')
|
|
14
14
|
.option('-j, --json', 'Output as JSON')
|
|
15
15
|
.option('-t, --types', 'Show breakdown by type')
|
|
16
|
+
.addHelpText('after', `
|
|
17
|
+
Examples:
|
|
18
|
+
grafema stats Show basic graph statistics
|
|
19
|
+
grafema stats --types Show breakdown by node/edge types
|
|
20
|
+
grafema stats --json Output statistics as JSON
|
|
21
|
+
grafema stats -p ./app Statistics for specific project
|
|
22
|
+
`)
|
|
16
23
|
.action(async (options: { project: string; json?: boolean; types?: boolean }) => {
|
|
17
24
|
const projectPath = resolve(options.project);
|
|
18
25
|
const grafemaDir = join(projectPath, '.grafema');
|