codebase-context 1.7.0 → 1.8.1

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 (117) hide show
  1. package/README.md +149 -90
  2. package/dist/analyzers/angular/index.d.ts.map +1 -1
  3. package/dist/analyzers/angular/index.js +85 -39
  4. package/dist/analyzers/angular/index.js.map +1 -1
  5. package/dist/analyzers/generic/index.d.ts.map +1 -1
  6. package/dist/analyzers/generic/index.js +5 -4
  7. package/dist/analyzers/generic/index.js.map +1 -1
  8. package/dist/cli-formatters.d.ts +47 -0
  9. package/dist/cli-formatters.d.ts.map +1 -0
  10. package/dist/cli-formatters.js +803 -0
  11. package/dist/cli-formatters.js.map +1 -0
  12. package/dist/cli-memory.d.ts +5 -0
  13. package/dist/cli-memory.d.ts.map +1 -0
  14. package/dist/cli-memory.js +218 -0
  15. package/dist/cli-memory.js.map +1 -0
  16. package/dist/cli.d.ts +1 -1
  17. package/dist/cli.d.ts.map +1 -1
  18. package/dist/cli.js +168 -178
  19. package/dist/cli.js.map +1 -1
  20. package/dist/core/auto-refresh.d.ts +16 -0
  21. package/dist/core/auto-refresh.d.ts.map +1 -0
  22. package/dist/core/auto-refresh.js +25 -0
  23. package/dist/core/auto-refresh.js.map +1 -0
  24. package/dist/core/file-watcher.d.ts +15 -0
  25. package/dist/core/file-watcher.d.ts.map +1 -0
  26. package/dist/core/file-watcher.js +59 -0
  27. package/dist/core/file-watcher.js.map +1 -0
  28. package/dist/core/index-meta.d.ts +3 -0
  29. package/dist/core/index-meta.d.ts.map +1 -1
  30. package/dist/core/index-meta.js +9 -1
  31. package/dist/core/index-meta.js.map +1 -1
  32. package/dist/core/indexer.d.ts.map +1 -1
  33. package/dist/core/indexer.js +74 -15
  34. package/dist/core/indexer.js.map +1 -1
  35. package/dist/core/reranker.d.ts.map +1 -1
  36. package/dist/core/reranker.js +3 -0
  37. package/dist/core/reranker.js.map +1 -1
  38. package/dist/core/search-quality.js +2 -2
  39. package/dist/core/search-quality.js.map +1 -1
  40. package/dist/core/search.d.ts.map +1 -1
  41. package/dist/core/search.js +20 -7
  42. package/dist/core/search.js.map +1 -1
  43. package/dist/core/symbol-references.d.ts +2 -3
  44. package/dist/core/symbol-references.d.ts.map +1 -1
  45. package/dist/core/symbol-references.js +111 -16
  46. package/dist/core/symbol-references.js.map +1 -1
  47. package/dist/embeddings/index.d.ts +8 -0
  48. package/dist/embeddings/index.d.ts.map +1 -1
  49. package/dist/embeddings/index.js +17 -2
  50. package/dist/embeddings/index.js.map +1 -1
  51. package/dist/embeddings/openai.d.ts +1 -1
  52. package/dist/embeddings/openai.d.ts.map +1 -1
  53. package/dist/embeddings/openai.js +3 -1
  54. package/dist/embeddings/openai.js.map +1 -1
  55. package/dist/embeddings/transformers.d.ts +6 -0
  56. package/dist/embeddings/transformers.d.ts.map +1 -1
  57. package/dist/embeddings/transformers.js +12 -5
  58. package/dist/embeddings/transformers.js.map +1 -1
  59. package/dist/embeddings/types.d.ts +1 -0
  60. package/dist/embeddings/types.d.ts.map +1 -1
  61. package/dist/embeddings/types.js +7 -1
  62. package/dist/embeddings/types.js.map +1 -1
  63. package/dist/grammars/manifest.d.ts.map +1 -1
  64. package/dist/grammars/manifest.js +2 -1
  65. package/dist/grammars/manifest.js.map +1 -1
  66. package/dist/index.d.ts +5 -1
  67. package/dist/index.d.ts.map +1 -1
  68. package/dist/index.js +46 -3
  69. package/dist/index.js.map +1 -1
  70. package/dist/patterns/semantics.d.ts +2 -1
  71. package/dist/patterns/semantics.d.ts.map +1 -1
  72. package/dist/patterns/semantics.js +0 -2
  73. package/dist/patterns/semantics.js.map +1 -1
  74. package/dist/storage/index.d.ts +4 -1
  75. package/dist/storage/index.d.ts.map +1 -1
  76. package/dist/storage/index.js +2 -2
  77. package/dist/storage/index.js.map +1 -1
  78. package/dist/storage/lancedb.d.ts +2 -0
  79. package/dist/storage/lancedb.d.ts.map +1 -1
  80. package/dist/storage/lancedb.js +20 -4
  81. package/dist/storage/lancedb.js.map +1 -1
  82. package/dist/storage/types.d.ts +4 -1
  83. package/dist/storage/types.d.ts.map +1 -1
  84. package/dist/storage/types.js.map +1 -1
  85. package/dist/tools/detect-circular-dependencies.d.ts.map +1 -1
  86. package/dist/tools/detect-circular-dependencies.js.map +1 -1
  87. package/dist/tools/get-team-patterns.d.ts.map +1 -1
  88. package/dist/tools/get-team-patterns.js +37 -14
  89. package/dist/tools/get-team-patterns.js.map +1 -1
  90. package/dist/tools/search-codebase.d.ts.map +1 -1
  91. package/dist/tools/search-codebase.js +296 -189
  92. package/dist/tools/search-codebase.js.map +1 -1
  93. package/dist/tools/types.d.ts +193 -1
  94. package/dist/tools/types.d.ts.map +1 -1
  95. package/dist/types/index.d.ts +73 -11
  96. package/dist/types/index.d.ts.map +1 -1
  97. package/dist/types/index.js +0 -1
  98. package/dist/types/index.js.map +1 -1
  99. package/dist/utils/language-detection.d.ts.map +1 -1
  100. package/dist/utils/language-detection.js +6 -1
  101. package/dist/utils/language-detection.js.map +1 -1
  102. package/dist/utils/tree-sitter.d.ts +11 -0
  103. package/dist/utils/tree-sitter.d.ts.map +1 -1
  104. package/dist/utils/tree-sitter.js +111 -0
  105. package/dist/utils/tree-sitter.js.map +1 -1
  106. package/dist/utils/usage-tracker.d.ts +30 -40
  107. package/dist/utils/usage-tracker.d.ts.map +1 -1
  108. package/dist/utils/usage-tracker.js +66 -8
  109. package/dist/utils/usage-tracker.js.map +1 -1
  110. package/docs/capabilities.md +22 -8
  111. package/docs/cli.md +196 -0
  112. package/grammars/tree-sitter-kotlin.wasm +0 -0
  113. package/package.json +9 -6
  114. package/dist/tools/get-component-usage.d.ts +0 -5
  115. package/dist/tools/get-component-usage.d.ts.map +0 -1
  116. package/dist/tools/get-component-usage.js +0 -83
  117. package/dist/tools/get-component-usage.js.map +0 -1
