@grafema/cli 0.1.1-alpha → 0.2.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.
- package/dist/cli.js +10 -0
- package/dist/commands/analyze.d.ts.map +1 -1
- package/dist/commands/analyze.js +69 -11
- 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 +84 -9
- 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
package/dist/cli.js
CHANGED
|
@@ -7,6 +7,8 @@ import { initCommand } from './commands/init.js';
|
|
|
7
7
|
import { analyzeCommand } from './commands/analyze.js';
|
|
8
8
|
import { overviewCommand } from './commands/overview.js';
|
|
9
9
|
import { queryCommand } from './commands/query.js';
|
|
10
|
+
import { typesCommand } from './commands/types.js';
|
|
11
|
+
import { lsCommand } from './commands/ls.js';
|
|
10
12
|
import { getCommand } from './commands/get.js';
|
|
11
13
|
import { traceCommand } from './commands/trace.js';
|
|
12
14
|
import { impactCommand } from './commands/impact.js';
|
|
@@ -15,6 +17,9 @@ import { statsCommand } from './commands/stats.js';
|
|
|
15
17
|
import { checkCommand } from './commands/check.js';
|
|
16
18
|
import { serverCommand } from './commands/server.js';
|
|
17
19
|
import { coverageCommand } from './commands/coverage.js';
|
|
20
|
+
import { doctorCommand } from './commands/doctor.js';
|
|
21
|
+
import { schemaCommand } from './commands/schema.js';
|
|
22
|
+
import { explainCommand } from './commands/explain.js';
|
|
18
23
|
const program = new Command();
|
|
19
24
|
program
|
|
20
25
|
.name('grafema')
|
|
@@ -25,6 +30,8 @@ program.addCommand(initCommand);
|
|
|
25
30
|
program.addCommand(analyzeCommand);
|
|
26
31
|
program.addCommand(overviewCommand);
|
|
27
32
|
program.addCommand(queryCommand);
|
|
33
|
+
program.addCommand(typesCommand);
|
|
34
|
+
program.addCommand(lsCommand);
|
|
28
35
|
program.addCommand(getCommand);
|
|
29
36
|
program.addCommand(traceCommand);
|
|
30
37
|
program.addCommand(impactCommand);
|
|
@@ -33,4 +40,7 @@ program.addCommand(statsCommand); // Keep for backwards compat
|
|
|
33
40
|
program.addCommand(coverageCommand);
|
|
34
41
|
program.addCommand(checkCommand);
|
|
35
42
|
program.addCommand(serverCommand);
|
|
43
|
+
program.addCommand(doctorCommand);
|
|
44
|
+
program.addCommand(schemaCommand);
|
|
45
|
+
program.addCommand(explainCommand);
|
|
36
46
|
program.parse();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"analyze.d.ts","sourceRoot":"","sources":["../../src/commands/analyze.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"analyze.d.ts","sourceRoot":"","sources":["../../src/commands/analyze.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAgLpC,eAAO,MAAM,cAAc,SAqKvB,CAAC"}
|
package/dist/commands/analyze.js
CHANGED
|
@@ -3,18 +3,19 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { Command } from 'commander';
|
|
5
5
|
import { resolve, join } from 'path';
|
|
6
|
-
import { existsSync, mkdirSync } from 'fs';
|
|
6
|
+
import { existsSync, mkdirSync, readdirSync } from 'fs';
|
|
7
|
+
import { pathToFileURL } from 'url';
|
|
7
8
|
import { Orchestrator, RFDBServerBackend, DiagnosticReporter, DiagnosticWriter, createLogger, loadConfig,
|
|
8
9
|
// Discovery
|
|
9
10
|
SimpleProjectDiscovery, MonorepoServiceDiscovery, WorkspaceDiscovery,
|
|
10
11
|
// Indexing
|
|
11
12
|
JSModuleIndexer, RustModuleIndexer,
|
|
12
13
|
// Analysis
|
|
13
|
-
JSASTAnalyzer, ExpressRouteAnalyzer, SocketIOAnalyzer, DatabaseAnalyzer, FetchAnalyzer, ServiceLayerAnalyzer, ReactAnalyzer, RustAnalyzer,
|
|
14
|
+
JSASTAnalyzer, ExpressRouteAnalyzer, ExpressResponseAnalyzer, SocketIOAnalyzer, DatabaseAnalyzer, FetchAnalyzer, ServiceLayerAnalyzer, ReactAnalyzer, RustAnalyzer,
|
|
14
15
|
// Enrichment
|
|
15
|
-
MethodCallResolver, AliasTracker, ValueDomainAnalyzer, MountPointResolver, PrefixEvaluator, InstanceOfResolver, ImportExportLinker, HTTPConnectionEnricher, RustFFIEnricher,
|
|
16
|
+
MethodCallResolver, ArgumentParameterLinker, AliasTracker, ValueDomainAnalyzer, MountPointResolver, PrefixEvaluator, InstanceOfResolver, ImportExportLinker, FunctionCallResolver, HTTPConnectionEnricher, RustFFIEnricher,
|
|
16
17
|
// Validation
|
|
17
|
-
CallResolverValidator, EvalBanValidator, SQLInjectionValidator, ShadowingDetector, GraphConnectivityValidator, DataFlowValidator, TypeScriptDeadCodeValidator, } from '@grafema/core';
|
|
18
|
+
CallResolverValidator, EvalBanValidator, SQLInjectionValidator, ShadowingDetector, GraphConnectivityValidator, DataFlowValidator, TypeScriptDeadCodeValidator, BrokenImportValidator, } from '@grafema/core';
|
|
18
19
|
const BUILTIN_PLUGINS = {
|
|
19
20
|
// Discovery
|
|
20
21
|
SimpleProjectDiscovery: () => new SimpleProjectDiscovery(),
|
|
@@ -26,6 +27,7 @@ const BUILTIN_PLUGINS = {
|
|
|
26
27
|
// Analysis
|
|
27
28
|
JSASTAnalyzer: () => new JSASTAnalyzer(),
|
|
28
29
|
ExpressRouteAnalyzer: () => new ExpressRouteAnalyzer(),
|
|
30
|
+
ExpressResponseAnalyzer: () => new ExpressResponseAnalyzer(),
|
|
29
31
|
SocketIOAnalyzer: () => new SocketIOAnalyzer(),
|
|
30
32
|
DatabaseAnalyzer: () => new DatabaseAnalyzer(),
|
|
31
33
|
FetchAnalyzer: () => new FetchAnalyzer(),
|
|
@@ -34,12 +36,14 @@ const BUILTIN_PLUGINS = {
|
|
|
34
36
|
RustAnalyzer: () => new RustAnalyzer(),
|
|
35
37
|
// Enrichment
|
|
36
38
|
MethodCallResolver: () => new MethodCallResolver(),
|
|
39
|
+
ArgumentParameterLinker: () => new ArgumentParameterLinker(),
|
|
37
40
|
AliasTracker: () => new AliasTracker(),
|
|
38
41
|
ValueDomainAnalyzer: () => new ValueDomainAnalyzer(),
|
|
39
42
|
MountPointResolver: () => new MountPointResolver(),
|
|
40
43
|
PrefixEvaluator: () => new PrefixEvaluator(),
|
|
41
44
|
InstanceOfResolver: () => new InstanceOfResolver(),
|
|
42
45
|
ImportExportLinker: () => new ImportExportLinker(),
|
|
46
|
+
FunctionCallResolver: () => new FunctionCallResolver(),
|
|
43
47
|
HTTPConnectionEnricher: () => new HTTPConnectionEnricher(),
|
|
44
48
|
RustFFIEnricher: () => new RustFFIEnricher(),
|
|
45
49
|
// Validation
|
|
@@ -50,14 +54,49 @@ const BUILTIN_PLUGINS = {
|
|
|
50
54
|
GraphConnectivityValidator: () => new GraphConnectivityValidator(),
|
|
51
55
|
DataFlowValidator: () => new DataFlowValidator(),
|
|
52
56
|
TypeScriptDeadCodeValidator: () => new TypeScriptDeadCodeValidator(),
|
|
57
|
+
BrokenImportValidator: () => new BrokenImportValidator(),
|
|
53
58
|
};
|
|
54
|
-
|
|
59
|
+
/**
|
|
60
|
+
* Load custom plugins from .grafema/plugins/ directory
|
|
61
|
+
*/
|
|
62
|
+
async function loadCustomPlugins(projectPath, log) {
|
|
63
|
+
const pluginsDir = join(projectPath, '.grafema', 'plugins');
|
|
64
|
+
if (!existsSync(pluginsDir)) {
|
|
65
|
+
return {};
|
|
66
|
+
}
|
|
67
|
+
const customPlugins = {};
|
|
68
|
+
try {
|
|
69
|
+
const files = readdirSync(pluginsDir).filter((f) => f.endsWith('.js') || f.endsWith('.mjs'));
|
|
70
|
+
for (const file of files) {
|
|
71
|
+
try {
|
|
72
|
+
const pluginPath = join(pluginsDir, file);
|
|
73
|
+
const pluginUrl = pathToFileURL(pluginPath).href;
|
|
74
|
+
const module = await import(pluginUrl);
|
|
75
|
+
const PluginClass = module.default || module[file.replace(/\.(m?js)$/, '')];
|
|
76
|
+
if (PluginClass && typeof PluginClass === 'function') {
|
|
77
|
+
const pluginName = PluginClass.name || file.replace(/\.(m?js)$/, '');
|
|
78
|
+
customPlugins[pluginName] = () => new PluginClass();
|
|
79
|
+
log(`Loaded custom plugin: ${pluginName}`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
catch (err) {
|
|
83
|
+
console.warn(`Failed to load plugin ${file}: ${err.message}`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
catch (err) {
|
|
88
|
+
console.warn(`Error loading custom plugins: ${err.message}`);
|
|
89
|
+
}
|
|
90
|
+
return customPlugins;
|
|
91
|
+
}
|
|
92
|
+
function createPlugins(config, customPlugins = {}) {
|
|
55
93
|
const plugins = [];
|
|
56
94
|
const phases = ['discovery', 'indexing', 'analysis', 'enrichment', 'validation'];
|
|
57
95
|
for (const phase of phases) {
|
|
58
96
|
const names = config[phase] || [];
|
|
59
97
|
for (const name of names) {
|
|
60
|
-
|
|
98
|
+
// Check built-in first, then custom
|
|
99
|
+
const factory = BUILTIN_PLUGINS[name] || customPlugins[name];
|
|
61
100
|
if (factory) {
|
|
62
101
|
plugins.push(factory());
|
|
63
102
|
}
|
|
@@ -95,6 +134,17 @@ export const analyzeCommand = new Command('analyze')
|
|
|
95
134
|
.option('-v, --verbose', 'Show verbose logging')
|
|
96
135
|
.option('--debug', 'Enable debug mode (writes diagnostics.log)')
|
|
97
136
|
.option('--log-level <level>', 'Set log level (silent, errors, warnings, info, debug)')
|
|
137
|
+
.option('--strict', 'Enable strict mode (fail on unresolved references)')
|
|
138
|
+
.addHelpText('after', `
|
|
139
|
+
Examples:
|
|
140
|
+
grafema analyze Analyze current project
|
|
141
|
+
grafema analyze ./my-project Analyze specific directory
|
|
142
|
+
grafema analyze --clear Clear database and rebuild from scratch
|
|
143
|
+
grafema analyze -s api Analyze only "api" service (monorepo)
|
|
144
|
+
grafema analyze -v Verbose output with progress details
|
|
145
|
+
grafema analyze --debug Write diagnostics.log for debugging
|
|
146
|
+
grafema analyze --strict Fail on unresolved references (debugging)
|
|
147
|
+
`)
|
|
98
148
|
.action(async (path, options) => {
|
|
99
149
|
const projectPath = resolve(path);
|
|
100
150
|
const grafemaDir = join(projectPath, '.grafema');
|
|
@@ -122,8 +172,15 @@ export const analyzeCommand = new Command('analyze')
|
|
|
122
172
|
log(` - ${svc.name}: ${svc.path}${entry}`);
|
|
123
173
|
}
|
|
124
174
|
}
|
|
125
|
-
|
|
175
|
+
// Load custom plugins from .grafema/plugins/
|
|
176
|
+
const customPlugins = await loadCustomPlugins(projectPath, log);
|
|
177
|
+
const plugins = createPlugins(config.plugins, customPlugins);
|
|
126
178
|
log(`Loaded ${plugins.length} plugins`);
|
|
179
|
+
// Resolve strict mode: CLI flag overrides config
|
|
180
|
+
const strictMode = options.strict ?? config.strict ?? false;
|
|
181
|
+
if (strictMode) {
|
|
182
|
+
log('Strict mode enabled - analysis will fail on unresolved references');
|
|
183
|
+
}
|
|
127
184
|
const startTime = Date.now();
|
|
128
185
|
const orchestrator = new Orchestrator({
|
|
129
186
|
graph: backend,
|
|
@@ -133,6 +190,7 @@ export const analyzeCommand = new Command('analyze')
|
|
|
133
190
|
forceAnalysis: options.clear || false,
|
|
134
191
|
logger,
|
|
135
192
|
services: config.services.length > 0 ? config.services : undefined, // Pass config services (REG-174)
|
|
193
|
+
strictMode, // REG-330: Pass strict mode flag
|
|
136
194
|
onProgress: (progress) => {
|
|
137
195
|
if (options.verbose) {
|
|
138
196
|
log(`[${progress.phase}] ${progress.message}`);
|
|
@@ -155,17 +213,17 @@ export const analyzeCommand = new Command('analyze')
|
|
|
155
213
|
// Print summary if there are any issues
|
|
156
214
|
if (diagnostics.count() > 0) {
|
|
157
215
|
log('');
|
|
158
|
-
log(reporter.
|
|
216
|
+
log(reporter.categorizedSummary());
|
|
159
217
|
// In verbose mode, print full report
|
|
160
218
|
if (options.verbose) {
|
|
161
219
|
log('');
|
|
162
220
|
log(reporter.report({ format: 'text', includeSummary: false }));
|
|
163
221
|
}
|
|
164
222
|
}
|
|
165
|
-
//
|
|
223
|
+
// Always write diagnostics.log (required for `grafema check` command)
|
|
224
|
+
const writer = new DiagnosticWriter();
|
|
225
|
+
await writer.write(diagnostics, grafemaDir);
|
|
166
226
|
if (options.debug) {
|
|
167
|
-
const writer = new DiagnosticWriter();
|
|
168
|
-
await writer.write(diagnostics, grafemaDir);
|
|
169
227
|
log(`Diagnostics written to ${writer.getLogPath(grafemaDir)}`);
|
|
170
228
|
}
|
|
171
229
|
// Determine exit code based on severity
|
package/dist/commands/check.d.ts
CHANGED
|
@@ -6,5 +6,11 @@
|
|
|
6
6
|
* 2. Built-in validators: --guarantee=<name> (e.g., --guarantee=node-creation)
|
|
7
7
|
*/
|
|
8
8
|
import { Command } from 'commander';
|
|
9
|
+
export interface DiagnosticCheckCategory {
|
|
10
|
+
name: string;
|
|
11
|
+
description: string;
|
|
12
|
+
codes: string[];
|
|
13
|
+
}
|
|
14
|
+
export declare const CHECK_CATEGORIES: Record<string, DiagnosticCheckCategory>;
|
|
9
15
|
export declare const checkCommand: Command;
|
|
10
16
|
//# sourceMappingURL=check.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"check.d.ts","sourceRoot":"","sources":["../../src/commands/check.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"check.d.ts","sourceRoot":"","sources":["../../src/commands/check.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAiCpC,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAGD,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,uBAAuB,CAqBpE,CAAC;AAEF,eAAO,MAAM,YAAY,SA2OtB,CAAC"}
|
package/dist/commands/check.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { Command } from 'commander';
|
|
9
9
|
import { resolve, join } from 'path';
|
|
10
|
-
import { existsSync } from 'fs';
|
|
10
|
+
import { existsSync, readFileSync } from 'fs';
|
|
11
11
|
import { RFDBServerBackend, GuaranteeManager, NodeCreationValidator, GraphFreshnessChecker, IncrementalReanalyzer } from '@grafema/core';
|
|
12
12
|
import { exitWithError } from '../utils/errorFormatter.js';
|
|
13
13
|
// Available built-in validators
|
|
@@ -17,6 +17,29 @@ const BUILT_IN_VALIDATORS = {
|
|
|
17
17
|
description: 'Validates that all nodes are created through NodeFactory'
|
|
18
18
|
}
|
|
19
19
|
};
|
|
20
|
+
// Available diagnostic categories
|
|
21
|
+
export const CHECK_CATEGORIES = {
|
|
22
|
+
'connectivity': {
|
|
23
|
+
name: 'Graph Connectivity',
|
|
24
|
+
description: 'Check for disconnected nodes in the graph',
|
|
25
|
+
codes: ['ERR_DISCONNECTED_NODES', 'ERR_DISCONNECTED_NODE'],
|
|
26
|
+
},
|
|
27
|
+
'calls': {
|
|
28
|
+
name: 'Call Resolution',
|
|
29
|
+
description: 'Check for unresolved function calls',
|
|
30
|
+
codes: ['ERR_UNRESOLVED_CALL'],
|
|
31
|
+
},
|
|
32
|
+
'dataflow': {
|
|
33
|
+
name: 'Data Flow',
|
|
34
|
+
description: 'Check for missing assignments and broken references',
|
|
35
|
+
codes: ['ERR_MISSING_ASSIGNMENT', 'ERR_BROKEN_REFERENCE', 'ERR_NO_LEAF_NODE'],
|
|
36
|
+
},
|
|
37
|
+
'imports': {
|
|
38
|
+
name: 'Import Validation',
|
|
39
|
+
description: 'Check for broken imports and undefined symbols',
|
|
40
|
+
codes: ['ERR_BROKEN_IMPORT', 'ERR_UNDEFINED_SYMBOL'],
|
|
41
|
+
},
|
|
42
|
+
};
|
|
20
43
|
export const checkCommand = new Command('check')
|
|
21
44
|
.description('Check invariants/guarantees')
|
|
22
45
|
.argument('[rule]', 'Specific rule ID to check (or "all" for all rules)')
|
|
@@ -26,9 +49,36 @@ export const checkCommand = new Command('check')
|
|
|
26
49
|
.option('-j, --json', 'Output results as JSON')
|
|
27
50
|
.option('-q, --quiet', 'Only output failures')
|
|
28
51
|
.option('--list-guarantees', 'List available built-in guarantees')
|
|
52
|
+
.option('--list-categories', 'List available diagnostic categories')
|
|
29
53
|
.option('--skip-reanalysis', 'Skip automatic reanalysis of stale modules')
|
|
30
54
|
.option('--fail-on-stale', 'Exit with error if stale modules found (CI mode)')
|
|
55
|
+
.addHelpText('after', `
|
|
56
|
+
Examples:
|
|
57
|
+
grafema check Run all guarantee checks
|
|
58
|
+
grafema check connectivity Check graph connectivity
|
|
59
|
+
grafema check calls Check call resolution
|
|
60
|
+
grafema check dataflow Check data flow integrity
|
|
61
|
+
grafema check all Run all diagnostic categories
|
|
62
|
+
grafema check --guarantee node-creation Run built-in validator
|
|
63
|
+
grafema check --list-categories List available categories
|
|
64
|
+
grafema check --list-guarantees List built-in guarantees
|
|
65
|
+
grafema check --fail-on-stale CI mode: fail if graph is stale
|
|
66
|
+
grafema check -q Only show failures (quiet mode)
|
|
67
|
+
`)
|
|
31
68
|
.action(async (rule, options) => {
|
|
69
|
+
// List available categories
|
|
70
|
+
if (options.listCategories) {
|
|
71
|
+
console.log('Available diagnostic categories:');
|
|
72
|
+
console.log('');
|
|
73
|
+
for (const [key, category] of Object.entries(CHECK_CATEGORIES)) {
|
|
74
|
+
console.log(` ${key}`);
|
|
75
|
+
console.log(` ${category.name}`);
|
|
76
|
+
console.log(` ${category.description}`);
|
|
77
|
+
console.log(` Usage: grafema check ${key}`);
|
|
78
|
+
console.log('');
|
|
79
|
+
}
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
32
82
|
// List available guarantees
|
|
33
83
|
if (options.listGuarantees) {
|
|
34
84
|
console.log('Available built-in guarantees:');
|
|
@@ -40,6 +90,11 @@ export const checkCommand = new Command('check')
|
|
|
40
90
|
}
|
|
41
91
|
return;
|
|
42
92
|
}
|
|
93
|
+
// Check if rule argument is a category name
|
|
94
|
+
if (rule && (rule in CHECK_CATEGORIES || rule === 'all')) {
|
|
95
|
+
await runCategoryCheck(rule, options);
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
43
98
|
// Run built-in guarantee validator
|
|
44
99
|
if (options.guarantee) {
|
|
45
100
|
const validatorInfo = BUILT_IN_VALIDATORS[options.guarantee];
|
|
@@ -293,3 +348,124 @@ async function runBuiltInValidator(guaranteeName, projectPath, options) {
|
|
|
293
348
|
await backend.close();
|
|
294
349
|
}
|
|
295
350
|
}
|
|
351
|
+
/**
|
|
352
|
+
* Run category-based diagnostic check
|
|
353
|
+
*/
|
|
354
|
+
async function runCategoryCheck(category, options) {
|
|
355
|
+
const resolvedPath = resolve(options.project);
|
|
356
|
+
const grafemaDir = join(resolvedPath, '.grafema');
|
|
357
|
+
const diagnosticsLogPath = join(grafemaDir, 'diagnostics.log');
|
|
358
|
+
if (!existsSync(diagnosticsLogPath)) {
|
|
359
|
+
exitWithError('No diagnostics found', [
|
|
360
|
+
'Run: grafema analyze',
|
|
361
|
+
'Diagnostics are collected during analysis'
|
|
362
|
+
]);
|
|
363
|
+
}
|
|
364
|
+
// Read diagnostics from log file (JSON lines format)
|
|
365
|
+
const diagnosticsContent = readFileSync(diagnosticsLogPath, 'utf-8');
|
|
366
|
+
const allDiagnostics = diagnosticsContent
|
|
367
|
+
.split('\n')
|
|
368
|
+
.filter(line => line.trim())
|
|
369
|
+
.map(line => {
|
|
370
|
+
try {
|
|
371
|
+
return JSON.parse(line);
|
|
372
|
+
}
|
|
373
|
+
catch (e) {
|
|
374
|
+
return null;
|
|
375
|
+
}
|
|
376
|
+
})
|
|
377
|
+
.filter(Boolean);
|
|
378
|
+
// Filter diagnostics by category codes
|
|
379
|
+
let filteredDiagnostics = allDiagnostics;
|
|
380
|
+
if (category !== 'all') {
|
|
381
|
+
const categoryInfo = CHECK_CATEGORIES[category];
|
|
382
|
+
if (!categoryInfo) {
|
|
383
|
+
exitWithError(`Unknown category: ${category}`, [
|
|
384
|
+
'Use --list-categories to see available options'
|
|
385
|
+
]);
|
|
386
|
+
}
|
|
387
|
+
filteredDiagnostics = allDiagnostics.filter((d) => categoryInfo.codes.includes(d.code));
|
|
388
|
+
}
|
|
389
|
+
if (options.json) {
|
|
390
|
+
console.log(JSON.stringify({
|
|
391
|
+
category: category,
|
|
392
|
+
total: filteredDiagnostics.length,
|
|
393
|
+
diagnostics: filteredDiagnostics
|
|
394
|
+
}, null, 2));
|
|
395
|
+
}
|
|
396
|
+
else {
|
|
397
|
+
const categoryName = category === 'all'
|
|
398
|
+
? 'All Categories'
|
|
399
|
+
: CHECK_CATEGORIES[category].name;
|
|
400
|
+
if (!options.quiet) {
|
|
401
|
+
console.log(`Checking ${categoryName}...`);
|
|
402
|
+
console.log('');
|
|
403
|
+
}
|
|
404
|
+
if (filteredDiagnostics.length === 0) {
|
|
405
|
+
console.log('\x1b[32m✓\x1b[0m No issues found');
|
|
406
|
+
}
|
|
407
|
+
else {
|
|
408
|
+
console.log(`\x1b[33m⚠\x1b[0m Found ${filteredDiagnostics.length} diagnostic(s):`);
|
|
409
|
+
console.log('');
|
|
410
|
+
// Group by severity
|
|
411
|
+
const errors = filteredDiagnostics.filter((d) => d.severity === 'error' || d.severity === 'fatal');
|
|
412
|
+
const warnings = filteredDiagnostics.filter((d) => d.severity === 'warning');
|
|
413
|
+
const infos = filteredDiagnostics.filter((d) => d.severity === 'info');
|
|
414
|
+
// Display errors first
|
|
415
|
+
if (errors.length > 0) {
|
|
416
|
+
console.log(`\x1b[31mErrors (${errors.length}):\x1b[0m`);
|
|
417
|
+
for (const diag of errors.slice(0, 10)) {
|
|
418
|
+
const location = diag.file ? `${diag.file}${diag.line ? `:${diag.line}` : ''}` : '';
|
|
419
|
+
console.log(` \x1b[31m•\x1b[0m [${diag.code}] ${diag.message}`);
|
|
420
|
+
if (location) {
|
|
421
|
+
console.log(` ${location}`);
|
|
422
|
+
}
|
|
423
|
+
if (diag.suggestion && !options.quiet) {
|
|
424
|
+
console.log(` Suggestion: ${diag.suggestion}`);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
if (errors.length > 10) {
|
|
428
|
+
console.log(` ... and ${errors.length - 10} more errors`);
|
|
429
|
+
}
|
|
430
|
+
console.log('');
|
|
431
|
+
}
|
|
432
|
+
// Display warnings
|
|
433
|
+
if (warnings.length > 0) {
|
|
434
|
+
console.log(`\x1b[33mWarnings (${warnings.length}):\x1b[0m`);
|
|
435
|
+
for (const diag of warnings.slice(0, 10)) {
|
|
436
|
+
const location = diag.file ? `${diag.file}${diag.line ? `:${diag.line}` : ''}` : '';
|
|
437
|
+
console.log(` \x1b[33m•\x1b[0m [${diag.code}] ${diag.message}`);
|
|
438
|
+
if (location) {
|
|
439
|
+
console.log(` ${location}`);
|
|
440
|
+
}
|
|
441
|
+
if (diag.suggestion && !options.quiet) {
|
|
442
|
+
console.log(` Suggestion: ${diag.suggestion}`);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
if (warnings.length > 10) {
|
|
446
|
+
console.log(` ... and ${warnings.length - 10} more warnings`);
|
|
447
|
+
}
|
|
448
|
+
console.log('');
|
|
449
|
+
}
|
|
450
|
+
// Display infos
|
|
451
|
+
if (infos.length > 0 && !options.quiet) {
|
|
452
|
+
console.log(`\x1b[36mInfo (${infos.length}):\x1b[0m`);
|
|
453
|
+
for (const diag of infos.slice(0, 5)) {
|
|
454
|
+
const location = diag.file ? `${diag.file}${diag.line ? `:${diag.line}` : ''}` : '';
|
|
455
|
+
console.log(` \x1b[36m•\x1b[0m [${diag.code}] ${diag.message}`);
|
|
456
|
+
if (location) {
|
|
457
|
+
console.log(` ${location}`);
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
if (infos.length > 5) {
|
|
461
|
+
console.log(` ... and ${infos.length - 5} more info messages`);
|
|
462
|
+
}
|
|
463
|
+
console.log('');
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
console.log('');
|
|
467
|
+
if (filteredDiagnostics.some((d) => d.severity === 'error' || d.severity === 'fatal')) {
|
|
468
|
+
process.exit(1);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"coverage.d.ts","sourceRoot":"","sources":["../../src/commands/coverage.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAMpC,eAAO,MAAM,eAAe,
|
|
1
|
+
{"version":3,"file":"coverage.d.ts","sourceRoot":"","sources":["../../src/commands/coverage.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAMpC,eAAO,MAAM,eAAe,SAoCxB,CAAC"}
|
|
@@ -16,6 +16,13 @@ export const coverageCommand = new Command('coverage')
|
|
|
16
16
|
.option('-p, --project <path>', 'Project path', '.')
|
|
17
17
|
.option('-j, --json', 'Output as JSON')
|
|
18
18
|
.option('-v, --verbose', 'Show detailed file lists')
|
|
19
|
+
.addHelpText('after', `
|
|
20
|
+
Examples:
|
|
21
|
+
grafema coverage Show coverage summary
|
|
22
|
+
grafema coverage --verbose Show detailed file lists
|
|
23
|
+
grafema coverage --json Output coverage as JSON
|
|
24
|
+
grafema coverage -p ./app Coverage for specific project
|
|
25
|
+
`)
|
|
19
26
|
.action(async (options) => {
|
|
20
27
|
const projectPath = resolve(options.project);
|
|
21
28
|
const grafemaDir = join(projectPath, '.grafema');
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Diagnostic check functions for `grafema doctor` command - REG-214
|
|
3
|
+
*
|
|
4
|
+
* Checks are organized in levels:
|
|
5
|
+
* - Level 1: Prerequisites (fail-fast) - checkGrafemaInitialized, checkServerStatus
|
|
6
|
+
* - Level 2: Configuration - checkConfigValidity, checkEntrypoints
|
|
7
|
+
* - Level 3: Graph Health - checkDatabaseExists, checkGraphStats, checkConnectivity, checkFreshness
|
|
8
|
+
* - Level 4: Informational - checkVersions
|
|
9
|
+
*/
|
|
10
|
+
import type { DoctorCheckResult } from './types.js';
|
|
11
|
+
/**
|
|
12
|
+
* Check if .grafema directory exists with config file.
|
|
13
|
+
* FAIL if not initialized.
|
|
14
|
+
*/
|
|
15
|
+
export declare function checkGrafemaInitialized(projectPath: string): Promise<DoctorCheckResult>;
|
|
16
|
+
/**
|
|
17
|
+
* Check if RFDB server is running and responsive.
|
|
18
|
+
* WARN if not running (server starts on-demand during analyze).
|
|
19
|
+
*/
|
|
20
|
+
export declare function checkServerStatus(projectPath: string): Promise<DoctorCheckResult>;
|
|
21
|
+
/**
|
|
22
|
+
* Validate config file syntax and structure.
|
|
23
|
+
* Uses existing loadConfig() which throws on errors.
|
|
24
|
+
*/
|
|
25
|
+
export declare function checkConfigValidity(projectPath: string): Promise<DoctorCheckResult>;
|
|
26
|
+
/**
|
|
27
|
+
* Check that entrypoints can be resolved.
|
|
28
|
+
* For config-defined services, validates that entrypoint files exist.
|
|
29
|
+
*/
|
|
30
|
+
export declare function checkEntrypoints(projectPath: string): Promise<DoctorCheckResult>;
|
|
31
|
+
/**
|
|
32
|
+
* Check if database file exists and has data.
|
|
33
|
+
*/
|
|
34
|
+
export declare function checkDatabaseExists(projectPath: string): Promise<DoctorCheckResult>;
|
|
35
|
+
/**
|
|
36
|
+
* Get graph statistics (requires server running).
|
|
37
|
+
*/
|
|
38
|
+
export declare function checkGraphStats(projectPath: string): Promise<DoctorCheckResult>;
|
|
39
|
+
/**
|
|
40
|
+
* Check graph connectivity - find disconnected nodes.
|
|
41
|
+
* Thresholds:
|
|
42
|
+
* 0-5%: pass (normal for external modules)
|
|
43
|
+
* 5-20%: warn
|
|
44
|
+
* >20%: fail (critical issue)
|
|
45
|
+
*/
|
|
46
|
+
export declare function checkConnectivity(projectPath: string): Promise<DoctorCheckResult>;
|
|
47
|
+
/**
|
|
48
|
+
* Check if graph is fresh (no stale modules).
|
|
49
|
+
*/
|
|
50
|
+
export declare function checkFreshness(projectPath: string): Promise<DoctorCheckResult>;
|
|
51
|
+
/**
|
|
52
|
+
* Collect version information (always passes).
|
|
53
|
+
*/
|
|
54
|
+
export declare function checkVersions(projectPath: string): Promise<DoctorCheckResult>;
|
|
55
|
+
//# sourceMappingURL=checks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"checks.d.ts","sourceRoot":"","sources":["../../../src/commands/doctor/checks.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAYH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AA2BpD;;;GAGG;AACH,wBAAsB,uBAAuB,CAC3C,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,iBAAiB,CAAC,CAgC5B;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,CACrC,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,iBAAiB,CAAC,CAkC5B;AAMD;;;GAGG;AACH,wBAAsB,mBAAmB,CACvC,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,iBAAiB,CAAC,CA+C5B;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,CACpC,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,iBAAiB,CAAC,CA+E5B;AAMD;;GAEG;AACH,wBAAsB,mBAAmB,CACvC,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,iBAAiB,CAAC,CA6B5B;AAED;;GAEG;AACH,wBAAsB,eAAe,CACnC,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,iBAAiB,CAAC,CA6C5B;AAED;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CACrC,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,iBAAiB,CAAC,CAgI5B;AAED;;GAEG;AACH,wBAAsB,cAAc,CAClC,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,iBAAiB,CAAC,CA4C5B;AAMD;;GAEG;AACH,wBAAsB,aAAa,CACjC,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,iBAAiB,CAAC,CA6C5B"}
|