@aiready/agent-grounding 0.1.5 → 0.1.8

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.
@@ -0,0 +1,339 @@
1
+ // src/analyzer.ts
2
+ import { readdirSync, statSync, existsSync, readFileSync } from "fs";
3
+ import { join, extname, basename } from "path";
4
+ import { parse } from "@typescript-eslint/typescript-estree";
5
+ import { calculateAgentGrounding } from "@aiready/core";
6
+ var VAGUE_FILE_NAMES = /* @__PURE__ */ new Set([
7
+ "utils",
8
+ "helpers",
9
+ "helper",
10
+ "misc",
11
+ "common",
12
+ "shared",
13
+ "tools",
14
+ "util",
15
+ "lib",
16
+ "libs",
17
+ "stuff",
18
+ "functions",
19
+ "methods",
20
+ "handlers",
21
+ "data",
22
+ "temp",
23
+ "tmp",
24
+ "test-utils",
25
+ "test-helpers",
26
+ "mocks"
27
+ ]);
28
+ var SUPPORTED_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx"]);
29
+ var DEFAULT_EXCLUDES = [
30
+ "node_modules",
31
+ "dist",
32
+ ".git",
33
+ "coverage",
34
+ ".turbo",
35
+ "build"
36
+ ];
37
+ function collectEntries(dir, options, depth = 0, dirs = [], files = []) {
38
+ if (depth > (options.maxDepth ?? 20)) return { dirs, files };
39
+ const excludes = [...DEFAULT_EXCLUDES, ...options.exclude ?? []];
40
+ let entries;
41
+ try {
42
+ entries = readdirSync(dir);
43
+ } catch {
44
+ return { dirs, files };
45
+ }
46
+ for (const entry of entries) {
47
+ if (excludes.some((ex) => entry === ex || entry.includes(ex))) continue;
48
+ const full = join(dir, entry);
49
+ let stat;
50
+ try {
51
+ stat = statSync(full);
52
+ } catch {
53
+ continue;
54
+ }
55
+ if (stat.isDirectory()) {
56
+ dirs.push({ path: full, depth });
57
+ collectEntries(full, options, depth + 1, dirs, files);
58
+ } else if (stat.isFile() && SUPPORTED_EXTENSIONS.has(extname(full))) {
59
+ if (!options.include || options.include.some((p) => full.includes(p))) {
60
+ files.push(full);
61
+ }
62
+ }
63
+ }
64
+ return { dirs, files };
65
+ }
66
+ function analyzeFile(filePath) {
67
+ let code;
68
+ try {
69
+ code = readFileSync(filePath, "utf-8");
70
+ } catch {
71
+ return {
72
+ isBarrel: false,
73
+ exportedNames: [],
74
+ untypedExports: 0,
75
+ totalExports: 0,
76
+ domainTerms: []
77
+ };
78
+ }
79
+ let ast;
80
+ try {
81
+ ast = parse(code, {
82
+ jsx: filePath.endsWith(".tsx") || filePath.endsWith(".jsx"),
83
+ range: false,
84
+ loc: false
85
+ });
86
+ } catch {
87
+ return {
88
+ isBarrel: false,
89
+ exportedNames: [],
90
+ untypedExports: 0,
91
+ totalExports: 0,
92
+ domainTerms: []
93
+ };
94
+ }
95
+ let isBarrel = false;
96
+ const exportedNames = [];
97
+ let untypedExports = 0;
98
+ let totalExports = 0;
99
+ const domainTerms = [];
100
+ for (const node of ast.body) {
101
+ if (node.type === "ExportAllDeclaration") {
102
+ isBarrel = true;
103
+ continue;
104
+ }
105
+ if (node.type === "ExportNamedDeclaration") {
106
+ totalExports++;
107
+ const decl = node.declaration;
108
+ if (decl) {
109
+ const name = decl.id?.name ?? decl.declarations?.[0]?.id?.name;
110
+ if (name) {
111
+ exportedNames.push(name);
112
+ domainTerms.push(
113
+ ...name.replace(/([A-Z])/g, " $1").toLowerCase().split(/\s+/).filter(Boolean)
114
+ );
115
+ const hasType = decl.returnType != null || decl.declarations?.[0]?.id?.typeAnnotation != null || decl.typeParameters != null;
116
+ if (!hasType) untypedExports++;
117
+ }
118
+ } else if (node.specifiers && node.specifiers.length > 0) {
119
+ isBarrel = true;
120
+ }
121
+ }
122
+ if (node.type === "ExportDefaultDeclaration") {
123
+ totalExports++;
124
+ }
125
+ }
126
+ return { isBarrel, exportedNames, untypedExports, totalExports, domainTerms };
127
+ }
128
+ function detectInconsistentTerms(allTerms) {
129
+ const termFreq = /* @__PURE__ */ new Map();
130
+ for (const term of allTerms) {
131
+ if (term.length >= 3) {
132
+ termFreq.set(term, (termFreq.get(term) ?? 0) + 1);
133
+ }
134
+ }
135
+ const orphans = [...termFreq.values()].filter((count) => count === 1).length;
136
+ const common = [...termFreq.values()].filter((count) => count >= 3).length;
137
+ const vocabularySize = termFreq.size;
138
+ const inconsistent = Math.max(0, orphans - common * 2);
139
+ return { inconsistent, vocabularySize };
140
+ }
141
+ async function analyzeAgentGrounding(options) {
142
+ const rootDir = options.rootDir;
143
+ const maxRecommendedDepth = options.maxRecommendedDepth ?? 4;
144
+ const readmeStaleDays = options.readmeStaleDays ?? 90;
145
+ const { dirs, files } = collectEntries(rootDir, options);
146
+ const deepDirectories = dirs.filter(
147
+ (d) => d.depth > maxRecommendedDepth
148
+ ).length;
149
+ const additionalVague = new Set(
150
+ (options.additionalVagueNames ?? []).map((n) => n.toLowerCase())
151
+ );
152
+ let vagueFileNames = 0;
153
+ for (const f of files) {
154
+ const base = basename(f, extname(f)).toLowerCase();
155
+ if (VAGUE_FILE_NAMES.has(base) || additionalVague.has(base)) {
156
+ vagueFileNames++;
157
+ }
158
+ }
159
+ const readmePath = join(rootDir, "README.md");
160
+ const hasRootReadme = existsSync(readmePath);
161
+ let readmeIsFresh = false;
162
+ if (hasRootReadme) {
163
+ try {
164
+ const stat = statSync(readmePath);
165
+ const ageDays = (Date.now() - stat.mtimeMs) / (1e3 * 60 * 60 * 24);
166
+ readmeIsFresh = ageDays < readmeStaleDays;
167
+ } catch {
168
+ }
169
+ }
170
+ const allDomainTerms = [];
171
+ let barrelExports = 0;
172
+ let untypedExports = 0;
173
+ let totalExports = 0;
174
+ let processed = 0;
175
+ for (const f of files) {
176
+ processed++;
177
+ options.onProgress?.(processed, files.length, `agent-grounding: analyzing ${f.substring(rootDir.length + 1)}`);
178
+ const analysis = analyzeFile(f);
179
+ if (analysis.isBarrel) barrelExports++;
180
+ untypedExports += analysis.untypedExports;
181
+ totalExports += analysis.totalExports;
182
+ allDomainTerms.push(...analysis.domainTerms);
183
+ }
184
+ const {
185
+ inconsistent: inconsistentDomainTerms,
186
+ vocabularySize: domainVocabularySize
187
+ } = detectInconsistentTerms(allDomainTerms);
188
+ const groundingResult = calculateAgentGrounding({
189
+ deepDirectories,
190
+ totalDirectories: dirs.length,
191
+ vagueFileNames,
192
+ totalFiles: files.length,
193
+ hasRootReadme,
194
+ readmeIsFresh,
195
+ barrelExports,
196
+ untypedExports,
197
+ totalExports: Math.max(1, totalExports),
198
+ inconsistentDomainTerms,
199
+ domainVocabularySize: Math.max(1, domainVocabularySize)
200
+ });
201
+ const issues = [];
202
+ if (groundingResult.dimensions.structureClarityScore < 70) {
203
+ issues.push({
204
+ type: "agent-navigation-failure",
205
+ dimension: "structure-clarity",
206
+ severity: "major",
207
+ message: `${deepDirectories} directories exceed recommended depth of ${maxRecommendedDepth} \u2014 agents struggle to navigate deep trees.`,
208
+ location: { file: rootDir, line: 0 },
209
+ suggestion: `Flatten nested directories to ${maxRecommendedDepth} levels or fewer.`
210
+ });
211
+ }
212
+ if (groundingResult.dimensions.selfDocumentationScore < 70) {
213
+ issues.push({
214
+ type: "agent-navigation-failure",
215
+ dimension: "self-documentation",
216
+ severity: "major",
217
+ message: `${vagueFileNames} files use vague names (utils, helpers, misc) \u2014 an agent cannot determine their purpose from the name alone.`,
218
+ location: { file: rootDir, line: 0 },
219
+ suggestion: "Rename to domain-specific names: e.g., userAuthUtils \u2192 tokenValidator."
220
+ });
221
+ }
222
+ if (!hasRootReadme) {
223
+ issues.push({
224
+ type: "agent-navigation-failure",
225
+ dimension: "entry-point",
226
+ severity: "critical",
227
+ message: "No root README.md found \u2014 agents have no orientation document to start from.",
228
+ location: { file: join(rootDir, "README.md"), line: 0 },
229
+ suggestion: "Add a README.md explaining the project structure, entry points, and key conventions."
230
+ });
231
+ } else if (!readmeIsFresh) {
232
+ issues.push({
233
+ type: "agent-navigation-failure",
234
+ dimension: "entry-point",
235
+ severity: "minor",
236
+ message: `README.md is stale (>${readmeStaleDays} days without updates) \u2014 agents may be misled by outdated context.`,
237
+ location: { file: readmePath, line: 0 },
238
+ suggestion: "Update README.md to reflect the current codebase structure."
239
+ });
240
+ }
241
+ if (groundingResult.dimensions.apiClarityScore < 70) {
242
+ issues.push({
243
+ type: "agent-navigation-failure",
244
+ dimension: "api-clarity",
245
+ severity: "major",
246
+ message: `${untypedExports} of ${totalExports} public exports lack TypeScript type annotations \u2014 agents cannot infer the API contract.`,
247
+ location: { file: rootDir, line: 0 },
248
+ suggestion: "Add explicit return type and parameter annotations to all exported functions."
249
+ });
250
+ }
251
+ if (groundingResult.dimensions.domainConsistencyScore < 70) {
252
+ issues.push({
253
+ type: "agent-navigation-failure",
254
+ dimension: "domain-consistency",
255
+ severity: "major",
256
+ message: `${inconsistentDomainTerms} domain terms appear to be used inconsistently \u2014 agents get confused when one concept has multiple names.`,
257
+ location: { file: rootDir, line: 0 },
258
+ suggestion: "Establish a domain glossary and enforce one term per concept across the codebase."
259
+ });
260
+ }
261
+ return {
262
+ summary: {
263
+ filesAnalyzed: files.length,
264
+ directoriesAnalyzed: dirs.length,
265
+ score: groundingResult.score,
266
+ rating: groundingResult.rating,
267
+ dimensions: groundingResult.dimensions
268
+ },
269
+ issues,
270
+ rawData: {
271
+ deepDirectories,
272
+ totalDirectories: dirs.length,
273
+ vagueFileNames,
274
+ totalFiles: files.length,
275
+ hasRootReadme,
276
+ readmeIsFresh,
277
+ barrelExports,
278
+ untypedExports,
279
+ totalExports,
280
+ inconsistentDomainTerms,
281
+ domainVocabularySize
282
+ },
283
+ recommendations: groundingResult.recommendations
284
+ };
285
+ }
286
+
287
+ // src/scoring.ts
288
+ function calculateGroundingScore(report) {
289
+ const { summary, rawData, recommendations } = report;
290
+ const factors = [
291
+ {
292
+ name: "Structure Clarity",
293
+ impact: Math.round(summary.dimensions.structureClarityScore - 50),
294
+ description: `${rawData.deepDirectories} of ${rawData.totalDirectories} dirs exceed recommended depth`
295
+ },
296
+ {
297
+ name: "Self-Documentation",
298
+ impact: Math.round(summary.dimensions.selfDocumentationScore - 50),
299
+ description: `${rawData.vagueFileNames} of ${rawData.totalFiles} files have vague names`
300
+ },
301
+ {
302
+ name: "Entry Points",
303
+ impact: Math.round(summary.dimensions.entryPointScore - 50),
304
+ description: rawData.hasRootReadme ? rawData.readmeIsFresh ? "README present and fresh" : "README present but stale" : "No root README"
305
+ },
306
+ {
307
+ name: "API Clarity",
308
+ impact: Math.round(summary.dimensions.apiClarityScore - 50),
309
+ description: `${rawData.untypedExports} of ${rawData.totalExports} exports lack type annotations`
310
+ },
311
+ {
312
+ name: "Domain Consistency",
313
+ impact: Math.round(summary.dimensions.domainConsistencyScore - 50),
314
+ description: `${rawData.inconsistentDomainTerms} inconsistent domain terms detected`
315
+ }
316
+ ];
317
+ const recs = recommendations.map(
318
+ (action) => ({
319
+ action,
320
+ estimatedImpact: 6,
321
+ priority: summary.score < 50 ? "high" : "medium"
322
+ })
323
+ );
324
+ return {
325
+ toolName: "agent-grounding",
326
+ score: summary.score,
327
+ rawMetrics: {
328
+ ...rawData,
329
+ rating: summary.rating
330
+ },
331
+ factors,
332
+ recommendations: recs
333
+ };
334
+ }
335
+
336
+ export {
337
+ analyzeAgentGrounding,
338
+ calculateGroundingScore
339
+ };
package/dist/cli.js CHANGED
@@ -27,69 +27,22 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
27
27
  var import_commander = require("commander");
