@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.
Files changed (84) hide show
  1. package/.claude-plugin/plugin.json +10 -0
  2. package/LICENSE +21 -0
  3. package/README.md +486 -0
  4. package/agents/architecture-advisor.md +109 -0
  5. package/commands/nav-check.md +64 -0
  6. package/commands/nav-connections.md +58 -0
  7. package/commands/nav-diagram.md +106 -0
  8. package/commands/nav-export.md +71 -0
  9. package/commands/nav-impact.md +58 -0
  10. package/commands/nav-scan.md +46 -0
  11. package/commands/nav-status.md +44 -0
  12. package/dist/cli/index.d.ts +7 -0
  13. package/dist/cli/index.d.ts.map +1 -0
  14. package/dist/cli/index.js +627 -0
  15. package/dist/cli/index.js.map +1 -0
  16. package/dist/config.d.ts +95 -0
  17. package/dist/config.d.ts.map +1 -0
  18. package/dist/config.js +262 -0
  19. package/dist/config.js.map +1 -0
  20. package/dist/diagram.d.ts +36 -0
  21. package/dist/diagram.d.ts.map +1 -0
  22. package/dist/diagram.js +333 -0
  23. package/dist/diagram.js.map +1 -0
  24. package/dist/index.d.ts +16 -0
  25. package/dist/index.d.ts.map +1 -0
  26. package/dist/index.js +18 -0
  27. package/dist/index.js.map +1 -0
  28. package/dist/scanner.d.ts +57 -0
  29. package/dist/scanner.d.ts.map +1 -0
  30. package/dist/scanner.js +282 -0
  31. package/dist/scanner.js.map +1 -0
  32. package/dist/scanners/connections/ast-scanner.d.ts +26 -0
  33. package/dist/scanners/connections/ast-scanner.d.ts.map +1 -0
  34. package/dist/scanners/connections/ast-scanner.js +430 -0
  35. package/dist/scanners/connections/ast-scanner.js.map +1 -0
  36. package/dist/scanners/connections/service-calls.d.ts +14 -0
  37. package/dist/scanners/connections/service-calls.d.ts.map +1 -0
  38. package/dist/scanners/connections/service-calls.js +719 -0
  39. package/dist/scanners/connections/service-calls.js.map +1 -0
  40. package/dist/scanners/infrastructure/index.d.ts +27 -0
  41. package/dist/scanners/infrastructure/index.d.ts.map +1 -0
  42. package/dist/scanners/infrastructure/index.js +233 -0
  43. package/dist/scanners/infrastructure/index.js.map +1 -0
  44. package/dist/scanners/packages/npm.d.ts +18 -0
  45. package/dist/scanners/packages/npm.d.ts.map +1 -0
  46. package/dist/scanners/packages/npm.js +256 -0
  47. package/dist/scanners/packages/npm.js.map +1 -0
  48. package/dist/scanners/packages/pip.d.ts +14 -0
  49. package/dist/scanners/packages/pip.d.ts.map +1 -0
  50. package/dist/scanners/packages/pip.js +228 -0
  51. package/dist/scanners/packages/pip.js.map +1 -0
  52. package/dist/scanners/prompts/detector.d.ts +119 -0
  53. package/dist/scanners/prompts/detector.d.ts.map +1 -0
  54. package/dist/scanners/prompts/detector.js +617 -0
  55. package/dist/scanners/prompts/detector.js.map +1 -0
  56. package/dist/scanners/prompts/index.d.ts +51 -0
  57. package/dist/scanners/prompts/index.d.ts.map +1 -0
  58. package/dist/scanners/prompts/index.js +340 -0
  59. package/dist/scanners/prompts/index.js.map +1 -0
  60. package/dist/scanners/prompts/types.d.ts +127 -0
  61. package/dist/scanners/prompts/types.d.ts.map +1 -0
  62. package/dist/scanners/prompts/types.js +37 -0
  63. package/dist/scanners/prompts/types.js.map +1 -0
  64. package/dist/setup.d.ts +65 -0
  65. package/dist/setup.d.ts.map +1 -0
  66. package/dist/setup.js +261 -0
  67. package/dist/setup.js.map +1 -0
  68. package/dist/storage.d.ts +147 -0
  69. package/dist/storage.d.ts.map +1 -0
  70. package/dist/storage.js +931 -0
  71. package/dist/storage.js.map +1 -0
  72. package/dist/types.d.ts +296 -0
  73. package/dist/types.d.ts.map +1 -0
  74. package/dist/types.js +55 -0
  75. package/dist/types.js.map +1 -0
  76. package/dist/ui-server.d.ts +17 -0
  77. package/dist/ui-server.d.ts.map +1 -0
  78. package/dist/ui-server.js +815 -0
  79. package/dist/ui-server.js.map +1 -0
  80. package/hooks/hooks.json +57 -0
  81. package/package.json +80 -0
  82. package/scripts/ibr-ui-test.mjs +359 -0
  83. package/scripts/postinstall.cjs +35 -0
  84. 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