@plumpslabs/kuma 2.3.20 → 2.3.22

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 (39) hide show
  1. package/README.md +18 -0
  2. package/dist/index.js +10221 -332
  3. package/package.json +2 -2
  4. package/packages/ide/studio/dist/index.js +37 -5
  5. package/packages/ide/studio/public/index.html +239 -68
  6. package/dist/agentDetector-YOWQVJFR.js +0 -186
  7. package/dist/chunk-3BRBJZ7P.js +0 -1055
  8. package/dist/chunk-3OHYYXYN.js +0 -71
  9. package/dist/chunk-ABKE45T4.js +0 -1264
  10. package/dist/chunk-E2KFPEBT.js +0 -183
  11. package/dist/chunk-FKRSI5U5.js +0 -282
  12. package/dist/chunk-GFLSAXAH.js +0 -155
  13. package/dist/chunk-L7F67KUP.js +0 -172
  14. package/dist/chunk-LVKOGXLC.js +0 -658
  15. package/dist/chunk-PRUTTZBS.js +0 -1113
  16. package/dist/contextDigest-QB5XHPXE.js +0 -177
  17. package/dist/domainRules-QLPAQASB.js +0 -20
  18. package/dist/init-PL4XL662.js +0 -15
  19. package/dist/kumaAstValidator-CNM7FHYA.js +0 -150
  20. package/dist/kumaCheckpoint-J2LDQMEO.js +0 -207
  21. package/dist/kumaCodeScanner-J6B2EHGI.js +0 -563
  22. package/dist/kumaContractEngine-KX27T4N7.js +0 -305
  23. package/dist/kumaDb-4XZ5S2LH.js +0 -65
  24. package/dist/kumaDriftDetector-TOORILSZ.js +0 -237
  25. package/dist/kumaGotchas-XRGFFBTA.js +0 -151
  26. package/dist/kumaGraph-UMXZNGYF.js +0 -44
  27. package/dist/kumaMemory-FBJMV77G.js +0 -16
  28. package/dist/kumaMiner-XJETL7TL.js +0 -176
  29. package/dist/kumaPolicyEngine-2QDJDLM7.js +0 -311
  30. package/dist/kumaProgressiveContext-MWEDRXOH.js +0 -231
  31. package/dist/kumaSearch-PV4QTKE7.js +0 -321
  32. package/dist/kumaTrajectory-7NOAVNG2.js +0 -460
  33. package/dist/kumaVerifier-6YEGC77M.js +0 -265
  34. package/dist/kumaVisualize-264OEBGJ.js +0 -264
  35. package/dist/pathValidator-V4DC6U6Z.js +0 -22
  36. package/dist/safetyAudit-O45SPNTS.js +0 -12
  37. package/dist/safetyScore-TMMRD2MV.js +0 -333
  38. package/dist/sessionMemory-YPKVIOMV.js +0 -6
  39. package/dist/skillGenerator-PWEJKZNX.js +0 -304