28
28
 
29
29
  // src/analyzer.ts
30
+ var import_core = require("@aiready/core");
30
31
  var import_fs = require("fs");
31
32
  var import_path = require("path");
32
33
  var import_typescript_estree = require("@typescript-eslint/typescript-estree");
33
- var import_core = require("@aiready/core");
34
- var VAGUE_FILE_NAMES = /* @__PURE__ */ new Set([
35
- "utils",
36
- "helpers",
37
- "helper",
38
- "misc",
39
- "common",
40
- "shared",
41
- "tools",
42
- "util",
43
- "lib",
44
- "libs",
45
- "stuff",
46
- "functions",
47
- "methods",
48
- "handlers",
49
- "data",
50
- "temp",
51
- "tmp",
52
- "test-utils",
53
- "test-helpers",
54
- "mocks"
55
- ]);
56
- var SUPPORTED_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx"]);
57
- var DEFAULT_EXCLUDES = ["node_modules", "dist", ".git", "coverage", ".turbo", "build"];
58
- function collectEntries(dir, options, depth = 0, dirs = [], files = []) {
59
- if (depth > (options.maxDepth ?? 20)) return { dirs, files };
60
- const excludes = [...DEFAULT_EXCLUDES, ...options.exclude ?? []];
61
- let entries;
62
- try {
63
- entries = (0, import_fs.readdirSync)(dir);
64
- } catch {
65
- return { dirs, files };
66
- }
67
- for (const entry of entries) {
68
- if (excludes.some((ex) => entry === ex || entry.includes(ex))) continue;
69
- const full = (0, import_path.join)(dir, entry);
70
- let stat;
71
- try {
72
- stat = (0, import_fs.statSync)(full);
73
- } catch {
74
- continue;
75
- }
76
- if (stat.isDirectory()) {
77
- dirs.push({ path: full, depth });
78
- collectEntries(full, options, depth + 1, dirs, files);
79
- } else if (stat.isFile() && SUPPORTED_EXTENSIONS.has((0, import_path.extname)(full))) {
80
- if (!options.include || options.include.some((p) => full.includes(p))) {
81
- files.push(full);
82
- }
83
- }
84
- }
85
- return { dirs, files };
86
- }
87
34
  function analyzeFile(filePath) {
88
35
  let code;
89
36
  try {
90
37
  code = (0, import_fs.readFileSync)(filePath, "utf-8");
91
38
  } catch {
92
- return { isBarrel: false, exportedNames: [], untypedExports: 0, totalExports: 0, domainTerms: [] };
39
+ return {
40
+ isBarrel: false,
41
+ exportedNames: [],
42
+ untypedExports: 0,
43
+ totalExports: 0,
44
+ domainTerms: []
45
+ };
93
46
  }
94
47
  let ast;
95
48
  try {
@@ -99,10 +52,16 @@ function analyzeFile(filePath) {
99
52
  loc: false
100
53
  });
101
54
  } catch {
102
- return { isBarrel: false, exportedNames: [], untypedExports: 0, totalExports: 0, domainTerms: [] };
55
+ return {
56
+ isBarrel: false,
57
+ exportedNames: [],
58
+ untypedExports: 0,
59
+ totalExports: 0,
60
+ domainTerms: []
61
+ };
103
62
  }
