coderoast 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.
@@ -0,0 +1,95 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.runInsightAggregatorAgent = runInsightAggregatorAgent;
4
+ const MAX_LONG_FUNCTIONS = 5;
5
+ const MAX_DUPLICATE_BLOCKS = 3;
6
+ const MAX_DUPLICATE_OCCURRENCES = 3;
7
+ const MAX_CIRCULAR_CYCLES = 3;
8
+ function buildLongFunctionEvidence(longFunctions) {
9
+ return [...longFunctions]
10
+ .sort((a, b) => b.length - a.length)
11
+ .slice(0, MAX_LONG_FUNCTIONS)
12
+ .map((fn) => ({
13
+ file: fn.file,
14
+ startLine: fn.startLine,
15
+ endLine: fn.endLine,
16
+ metrics: [{ type: "loc", value: fn.length }],
17
+ }));
18
+ }
19
+ function buildDuplicateEvidence(duplicateBlocks) {
20
+ const blocks = [...duplicateBlocks]
21
+ .sort((a, b) => b.occurrences.length - a.occurrences.length || b.length - a.length)
22
+ .slice(0, MAX_DUPLICATE_BLOCKS);
23
+ const evidence = [];
24
+ for (const block of blocks) {
25
+ const occurrences = block.occurrences.slice(0, MAX_DUPLICATE_OCCURRENCES);
26
+ for (const occurrence of occurrences) {
27
+ evidence.push({
28
+ file: occurrence.file,
29
+ startLine: occurrence.startLine,
30
+ endLine: occurrence.endLine,
31
+ metrics: [
32
+ { type: "loc", value: block.length },
33
+ { type: "count", value: block.occurrences.length },
34
+ { type: "hash", value: block.hash },
35
+ ],
36
+ });
37
+ }
38
+ }
39
+ return evidence;
40
+ }
41
+ function buildCircularEvidence(circularDependencies) {
42
+ const evidence = [];
43
+ const cycles = circularDependencies.slice(0, MAX_CIRCULAR_CYCLES);
44
+ for (const cycle of cycles) {
45
+ evidence.push({
46
+ file: cycle.from,
47
+ startLine: cycle.fromStartLine,
48
+ endLine: cycle.fromEndLine,
49
+ metrics: [{ type: "count", value: 1 }],
50
+ });
51
+ evidence.push({
52
+ file: cycle.to,
53
+ startLine: cycle.toStartLine,
54
+ endLine: cycle.toEndLine,
55
+ metrics: [{ type: "count", value: 1 }],
56
+ });
57
+ }
58
+ return evidence;
59
+ }
60
+ function runInsightAggregatorAgent(_scan, analysis) {
61
+ const issues = [];
62
+ if (analysis.signals.longFunctions.length > 0) {
63
+ issues.push({
64
+ type: "maintainability",
65
+ signal: "longFunctions",
66
+ confidence: "medium",
67
+ evidence: buildLongFunctionEvidence(analysis.signals.longFunctions),
68
+ });
69
+ }
70
+ if (analysis.signals.duplicateBlocks.length > 0) {
71
+ issues.push({
72
+ type: "duplication",
73
+ signal: "duplicateBlocks",
74
+ confidence: "high",
75
+ evidence: buildDuplicateEvidence(analysis.signals.duplicateBlocks),
76
+ });
77
+ }
78
+ if (analysis.signals.circularDependencies.length > 0) {
79
+ issues.push({
80
+ type: "architecture",
81
+ signal: "circularDependencies",
82
+ confidence: "high",
83
+ evidence: buildCircularEvidence(analysis.signals.circularDependencies),
84
+ });
85
+ }
86
+ if (!analysis.signals.testPresence.hasTests) {
87
+ issues.push({
88
+ type: "testing",
89
+ signal: "testPresence",
90
+ confidence: "medium",
91
+ evidence: [],
92
+ });
93
+ }
94
+ return { issues };
95
+ }
@@ -0,0 +1,158 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.runOutputFormatterAgent = runOutputFormatterAgent;
4
+ function humanizeFixMessage(message) {
5
+ const mappings = [
6
+ {
7
+ pattern: /Malformed hunk header/i,
8
+ replacement: "The AI response was not a valid patch format.",
9
+ },
10
+ {
11
+ pattern: /Patch contains no changes/i,
12
+ replacement: "The AI did not include any actual code changes.",
13
+ },
14
+ {
15
+ pattern: /outside evidence/i,
16
+ replacement: "The change touched code outside the allowed evidence range.",
17
+ },
18
+ {
19
+ pattern: /Empty patch response/i,
20
+ replacement: "The AI did not return a patch.",
21
+ },
22
+ {
23
+ pattern: /Long function length reduced/i,
24
+ replacement: "This change would shorten a long function.",
25
+ },
26
+ {
27
+ pattern: /Long function length did not improve/i,
28
+ replacement: "This change does not shorten the long function.",
29
+ },
30
+ {
31
+ pattern: /Duplicate blocks reduced/i,
32
+ replacement: "This change would reduce repeated code.",
33
+ },
34
+ {
35
+ pattern: /Duplicate blocks did not improve/i,
36
+ replacement: "This change does not reduce repeated code.",
37
+ },
38
+ ];
39
+ for (const mapping of mappings) {
40
+ if (mapping.pattern.test(message)) {
41
+ return mapping.replacement;
42
+ }
43
+ }
44
+ return message;
45
+ }
46
+ function formatFixSummary(suggestion) {
47
+ const status = suggestion.verified ? "Fix preview looks good" : "Fix preview failed";
48
+ const detail = humanizeFixMessage(suggestion.verificationMessage);
49
+ return `${suggestion.issueId}. [${suggestion.issueType}] ${status}: ${detail}`;
50
+ }
51
+ function formatPreviewSummary(summary) {
52
+ const lines = ["", "Impact Summary (preview)"];
53
+ const before = summary.before;
54
+ if (!summary.after || !summary.delta) {
55
+ lines.push("No verified patches to compare yet.");
56
+ return lines;
57
+ }
58
+ const after = summary.after;
59
+ const delta = summary.delta;
60
+ lines.push(`Max function length: ${before.maxFunctionLength} -> ${after.maxFunctionLength} (${delta.maxFunctionLength})`);
61
+ lines.push(`Avg function length: ${before.avgFunctionLength} -> ${after.avgFunctionLength} (${delta.avgFunctionLength})`);
62
+ lines.push(`Duplicate blocks: ${before.duplicateBlocks} -> ${after.duplicateBlocks} (${delta.duplicateBlocks})`);
63
+ lines.push(`Total functions: ${before.totalFunctions} -> ${after.totalFunctions} (${delta.totalFunctions})`);
64
+ if (summary.note) {
65
+ lines.push(summary.note);
66
+ }
67
+ return lines;
68
+ }
69
+ function formatApplyResult(result) {
70
+ const lines = ["", "Proof-Locked Apply"];
71
+ lines.push(result.message);
72
+ if (result.branch) {
73
+ lines.push(`Branch: ${result.branch}`);
74
+ }
75
+ if (result.testCommand) {
76
+ lines.push(`Tests: ${result.testCommand}`);
77
+ }
78
+ if (result.testsPassed !== undefined) {
79
+ lines.push(`Tests passed: ${result.testsPassed ? "yes" : "no"}`);
80
+ }
81
+ return lines;
82
+ }
83
+ function formatArchitectureSummary(analysis) {
84
+ if (!analysis?.dependencySummary) {
85
+ return [];
86
+ }
87
+ const summary = analysis.dependencySummary;
88
+ const lines = ["", "Architecture Map"];
89
+ lines.push(`Internal modules: ${summary.nodes}`);
90
+ lines.push(`Import links: ${summary.edges}`);
91
+ if (summary.topImporters.length > 0) {
92
+ lines.push("Top importers:");
93
+ for (const entry of summary.topImporters) {
94
+ lines.push(`- ${entry.file} (${entry.imports})`);
95
+ }
96
+ }
97
+ if (summary.topImported.length > 0) {
98
+ lines.push("Most imported:");
99
+ for (const entry of summary.topImported) {
100
+ lines.push(`- ${entry.file} (${entry.importedBy})`);
101
+ }
102
+ }
103
+ if (summary.cycles > 0 && summary.sampleCycle) {
104
+ lines.push(`Cycles detected: ${summary.cycles}`);
105
+ lines.push(`Example cycle: ${summary.sampleCycle.from} ↔ ${summary.sampleCycle.to}`);
106
+ }
107
+ else {
108
+ lines.push("Cycles detected: 0");
109
+ }
110
+ return lines;
111
+ }
112
+ function runOutputFormatterAgent(config, roast, fixResult, analysis) {
113
+ const title = `CodeRoast (${config.severity}, ${config.focus})`;
114
+ const divider = "-".repeat(title.length);
115
+ const sections = [`${title}\n${divider}\n${roast.content}`];
116
+ if (roast.actionItems && roast.actionItems.length > 0 && roast.usedGemini === false) {
117
+ sections.push(["", "Action Items", ...roast.actionItems.map((item) => `- ${item}`)].join("\n"));
118
+ }
119
+ if (analysis?.dependencySummary) {
120
+ sections.push(formatArchitectureSummary(analysis).join("\n").trimEnd());
121
+ }
122
+ if (fixResult?.previewSummary) {
123
+ sections.push(formatPreviewSummary(fixResult.previewSummary).join("\n").trimEnd());
124
+ }
125
+ if (fixResult && fixResult.suggestions.length > 0) {
126
+ const fixLines = ["", "Fix-It (preview)"];
127
+ let hiddenPatch = false;
128
+ for (const suggestion of fixResult.suggestions) {
129
+ fixLines.push(formatFixSummary(suggestion));
130
+ if (config.showDetails && suggestion.verificationDetails) {
131
+ fixLines.push(`Details: ${suggestion.verificationDetails}`);
132
+ }
133
+ if (suggestion.debugPaths && suggestion.debugPaths.length > 0) {
134
+ fixLines.push("Debug output:");
135
+ for (const debugPath of suggestion.debugPaths) {
136
+ fixLines.push(`- ${debugPath}`);
137
+ }
138
+ }
139
+ if (config.showDetails && suggestion.patch) {
140
+ fixLines.push(suggestion.patch);
141
+ }
142
+ else if (suggestion.patch) {
143
+ hiddenPatch = true;
144
+ }
145
+ fixLines.push("");
146
+ }
147
+ if (hiddenPatch && !config.showDetails) {
148
+ fixLines.push("Run with --details to see the patch diff.");
149
+ }
150
+ sections.push(fixLines.join("\n").trimEnd());
151
+ }
152
+ if (fixResult?.applyResult) {
153
+ sections.push(formatApplyResult(fixResult.applyResult).join("\n").trimEnd());
154
+ }
155
+ return {
156
+ text: `${sections.join("\n")}\n`,
157
+ };
158
+ }
@@ -0,0 +1,407 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.runRepoScannerAgent = runRepoScannerAgent;
7
+ const node_fs_1 = __importDefault(require("node:fs"));
8
+ const node_path_1 = __importDefault(require("node:path"));
9
+ const ignore_1 = __importDefault(require("ignore"));
10
+ const DEFAULT_MAX_FILE_SIZE_BYTES = 5 * 1024 * 1024;
11
+ const MAX_BINARY_CHECK_BYTES = 8000;
12
+ const MAX_IGNORED_PATHS = 50;
13
+ const NO_EXTENSION_KEY = "<none>";
14
+ const AUTO_IGNORE_DIRS = [".git", "node_modules", "dist", "build", ".next", "out", ".turbo"];
15
+ const PROJECT_FILE_NAMES = new Set([
16
+ "package.json",
17
+ "tsconfig.json",
18
+ "requirements.txt",
19
+ "pyproject.toml",
20
+ "go.mod",
21
+ ]);
22
+ const ENTRY_ROOT_BASE_NAMES = new Set(["app", "server"]);
23
+ const ENTRY_SRC_BASE_NAMES = new Set(["index", "main"]);
24
+ const LANGUAGE_BY_EXTENSION = {
25
+ ".ts": "ts",
26
+ ".tsx": "ts",
27
+ ".js": "js",
28
+ ".jsx": "js",
29
+ ".mjs": "js",
30
+ ".cjs": "js",
31
+ ".json": "json",
32
+ ".yml": "yaml",
33
+ ".yaml": "yaml",
34
+ ".toml": "toml",
35
+ ".py": "py",
36
+ ".go": "go",
37
+ ".rs": "rs",
38
+ ".java": "java",
39
+ ".kt": "kt",
40
+ ".cs": "cs",
41
+ ".rb": "rb",
42
+ ".php": "php",
43
+ ".swift": "swift",
44
+ ".scala": "scala",
45
+ ".sh": "shell",
46
+ ".ps1": "powershell",
47
+ ".html": "html",
48
+ ".css": "css",
49
+ ".scss": "scss",
50
+ ".sass": "sass",
51
+ ".less": "less",
52
+ ".sql": "sql",
53
+ };
54
+ const TEXT_EXTENSIONS = new Set([
55
+ ...Object.keys(LANGUAGE_BY_EXTENSION),
56
+ ".md",
57
+ ".mdx",
58
+ ".txt",
59
+ ".ini",
60
+ ".env",
61
+ ".xml",
62
+ ".csv",
63
+ ".tsv",
64
+ ".graphql",
65
+ ".gql",
66
+ ".vue",
67
+ ".svelte",
68
+ ".astro",
69
+ ".lock",
70
+ ]);
71
+ const BINARY_EXTENSIONS = new Set([
72
+ ".png",
73
+ ".jpg",
74
+ ".jpeg",
75
+ ".gif",
76
+ ".webp",
77
+ ".bmp",
78
+ ".ico",
79
+ ".tiff",
80
+ ".psd",
81
+ ".ai",
82
+ ".sketch",
83
+ ".zip",
84
+ ".tar",
85
+ ".gz",
86
+ ".tgz",
87
+ ".7z",
88
+ ".rar",
89
+ ".pdf",
90
+ ".exe",
91
+ ".dll",
92
+ ".so",
93
+ ".dylib",
94
+ ".class",
95
+ ".jar",
96
+ ".war",
97
+ ".mp3",
98
+ ".mp4",
99
+ ".mov",
100
+ ".avi",
101
+ ".mkv",
102
+ ".woff",
103
+ ".woff2",
104
+ ".ttf",
105
+ ".eot",
106
+ ".otf",
107
+ ".db",
108
+ ".sqlite",
109
+ ".sqlite3",
110
+ ".bin",
111
+ ".dat",
112
+ ]);
113
+ function toPosixPath(value) {
114
+ return value.split(node_path_1.default.sep).join("/");
115
+ }
116
+ function normalizeRelativePath(value) {
117
+ const normalized = toPosixPath(node_path_1.default.normalize(value));
118
+ return normalized.replace(/^\.\/+/, "");
119
+ }
120
+ function increment(record, key) {
121
+ record[key] = (record[key] ?? 0) + 1;
122
+ }
123
+ function isBinaryExtension(extension) {
124
+ return BINARY_EXTENSIONS.has(extension);
125
+ }
126
+ function shouldCheckBinary(extension) {
127
+ if (!extension) {
128
+ return true;
129
+ }
130
+ return !TEXT_EXTENSIONS.has(extension);
131
+ }
132
+ async function isBinaryFile(filePath, size) {
133
+ const bytesToRead = Math.min(size, MAX_BINARY_CHECK_BYTES);
134
+ if (bytesToRead === 0) {
135
+ return false;
136
+ }
137
+ let handle;
138
+ try {
139
+ handle = await node_fs_1.default.promises.open(filePath, "r");
140
+ const buffer = Buffer.alloc(bytesToRead);
141
+ await handle.read(buffer, 0, bytesToRead, 0);
142
+ return buffer.includes(0);
143
+ }
144
+ catch {
145
+ return true;
146
+ }
147
+ finally {
148
+ if (handle) {
149
+ await handle.close().catch(() => undefined);
150
+ }
151
+ }
152
+ }
153
+ async function readGitignore(rootPath) {
154
+ try {
155
+ const content = await node_fs_1.default.promises.readFile(node_path_1.default.join(rootPath, ".gitignore"), "utf8");
156
+ return content.split(/\r?\n/);
157
+ }
158
+ catch {
159
+ return [];
160
+ }
161
+ }
162
+ async function createIgnoreMatcher(rootPath) {
163
+ const matcher = (0, ignore_1.default)();
164
+ const gitignoreLines = await readGitignore(rootPath);
165
+ if (gitignoreLines.length > 0) {
166
+ matcher.add(gitignoreLines);
167
+ }
168
+ matcher.add(AUTO_IGNORE_DIRS.map((dir) => `${dir}/`));
169
+ return (relativePath, isDir) => {
170
+ if (!relativePath) {
171
+ return false;
172
+ }
173
+ const target = isDir ? `${relativePath}/` : relativePath;
174
+ return matcher.ignores(target);
175
+ };
176
+ }
177
+ function isWithinRoot(rootPath, targetPath) {
178
+ const relative = node_path_1.default.relative(rootPath, targetPath);
179
+ if (!relative) {
180
+ return false;
181
+ }
182
+ return !relative.startsWith("..") && !node_path_1.default.isAbsolute(relative);
183
+ }
184
+ async function safeStat(filePath) {
185
+ try {
186
+ return await node_fs_1.default.promises.stat(filePath);
187
+ }
188
+ catch {
189
+ return null;
190
+ }
191
+ }
192
+ function isEntryPointPath(relativePath) {
193
+ const normalized = relativePath;
194
+ const parts = normalized.split("/");
195
+ const extension = node_path_1.default.posix.extname(normalized);
196
+ const baseName = node_path_1.default.posix.basename(normalized, extension);
197
+ if (parts.length === 2 && parts[0] === "src" && ENTRY_SRC_BASE_NAMES.has(baseName)) {
198
+ return true;
199
+ }
200
+ if (parts.length === 1 && ENTRY_ROOT_BASE_NAMES.has(baseName)) {
201
+ return true;
202
+ }
203
+ return false;
204
+ }
205
+ async function resolvePackageEntryPoints(rootPath, maxFileSizeBytes, shouldIgnore) {
206
+ const packageRelativePath = "package.json";
207
+ if (shouldIgnore(packageRelativePath, false)) {
208
+ return [];
209
+ }
210
+ const packagePath = node_path_1.default.join(rootPath, packageRelativePath);
211
+ const stats = await safeStat(packagePath);
212
+ if (!stats || !stats.isFile()) {
213
+ return [];
214
+ }
215
+ if (stats.size > maxFileSizeBytes) {
216
+ return [];
217
+ }
218
+ let packageJson;
219
+ try {
220
+ const content = await node_fs_1.default.promises.readFile(packagePath, "utf8");
221
+ packageJson = JSON.parse(content);
222
+ }
223
+ catch {
224
+ return [];
225
+ }
226
+ const entries = [];
227
+ if (packageJson && typeof packageJson === "object") {
228
+ const data = packageJson;
229
+ if (typeof data.main === "string") {
230
+ entries.push(data.main);
231
+ }
232
+ if (typeof data.bin === "string") {
233
+ entries.push(data.bin);
234
+ }
235
+ else if (data.bin && typeof data.bin === "object") {
236
+ for (const value of Object.values(data.bin)) {
237
+ if (typeof value === "string") {
238
+ entries.push(value);
239
+ }
240
+ }
241
+ }
242
+ }
243
+ const entryPoints = [];
244
+ for (const entry of entries) {
245
+ const normalized = normalizeRelativePath(entry);
246
+ if (!normalized) {
247
+ continue;
248
+ }
249
+ const absolute = node_path_1.default.resolve(rootPath, normalized);
250
+ if (!isWithinRoot(rootPath, absolute)) {
251
+ continue;
252
+ }
253
+ const entryStats = await safeStat(absolute);
254
+ if (!entryStats || !entryStats.isFile()) {
255
+ continue;
256
+ }
257
+ entryPoints.push(toPosixPath(node_path_1.default.relative(rootPath, absolute)));
258
+ }
259
+ return entryPoints;
260
+ }
261
+ function toSortedRecord(record) {
262
+ const sorted = {};
263
+ for (const key of Object.keys(record).sort()) {
264
+ sorted[key] = record[key];
265
+ }
266
+ return sorted;
267
+ }
268
+ function bytesToMB(bytes) {
269
+ if (bytes === 0) {
270
+ return 0;
271
+ }
272
+ return Math.round((bytes / (1024 * 1024)) * 100) / 100;
273
+ }
274
+ function getMaxFileSizeBytes(config) {
275
+ if (!config.maxFileSizeMB || config.maxFileSizeMB <= 0) {
276
+ return DEFAULT_MAX_FILE_SIZE_BYTES;
277
+ }
278
+ return config.maxFileSizeMB * 1024 * 1024;
279
+ }
280
+ async function runRepoScannerAgent(config) {
281
+ const rootPath = node_path_1.default.resolve(config.path);
282
+ const maxFileSizeBytes = getMaxFileSizeBytes(config);
283
+ const shouldIgnore = await createIgnoreMatcher(rootPath);
284
+ const deadline = typeof config.scanTimeoutMs === "number" && config.scanTimeoutMs > 0
285
+ ? Date.now() + config.scanTimeoutMs
286
+ : null;
287
+ const scanState = {
288
+ languages: {},
289
+ fileTypes: {},
290
+ totalFiles: 0,
291
+ totalFolders: 0,
292
+ entryPoints: new Set(),
293
+ projectFiles: new Set(),
294
+ ignoredCount: 0,
295
+ ignoredPaths: new Set(),
296
+ repoSizeBytes: 0,
297
+ files: [],
298
+ warnings: [],
299
+ };
300
+ const pending = [rootPath];
301
+ let timedOut = false;
302
+ while (pending.length > 0) {
303
+ if (deadline && Date.now() > deadline) {
304
+ timedOut = true;
305
+ break;
306
+ }
307
+ const currentPath = pending.pop();
308
+ if (!currentPath) {
309
+ continue;
310
+ }
311
+ let directory = null;
312
+ try {
313
+ directory = await node_fs_1.default.promises.opendir(currentPath);
314
+ }
315
+ catch {
316
+ scanState.warnings.push(`Unable to read directory: ${currentPath}`);
317
+ continue;
318
+ }
319
+ scanState.totalFolders += 1;
320
+ for await (const entry of directory) {
321
+ if (deadline && Date.now() > deadline) {
322
+ timedOut = true;
323
+ break;
324
+ }
325
+ if (entry.isSymbolicLink()) {
326
+ continue;
327
+ }
328
+ const fullPath = node_path_1.default.join(currentPath, entry.name);
329
+ const relativePath = toPosixPath(node_path_1.default.relative(rootPath, fullPath));
330
+ const isDir = entry.isDirectory();
331
+ if (shouldIgnore(relativePath, isDir)) {
332
+ scanState.ignoredCount += 1;
333
+ if (isDir && scanState.ignoredPaths.size < MAX_IGNORED_PATHS) {
334
+ scanState.ignoredPaths.add(relativePath);
335
+ }
336
+ continue;
337
+ }
338
+ if (isDir) {
339
+ pending.push(fullPath);
340
+ continue;
341
+ }
342
+ if (!entry.isFile()) {
343
+ continue;
344
+ }
345
+ const stats = await safeStat(fullPath);
346
+ if (!stats || !stats.isFile()) {
347
+ scanState.warnings.push(`Unable to stat file: ${fullPath}`);
348
+ continue;
349
+ }
350
+ scanState.repoSizeBytes += stats.size;
351
+ if (PROJECT_FILE_NAMES.has(entry.name)) {
352
+ scanState.projectFiles.add(relativePath);
353
+ }
354
+ if (isEntryPointPath(relativePath)) {
355
+ scanState.entryPoints.add(relativePath);
356
+ }
357
+ if (stats.size > maxFileSizeBytes) {
358
+ continue;
359
+ }
360
+ const extension = node_path_1.default.extname(entry.name).toLowerCase();
361
+ if (isBinaryExtension(extension)) {
362
+ continue;
363
+ }
364
+ if (shouldCheckBinary(extension)) {
365
+ const binary = await isBinaryFile(fullPath, stats.size);
366
+ if (binary) {
367
+ continue;
368
+ }
369
+ }
370
+ const language = LANGUAGE_BY_EXTENSION[extension] ?? "other";
371
+ scanState.files.push({
372
+ path: relativePath,
373
+ sizeBytes: stats.size,
374
+ extension,
375
+ language,
376
+ });
377
+ scanState.totalFiles += 1;
378
+ increment(scanState.fileTypes, extension || NO_EXTENSION_KEY);
379
+ increment(scanState.languages, language);
380
+ }
381
+ if (timedOut) {
382
+ break;
383
+ }
384
+ }
385
+ const packageEntryPoints = await resolvePackageEntryPoints(rootPath, maxFileSizeBytes, shouldIgnore);
386
+ for (const entryPoint of packageEntryPoints) {
387
+ scanState.entryPoints.add(entryPoint);
388
+ }
389
+ if (timedOut) {
390
+ scanState.warnings.push("Scan timed out");
391
+ }
392
+ const result = {
393
+ languages: toSortedRecord(scanState.languages),
394
+ fileTypes: toSortedRecord(scanState.fileTypes),
395
+ totalFiles: scanState.totalFiles,
396
+ totalFolders: scanState.totalFolders,
397
+ entryPoints: Array.from(scanState.entryPoints).sort(),
398
+ projectFiles: Array.from(scanState.projectFiles).sort(),
399
+ ignoredCount: scanState.ignoredCount,
400
+ repoSizeMB: bytesToMB(scanState.repoSizeBytes),
401
+ files: scanState.files.sort((a, b) => a.path.localeCompare(b.path)),
402
+ };
403
+ if (scanState.ignoredPaths.size > 0) {
404
+ result.ignoredPaths = Array.from(scanState.ignoredPaths).sort();
405
+ }
406
+ return result;
407
+ }