@tyroneross/navgator 0.1.0
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/.claude-plugin/plugin.json +10 -0
- package/LICENSE +21 -0
- package/README.md +486 -0
- package/agents/architecture-advisor.md +109 -0
- package/commands/nav-check.md +64 -0
- package/commands/nav-connections.md +58 -0
- package/commands/nav-diagram.md +106 -0
- package/commands/nav-export.md +71 -0
- package/commands/nav-impact.md +58 -0
- package/commands/nav-scan.md +46 -0
- package/commands/nav-status.md +44 -0
- package/dist/cli/index.d.ts +7 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +627 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/config.d.ts +95 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +262 -0
- package/dist/config.js.map +1 -0
- package/dist/diagram.d.ts +36 -0
- package/dist/diagram.d.ts.map +1 -0
- package/dist/diagram.js +333 -0
- package/dist/diagram.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/scanner.d.ts +57 -0
- package/dist/scanner.d.ts.map +1 -0
- package/dist/scanner.js +282 -0
- package/dist/scanner.js.map +1 -0
- package/dist/scanners/connections/ast-scanner.d.ts +26 -0
- package/dist/scanners/connections/ast-scanner.d.ts.map +1 -0
- package/dist/scanners/connections/ast-scanner.js +430 -0
- package/dist/scanners/connections/ast-scanner.js.map +1 -0
- package/dist/scanners/connections/service-calls.d.ts +14 -0
- package/dist/scanners/connections/service-calls.d.ts.map +1 -0
- package/dist/scanners/connections/service-calls.js +719 -0
- package/dist/scanners/connections/service-calls.js.map +1 -0
- package/dist/scanners/infrastructure/index.d.ts +27 -0
- package/dist/scanners/infrastructure/index.d.ts.map +1 -0
- package/dist/scanners/infrastructure/index.js +233 -0
- package/dist/scanners/infrastructure/index.js.map +1 -0
- package/dist/scanners/packages/npm.d.ts +18 -0
- package/dist/scanners/packages/npm.d.ts.map +1 -0
- package/dist/scanners/packages/npm.js +256 -0
- package/dist/scanners/packages/npm.js.map +1 -0
- package/dist/scanners/packages/pip.d.ts +14 -0
- package/dist/scanners/packages/pip.d.ts.map +1 -0
- package/dist/scanners/packages/pip.js +228 -0
- package/dist/scanners/packages/pip.js.map +1 -0
- package/dist/scanners/prompts/detector.d.ts +119 -0
- package/dist/scanners/prompts/detector.d.ts.map +1 -0
- package/dist/scanners/prompts/detector.js +617 -0
- package/dist/scanners/prompts/detector.js.map +1 -0
- package/dist/scanners/prompts/index.d.ts +51 -0
- package/dist/scanners/prompts/index.d.ts.map +1 -0
- package/dist/scanners/prompts/index.js +340 -0
- package/dist/scanners/prompts/index.js.map +1 -0
- package/dist/scanners/prompts/types.d.ts +127 -0
- package/dist/scanners/prompts/types.d.ts.map +1 -0
- package/dist/scanners/prompts/types.js +37 -0
- package/dist/scanners/prompts/types.js.map +1 -0
- package/dist/setup.d.ts +65 -0
- package/dist/setup.d.ts.map +1 -0
- package/dist/setup.js +261 -0
- package/dist/setup.js.map +1 -0
- package/dist/storage.d.ts +147 -0
- package/dist/storage.d.ts.map +1 -0
- package/dist/storage.js +931 -0
- package/dist/storage.js.map +1 -0
- package/dist/types.d.ts +296 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +55 -0
- package/dist/types.js.map +1 -0
- package/dist/ui-server.d.ts +17 -0
- package/dist/ui-server.d.ts.map +1 -0
- package/dist/ui-server.js +815 -0
- package/dist/ui-server.js.map +1 -0
- package/hooks/hooks.json +57 -0
- package/package.json +80 -0
- package/scripts/ibr-ui-test.mjs +359 -0
- package/scripts/postinstall.cjs +35 -0
- package/skills/architecture-awareness/SKILL.md +141 -0
|
@@ -0,0 +1,627 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* NavGator CLI
|
|
4
|
+
* Architecture connection tracker for Claude Code
|
|
5
|
+
*/
|
|
6
|
+
import { Command } from 'commander';
|
|
7
|
+
import * as fs from 'fs';
|
|
8
|
+
import { scan, scanPromptsOnly, formatPromptsOutput, formatPromptDetail } from '../scanner.js';
|
|
9
|
+
import { loadIndex, loadAllComponents, loadAllConnections, loadGraph } from '../storage.js';
|
|
10
|
+
import { getConfig } from '../config.js';
|
|
11
|
+
import { generateMermaidDiagram, generateComponentDiagram, generateLayerDiagram, generateSummaryDiagram, wrapInMarkdown, } from '../diagram.js';
|
|
12
|
+
import { setup, isSetupComplete, formatSetupStatus } from '../setup.js';
|
|
13
|
+
import { startUIServer } from '../ui-server.js';
|
|
14
|
+
const NAVGATOR_LOGO = `
|
|
15
|
+
_ _ ____ _
|
|
16
|
+
| \\ | | __ ___ _/ ___| __ _| |_ ___ _ __
|
|
17
|
+
| \\| |/ _\` \\ \\ / / | _ / _\` | __/ _ \\| '__|
|
|
18
|
+
| |\\ | (_| |\\ V /| |_| | (_| | || (_) | |
|
|
19
|
+
|_| \\_|\\__,_| \\_/ \\____|\\__,_|\\__\\___/|_|
|
|
20
|
+
|
|
21
|
+
Architecture Connection Tracker
|
|
22
|
+
Know your stack before you change it
|
|
23
|
+
`;
|
|
24
|
+
const program = new Command();
|
|
25
|
+
program
|
|
26
|
+
.name('navgator')
|
|
27
|
+
.description('Architecture connection tracker - know your stack before you change it')
|
|
28
|
+
.version('0.1.0')
|
|
29
|
+
.addHelpText('beforeAll', NAVGATOR_LOGO);
|
|
30
|
+
// =============================================================================
|
|
31
|
+
// SETUP COMMAND (New - Initial Installation)
|
|
32
|
+
// =============================================================================
|
|
33
|
+
program
|
|
34
|
+
.command('setup')
|
|
35
|
+
.description('Initialize NavGator with a two-phase scan (fast initial + deep follow-up)')
|
|
36
|
+
.option('-f, --fast', 'Run fast scan only (skip deep analysis)')
|
|
37
|
+
.option('-v, --verbose', 'Show detailed progress')
|
|
38
|
+
.option('--no-diagram', 'Skip diagram generation')
|
|
39
|
+
.action(async (options) => {
|
|
40
|
+
try {
|
|
41
|
+
console.log('');
|
|
42
|
+
console.log('🐊 NavGator - Architecture Connection Tracker');
|
|
43
|
+
console.log(' Know your stack before you change it');
|
|
44
|
+
console.log('');
|
|
45
|
+
// Check if already set up
|
|
46
|
+
const status = await isSetupComplete();
|
|
47
|
+
if (status.hasScanned && !status.stale) {
|
|
48
|
+
console.log('NavGator is already set up for this project.');
|
|
49
|
+
console.log(`Last scan: ${status.lastScan?.toLocaleString()}`);
|
|
50
|
+
console.log(`Scan depth: ${status.phase}`);
|
|
51
|
+
console.log('');
|
|
52
|
+
console.log('Run `navgator scan` to refresh, or `navgator status` to view.');
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
// Progress callback
|
|
56
|
+
const onProgress = (phase, message) => {
|
|
57
|
+
const icon = phase === 'FAST' ? '⚡' : '🔍';
|
|
58
|
+
console.log(`${icon} [${phase}] ${message}`);
|
|
59
|
+
};
|
|
60
|
+
// Run setup
|
|
61
|
+
const result = await setup({
|
|
62
|
+
fastOnly: options.fast,
|
|
63
|
+
generateDiagram: options.diagram !== false,
|
|
64
|
+
verbose: options.verbose,
|
|
65
|
+
onProgress,
|
|
66
|
+
});
|
|
67
|
+
// Display results
|
|
68
|
+
console.log(formatSetupStatus(result));
|
|
69
|
+
// Show diagram preview if generated
|
|
70
|
+
if (result.diagram) {
|
|
71
|
+
console.log('Architecture Diagram Preview:');
|
|
72
|
+
console.log('─'.repeat(60));
|
|
73
|
+
// Show first 30 lines
|
|
74
|
+
const lines = result.diagram.split('\n').slice(0, 30);
|
|
75
|
+
console.log(lines.join('\n'));
|
|
76
|
+
if (result.diagram.split('\n').length > 30) {
|
|
77
|
+
console.log('... (run `navgator diagram` to see full diagram)');
|
|
78
|
+
}
|
|
79
|
+
console.log('');
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
console.error('Setup failed:', error);
|
|
84
|
+
process.exit(1);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
// =============================================================================
|
|
88
|
+
// SCAN COMMAND
|
|
89
|
+
// =============================================================================
|
|
90
|
+
program
|
|
91
|
+
.command('scan')
|
|
92
|
+
.description('Scan project architecture and update connection tracking')
|
|
93
|
+
.option('-q, --quick', 'Quick scan (packages only, no code analysis)')
|
|
94
|
+
.option('-c, --connections', 'Focus on connection detection')
|
|
95
|
+
.option('-p, --prompts', 'Enhanced AI prompt scanning with full content')
|
|
96
|
+
.option('-v, --verbose', 'Show detailed output')
|
|
97
|
+
.option('--clear', 'Clear existing data before scanning')
|
|
98
|
+
.option('--ast', 'Use AST-based scanning (more accurate, slightly slower)')
|
|
99
|
+
.action(async (options) => {
|
|
100
|
+
try {
|
|
101
|
+
console.log('NavGator - Scanning architecture...\n');
|
|
102
|
+
const result = await scan(process.cwd(), {
|
|
103
|
+
quick: options.quick,
|
|
104
|
+
connections: options.connections,
|
|
105
|
+
prompts: options.prompts,
|
|
106
|
+
verbose: options.verbose,
|
|
107
|
+
clearFirst: options.clear,
|
|
108
|
+
useAST: options.ast,
|
|
109
|
+
});
|
|
110
|
+
console.log('\n========================================');
|
|
111
|
+
console.log('SCAN COMPLETE');
|
|
112
|
+
console.log('========================================\n');
|
|
113
|
+
// Group components by type
|
|
114
|
+
const byType = {};
|
|
115
|
+
for (const c of result.components) {
|
|
116
|
+
byType[c.type] = (byType[c.type] || 0) + 1;
|
|
117
|
+
}
|
|
118
|
+
console.log('COMPONENTS:');
|
|
119
|
+
for (const [type, count] of Object.entries(byType)) {
|
|
120
|
+
console.log(` ${type}: ${count}`);
|
|
121
|
+
}
|
|
122
|
+
// Group connections by type
|
|
123
|
+
const connByType = {};
|
|
124
|
+
for (const c of result.connections) {
|
|
125
|
+
connByType[c.connection_type] = (connByType[c.connection_type] || 0) + 1;
|
|
126
|
+
}
|
|
127
|
+
if (Object.keys(connByType).length > 0) {
|
|
128
|
+
console.log('\nCONNECTIONS:');
|
|
129
|
+
for (const [type, count] of Object.entries(connByType)) {
|
|
130
|
+
console.log(` ${type}: ${count}`);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
if (result.warnings.length > 0) {
|
|
134
|
+
console.log(`\nWARNINGS: ${result.warnings.length}`);
|
|
135
|
+
for (const w of result.warnings.slice(0, 5)) {
|
|
136
|
+
console.log(` - ${w.message}`);
|
|
137
|
+
}
|
|
138
|
+
if (result.warnings.length > 5) {
|
|
139
|
+
console.log(` ... and ${result.warnings.length - 5} more`);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
// Show file change summary
|
|
143
|
+
if (result.fileChanges) {
|
|
144
|
+
const { added, modified, removed } = result.fileChanges;
|
|
145
|
+
if (added.length > 0 || modified.length > 0 || removed.length > 0) {
|
|
146
|
+
console.log('\nFILE CHANGES:');
|
|
147
|
+
if (added.length > 0)
|
|
148
|
+
console.log(` Added: ${added.length}`);
|
|
149
|
+
if (modified.length > 0)
|
|
150
|
+
console.log(` Modified: ${modified.length}`);
|
|
151
|
+
if (removed.length > 0)
|
|
152
|
+
console.log(` Removed: ${removed.length}`);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
// Show prompt scan results if enhanced scanning was used
|
|
156
|
+
if (result.promptScan && result.promptScan.prompts.length > 0) {
|
|
157
|
+
console.log('\nAI PROMPTS:');
|
|
158
|
+
console.log(` Total: ${result.promptScan.summary.totalPrompts}`);
|
|
159
|
+
console.log(` Templates: ${result.promptScan.summary.templatesCount}`);
|
|
160
|
+
if (Object.keys(result.promptScan.summary.byProvider).length > 0) {
|
|
161
|
+
console.log(' By provider:');
|
|
162
|
+
for (const [provider, count] of Object.entries(result.promptScan.summary.byProvider)) {
|
|
163
|
+
console.log(` ${provider}: ${count}`);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
console.log(`\nFiles scanned: ${result.stats.files_scanned}`);
|
|
168
|
+
console.log(`Scan completed in ${result.stats.scan_duration_ms}ms`);
|
|
169
|
+
// Auto-register project in ~/.navgator/projects.json
|
|
170
|
+
try {
|
|
171
|
+
const os = await import('os');
|
|
172
|
+
const path = await import('path');
|
|
173
|
+
const registryDir = path.join(os.homedir(), '.navgator');
|
|
174
|
+
const registryPath = path.join(registryDir, 'projects.json');
|
|
175
|
+
await fs.promises.mkdir(registryDir, { recursive: true });
|
|
176
|
+
let registry;
|
|
177
|
+
try {
|
|
178
|
+
registry = JSON.parse(await fs.promises.readFile(registryPath, 'utf-8'));
|
|
179
|
+
}
|
|
180
|
+
catch {
|
|
181
|
+
registry = { version: 1, projects: [] };
|
|
182
|
+
}
|
|
183
|
+
const projectRoot = process.cwd();
|
|
184
|
+
const existing = registry.projects.find(p => p.path === projectRoot);
|
|
185
|
+
if (existing) {
|
|
186
|
+
existing.lastScan = Date.now();
|
|
187
|
+
}
|
|
188
|
+
else {
|
|
189
|
+
const dirName = projectRoot.split(path.sep).pop() || 'project';
|
|
190
|
+
const name = dirName.replace(/[-_]/g, ' ').replace(/\b\w/g, c => c.toUpperCase()).trim();
|
|
191
|
+
registry.projects.push({ path: projectRoot, name, addedAt: Date.now(), lastScan: Date.now() });
|
|
192
|
+
}
|
|
193
|
+
await fs.promises.writeFile(registryPath, JSON.stringify(registry, null, 2), 'utf-8');
|
|
194
|
+
}
|
|
195
|
+
catch {
|
|
196
|
+
// Non-critical — don't fail the scan
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
catch (error) {
|
|
200
|
+
console.error('Scan failed:', error);
|
|
201
|
+
process.exit(1);
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
// =============================================================================
|
|
205
|
+
// STATUS COMMAND
|
|
206
|
+
// =============================================================================
|
|
207
|
+
program
|
|
208
|
+
.command('status')
|
|
209
|
+
.description('Show architecture summary and health status')
|
|
210
|
+
.option('--json', 'Output as JSON')
|
|
211
|
+
.action(async (options) => {
|
|
212
|
+
try {
|
|
213
|
+
const config = getConfig();
|
|
214
|
+
const index = await loadIndex(config);
|
|
215
|
+
if (!index) {
|
|
216
|
+
console.log('No architecture data found. Run `navgator scan` first.');
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
if (options.json) {
|
|
220
|
+
console.log(JSON.stringify(index, null, 2));
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
console.log('NavGator - Architecture Status\n');
|
|
224
|
+
console.log('========================================');
|
|
225
|
+
const lastScan = new Date(index.last_scan);
|
|
226
|
+
const hoursSince = Math.round((Date.now() - index.last_scan) / (1000 * 60 * 60));
|
|
227
|
+
console.log(`Last scan: ${lastScan.toLocaleString()} (${hoursSince}h ago)`);
|
|
228
|
+
console.log(`Total components: ${index.stats.total_components}`);
|
|
229
|
+
console.log(`Total connections: ${index.stats.total_connections}`);
|
|
230
|
+
if (index.stats.outdated_count > 0) {
|
|
231
|
+
console.log(`Outdated packages: ${index.stats.outdated_count}`);
|
|
232
|
+
}
|
|
233
|
+
if (index.stats.vulnerable_count > 0) {
|
|
234
|
+
console.log(`Vulnerable packages: ${index.stats.vulnerable_count}`);
|
|
235
|
+
}
|
|
236
|
+
console.log('\nCOMPONENTS BY TYPE:');
|
|
237
|
+
for (const [type, count] of Object.entries(index.stats.components_by_type)) {
|
|
238
|
+
console.log(` ${type}: ${count}`);
|
|
239
|
+
}
|
|
240
|
+
if (Object.keys(index.stats.connections_by_type).length > 0) {
|
|
241
|
+
console.log('\nCONNECTIONS BY TYPE:');
|
|
242
|
+
for (const [type, count] of Object.entries(index.stats.connections_by_type)) {
|
|
243
|
+
console.log(` ${type}: ${count}`);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
if (hoursSince > 24) {
|
|
247
|
+
console.log('\n⚠️ Architecture data is stale. Consider running `navgator scan`');
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
catch (error) {
|
|
251
|
+
console.error('Status check failed:', error);
|
|
252
|
+
process.exit(1);
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
// =============================================================================
|
|
256
|
+
// IMPACT COMMAND
|
|
257
|
+
// =============================================================================
|
|
258
|
+
program
|
|
259
|
+
.command('impact <component>')
|
|
260
|
+
.description('Show what\'s affected if you change a component')
|
|
261
|
+
.option('--json', 'Output as JSON')
|
|
262
|
+
.action(async (componentName, options) => {
|
|
263
|
+
try {
|
|
264
|
+
const config = getConfig();
|
|
265
|
+
const components = await loadAllComponents(config);
|
|
266
|
+
const connections = await loadAllConnections(config);
|
|
267
|
+
// Find the component
|
|
268
|
+
const component = components.find((c) => c.name.toLowerCase() === componentName.toLowerCase());
|
|
269
|
+
if (!component) {
|
|
270
|
+
console.log(`Component "${componentName}" not found.`);
|
|
271
|
+
console.log('\nAvailable components:');
|
|
272
|
+
for (const c of components.slice(0, 10)) {
|
|
273
|
+
console.log(` - ${c.name} (${c.type})`);
|
|
274
|
+
}
|
|
275
|
+
if (components.length > 10) {
|
|
276
|
+
console.log(` ... and ${components.length - 10} more`);
|
|
277
|
+
}
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
// Find connections TO this component
|
|
281
|
+
const incoming = connections.filter((c) => c.to.component_id === component.component_id);
|
|
282
|
+
// Find connections FROM this component
|
|
283
|
+
const outgoing = connections.filter((c) => c.from.component_id === component.component_id);
|
|
284
|
+
if (options.json) {
|
|
285
|
+
console.log(JSON.stringify({ component, incoming, outgoing }, null, 2));
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
console.log(`NavGator - Impact Analysis: ${component.name}\n`);
|
|
289
|
+
console.log('========================================');
|
|
290
|
+
console.log(`Component: ${component.name}`);
|
|
291
|
+
console.log(`Type: ${component.type}`);
|
|
292
|
+
console.log(`Layer: ${component.role.layer}`);
|
|
293
|
+
console.log(`Purpose: ${component.role.purpose}`);
|
|
294
|
+
if (incoming.length > 0) {
|
|
295
|
+
console.log(`\nINCOMING CONNECTIONS (${incoming.length}):`);
|
|
296
|
+
console.log('These files/components USE this component:\n');
|
|
297
|
+
for (const conn of incoming) {
|
|
298
|
+
const lineInfo = conn.code_reference.line_start ? `:${conn.code_reference.line_start}` : '';
|
|
299
|
+
console.log(` ${conn.code_reference.file}${lineInfo}`);
|
|
300
|
+
// Use symbol as primary identifier
|
|
301
|
+
if (conn.code_reference.symbol) {
|
|
302
|
+
const symbolType = conn.code_reference.symbol_type ? ` (${conn.code_reference.symbol_type})` : '';
|
|
303
|
+
console.log(` Symbol: ${conn.code_reference.symbol}${symbolType}`);
|
|
304
|
+
}
|
|
305
|
+
if (conn.code_reference.code_snippet) {
|
|
306
|
+
console.log(` Code: ${conn.code_reference.code_snippet}`);
|
|
307
|
+
}
|
|
308
|
+
console.log('');
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
if (outgoing.length > 0) {
|
|
312
|
+
console.log(`\nOUTGOING CONNECTIONS (${outgoing.length}):`);
|
|
313
|
+
console.log('This component USES these:\n');
|
|
314
|
+
for (const conn of outgoing) {
|
|
315
|
+
const target = components.find((c) => c.component_id === conn.to.component_id);
|
|
316
|
+
console.log(` → ${target?.name || conn.to.component_id}`);
|
|
317
|
+
console.log(` Type: ${conn.connection_type}`);
|
|
318
|
+
console.log('');
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
if (incoming.length === 0 && outgoing.length === 0) {
|
|
322
|
+
console.log('\nNo connections found for this component.');
|
|
323
|
+
}
|
|
324
|
+
console.log('\n========================================');
|
|
325
|
+
console.log(`Files that may need changes if you modify ${component.name}:`);
|
|
326
|
+
const affectedFiles = new Set(incoming.map((c) => c.code_reference.file));
|
|
327
|
+
for (const file of affectedFiles) {
|
|
328
|
+
console.log(` - ${file}`);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
catch (error) {
|
|
332
|
+
console.error('Impact analysis failed:', error);
|
|
333
|
+
process.exit(1);
|
|
334
|
+
}
|
|
335
|
+
});
|
|
336
|
+
// =============================================================================
|
|
337
|
+
// CONNECTIONS COMMAND
|
|
338
|
+
// =============================================================================
|
|
339
|
+
program
|
|
340
|
+
.command('connections <component>')
|
|
341
|
+
.description('Show all connections for a specific component')
|
|
342
|
+
.option('--json', 'Output as JSON')
|
|
343
|
+
.option('--incoming', 'Show only incoming connections')
|
|
344
|
+
.option('--outgoing', 'Show only outgoing connections')
|
|
345
|
+
.action(async (componentName, options) => {
|
|
346
|
+
try {
|
|
347
|
+
const config = getConfig();
|
|
348
|
+
const components = await loadAllComponents(config);
|
|
349
|
+
const connections = await loadAllConnections(config);
|
|
350
|
+
const component = components.find((c) => c.name.toLowerCase() === componentName.toLowerCase());
|
|
351
|
+
if (!component) {
|
|
352
|
+
console.log(`Component "${componentName}" not found.`);
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
const incoming = options.outgoing
|
|
356
|
+
? []
|
|
357
|
+
: connections.filter((c) => c.to.component_id === component.component_id);
|
|
358
|
+
const outgoing = options.incoming
|
|
359
|
+
? []
|
|
360
|
+
: connections.filter((c) => c.from.component_id === component.component_id);
|
|
361
|
+
if (options.json) {
|
|
362
|
+
console.log(JSON.stringify({ component, incoming, outgoing }, null, 2));
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
console.log(`NavGator - Connections: ${component.name}\n`);
|
|
366
|
+
console.log('========================================');
|
|
367
|
+
console.log(`Component: ${component.name} (${component.type})`);
|
|
368
|
+
console.log(`Layer: ${component.role.layer}`);
|
|
369
|
+
if (!options.outgoing && incoming.length > 0) {
|
|
370
|
+
console.log(`\nINCOMING (${incoming.length}):`);
|
|
371
|
+
for (const conn of incoming) {
|
|
372
|
+
const lineInfo = conn.code_reference.line_start ? `:${conn.code_reference.line_start}` : '';
|
|
373
|
+
const symbolInfo = conn.code_reference.symbol ? ` (${conn.code_reference.symbol})` : '';
|
|
374
|
+
console.log(`├── ${conn.connection_type}`);
|
|
375
|
+
console.log(`│ └── ${conn.code_reference.file}${lineInfo}${symbolInfo}`);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
if (!options.incoming && outgoing.length > 0) {
|
|
379
|
+
console.log(`\nOUTGOING (${outgoing.length}):`);
|
|
380
|
+
for (const conn of outgoing) {
|
|
381
|
+
const target = components.find((c) => c.component_id === conn.to.component_id);
|
|
382
|
+
console.log(`├── ${conn.connection_type} → ${target?.name || 'unknown'}`);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
catch (error) {
|
|
387
|
+
console.error('Connections query failed:', error);
|
|
388
|
+
process.exit(1);
|
|
389
|
+
}
|
|
390
|
+
});
|
|
391
|
+
// =============================================================================
|
|
392
|
+
// LIST COMMAND
|
|
393
|
+
// =============================================================================
|
|
394
|
+
program
|
|
395
|
+
.command('list')
|
|
396
|
+
.description('List all tracked components')
|
|
397
|
+
.option('-t, --type <type>', 'Filter by type')
|
|
398
|
+
.option('-l, --layer <layer>', 'Filter by layer')
|
|
399
|
+
.option('--json', 'Output as JSON')
|
|
400
|
+
.action(async (options) => {
|
|
401
|
+
try {
|
|
402
|
+
const config = getConfig();
|
|
403
|
+
let components = await loadAllComponents(config);
|
|
404
|
+
if (options.type) {
|
|
405
|
+
components = components.filter((c) => c.type === options.type);
|
|
406
|
+
}
|
|
407
|
+
if (options.layer) {
|
|
408
|
+
components = components.filter((c) => c.role.layer === options.layer);
|
|
409
|
+
}
|
|
410
|
+
if (options.json) {
|
|
411
|
+
console.log(JSON.stringify(components, null, 2));
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
console.log(`NavGator - Components (${components.length})\n`);
|
|
415
|
+
// Group by layer
|
|
416
|
+
const byLayer = {};
|
|
417
|
+
for (const c of components) {
|
|
418
|
+
if (!byLayer[c.role.layer])
|
|
419
|
+
byLayer[c.role.layer] = [];
|
|
420
|
+
byLayer[c.role.layer].push(c);
|
|
421
|
+
}
|
|
422
|
+
for (const [layer, comps] of Object.entries(byLayer)) {
|
|
423
|
+
console.log(`\n${layer.toUpperCase()}:`);
|
|
424
|
+
for (const c of comps) {
|
|
425
|
+
const version = c.version ? `@${c.version}` : '';
|
|
426
|
+
console.log(` ${c.name}${version} (${c.type})`);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
catch (error) {
|
|
431
|
+
console.error('List failed:', error);
|
|
432
|
+
process.exit(1);
|
|
433
|
+
}
|
|
434
|
+
});
|
|
435
|
+
// =============================================================================
|
|
436
|
+
// DIAGRAM COMMAND
|
|
437
|
+
// =============================================================================
|
|
438
|
+
program
|
|
439
|
+
.command('diagram')
|
|
440
|
+
.description('Generate a Mermaid diagram of the architecture')
|
|
441
|
+
.option('-f, --focus <component>', 'Center diagram on a specific component')
|
|
442
|
+
.option('-l, --layer <layer>', 'Show only a specific layer')
|
|
443
|
+
.option('-s, --summary', 'Show only top connected components')
|
|
444
|
+
.option('-d, --direction <dir>', 'Diagram direction: TB, BT, LR, RL', 'TB')
|
|
445
|
+
.option('--no-styles', 'Disable color styling')
|
|
446
|
+
.option('--no-labels', 'Hide connection labels')
|
|
447
|
+
.option('-o, --output <file>', 'Save to file instead of stdout')
|
|
448
|
+
.option('-m, --max-nodes <n>', 'Maximum nodes to show', '50')
|
|
449
|
+
.option('--markdown', 'Wrap diagram in markdown code block')
|
|
450
|
+
.action(async (options) => {
|
|
451
|
+
try {
|
|
452
|
+
const config = getConfig();
|
|
453
|
+
const graph = await loadGraph(config);
|
|
454
|
+
if (!graph) {
|
|
455
|
+
console.error('No architecture data found. Run `navgator scan` first.');
|
|
456
|
+
process.exit(1);
|
|
457
|
+
}
|
|
458
|
+
const diagramOpts = {
|
|
459
|
+
direction: options.direction,
|
|
460
|
+
includeStyles: options.styles !== false,
|
|
461
|
+
showLabels: options.labels !== false,
|
|
462
|
+
maxNodes: parseInt(options.maxNodes, 10),
|
|
463
|
+
};
|
|
464
|
+
let diagram;
|
|
465
|
+
if (options.focus) {
|
|
466
|
+
// Find component by name
|
|
467
|
+
const components = await loadAllComponents(config);
|
|
468
|
+
const component = components.find((c) => c.name.toLowerCase() === options.focus.toLowerCase());
|
|
469
|
+
if (!component) {
|
|
470
|
+
console.error(`Component "${options.focus}" not found.`);
|
|
471
|
+
console.log('Available components:');
|
|
472
|
+
for (const c of components.slice(0, 10)) {
|
|
473
|
+
console.log(` - ${c.name}`);
|
|
474
|
+
}
|
|
475
|
+
process.exit(1);
|
|
476
|
+
}
|
|
477
|
+
diagram = generateComponentDiagram(graph, component.component_id, 2, diagramOpts);
|
|
478
|
+
}
|
|
479
|
+
else if (options.layer) {
|
|
480
|
+
const validLayers = ['frontend', 'backend', 'database', 'queue', 'infra', 'external'];
|
|
481
|
+
if (!validLayers.includes(options.layer)) {
|
|
482
|
+
console.error(`Invalid layer "${options.layer}". Valid layers: ${validLayers.join(', ')}`);
|
|
483
|
+
process.exit(1);
|
|
484
|
+
}
|
|
485
|
+
diagram = generateLayerDiagram(graph, options.layer, diagramOpts);
|
|
486
|
+
}
|
|
487
|
+
else if (options.summary) {
|
|
488
|
+
diagram = generateSummaryDiagram(graph, { ...diagramOpts, maxNodes: 20 });
|
|
489
|
+
}
|
|
490
|
+
else {
|
|
491
|
+
diagram = generateMermaidDiagram(graph, diagramOpts);
|
|
492
|
+
}
|
|
493
|
+
// Optionally wrap in markdown
|
|
494
|
+
if (options.markdown) {
|
|
495
|
+
const title = options.focus
|
|
496
|
+
? `Architecture: ${options.focus}`
|
|
497
|
+
: options.layer
|
|
498
|
+
? `${options.layer} Layer`
|
|
499
|
+
: 'Architecture Diagram';
|
|
500
|
+
diagram = wrapInMarkdown(diagram, title);
|
|
501
|
+
}
|
|
502
|
+
// Output
|
|
503
|
+
if (options.output) {
|
|
504
|
+
await fs.promises.writeFile(options.output, diagram, 'utf-8');
|
|
505
|
+
console.log(`Diagram saved to ${options.output}`);
|
|
506
|
+
}
|
|
507
|
+
else {
|
|
508
|
+
console.log(diagram);
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
catch (error) {
|
|
512
|
+
console.error('Diagram generation failed:', error);
|
|
513
|
+
process.exit(1);
|
|
514
|
+
}
|
|
515
|
+
});
|
|
516
|
+
// =============================================================================
|
|
517
|
+
// UI COMMAND
|
|
518
|
+
// =============================================================================
|
|
519
|
+
program
|
|
520
|
+
.command('ui')
|
|
521
|
+
.description('Launch the NavGator dashboard in your browser')
|
|
522
|
+
.option('-p, --port <port>', 'Port to serve on', '3333')
|
|
523
|
+
.option('--path <path>', 'Project path to analyze (defaults to current directory)')
|
|
524
|
+
.option('--no-open', 'Don\'t open browser automatically')
|
|
525
|
+
.action(async (options) => {
|
|
526
|
+
try {
|
|
527
|
+
const port = parseInt(options.port, 10);
|
|
528
|
+
const projectPath = options.path
|
|
529
|
+
? (await import('path')).resolve(options.path)
|
|
530
|
+
: process.cwd();
|
|
531
|
+
console.log('');
|
|
532
|
+
console.log('🐊 NavGator Dashboard');
|
|
533
|
+
console.log(` Project: ${projectPath}`);
|
|
534
|
+
console.log('');
|
|
535
|
+
const { port: actualPort } = await startUIServer({
|
|
536
|
+
port,
|
|
537
|
+
projectPath,
|
|
538
|
+
});
|
|
539
|
+
const url = `http://localhost:${actualPort}`;
|
|
540
|
+
console.log(`Dashboard running at: ${url}`);
|
|
541
|
+
console.log('');
|
|
542
|
+
console.log('Press Ctrl+C to stop');
|
|
543
|
+
console.log('');
|
|
544
|
+
// Try to open browser
|
|
545
|
+
if (options.open !== false) {
|
|
546
|
+
const { exec } = await import('child_process');
|
|
547
|
+
const openCmd = process.platform === 'darwin' ? 'open' :
|
|
548
|
+
process.platform === 'win32' ? 'start' : 'xdg-open';
|
|
549
|
+
exec(`${openCmd} ${url}`);
|
|
550
|
+
}
|
|
551
|
+
// Keep process running
|
|
552
|
+
process.on('SIGINT', () => {
|
|
553
|
+
console.log('\nShutting down...');
|
|
554
|
+
process.exit(0);
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
catch (error) {
|
|
558
|
+
console.error('Failed to start UI:', error);
|
|
559
|
+
process.exit(1);
|
|
560
|
+
}
|
|
561
|
+
});
|
|
562
|
+
// =============================================================================
|
|
563
|
+
// PROMPTS COMMAND
|
|
564
|
+
// =============================================================================
|
|
565
|
+
program
|
|
566
|
+
.command('prompts')
|
|
567
|
+
.description('Scan and display AI prompts in the codebase')
|
|
568
|
+
.option('-v, --verbose', 'Show full prompt content')
|
|
569
|
+
.option('--json', 'Output as JSON')
|
|
570
|
+
.option('--detail <name>', 'Show detailed view of a specific prompt')
|
|
571
|
+
.action(async (options) => {
|
|
572
|
+
try {
|
|
573
|
+
const result = await scanPromptsOnly(process.cwd());
|
|
574
|
+
if (options.json) {
|
|
575
|
+
console.log(JSON.stringify(result, null, 2));
|
|
576
|
+
return;
|
|
577
|
+
}
|
|
578
|
+
if (options.detail) {
|
|
579
|
+
// Find specific prompt
|
|
580
|
+
const prompt = result.prompts.find((p) => p.name.toLowerCase() === options.detail.toLowerCase() ||
|
|
581
|
+
p.id.toLowerCase() === options.detail.toLowerCase());
|
|
582
|
+
if (!prompt) {
|
|
583
|
+
console.log(`Prompt "${options.detail}" not found.`);
|
|
584
|
+
console.log('\nAvailable prompts:');
|
|
585
|
+
for (const p of result.prompts.slice(0, 10)) {
|
|
586
|
+
console.log(` - ${p.name} (${p.location.file}:${p.location.lineStart})`);
|
|
587
|
+
}
|
|
588
|
+
return;
|
|
589
|
+
}
|
|
590
|
+
console.log(formatPromptDetail(prompt));
|
|
591
|
+
return;
|
|
592
|
+
}
|
|
593
|
+
// Standard output
|
|
594
|
+
console.log(formatPromptsOutput(result));
|
|
595
|
+
// Show prompt details if verbose
|
|
596
|
+
if (options.verbose && result.prompts.length > 0) {
|
|
597
|
+
console.log('\n' + '='.repeat(60));
|
|
598
|
+
console.log('PROMPT DETAILS');
|
|
599
|
+
console.log('='.repeat(60));
|
|
600
|
+
for (const prompt of result.prompts) {
|
|
601
|
+
console.log(`\n--- ${prompt.name} ---`);
|
|
602
|
+
console.log(`File: ${prompt.location.file}:${prompt.location.lineStart}`);
|
|
603
|
+
if (prompt.purpose) {
|
|
604
|
+
console.log(`Purpose: ${prompt.purpose}`);
|
|
605
|
+
}
|
|
606
|
+
for (const msg of prompt.messages) {
|
|
607
|
+
console.log(`\n[${msg.role.toUpperCase()}]:`);
|
|
608
|
+
// Show up to 300 chars of content
|
|
609
|
+
const preview = msg.content.slice(0, 300);
|
|
610
|
+
console.log(preview);
|
|
611
|
+
if (msg.content.length > 300) {
|
|
612
|
+
console.log(`... (${msg.content.length - 300} more chars)`);
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
catch (error) {
|
|
619
|
+
console.error('Prompt scan failed:', error);
|
|
620
|
+
process.exit(1);
|
|
621
|
+
}
|
|
622
|
+
});
|
|
623
|
+
// =============================================================================
|
|
624
|
+
// PARSE AND RUN
|
|
625
|
+
// =============================================================================
|
|
626
|
+
program.parse();
|
|
627
|
+
//# sourceMappingURL=index.js.map
|