@@ -1,563 +0,0 @@
1
- import {
2
- addEdge,
3
- nodeId,
4
- upsertNode
5
- } from "./chunk-PRUTTZBS.js";
6
- import "./chunk-3BRBJZ7P.js";
7
- import {
8
- getProjectRoot
9
- } from "./chunk-E2KFPEBT.js";
10
-
11
- // src/engine/kumaCodeScanner.ts
12
- import fastGlob from "fast-glob";
13
- import fs from "fs";
14
- import path from "path";
15
- var DEFAULT_MAX_FILES = 200;
16
- var DEFAULT_MAX_FILE_SIZE = 100 * 1024;
17
- var fileCache = /* @__PURE__ */ new Map();
18
- var _cachedRoot = null;
19
- function getRoot() {
20
- if (!_cachedRoot) _cachedRoot = getProjectRoot();
21
- return _cachedRoot;
22
- }
23
- var FUNCTION_DECL_RE = /(?:export\s+)?(?:async\s+)?function\s*(?:\*\s*)?(\w+)\s*\(/g;
24
- var ARROW_FN_RE = /(?:export\s+)?(?:const|let|var)\s+(\w+)\s*[=:]\s*(?:async\s*)?(?:<[^>]+>\s*)?(?:\([\s\S]*?\)|\w+)\s*(?::\s*\w+(?:<[^>]*>)?)?\s*=>/g;
25
- var TYPED_ARROW_RE = /(?:export\s+)?(?:const|let|var)\s+(\w+)\s*:\s*(?:\w+(?:<[^>]*>)?)?\s*=\s*(?:async\s*)?(?:<[^>]+>\s*)?\(/g;
26
- var CLASS_RE = /(?:export\s+)?(?:abstract\s+)?class\s+(\w+)(?:\s+extends\s+(\w+))?(?:\s+implements\s+([\w,\s]+))?/g;
27
- var IMPORT_RE = /import\s+(?:\{([^}]*)\}\s+from\s+)?(?:\w+\s+from\s+)?(?:\*\s+as\s+\w+\s+from\s+)?['"]([^'"]+)['"]/g;
28
- var JSX_RETURN_RE = /return\s*\(?\s*</;
29
- var JSX_ELEMENT_RE = /<([A-Z]\w+)[\s/>]/g;
30
- var JSX_IMPLICIT_RE = /=>\s*(?:\(\s*)?</;
31
- var EXPRESS_ROUTE_RE = /\.(get|post|put|delete|patch)\s*\(\s*['"]([^'"]+)['"]\s*,\s*(\w+)/g;
32
- var HONO_ROUTE_RE = /c\.(get|post|put|delete|patch)\s*\(\s*['"]([^'"]+)['"]/g;
33
- var EXPORT_RE = /export\s+(?:default\s+)?(\w+)/g;
34
- var CALL_RE = /(\w+)\s*\(/g;
35
- var TEST_PATTERNS = [".test.", ".spec.", "__tests__", "/test/"];
36
- var knownComponents = /* @__PURE__ */ new Set();
37
- var knownFunctions = /* @__PURE__ */ new Set();
38
- var symbolLocations = /* @__PURE__ */ new Map();
39
- function lineAt(content, index) {
40
- return content.substring(0, index).split("\n").length;
41
- }
42
- function getDirectoryDirs(filePaths) {
43
- const dirs = /* @__PURE__ */ new Set();
44
- for (const fp of filePaths) {
45
- let dir = path.dirname(fp);
46
- while (dir && dir !== "." && dir !== "/") {
47
- dirs.add(dir);
48
- dir = path.dirname(dir);
49
- }
50
- }
51
- return [...dirs].sort();
52
- }
53
- async function scanCodebase(options = {}) {
54
- const root = getRoot();
55
- const maxFiles = options.maxFiles ?? DEFAULT_MAX_FILES;
56
- const maxFileSize = options.maxFileSize ?? DEFAULT_MAX_FILE_SIZE;
57
- const result = { nodeCount: 0, edgeCount: 0, filesScanned: 0, errors: [] };
58
- const includePatterns = options.include || [
59
- "src/**/*.{ts,tsx,js,jsx}",
60
- "app/**/*.{ts,tsx,js,jsx}",
61
- "lib/**/*.{ts,tsx,js,jsx}",
62
- "packages/**/*.{ts,tsx,js,jsx}",
63
- "server/**/*.{ts,tsx,js,jsx}",
64
- "api/**/*.{ts,tsx,js,jsx}"
65
- ];
66
- if (options.scope) {
67
- includePatterns.length = 0;
68
- if (options.scope.includes("/") || options.scope.includes(".")) {
69
- includePatterns.push(options.scope);
70
- } else {
71
- includePatterns.push(`**/*${options.scope}*/**/*.{ts,tsx,js,jsx}`);
72
- includePatterns.push(`**/*${options.scope}*.{ts,tsx,js,jsx}`);
73
- }
74
- }
75
- const ignorePatterns = [
76
- "**/node_modules/**",
77
- "**/.git/**",
78
- "**/dist/**",
79
- "**/build/**",
80
- "**/.next/**",
81
- "**/coverage/**",
82
- "**/*.d.ts"
83
- ];
84
- let files;
85
- try {
86
- files = await fastGlob(includePatterns, {
87
- cwd: root,
88
- ignore: ignorePatterns,
89
- onlyFiles: true,
90
- deep: options.scope ? 10 : 6,
91
- dot: false
92
- });
93
- } catch (err) {
94
- result.errors.push(`Glob failed: ${err}`);
95
- return result;
96
- }
97
- knownComponents.clear();
98
- knownFunctions.clear();
99
- symbolLocations.clear();
100
- const scannedCount = Math.min(files.length, maxFiles);
101
- const allParsed = [];
102
- const scannedFilePaths = [];
103
- for (let i = 0; i < scannedCount; i++) {
104
- const filePath = files[i];
105
- const fullPath = path.join(root, filePath);
106
- if (!fs.existsSync(fullPath)) continue;
107
- const stat = fs.statSync(fullPath);
108
- if (stat.size > maxFileSize) continue;
109
- const mtime = stat.mtimeMs;
110
- const cached = fileCache.get(filePath);
111
- if (cached === mtime && !options.force) continue;
112
- fileCache.set(filePath, mtime);
113
- try {
114
- const content = fs.readFileSync(fullPath, "utf-8");
115
- const parsed = parseFile(filePath, content);
116
- allParsed.push({ filePath, parsed, content });
117
- scannedFilePaths.push(filePath);
118
- for (const comp of parsed.components) {
119
- knownComponents.add(comp.name);
120
- if (!symbolLocations.has(comp.name)) symbolLocations.set(comp.name, filePath);
121
- }
122
- for (const fn of parsed.functions) {
123
- knownFunctions.add(fn.name);
124
- if (!symbolLocations.has(fn.name)) symbolLocations.set(fn.name, filePath);
125
- }
126
- } catch (err) {
127
- result.errors.push(`Error scanning ${filePath}: ${err}`);
128
- }
129
- }
130
- for (const { parsed } of allParsed) {
131
- for (const cls of parsed.classes) {
132
- knownFunctions.add(cls.name);
133
- if (!symbolLocations.has(cls.name)) symbolLocations.set(cls.name, parsed.filePath);
134
- }
135
- }
136
- for (const { filePath, parsed, content } of allParsed) {
137
- try {
138
- await recordParsedFile(filePath, parsed, content, result);
139
- result.filesScanned++;
140
- } catch (err) {
141
- result.errors.push(`Error recording ${filePath}: ${err}`);
142
- }
143
- }
144
- if (scannedFilePaths.length > 0) {
145
- try {
146
- const dirs = getDirectoryDirs(scannedFilePaths);
147
- for (const dir of dirs) {
148
- const moduleId = nodeId("module", dir);
149
- await upsertNode({
150
- id: moduleId,
151
- type: "module",
152
- name: dir,
153
- metadata: { path: dir }
154
- });
155
- result.nodeCount++;
156
- for (const fp of scannedFilePaths) {
157
- if (path.dirname(fp) === dir) {
158
- const fileId = nodeId("file", fp);
159
- try {
160
- await addEdge({ sourceId: moduleId, targetId: fileId, type: "owns" });
161
- result.edgeCount++;
162
- } catch {
163
- }
164
- }
165
- }
166
- }
167
- } catch (err) {
168
- result.errors.push(`Module nodes error: ${err}`);
169
- }
170
- }
171
- return result;
172
- }
173
- function parseFile(filePath, content) {
174
- const result = {
175
- filePath,
176
- functions: [],
177
- classes: [],
178
- components: [],
179
- routes: [],
180
- imports: [],
181
- exports: [],
182
- isTest: TEST_PATTERNS.some((p) => filePath.includes(p))
183
- };
184
- const lines = content.split("\n");
185
- const isTsx = /\.(tsx|jsx)$/.test(filePath);
186
- const hasJSX = isTsx && content.includes("<") && content.includes(">");
187
- for (let i = 0; i < lines.length; i++) {
188
- const line = lines[i];
189
- const lineNum = i + 1;
190
- const trimmed = line.trim();
191
- if (trimmed.startsWith("//") || trimmed.startsWith("*") || trimmed.startsWith("/*")) continue;
192
- let m;
193
- FUNCTION_DECL_RE.lastIndex = 0;
194
- while ((m = FUNCTION_DECL_RE.exec(line)) !== null) {
195
- const fnName = m[1];
196
- if (fnName) {
197
- const prevLine = (lines[i - 1] || "").trim();
198
- if (!prevLine.match(/^\s*(?:export\s+)?(?:abstract\s+)?class\s+/)) {
199
- result.functions.push({ name: fnName, line: lineNum });
200
- }
201
- }
202
- }
203
- ARROW_FN_RE.lastIndex = 0;
204
- while ((m = ARROW_FN_RE.exec(line)) !== null) {
205
- const fnName = m[1];
206
- if (fnName && !fnName.startsWith("_")) result.functions.push({ name: fnName, line: lineNum });
207
- }
208
- TYPED_ARROW_RE.lastIndex = 0;
209
- while ((m = TYPED_ARROW_RE.exec(line)) !== null) {
210
- const fnName = m[1];
211
- if (fnName && !fnName.startsWith("_") && /^\s*(?:const|let|var)\s+/.test(line)) {
212
- if (line.includes("=>") || line.includes("function(") || line.includes("async")) {
213
- result.functions.push({ name: fnName, line: lineNum });
214
- }
215
- }
216
- }
217
- CLASS_RE.lastIndex = 0;
218
- while ((m = CLASS_RE.exec(line)) !== null) {
219
- const cls = { name: m[1], line: lineNum };
220
- if (m[2]) cls.extends = m[2];
221
- if (m[3]) cls.implements = m[3].split(",").map((s) => s.trim());
222
- result.classes.push(cls);
223
- }
224
- IMPORT_RE.lastIndex = 0;
225
- while ((m = IMPORT_RE.exec(line)) !== null) {
226
- const symbols = m[1] ? m[1].split(",").map((s) => s.trim().split(" as ")[0].trim()) : [];
227
- const source = m[2] || "";
228
- if (source.startsWith(".") || source.startsWith("/")) {
229
- result.imports.push({ source, symbols, isDefault: false });
230
- }
231
- }
232
- EXPORT_RE.lastIndex = 0;
233
- while ((m = EXPORT_RE.exec(line)) !== null) {
234
- const exportName = m[1];
235
- if (exportName && !["const", "let", "var", "function", "class", "interface", "type", "default"].includes(exportName)) {
236
- result.exports.push(exportName);
237
- }
238
- }
239
- }
240
- if (hasJSX) {
241
- for (const fn of [...result.functions]) {
242
- const fnStartLine = fn.line - 1;
243
- const endLine = Math.min(fnStartLine + 50, lines.length);
244
- let isComponent = false;
245
- for (let i = fnStartLine; i < endLine; i++) {
246
- if (JSX_RETURN_RE.test(lines[i])) {
247
- isComponent = true;
248
- break;
249
- }
250
- }
251
- if (!isComponent && JSX_IMPLICIT_RE.test(lines[fnStartLine])) isComponent = true;
252
- if (!isComponent && fnStartLine + 1 < endLine) {
253
- const nextLine = lines[fnStartLine + 1].trim();
254
- if (nextLine.startsWith("<") || nextLine.startsWith("(") || nextLine.startsWith("<>")) isComponent = true;
255
- }
256
- if (isComponent) {
257
- result.components.push({ name: fn.name, line: fn.line });
258
- result.functions = result.functions.filter((f) => f.name !== fn.name);
259
- }
260
- }
261
- }
262
- let rm;
263
- EXPRESS_ROUTE_RE.lastIndex = 0;
264
- while ((rm = EXPRESS_ROUTE_RE.exec(content)) !== null) {
265
- result.routes.push({
266
- method: rm[1].toUpperCase(),
267
- pathPattern: rm[2],
268
- handler: rm[3],
269
- line: lineAt(content, rm.index)
270
- });
271
- }
272
- HONO_ROUTE_RE.lastIndex = 0;
273
- while ((rm = HONO_ROUTE_RE.exec(content)) !== null) {
274
- result.routes.push({
275
- method: rm[1].toUpperCase(),
276
- pathPattern: rm[2],
277
- handler: "",
278
- line: lineAt(content, rm.index)
279
- });
280
- }
281
- return result;
282
- }
283
- async function recordParsedFile(filePath, parsed, content, result) {
284
- const { functions, classes, components, routes, imports } = parsed;
285
- const root = getRoot();
286
- const fileNodeId = nodeId("file", filePath);
287
- await upsertNode({ id: fileNodeId, type: "file", name: filePath });
288
- result.nodeCount++;
289
- if (parsed.isTest) {
290
- const testNodeId = nodeId("test", filePath);
291
- await upsertNode({ id: testNodeId, type: "test", name: filePath, filePath });
292
- result.nodeCount++;
293
- try {
294
- await addEdge({ sourceId: fileNodeId, targetId: testNodeId, type: "contains" });
295
- result.edgeCount++;
296
- } catch {
297
- }
298
- }
299
- for (const imp of imports) {
300
- const resolved = resolveImportPath(filePath, imp.source, root);
301
- if (resolved) {
302
- const targetId = nodeId("file", resolved);
303
- await upsertNode({ id: targetId, type: "file", name: resolved });
304
- result.nodeCount++;
305
- try {
306
- await addEdge({ sourceId: fileNodeId, targetId, type: "imports" });
307
- result.edgeCount++;
308
- } catch {
309
- }
310
- }
311
- }
312
- function scopedId(type, name, fp) {
313
- return `${type}::${fp}::${name}`;
314
- }
315
- for (const fn of functions) {
316
- const fnNodeId = scopedId("function", fn.name, filePath);
317
- await upsertNode({ id: fnNodeId, type: "function", name: fn.name, filePath });
318
- result.nodeCount++;
319
- try {
320
- await addEdge({ sourceId: fileNodeId, targetId: fnNodeId, type: "contains" });
321
- result.edgeCount++;
322
- } catch {
323
- }
324
- }
325
- for (const comp of components) {
326
- const compNodeId = scopedId("component", comp.name, filePath);
327
- await upsertNode({ id: compNodeId, type: "component", name: comp.name, filePath });
328
- result.nodeCount++;
329
- try {
330
- await addEdge({ sourceId: fileNodeId, targetId: compNodeId, type: "contains" });
331
- result.edgeCount++;
332
- } catch {
333
- }
334
- }
335
- for (const cls of classes) {
336
- const clsNodeId = scopedId("class", cls.name, filePath);
337
- await upsertNode({ id: clsNodeId, type: "class", name: cls.name, filePath });
338
- result.nodeCount++;
339
- try {
340
- await addEdge({ sourceId: fileNodeId, targetId: clsNodeId, type: "contains" });
341
- result.edgeCount++;
342
- } catch {
343
- }
344
- if (cls.extends) {
345
- const parentFile = symbolLocations.get(cls.extends) || filePath;
346
- const parentId = scopedId("class", cls.extends, parentFile);
347
- await upsertNode({ id: parentId, type: "class", name: cls.extends, filePath: parentFile });
348
- result.nodeCount++;
349
- try {
350
- await addEdge({ sourceId: clsNodeId, targetId: parentId, type: "extends" });
351
- result.edgeCount++;
352
- } catch {
353
- }
354
- }
355
- if (cls.implements) {
356
- for (const iface of cls.implements) {
357
- const ifaceId = nodeId("interface", iface);
358
- await upsertNode({ id: ifaceId, type: "interface", name: iface });
359
- result.nodeCount++;
360
- try {
361
- await addEdge({ sourceId: clsNodeId, targetId: ifaceId, type: "implements" });
362
- result.edgeCount++;
363
- } catch {
364
- }
365
- }
366
- }
367
- }
368
- for (const route of routes) {
369
- const routeName = `${route.method} ${route.pathPattern}`;
370
- const routeNodeId = nodeId("api_route", routeName);
371
- await upsertNode({
372
- id: routeNodeId,
373
- type: "api_route",
374
- name: routeName,
375
- filePath,
376
- metadata: { method: route.method, path: route.pathPattern }
377
- });
378
- result.nodeCount++;
379
- try {
380
- await addEdge({ sourceId: fileNodeId, targetId: routeNodeId, type: "contains" });
381
- result.edgeCount++;
382
- } catch {
383
- }
384
- if (route.handler) {
385
- const handlerPath = symbolLocations.get(route.handler) || filePath;
386
- const handlerId = scopedId("function", route.handler, handlerPath);
387
- await upsertNode({ id: handlerId, type: "function", name: route.handler, filePath: handlerPath });
388
- result.nodeCount++;
389
- try {
390
- await addEdge({ sourceId: routeNodeId, targetId: handlerId, type: "routes" });
391
- result.edgeCount++;
392
- } catch {
393
- }
394
- }
395
- }
396
- if (content.includes("<")) {
397
- JSX_ELEMENT_RE.lastIndex = 0;
398
- let jm;
399
- while ((jm = JSX_ELEMENT_RE.exec(content)) !== null) {
400
- const subComp = jm[1];
401
- if (knownComponents.has(subComp) && components.some((c) => c.name === subComp) === false) {
402
- const tagLine = lineAt(content, jm.index);
403
- const parentComp = components.find((c) => {
404
- const endLine = c.line + 80;
405
- return tagLine >= c.line && tagLine <= endLine;
406
- });
407
- if (parentComp) {
408
- const childFile = symbolLocations.get(subComp) || filePath;
409
- const parentId = scopedId("component", parentComp.name, filePath);
410
- const childId = scopedId("component", subComp, childFile);
411
- try {
412
- await addEdge({ sourceId: parentId, targetId: childId, type: "composes" });
413
- result.edgeCount++;
414
- } catch {
415
- }
416
- }
417
- }
418
- }
419
- }
420
- const callContent = content.replace(/\/\*[\s\S]*?\*\//g, " ").replace(/\/\/.*$/gm, " ").replace(/\/[^\n\s][^\n]*?\/[gimsuy]*/g, " ").replace(/['"`][^'"`]*['"`]/g, " ");
421
- CALL_RE.lastIndex = 0;
422
- const fileCalls = /* @__PURE__ */ new Set();
423
- let cm;
424
- while ((cm = CALL_RE.exec(callContent)) !== null) {
425
- const callee = cm[1];
426
- if (!callee || callee.length < 2) continue;
427
- if ([
428
- "if",
429
- "for",
430
- "while",
431
- "switch",
432
- "catch",
433
- "return",
434
- "typeof",
435
- "delete",
436
- "throw",
437
- "import",
438
- "export",
439
- "function",
440
- "class",
441
- "new",
442
- "try",
443
- "yield",
444
- "await",
445
- "this",
446
- "super",
447
- "undefined",
448
- "null",
449
- "true",
450
- "false",
451
- "console",
452
- "describe",
453
- "it",
454
- "test",
455
- "expect",
456
- "assert",
457
- "beforeEach",
458
- "afterEach",
459
- "beforeAll",
460
- "afterAll",
461
- "jest",
462
- "process",
463
- "require",
464
- "setTimeout",
465
- "setInterval",
466
- "clearTimeout",
467
- "clearInterval",
468
- "Math",
469
- "JSON",
470
- "Object",
471
- "Array",
472
- "String",
473
- "Number",
474
- "Boolean",
475
- "Promise",
476
- "Error",
477
- "Date",
478
- "RegExp",
479
- "Map",
480
- "Set",
481
- "Symbol",
482
- "fetch",
483
- "localStorage",
484
- "sessionStorage",
485
- "exports",
486
- "call",
487
- "apply",
488
- "bind",
489
- "then",
490
- "finally",
491
- "resolve",
492
- "reject",
493
- "next",
494
- "value",
495
- "done"
496
- ].includes(callee)) continue;
497
- if (knownFunctions.has(callee)) {
498
- const isSelf = functions.some((f) => f.name === callee) || components.some((c) => c.name === callee);
499
- if (!isSelf && !fileCalls.has(callee)) {
500
- fileCalls.add(callee);
501
- const calleeFile = symbolLocations.get(callee) || filePath;
502
- const fnId = `function::${calleeFile}::${callee}`;
503
- try {
504
- await addEdge({ sourceId: fileNodeId, targetId: fnId, type: "calls" });
505
- result.edgeCount++;
506
- } catch {
507
- }
508
- }
509
- }
510
- }
511
- }
512
- function resolveImportPath(fromFile, importPath, root) {
513
- if (!importPath.startsWith(".") && !importPath.startsWith("/")) return null;
514
- const fromDir = path.dirname(path.join(root, fromFile));
515
- const exts = [".ts", ".tsx", ".js", ".jsx", "/index.ts", "/index.tsx", "/index.js", "/index.jsx"];
516
- for (const ext of exts) {
517
- const resolved = path.resolve(fromDir, importPath + ext);
518
- if (fs.existsSync(resolved)) return path.relative(root, resolved);
519
- }
520
- const dirPath = path.resolve(fromDir, importPath);
521
- if (fs.existsSync(dirPath) && fs.statSync(dirPath).isDirectory()) {
522
- for (const ext of [".ts", ".tsx", ".js", ".jsx"]) {
523
- const indexPath = path.join(dirPath, `index${ext}`);
524
- if (fs.existsSync(indexPath)) return path.relative(root, indexPath);
525
- }
526
- }
527
- return null;
528
- }
529
- function formatScanResult(result) {
530
- const lines = [
531
- "\u{1F52C} **Kuma Code Scan Complete**",
532
- `\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501`,
533
- "",
534
- `\u{1F4C1} **${result.filesScanned}** files scanned`,
535
- `\u{1F4CA} **${result.nodeCount}** nodes added to knowledge graph`,
536
- `\u{1F517} **${result.edgeCount}** edges added to knowledge graph`
537
- ];
538
- if (result.errors.length > 0) {
539
- lines.push("");
540
- lines.push(`\u26A0\uFE0F **${result.errors.length}** warning(s):`);
541
- for (const err of result.errors.slice(0, 5)) {
542
- lines.push(` \u2022 ${err.substring(0, 120)}`);
543
- }
544
- if (result.errors.length > 5) {
545
- lines.push(` \u2022 ... and ${result.errors.length - 5} more`);
546
- }
547
- }
548
- lines.push(
549
- "",
550
- "\u{1F4A1} The knowledge graph now has richer code structure data.",
551
- "\u{1F4A1} Use kuma_context({ action: 'visualize' }) to see the graph."
552
- );
553
- return lines.join("\n");
554
- }
555
- async function scanAndFormat(options = {}) {
556
- const result = await scanCodebase(options);
557
- return formatScanResult(result);
558
- }
559
- export {
560
- formatScanResult,
561
- scanAndFormat,
562
- scanCodebase
563
- };