104
63
  let isBarrel = false;
105
- let exportedNames = [];
64
+ const exportedNames = [];
106
65
  let untypedExports = 0;
107
66
  let totalExports = 0;
108
67
  const domainTerms = [];
@@ -118,7 +77,9 @@ function analyzeFile(filePath) {
118
77
  const name = decl.id?.name ?? decl.declarations?.[0]?.id?.name;
119
78
  if (name) {
120
79
  exportedNames.push(name);
121
- domainTerms.push(...name.replace(/([A-Z])/g, " $1").toLowerCase().split(/\s+/).filter(Boolean));
80
+ domainTerms.push(
81
+ ...name.replace(/([A-Z])/g, " $1").toLowerCase().split(/\s+/).filter(Boolean)
82
+ );
122
83
  const hasType = decl.returnType != null || decl.declarations?.[0]?.id?.typeAnnotation != null || decl.typeParameters != null;
123
84
  if (!hasType) untypedExports++;
124
85
  }
@@ -149,13 +110,24 @@ async function analyzeAgentGrounding(options) {
149
110
  const rootDir = options.rootDir;
150
111
  const maxRecommendedDepth = options.maxRecommendedDepth ?? 4;
151
112
  const readmeStaleDays = options.readmeStaleDays ?? 90;
152
- const { dirs, files } = collectEntries(rootDir, options);
153
- const deepDirectories = dirs.filter((d) => d.depth > maxRecommendedDepth).length;
154
- const additionalVague = new Set((options.additionalVagueNames ?? []).map((n) => n.toLowerCase()));
113
+ const { files, dirs: rawDirs } = await (0, import_core.scanEntries)({
114
+ ...options,
115
+ include: options.include || ["**/*.{ts,tsx,js,jsx}"]
116
+ });
117
+ const dirs = rawDirs.map((d) => ({
118
+ path: d,
119
+ depth: (0, import_path.relative)(rootDir, d).split(/[/\\]/).filter(Boolean).length
120
+ }));
121
+ const deepDirectories = dirs.filter(
122
+ (d) => d.depth > maxRecommendedDepth
123
+ ).length;
124
+ const additionalVague = new Set(
125
+ (options.additionalVagueNames ?? []).map((n) => n.toLowerCase())
126
+ );
155
127
  let vagueFileNames = 0;
156
128
  for (const f of files) {
157
129
  const base = (0, import_path.basename)(f, (0, import_path.extname)(f)).toLowerCase();
158
- if (VAGUE_FILE_NAMES.has(base) || additionalVague.has(base)) {
130
+ if (import_core.VAGUE_FILE_NAMES.has(base) || additionalVague.has(base)) {
159
131
  vagueFileNames++;
160
132
  }
161
133
  }
@@ -174,14 +146,24 @@ async function analyzeAgentGrounding(options) {
174
146
  let barrelExports = 0;
175
147
  let untypedExports = 0;
176
148
  let totalExports = 0;
149
+ let processed = 0;
177
150
  for (const f of files) {
151
+ processed++;
152
+ options.onProgress?.(
153
+ processed,
154
+ files.length,
155
+ `agent-grounding: analyzing files`
156
+ );
178
157
  const analysis = analyzeFile(f);
179
158
  if (analysis.isBarrel) barrelExports++;
180
159
  untypedExports += analysis.untypedExports;
181
160
  totalExports += analysis.totalExports;
182
161
  allDomainTerms.push(...analysis.domainTerms);
183
162
  }
184
- const { inconsistent: inconsistentDomainTerms, vocabularySize: domainVocabularySize } = detectInconsistentTerms(allDomainTerms);
163
+ const {
164
+ inconsistent: inconsistentDomainTerms,
165
+ vocabularySize: domainVocabularySize
166
+ } = detectInconsistentTerms(allDomainTerms);
185
167
  const groundingResult = (0, import_core.calculateAgentGrounding)({
186
168
  deepDirectories,
187
169
  totalDirectories: dirs.length,
@@ -283,7 +265,7 @@ async function analyzeAgentGrounding(options) {
283
265
 
284
266
  // src/scoring.ts
285
267
  function calculateGroundingScore(report) {
286
- const { summary, rawData, issues, recommendations } = report;
268
+ const { summary, rawData, recommendations } = report;
287
269
  const factors = [
288
270
  {
289
271
  name: "Structure Clarity",
@@ -311,11 +293,13 @@ function calculateGroundingScore(report) {
311
293
  description: `${rawData.inconsistentDomainTerms} inconsistent domain terms detected`
312
294
  }
313
295
  ];
314
- const recs = recommendations.map((action) => ({
315
- action,
316
- estimatedImpact: 6,
317
- priority: summary.score < 50 ? "high" : "medium"
318
- }));
296
+ const recs = recommendations.map(
297
+ (action) => ({
298
+ action,
299
+ estimatedImpact: 6,
300
+ priority: summary.score < 50 ? "high" : "medium"
301
+ })
302
+ );
319
303
  return {
320
304
  toolName: "agent-grounding",
321
305
  score: summary.score,
@@ -334,7 +318,11 @@ var import_fs2 = require("fs");
334
318
  var import_path2 = require("path");
335
319
  var import_core2 = require("@aiready/core");
336
320
  var program = new import_commander.Command();
337
- program.name("aiready-agent-grounding").description("Measure how well an AI agent can navigate your codebase autonomously").version("0.1.0").addHelpText("after", `
321
+ program.name("aiready-agent-grounding").description(
322
+ "Measure how well an AI agent can navigate your codebase autonomously"
323
+ ).version("0.1.0").addHelpText(
324
+ "after",
325
+ `
338
326
  GROUNDING DIMENSIONS:
339
327
  Structure Clarity Deep directory trees slow and confuse agents
340
328
  Self-Documentation Vague file names (utils, helpers) hide intent
@@ -346,7 +334,16 @@ EXAMPLES:
346
334
  aiready-agent-grounding . # Full analysis
347
335
  aiready-agent-grounding src/ --output json # JSON report
348
336
  aiready-agent-grounding . --max-depth 3 # Stricter depth limit
349
- `).argument("<directory>", "Directory to analyze").option("--max-depth <n>", "Max recommended directory depth (default: 4)", "4").option("--readme-stale-days <n>", "Days after which README is considered stale (default: 90)", "90").option("--include <patterns>", "File patterns to include (comma-separated)").option("--exclude <patterns>", "File patterns to exclude (comma-separated)").option("-o, --output <format>", "Output format: console|json", "console").option("--output-file <path>", "Output file path (for json)").action(async (directory, options) => {
337
+ `
338
+ ).argument("<directory>", "Directory to analyze").option(
339
+ "--max-depth <n>",
340
+ "Max recommended directory depth (default: 4)",
341
+ "4"
342
+ ).option(
343
+ "--readme-stale-days <n>",
344
+ "Days after which README is considered stale (default: 90)",
345
+ "90"
346
+ ).option("--include <patterns>", "File patterns to include (comma-separated)").option("--exclude <patterns>", "File patterns to exclude (comma-separated)").option("-o, --output <format>", "Output format: console|json", "console").option("--output-file <path>", "Output file path (for json)").action(async (directory, options) => {
350
347
  console.log(import_chalk.default.blue("\u{1F9ED} Analyzing agent grounding...\n"));
351
348
  const startTime = Date.now();
352
349
  const config = await (0, import_core2.loadConfig)(directory);
@@ -388,10 +385,14 @@ function scoreColor(score) {
388
385
  return import_chalk.default.bgRed.white;
389
386
  }
390
387
  function displayConsoleReport(report, scoring, elapsed) {
391
- const { summary, rawData, issues, recommendations } = report;
388
+ const { summary, issues, recommendations } = report;
392
389
  console.log(import_chalk.default.bold("\n\u{1F9ED} Agent Grounding Analysis\n"));
393
- console.log(`Score: ${scoreColor(summary.score)(summary.score + "/100")} (${summary.rating.toUpperCase()})`);
394
- console.log(`Files: ${import_chalk.default.cyan(summary.filesAnalyzed)} Directories: ${import_chalk.default.cyan(summary.directoriesAnalyzed)}`);
390
+ console.log(
391
+ `Score: ${scoreColor(summary.score)(summary.score + "/100")} (${summary.rating.toUpperCase()})`
392
+ );
393
+ console.log(
394
+ `Files: ${import_chalk.default.cyan(summary.filesAnalyzed)} Directories: ${import_chalk.default.cyan(summary.directoriesAnalyzed)}`
395
+ );
395
396
  console.log(`Analysis: ${import_chalk.default.gray(elapsed + "s")}
396
397
  `);
397
398
  console.log(import_chalk.default.bold("\u{1F4D0} Dimension Scores\n"));
@@ -404,22 +405,33 @@ function displayConsoleReport(report, scoring, elapsed) {
404
405
  ];
405
406
  for (const [name, val] of dims) {
406
407
  const bar = "\u2588".repeat(Math.round(val / 10)).padEnd(10, "\u2591");
407
- console.log(` ${String(name).padEnd(22)} ${scoreColor(val)(bar)} ${val}/100`);
408
+ console.log(
409
+ ` ${String(name).padEnd(22)} ${scoreColor(val)(bar)} ${val}/100`
410
+ );
408
411
  }
409
412
  if (issues.length > 0) {
410
413
  console.log(import_chalk.default.bold("\n\u26A0\uFE0F Issues Found\n"));
411
414
  for (const issue of issues) {
412
415
  const sev = issue.severity === "critical" ? import_chalk.default.red : issue.severity === "major" ? import_chalk.default.yellow : import_chalk.default.blue;
413
416
  console.log(`${sev(issue.severity.toUpperCase())} ${issue.message}`);
414
- if (issue.suggestion) console.log(` ${import_chalk.default.dim("\u2192")} ${import_chalk.default.italic(issue.suggestion)}`);
417
+ if (issue.suggestion)
418
+ console.log(
419
+ ` ${import_chalk.default.dim("\u2192")} ${import_chalk.default.italic(issue.suggestion)}`
420
+ );
415
421
  console.log();
416
422
  }
417
423
  } else {
418
- console.log(import_chalk.default.green("\n\u2728 No grounding issues found \u2014 agents can navigate freely!\n"));
424
+ console.log(
425
+ import_chalk.default.green(
426
+ "\n\u2728 No grounding issues found \u2014 agents can navigate freely!\n"
427
+ )
428
+ );
419
429
  }
420
430
  if (recommendations.length > 0) {
421
431
  console.log(import_chalk.default.bold("\u{1F4A1} Recommendations\n"));
422
- recommendations.forEach((rec, i) => console.log(`${i + 1}. ${rec}`));
432
+ recommendations.forEach(
433
+ (rec, i) => console.log(`${i + 1}. ${rec}`)
434
+ );
423
435
  }
424
436
  console.log();
425
437
  }