@@ -0,0 +1,803 @@
1
+ /**
2
+ * Human-readable CLI formatters for codebase-context commands.
3
+ * Use --json flag for raw JSON output instead.
4
+ */
5
+ import path from 'path';
6
+ export const BOX_WIDTH = 72;
7
+ function parseEnvBoolean(value) {
8
+ if (!value)
9
+ return false;
10
+ const normalized = value.trim().toLowerCase();
11
+ return normalized === '1' || normalized === 'true' || normalized === 'yes' || normalized === 'on';
12
+ }
13
+ function getCharset() {
14
+ if (parseEnvBoolean(process.env.CODEBASE_CONTEXT_ASCII))
15
+ return 'ascii';
16
+ if (process.stdout && process.stdout.isTTY === false)
17
+ return 'ascii';
18
+ return 'unicode';
19
+ }
20
+ const UNICODE_GLYPHS = {
21
+ charset: 'unicode',
22
+ box: { tl: '┌', tr: '┐', bl: '└', br: '┘', h: '─', v: '│' },
23
+ tree: { tee: '├─', elbow: '└─', pipe: '│' },
24
+ arrow: '→',
25
+ leftRight: '↔',
26
+ dot: '·',
27
+ warn: '⚠',
28
+ bar: { full: '█', empty: '░' }
29
+ };
30
+ const ASCII_GLYPHS = {
31
+ charset: 'ascii',
32
+ box: { tl: '+', tr: '+', bl: '+', br: '+', h: '-', v: '|' },
33
+ tree: { tee: '|-', elbow: '`-', pipe: '|' },
34
+ arrow: '->',
35
+ leftRight: '<->',
36
+ dot: '*',
37
+ warn: '!',
38
+ bar: { full: '#', empty: '.' }
39
+ };
40
+ function getGlyphs() {
41
+ return getCharset() === 'ascii' ? ASCII_GLYPHS : UNICODE_GLYPHS;
42
+ }
43
+ export function shortPath(filePath, rootPath) {
44
+ const normalized = filePath.replace(/\\/g, '/');
45
+ const normalizedRoot = rootPath.replace(/\\/g, '/');
46
+ if (normalized.startsWith(normalizedRoot)) {
47
+ return normalized.slice(normalizedRoot.length).replace(/^\//, '');
48
+ }
49
+ // Also strip common Repos/ prefix patterns
50
+ const reposIdx = normalized.indexOf('/Repos/');
51
+ if (reposIdx >= 0) {
52
+ const afterRepos = normalized.slice(reposIdx + 7);
53
+ const slashIdx = afterRepos.indexOf('/');
54
+ return slashIdx >= 0 ? afterRepos.slice(slashIdx + 1) : afterRepos;
55
+ }
56
+ return path.basename(filePath);
57
+ }
58
+ export function formatTrend(trend) {
59
+ if (trend === 'Rising')
60
+ return 'rising';
61
+ if (trend === 'Declining')
62
+ return 'declining';
63
+ return '';
64
+ }
65
+ export function formatType(type) {
66
+ if (!type)
67
+ return '';
68
+ // "interceptor:core" -> "interceptor (core)", "resolver:unknown" -> "resolver"
69
+ const [compType, layer] = type.split(':');
70
+ if (!layer || layer === 'unknown')
71
+ return compType;
72
+ return `${compType} (${layer})`;
73
+ }
74
+ export function padRight(str, len) {
75
+ return str.length >= len ? str : str + ' '.repeat(len - str.length);
76
+ }
77
+ export function padLeft(str, len) {
78
+ return str.length >= len ? str : ' '.repeat(len - str.length) + str;
79
+ }
80
+ export function barChart(pct, width = 10) {
81
+ const g = getGlyphs();
82
+ const clamped = Math.max(0, Math.min(100, pct));
83
+ const filled = Math.round((clamped / 100) * width);
84
+ return g.bar.full.repeat(filled) + g.bar.empty.repeat(width - filled);
85
+ }
86
+ export function scoreBar(score, width = 10) {
87
+ return barChart(Math.round(score * 100), width);
88
+ }
89
+ export function parsePercent(s) {
90
+ if (!s)
91
+ return 0;
92
+ const m = s.match(/(\d+)/);
93
+ return m ? parseInt(m[1], 10) : 0;
94
+ }
95
+ export function wrapLine(text, maxWidth) {
96
+ if (text.length <= maxWidth)
97
+ return [text];
98
+ const words = text.split(' ');
99
+ const out = [];
100
+ let cur = '';
101
+ for (const w of words) {
102
+ const candidate = cur ? `${cur} ${w}` : w;
103
+ if (candidate.length > maxWidth) {
104
+ if (cur)
105
+ out.push(cur);
106
+ cur = w;
107
+ }
108
+ else
109
+ cur = candidate;
110
+ }
111
+ if (cur)
112
+ out.push(cur);
113
+ return out;
114
+ }
115
+ export function drawBox(title, lines, width = 60) {
116
+ const g = getGlyphs();
117
+ const output = [];
118
+ const inner = width - 4; // 2 for "| " + 2 for " |"
119
+ const dashes = g.box.h;
120
+ const titlePart = `${g.box.tl}${g.box.h} ${title} `;
121
+ const remaining = Math.max(0, width - titlePart.length - 1);
122
+ output.push(titlePart + dashes.repeat(remaining) + g.box.tr);
123
+ for (const line of lines) {
124
+ const wrapped = wrapLine(line, inner);
125
+ for (const wl of wrapped) {
126
+ const padded = wl + ' '.repeat(Math.max(0, inner - wl.length));
127
+ output.push(`${g.box.v} ${padded} ${g.box.v}`);
128
+ }
129
+ }
130
+ output.push(g.box.bl + dashes.repeat(width - 2) + g.box.br);
131
+ return output;
132
+ }
133
+ export function getCycleFiles(cycle) {
134
+ if (cycle.files && cycle.files.length > 0)
135
+ return cycle.files;
136
+ return cycle.cycle ?? [];
137
+ }
138
+ export function formatPatterns(data) {
139
+ const g = getGlyphs();
140
+ const { patterns, goldenFiles, memories, conflicts } = data;
141
+ const lines = [];
142
+ if (patterns) {
143
+ const entries = Object.entries(patterns);
144
+ for (let ei = 0; ei < entries.length; ei++) {
145
+ const [category, catData] = entries[ei];
146
+ const label = category
147
+ .replace(/([A-Z])/g, ' $1')
148
+ .replace(/^./, (s) => s.toUpperCase())
149
+ .trim();
150
+ if (ei > 0) {
151
+ lines.push(' ' + g.box.h.repeat(66));
152
+ }
153
+ lines.push('');
154
+ lines.push(label.toUpperCase());
155
+ const renderEntry = (entry, isAlt) => {
156
+ const raw = entry;
157
+ const guidance = typeof raw.guidance === 'string' ? raw.guidance : null;
158
+ const prefix = isAlt ? 'alt ' : ' ';
159
+ if (guidance) {
160
+ lines.push(`${prefix}${guidance}`);
161
+ }
162
+ else {
163
+ const name = padRight(entry.name ?? '', 30);
164
+ const freq = padLeft(entry.frequency ?? '', 6);
165
+ const trend = formatTrend(entry.trend);
166
+ lines.push(`${prefix}${name} ${freq}${trend ? ` ${trend}` : ''}`);
167
+ }
168
+ };
169
+ const primary = catData.primary;
170
+ renderEntry(primary, false);
171
+ const alsoDetected = catData.alsoDetected;
172
+ if (alsoDetected) {
173
+ for (const alt of alsoDetected)
174
+ renderEntry(alt, true);
175
+ }
176
+ }
177
+ }
178
+ const topUsed = data.topUsed;
179
+ if (topUsed && topUsed.length > 0) {
180
+ lines.push('');
181
+ lines.push(g.box.h.repeat(66));
182
+ lines.push('');
183
+ lines.push('TOP LIBRARIES');
184
+ for (const lib of topUsed.slice(0, 15)) {
185
+ const src = padRight(lib.source ?? '', 52);
186
+ lines.push(` ${src} ${lib.count} imports`);
187
+ }
188
+ }
189
+ if (goldenFiles && goldenFiles.length > 0) {
190
+ lines.push('');
191
+ lines.push(g.box.h.repeat(66));
192
+ lines.push('');
193
+ lines.push('GOLDEN FILES');
194
+ for (const gf of goldenFiles.slice(0, 5)) {
195
+ const file = padRight(gf.file ?? '', 52);
196
+ lines.push(` ${file} score: ${gf.score}`);
197
+ }
198
+ }
199
+ if (conflicts && conflicts.length > 0) {
200
+ lines.push('');
201
+ lines.push(g.box.h.repeat(66));
202
+ lines.push('');
203
+ lines.push('CONFLICTS');
204
+ for (const c of conflicts) {
205
+ const p = c.primary;
206
+ const a = c.alternative;
207
+ const pTrend = p.trend ? ` (${p.trend})` : '';
208
+ const aTrend = a.trend ? ` (${a.trend})` : '';
209
+ lines.push(` split: ${p.name} ${p.adoption}${pTrend} vs ${a.name} ${a.adoption}${aTrend}`);
210
+ }
211
+ }
212
+ if (memories && memories.length > 0) {
213
+ lines.push('');
214
+ lines.push(g.box.h.repeat(66));
215
+ lines.push('');
216
+ lines.push('MEMORIES');
217
+ for (const m of memories.slice(0, 5)) {
218
+ lines.push(` [${m.type}] ${m.memory}`);
219
+ }
220
+ }
221
+ lines.push('');
222
+ const boxLines = drawBox('Team Patterns', lines, BOX_WIDTH);
223
+ console.log('');
224
+ for (const l of boxLines) {
225
+ console.log(l);
226
+ }
227
+ console.log('');
228
+ }
229
+ export function formatSearch(data, rootPath, query, intent) {
230
+ const g = getGlyphs();
231
+ const { searchQuality: quality, preflight, results, relatedMemories: memories } = data;
232
+ const boxLines = [];
233
+ const showPreflight = intent === 'edit' || intent === 'refactor' || intent === 'migrate';
234
+ if (quality) {
235
+ const status = quality.status === 'ok' ? 'ok' : 'low confidence';
236
+ const conf = quality.confidence ?? '';
237
+ const confStr = typeof conf === 'number' ? conf.toFixed(2) : String(conf);
238
+ boxLines.push(`Quality: ${status} (${confStr})`);
239
+ if (quality.hint) {
240
+ boxLines.push(`Hint: ${quality.hint}`);
241
+ }
242
+ }
243
+ if (preflight && showPreflight) {
244
+ const readyLabel = preflight.ready ? 'YES' : 'NO';
245
+ boxLines.push(`Ready to edit: ${readyLabel}`);
246
+ if (preflight.nextAction) {
247
+ boxLines.push(`Next: ${preflight.nextAction}`);
248
+ }
249
+ const patterns = preflight.patterns;
250
+ if (patterns) {
251
+ if ((patterns.do && patterns.do.length > 0) ||
252
+ (patterns.avoid && patterns.avoid.length > 0)) {
253
+ boxLines.push('');
254
+ boxLines.push('Patterns:');
255
+ for (const p of patterns.do ?? []) {
256
+ boxLines.push(` do: ${p}`);
257
+ }
258
+ for (const p of patterns.avoid ?? []) {
259
+ boxLines.push(` avoid: ${p}`);
260
+ }
261
+ }
262
+ }
263
+ if (preflight.bestExample) {
264
+ boxLines.push('');
265
+ boxLines.push(`Best example: ${shortPath(preflight.bestExample, rootPath)}`);
266
+ }
267
+ const impact = preflight.impact;
268
+ if (impact?.coverage) {
269
+ boxLines.push(`Callers: ${impact.coverage}`);
270
+ }
271
+ if (impact?.details && impact.details.length > 0) {
272
+ const shown = impact.details.slice(0, 3).map((d) => {
273
+ const p = shortPath(d.file, rootPath);
274
+ const suffix = d.line ? `:${d.line}` : '';
275
+ return `${p}${suffix} (hop ${d.hop})`;
276
+ });
277
+ boxLines.push(`Files: ${shown.join(', ')}`);
278
+ }
279
+ else if (impact?.files && impact.files.length > 0) {
280
+ const shown = impact.files.slice(0, 3).map((f) => shortPath(f, rootPath));
281
+ boxLines.push(`Files: ${shown.join(', ')}`);
282
+ }
283
+ const whatWouldHelp = preflight.whatWouldHelp;
284
+ if (whatWouldHelp && whatWouldHelp.length > 0) {
285
+ boxLines.push('');
286
+ for (const h of whatWouldHelp) {
287
+ boxLines.push(`${g.arrow} ${h}`);
288
+ }
289
+ }
290
+ }
291
+ const titleParts = [];
292
+ if (query)
293
+ titleParts.push(`"${query}"`);
294
+ if (intent)
295
+ titleParts.push(`intent: ${intent}`);
296
+ const boxTitle = titleParts.length > 0 ? `Search: ${titleParts.join(` ${g.box.h.repeat(3)} `)}` : 'Search';
297
+ console.log('');
298
+ if (boxLines.length > 0) {
299
+ const boxOut = drawBox(boxTitle, boxLines, BOX_WIDTH);
300
+ for (const l of boxOut) {
301
+ console.log(l);
302
+ }
303
+ console.log('');
304
+ }
305
+ else if (quality) {
306
+ const status = quality.status === 'ok' ? 'ok' : 'low confidence';
307
+ console.log(` ${results?.length ?? 0} results ${g.dot} quality: ${status}`);
308
+ console.log('');
309
+ }
310
+ if (results && results.length > 0) {
311
+ for (let i = 0; i < results.length; i++) {
312
+ const r = results[i];
313
+ const file = shortPath(r.file ?? '', rootPath);
314
+ const scoreValue = Number(r.score ?? 0);
315
+ const score = scoreValue.toFixed(2);
316
+ const typePart = formatType(r.type);
317
+ const trendPart = formatTrend(r.trend);
318
+ const metaParts = [`confidence: ${scoreBar(scoreValue)} ${score}`];
319
+ if (typePart)
320
+ metaParts.push(typePart);
321
+ if (trendPart)
322
+ metaParts.push(trendPart);
323
+ if (r.relationships?.hasTests)
324
+ metaParts.push('has tests');
325
+ console.log(`${i + 1}. ${file}`);
326
+ console.log(` ${metaParts.join(` ${g.dot} `)}`);
327
+ const summary = r.summary ?? '';
328
+ if (summary) {
329
+ const short = summary.length > 120 ? summary.slice(0, 117) + '...' : summary;
330
+ console.log(` ${short}`);
331
+ }
332
+ if (r.patternWarning) {
333
+ console.log(` ${g.warn} ${r.patternWarning}`);
334
+ }
335
+ const hints = r.hints;
336
+ if (hints?.callers && hints.callers.length > 0) {
337
+ const shortCallers = hints.callers.slice(0, 3).map((c) => shortPath(c, rootPath));
338
+ const total = r.relationships?.importedByCount ?? hints.callers.length;
339
+ const more = total > 3 ? ` (+${total - 3} more)` : '';
340
+ console.log(` used by: ${shortCallers.join(', ')}${more}`);
341
+ }
342
+ if (hints?.tests && hints.tests.length > 0) {
343
+ const shortTests = hints.tests.slice(0, 2).map((t) => shortPath(t, rootPath));
344
+ console.log(` tested: ${shortTests.join(', ')}`);
345
+ }
346
+ const snippet = r.snippet ?? '';
347
+ if (snippet) {
348
+ const snippetLines = snippet.split('\n');
349
+ const trimmed = snippetLines.map((l) => l.trimEnd());
350
+ while (trimmed.length > 0 && trimmed[trimmed.length - 1] === '')
351
+ trimmed.pop();
352
+ const shown = trimmed.slice(0, 8);
353
+ for (const sl of shown) {
354
+ console.log(` ${g.box.v} ${sl}`);
355
+ }
356
+ }
357
+ console.log('');
358
+ }
359
+ }
360
+ if (memories && memories.length > 0) {
361
+ console.log('Memories:');
362
+ for (const m of memories) {
363
+ console.log(` ${m}`);
364
+ }
365
+ console.log('');
366
+ }
367
+ }
368
+ export function formatRefs(data, rootPath) {
369
+ const g = getGlyphs();
370
+ const { symbol, usageCount: count, confidence, usages } = data;
371
+ const lines = [];
372
+ lines.push('');
373
+ lines.push(String(symbol));
374
+ if (usages && usages.length > 0) {
375
+ lines.push(g.tree.pipe);
376
+ for (let i = 0; i < usages.length; i++) {
377
+ const u = usages[i];
378
+ const isLast = i === usages.length - 1;
379
+ const branch = isLast ? g.tree.elbow : g.tree.tee;
380
+ const file = shortPath(u.file ?? '', rootPath);
381
+ lines.push(`${branch} ${file}:${u.line}`);
382
+ const preview = u.preview ?? '';
383
+ if (preview) {
384
+ const nonEmpty = preview
385
+ .split('\n')
386
+ .map((l) => l.trim())
387
+ .filter(Boolean)
388
+ .slice(0, 2);
389
+ const indent = isLast ? ' ' : `${g.tree.pipe} `;
390
+ const maxPrev = BOX_WIDTH - 10;
391
+ for (const pl of nonEmpty) {
392
+ const clipped = pl.length > maxPrev ? pl.slice(0, maxPrev - 3) + '...' : pl;
393
+ lines.push(`${indent} ${clipped}`);
394
+ }
395
+ }
396
+ if (!isLast) {
397
+ lines.push(g.tree.pipe);
398
+ }
399
+ }
400
+ }
401
+ if (data.isComplete === false) {
402
+ lines.push('');
403
+ lines.push(`${g.arrow} results capped at limit — use --limit to see more`);
404
+ }
405
+ lines.push('');
406
+ const confLabel = confidence === 'syntactic' ? 'static analysis' : (confidence ?? 'static analysis');
407
+ const boxTitle = `${symbol} ${g.box.h.repeat(3)} ${count} references ${g.box.h.repeat(3)} ${confLabel}`;
408
+ const boxOut = drawBox(boxTitle, lines, BOX_WIDTH);
409
+ console.log('');
410
+ for (const l of boxOut) {
411
+ console.log(l);
412
+ }
413
+ console.log('');
414
+ }
415
+ export function formatCycles(data, rootPath) {
416
+ const g = getGlyphs();
417
+ const cycles = data.cycles ?? [];
418
+ const stats = data.graphStats;
419
+ const statParts = [];
420
+ if (cycles.length === 0) {
421
+ statParts.push('No cycles found');
422
+ }
423
+ else {
424
+ statParts.push(`${cycles.length} cycle${cycles.length === 1 ? '' : 's'}`);
425
+ }
426
+ if (stats?.files != null)
427
+ statParts.push(`${stats.files} files`);
428
+ if (stats?.edges != null)
429
+ statParts.push(`${stats.edges} edges`);
430
+ if (stats?.avgDependencies != null)
431
+ statParts.push(`${stats.avgDependencies.toFixed(1)} avg deps`);
432
+ const lines = [];
433
+ lines.push('');
434
+ lines.push(statParts.join(` ${g.dot} `));
435
+ for (const c of cycles) {
436
+ const sev = (c.severity ?? 'low').toLowerCase();
437
+ const sevLabel = sev === 'high' ? 'HIGH' : sev === 'medium' ? 'MED ' : 'LOW ';
438
+ const nodes = getCycleFiles(c).map((f) => shortPath(f, rootPath));
439
+ lines.push('');
440
+ if (nodes.length === 2) {
441
+ lines.push(` ${sevLabel} ${nodes[0]} ${g.leftRight} ${nodes[1]}`);
442
+ }
443
+ else {
444
+ const arrow = ` ${g.arrow} `;
445
+ const full = nodes.join(arrow);
446
+ if (full.length <= 60) {
447
+ lines.push(` ${sevLabel} ${full}`);
448
+ }
449
+ else {
450
+ const indent = ' ';
451
+ let current = ` ${sevLabel} ${nodes[0]}`;
452
+ for (let ni = 1; ni < nodes.length; ni++) {
453
+ const next = arrow + nodes[ni];
454
+ if (current.length + next.length > 68) {
455
+ lines.push(current);
456
+ current = indent + nodes[ni];
457
+ }
458
+ else {
459
+ current += next;
460
+ }
461
+ }
462
+ lines.push(current);
463
+ }
464
+ }
465
+ }
466
+ lines.push('');
467
+ const boxOut = drawBox('Circular Dependencies', lines, BOX_WIDTH);
468
+ console.log('');
469
+ for (const l of boxOut) {
470
+ console.log(l);
471
+ }
472
+ console.log('');
473
+ }
474
+ export function formatMetadata(data) {
475
+ const m = data.metadata;
476
+ if (!m) {
477
+ console.log(JSON.stringify(data, null, 2));
478
+ return;
479
+ }
480
+ const g = getGlyphs();
481
+ const lines = [];
482
+ lines.push('');
483
+ // Framework line
484
+ const fw = m.framework;
485
+ const fwName = fw ? `${fw.name ?? ''}${fw.version ? ` ${fw.version}` : ''}`.trim() : '';
486
+ const archType = m.architecture?.type ?? '';
487
+ if (fwName || archType) {
488
+ const parts = [];
489
+ if (fwName)
490
+ parts.push(`Framework: ${fwName}`);
491
+ if (archType)
492
+ parts.push(`Architecture: ${archType}`);
493
+ lines.push(parts.join(' '));
494
+ }
495
+ // Languages line
496
+ const langs = m.languages ?? [];
497
+ if (langs.length > 0) {
498
+ const langStr = langs
499
+ .slice(0, 4)
500
+ .map((l) => {
501
+ const pct = l.percentage != null ? ` ${l.percentage}%` : '';
502
+ const files = l.fileCount != null ? ` (${l.fileCount} files)` : '';
503
+ return `${l.name ?? ''}${pct}${files}`;
504
+ })
505
+ .join(' ');
506
+ lines.push(langStr);
507
+ }
508
+ // Stats line
509
+ const stats = m.statistics;
510
+ if (stats) {
511
+ const statParts = [];
512
+ if (stats.totalFiles != null)
513
+ statParts.push(`${stats.totalFiles} files`);
514
+ if (stats.totalLines != null)
515
+ statParts.push(`${stats.totalLines.toLocaleString()} lines`);
516
+ if (stats.totalComponents != null)
517
+ statParts.push(`${stats.totalComponents} components`);
518
+ if (statParts.length > 0)
519
+ lines.push(statParts.join(` ${g.dot} `));
520
+ }
521
+ // Component breakdown by type (e.g. module 199 component 53 service 31)
522
+ const byType = stats?.componentsByType;
523
+ if (byType) {
524
+ const entries = Object.entries(byType)
525
+ .filter(([, v]) => v > 0)
526
+ .sort(([, a], [, b]) => b - a);
527
+ if (entries.length > 0) {
528
+ const shown = entries.slice(0, 3);
529
+ const more = entries.length > 3 ? ` (+${entries.length - 3} more)` : '';
530
+ const str = shown.map(([k, v]) => `${k} ${v}`).join(' ');
531
+ lines.push(`By type: ${str}${more}`);
532
+ }
533
+ }
534
+ // Layer breakdown — skip zero-count and 'unknown' (e.g. presentation 57 business 23)
535
+ const layers = m.architecture?.layers;
536
+ if (layers) {
537
+ const entries = Object.entries(layers)
538
+ .filter(([k, v]) => v > 0 && k !== 'unknown')
539
+ .sort(([, a], [, b]) => b - a);
540
+ if (entries.length > 0) {
541
+ const str = entries.map(([k, v]) => `${k} ${v}`).join(' ');
542
+ lines.push(`By layer: ${str}`);
543
+ }
544
+ }
545
+ // Framework extras: state, testing, ui
546
+ if (fw) {
547
+ const extras = [];
548
+ if (fw.stateManagement && fw.stateManagement.length > 0) {
549
+ extras.push(`State: ${fw.stateManagement.join(', ')}`);
550
+ }
551
+ if (fw.testingFrameworks && fw.testingFrameworks.length > 0) {
552
+ extras.push(`Testing: ${fw.testingFrameworks.join(', ')}`);
553
+ }
554
+ if (fw.uiLibraries && fw.uiLibraries.length > 0) {
555
+ extras.push(`UI: ${fw.uiLibraries.join(', ')}`);
556
+ }
557
+ if (extras.length > 0) {
558
+ lines.push('');
559
+ for (const e of extras)
560
+ lines.push(e);
561
+ }
562
+ }
563
+ // Dependencies grouped by category (framework / state / other)
564
+ const deps = m.dependencies ?? [];
565
+ if (deps.length > 0) {
566
+ lines.push('');
567
+ const grouped = {};
568
+ for (const d of deps) {
569
+ const cat = d.category ?? 'other';
570
+ if (!grouped[cat])
571
+ grouped[cat] = [];
572
+ grouped[cat].push(d.name);
573
+ }
574
+ const catOrder = ['framework', 'state', 'testing', 'other'];
575
+ const cats = [
576
+ ...catOrder.filter((c) => grouped[c]),
577
+ ...Object.keys(grouped).filter((c) => !catOrder.includes(c))
578
+ ];
579
+ for (const cat of cats) {
580
+ const names = grouped[cat];
581
+ if (!names || names.length === 0)
582
+ continue;
583
+ const label = padRight(cat, 9);
584
+ const shown = names.slice(0, 2).join(` ${g.dot} `);
585
+ const more = names.length > 2 ? ` (+${names.length - 2} more)` : '';
586
+ lines.push(` ${label} ${shown}${more}`);
587
+ }
588
+ }
589
+ // Modules (if any)
590
+ const modules = m.architecture?.modules;
591
+ if (modules && modules.length > 0) {
592
+ lines.push('');
593
+ lines.push(`Modules: ${modules
594
+ .slice(0, 6)
595
+ .map((mod) => mod.name)
596
+ .join(` ${g.dot} `)}${modules.length > 6 ? ` (+${modules.length - 6})` : ''}`);
597
+ }
598
+ lines.push('');
599
+ const projectName = m.name ?? 'Project';
600
+ const structureType = m.projectStructure?.type;
601
+ const titleSuffix = structureType ? ` [${structureType}]` : '';
602
+ const boxOut = drawBox(`${projectName}${titleSuffix}`, lines, BOX_WIDTH);
603
+ console.log('');
604
+ for (const l of boxOut) {
605
+ console.log(l);
606
+ }
607
+ console.log('');
608
+ }
609
+ export function formatStatus(data, rootPath) {
610
+ const g = getGlyphs();
611
+ const lines = [];
612
+ lines.push('');
613
+ const state = data.status ?? 'unknown';
614
+ lines.push(`State: ${state}`);
615
+ const effectiveRoot = data.rootPath ?? rootPath;
616
+ if (effectiveRoot) {
617
+ lines.push(`Root: ${effectiveRoot}`);
618
+ }
619
+ if (data.lastIndexed) {
620
+ lines.push(`Last: ${data.lastIndexed}`);
621
+ }
622
+ if (data.stats) {
623
+ const s = data.stats;
624
+ const parts = [];
625
+ if (s.indexedFiles != null)
626
+ parts.push(`${s.indexedFiles} files`);
627
+ if (s.totalChunks != null)
628
+ parts.push(`${s.totalChunks} chunks`);
629
+ if (s.duration)
630
+ parts.push(s.duration);
631
+ if (typeof s.incremental === 'boolean')
632
+ parts.push(s.incremental ? 'incremental' : 'full');
633
+ if (parts.length > 0)
634
+ lines.push(`Stats: ${parts.join(` ${g.dot} `)}`);
635
+ }
636
+ if (data.progress) {
637
+ const p = data.progress;
638
+ const pct = typeof p.percentage === 'number' ? p.percentage : undefined;
639
+ const phase = p.phase ?? 'working';
640
+ if (pct != null) {
641
+ lines.push(`Progress: ${padRight(phase, 12)} ${barChart(pct, 18)} ${pct}%`);
642
+ }
643
+ else {
644
+ lines.push(`Progress: ${phase}`);
645
+ }
646
+ }
647
+ if (data.error) {
648
+ lines.push('');
649
+ lines.push(`${g.warn} ${data.error}`);
650
+ }
651
+ if (data.hint) {
652
+ lines.push('');
653
+ lines.push(`${g.arrow} ${data.hint}`);
654
+ }
655
+ lines.push('');
656
+ const boxOut = drawBox('Index Status', lines, BOX_WIDTH);
657
+ console.log('');
658
+ for (const l of boxOut)
659
+ console.log(l);
660
+ console.log('');
661
+ }
662
+ export function formatStyleGuide(data, rootPath) {
663
+ const g = getGlyphs();
664
+ if (data.status === 'no_results' || !data.results || data.results.length === 0) {
665
+ console.log('');
666
+ console.log('No style guides found.');
667
+ if (data.hint) {
668
+ console.log(` Hint: ${data.hint}`);
669
+ }
670
+ if (data.searchedPatterns && data.searchedPatterns.length > 0) {
671
+ console.log(` Searched: .md files matching ${data.searchedPatterns.join(', ')}`);
672
+ }
673
+ console.log('');
674
+ return;
675
+ }
676
+ const lines = [];
677
+ lines.push('');
678
+ const totalFiles = data.totalFiles ?? data.results.length;
679
+ const totalMatches = data.totalMatches ?? 0;
680
+ const countParts = [];
681
+ if (data.limited) {
682
+ countParts.push(`showing top ${totalFiles} of ${totalMatches}`);
683
+ }
684
+ else {
685
+ const filePart = `${totalFiles} file${totalFiles === 1 ? '' : 's'}`;
686
+ const matchPart = `${totalMatches} match${totalMatches === 1 ? '' : 'es'}`;
687
+ countParts.push(`${filePart} ${g.dot} ${matchPart}`);
688
+ }
689
+ lines.push(countParts[0]);
690
+ if (data.notice) {
691
+ lines.push(`${g.arrow} ${data.notice}`);
692
+ }
693
+ for (const result of data.results) {
694
+ lines.push('');
695
+ lines.push(shortPath(result.file ?? '', rootPath));
696
+ for (const section of result.relevantSections ?? []) {
697
+ const stripped = section.replace(/^#+\s*/, '');
698
+ lines.push(` \u00a7 ${stripped}`);
699
+ }
700
+ }
701
+ lines.push('');
702
+ const titlePart = data.query ? `Style Guide: "${data.query}"` : 'Style Guide';
703
+ const boxOut = drawBox(titlePart, lines, BOX_WIDTH);
704
+ console.log('');
705
+ for (const l of boxOut) {
706
+ console.log(l);
707
+ }
708
+ console.log('');
709
+ }
710
+ export function formatJson(json, useJson, command, rootPath, query, intent) {
711
+ if (useJson) {
712
+ console.log(json);
713
+ return;
714
+ }
715
+ let data;
716
+ try {
717
+ data = JSON.parse(json);
718
+ }
719
+ catch {
720
+ console.log(json);
721
+ return;
722
+ }
723
+ // Tools return { status: 'error', message: '...' } in their JSON payload but
724
+ // don't always set isError on the MCP envelope. Route these to stderr before
725
+ // a command formatter would render a misleading empty box.
726
+ if (typeof data === 'object' &&
727
+ data !== null &&
728
+ data.status === 'error') {
729
+ const d = data;
730
+ const msg = typeof d.message === 'string' ? d.message : JSON.stringify(d, null, 2);
731
+ process.stderr.write(`Error: ${msg}\n`);
732
+ return;
733
+ }
734
+ switch (command) {
735
+ case 'status': {
736
+ try {
737
+ formatStatus(data, rootPath ?? '');
738
+ }
739
+ catch {
740
+ console.log(JSON.stringify(data, null, 2));
741
+ }
742
+ break;
743
+ }
744
+ case 'metadata': {
745
+ try {
746
+ formatMetadata(data);
747
+ }
748
+ catch {
749
+ console.log(JSON.stringify(data, null, 2));
750
+ }
751
+ break;
752
+ }
753
+ case 'style-guide': {
754
+ try {
755
+ formatStyleGuide(data, rootPath ?? '');
756
+ }
757
+ catch {
758
+ console.log(JSON.stringify(data, null, 2));
759
+ }
760
+ break;
761
+ }
762
+ case 'patterns': {
763
+ try {
764
+ formatPatterns(data);
765
+ }
766
+ catch {
767
+ console.log(JSON.stringify(data, null, 2));
768
+ }
769
+ break;
770
+ }
771
+ case 'search': {
772
+ try {
773
+ formatSearch(data, rootPath ?? '', query, intent);
774
+ }
775
+ catch {
776
+ console.log(JSON.stringify(data, null, 2));
777
+ }
778
+ break;
779
+ }
780
+ case 'refs': {
781
+ try {
782
+ formatRefs(data, rootPath ?? '');
783
+ }
784
+ catch {
785
+ console.log(JSON.stringify(data, null, 2));
786
+ }
787
+ break;
788
+ }
789
+ case 'cycles': {
790
+ try {
791
+ formatCycles(data, rootPath ?? '');
792
+ }
793
+ catch {
794
+ console.log(JSON.stringify(data, null, 2));
795
+ }
796
+ break;
797
+ }
798
+ default: {
799
+ console.log(JSON.stringify(data, null, 2));
800
+ }
801
+ }
802
+ }
803
+ //# sourceMappingURL=cli-formatters.js.map