Skip to content

Commit d273729

Browse files
PatrickSysclaude
andcommitted
fix(cli): formatter audit — render missing metadata fields, README callers qualifier
- Fix metadata formatter: render componentsByType, layers, deps by category - Fix search formatter: render hints.tests, hasTests flag - Fix refs formatter: show "results capped" when isComplete=false - Extend types.ts: MetadataStatistics.componentsByType/componentsByLayer, architecture.layers, CyclesResponse fields, RefsResponse.isComplete - README: replace "caller coverage" with "import-graph coverage" (2 places) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 3c1c53b commit d273729

File tree

4 files changed

+83
-14
lines changed

4 files changed

+83
-14
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,4 @@ grammars/*.wasm
2626

2727
.tmp-research-repos/
2828
docs/visuals.md
29+
.repolore/

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ This is where it all comes together. One call returns:
207207
- **Relationships** per result: `importedByCount` and `hasTests` (condensed) + **hints** (capped ranked callers, consumers, tests) — so you see suggested next reads and know what you haven't looked at yet
208208
- **Related memories**: up to 3 team decisions, gotchas, and failures matched to the query
209209
- **Search quality**: `ok` or `low_confidence` with confidence score and `hint` when low
210-
- **Preflight**: `ready` (boolean) with decision card when `intent="edit"|"refactor"|"migrate"`. Shows `nextAction` (if not ready), `warnings`, `patterns` (do/avoid), `bestExample`, `impact` (caller coverage), and `whatWouldHelp` (next steps). If search quality is low, `ready` is always `false`.
210+
- **Preflight**: `ready` (boolean) with decision card when `intent="edit"|"refactor"|"migrate"`. Shows `nextAction` (if not ready), `warnings`, `patterns` (do/avoid), `bestExample`, `impact` (import-graph coverage — how many files that import or reference the result are in your search), and `whatWouldHelp` (next steps). If search quality is low, `ready` is always `false`.
211211

212212
Snippets are optional (`includeSnippets: true`). When enabled, snippets that have symbol metadata (e.g. from the Generic analyzer's AST chunking or Angular component chunks) start with a scope header so you know where the code lives (e.g. `// AuthService.getToken()` or `// SpotifyApiService`). Example:
213213

@@ -246,7 +246,7 @@ Record a decision once. It surfaces automatically in search results and prefligh
246246

247247
| Tool | What it does |
248248
| ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
249-
| `search_codebase` | Hybrid search + decision card. Pass `intent="edit"` to get `ready`, `nextAction`, patterns, caller coverage, and `whatWouldHelp`. |
249+
| `search_codebase` | Hybrid search + decision card. Pass `intent="edit"` to get `ready`, `nextAction`, patterns, import-graph coverage, and `whatWouldHelp`. |
250250
| `get_team_patterns` | Pattern frequencies, golden files, conflict detection |
251251
| `get_symbol_references` | Find concrete references to a symbol (usageCount + top snippets). `confidence: "syntactic"` = static/source-based only; no runtime or dynamic dispatch. |
252252
| `remember` | Record a convention, decision, gotcha, or failure |

src/cli-formatters.ts

Lines changed: 59 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,7 @@ export function formatSearch(
390390
const metaParts = [`confidence: ${scoreBar(scoreValue)} ${score}`];
391391
if (typePart) metaParts.push(typePart);
392392
if (trendPart) metaParts.push(trendPart);
393+
if (r.relationships?.hasTests) metaParts.push('has tests');
393394

394395
console.log(`${i + 1}. ${file}`);
395396
console.log(` ${metaParts.join(` ${g.dot} `)}`);
@@ -411,6 +412,10 @@ export function formatSearch(
411412
const more = total > 3 ? ` (+${total - 3} more)` : '';
412413
console.log(` used by: ${shortCallers.join(', ')}${more}`);
413414
}
415+
if (hints?.tests && hints.tests.length > 0) {
416+
const shortTests = hints.tests.slice(0, 2).map((t) => shortPath(t, rootPath));
417+
console.log(` tested: ${shortTests.join(', ')}`);
418+
}
414419

415420
const snippet = r.snippet ?? '';
416421
if (snippet) {
@@ -474,6 +479,11 @@ export function formatRefs(data: RefsResponse, rootPath: string): void {
474479
}
475480
}
476481

482+
if (data.isComplete === false) {
483+
lines.push('');
484+
lines.push(`${g.arrow} results capped at limit — use --limit to see more`);
485+
}
486+
477487
lines.push('');
478488

479489
const confLabel =
@@ -594,16 +604,30 @@ export function formatMetadata(data: MetadataResponse): void {
594604
if (statParts.length > 0) lines.push(statParts.join(` ${g.dot} `));
595605
}
596606

597-
// Dependencies
598-
const deps = m.dependencies ?? [];
599-
if (deps.length > 0) {
600-
lines.push('');
601-
lines.push(
602-
`Dependencies: ${deps
603-
.slice(0, 6)
604-
.map((d: MetadataDependency) => d.name)
605-
.join(` ${g.dot} `)}${deps.length > 6 ? ` (+${deps.length - 6} more)` : ''}`
606-
);
607+
// Component breakdown by type (e.g. module 199 component 53 service 31)
608+
const byType = stats?.componentsByType;
609+
if (byType) {
610+
const entries = Object.entries(byType)
611+
.filter(([, v]) => v > 0)
612+
.sort(([, a], [, b]) => b - a);
613+
if (entries.length > 0) {
614+
const shown = entries.slice(0, 3);
615+
const more = entries.length > 3 ? ` (+${entries.length - 3} more)` : '';
616+
const str = shown.map(([k, v]) => `${k} ${v}`).join(' ');
617+
lines.push(`By type: ${str}${more}`);
618+
}
619+
}
620+
621+
// Layer breakdown — skip zero-count and 'unknown' (e.g. presentation 57 business 23)
622+
const layers = m.architecture?.layers;
623+
if (layers) {
624+
const entries = Object.entries(layers)
625+
.filter(([k, v]) => v > 0 && k !== 'unknown')
626+
.sort(([, a], [, b]) => b - a);
627+
if (entries.length > 0) {
628+
const str = entries.map(([k, v]) => `${k} ${v}`).join(' ');
629+
lines.push(`By layer: ${str}`);
630+
}
607631
}
608632

609633
// Framework extras: state, testing, ui
@@ -624,6 +648,31 @@ export function formatMetadata(data: MetadataResponse): void {
624648
}
625649
}
626650

651+
// Dependencies grouped by category (framework / state / other)
652+
const deps = m.dependencies ?? [];
653+
if (deps.length > 0) {
654+
lines.push('');
655+
const grouped: Record<string, string[]> = {};
656+
for (const d of deps) {
657+
const cat = d.category ?? 'other';
658+
if (!grouped[cat]) grouped[cat] = [];
659+
grouped[cat].push(d.name);
660+
}
661+
const catOrder = ['framework', 'state', 'testing', 'other'];
662+
const cats = [
663+
...catOrder.filter((c) => grouped[c]),
664+
...Object.keys(grouped).filter((c) => !catOrder.includes(c)),
665+
];
666+
for (const cat of cats) {
667+
const names = grouped[cat];
668+
if (!names || names.length === 0) continue;
669+
const label = padRight(cat, 9);
670+
const shown = names.slice(0, 2).join(` ${g.dot} `);
671+
const more = names.length > 2 ? ` (+${names.length - 2} more)` : '';
672+
lines.push(` ${label} ${shown}${more}`);
673+
}
674+
}
675+
627676
// Modules (if any)
628677
const modules = m.architecture?.modules;
629678
if (modules && modules.length > 0) {

src/tools/types.ts

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,16 +145,26 @@ export interface MetadataStatistics {
145145
totalFiles?: number;
146146
totalLines?: number;
147147
totalComponents?: number;
148+
componentsByType?: Record<string, number>;
149+
componentsByLayer?: Record<string, number>;
148150
}
149151

150152
export interface MetadataInner {
151153
name?: string;
152154
framework?: MetadataFramework;
153155
languages?: MetadataLanguage[];
154156
dependencies?: MetadataDependency[];
155-
architecture?: { type?: string; modules?: Array<{ name: string }> };
156-
projectStructure?: { type?: string };
157+
architecture?: {
158+
type?: string;
159+
layers?: Record<string, number>;
160+
patterns?: unknown[];
161+
modules?: Array<{ name: string }>;
162+
};
163+
projectStructure?: { type?: string; workspaces?: string[] };
157164
statistics?: MetadataStatistics;
165+
// teamPatterns mirrors the shape from get_team_patterns — not rendered here
166+
// since the `patterns` command covers it; modelled so the field isn't invisible
167+
teamPatterns?: Record<string, unknown>;
158168
}
159169

160170
export interface MetadataResponse {
@@ -188,6 +198,7 @@ export interface CycleItem {
188198
files?: string[];
189199
cycle?: string[];
190200
severity?: string;
201+
length?: number;
191202
}
192203

193204
export interface GraphStats {
@@ -197,8 +208,13 @@ export interface GraphStats {
197208
}
198209

199210
export interface CyclesResponse {
211+
status?: string;
212+
message?: string;
213+
scope?: string;
200214
cycles?: CycleItem[];
215+
count?: number;
201216
graphStats?: GraphStats;
217+
advice?: string;
202218
}
203219

204220
// --- Refs response types ---
@@ -209,8 +225,11 @@ export interface RefsUsage {
209225
}
210226

211227
export interface RefsResponse {
228+
status?: string;
212229
symbol: string;
213230
usageCount: number;
214231
confidence: string;
215232
usages: RefsUsage[];
233+
/** false when results were capped at limit — more references exist */
234+
isComplete?: boolean;
216235
}

0 commit comments

Comments
 (0)