@justmpm/ai-tool 0.2.0 → 0.3.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.
- package/LICENSE +21 -0
- package/dist/{chunk-HOVHD2N4.js → chunk-4HXWM7PK.js} +824 -1
- package/dist/cli.js +56 -15
- package/dist/index.d.ts +75 -2
- package/dist/index.js +7 -3
- package/dist/server-YV4BWISA.js +343 -0
- package/package.json +5 -2
|
@@ -373,6 +373,218 @@ function formatImpactText(result) {
|
|
|
373
373
|
}
|
|
374
374
|
return out;
|
|
375
375
|
}
|
|
376
|
+
function formatSuggestText(result) {
|
|
377
|
+
let out = "";
|
|
378
|
+
out += `
|
|
379
|
+
`;
|
|
380
|
+
out += `\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
|
|
381
|
+
`;
|
|
382
|
+
out += `\u2551 \u{1F4DA} SUGGEST \u2551
|
|
383
|
+
`;
|
|
384
|
+
out += `\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
|
|
385
|
+
|
|
386
|
+
`;
|
|
387
|
+
const icon = categoryIcons[result.category];
|
|
388
|
+
out += `\u{1F4CD} Antes de modificar: ${result.target}
|
|
389
|
+
`;
|
|
390
|
+
out += ` ${icon} ${result.category}
|
|
391
|
+
|
|
392
|
+
`;
|
|
393
|
+
if (result.suggestions.length === 0) {
|
|
394
|
+
out += `\u2705 Nenhuma sugestao de leitura.
|
|
395
|
+
`;
|
|
396
|
+
out += ` Este arquivo nao tem dependencias ou arquivos relacionados.
|
|
397
|
+
`;
|
|
398
|
+
return out;
|
|
399
|
+
}
|
|
400
|
+
out += `\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\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
|
|
401
|
+
|
|
402
|
+
`;
|
|
403
|
+
const byPriority = {
|
|
404
|
+
critical: result.suggestions.filter((s) => s.priority === "critical"),
|
|
405
|
+
high: result.suggestions.filter((s) => s.priority === "high"),
|
|
406
|
+
medium: result.suggestions.filter((s) => s.priority === "medium"),
|
|
407
|
+
low: result.suggestions.filter((s) => s.priority === "low")
|
|
408
|
+
};
|
|
409
|
+
if (byPriority.critical.length > 0) {
|
|
410
|
+
out += `\u{1F534} LEITURA CRITICA (${byPriority.critical.length})
|
|
411
|
+
`;
|
|
412
|
+
out += ` Tipos e interfaces que voce DEVE entender:
|
|
413
|
+
|
|
414
|
+
`;
|
|
415
|
+
for (const s of byPriority.critical) {
|
|
416
|
+
const sIcon = categoryIcons[s.category];
|
|
417
|
+
out += ` ${sIcon} ${s.path}
|
|
418
|
+
`;
|
|
419
|
+
out += ` ${s.reason}
|
|
420
|
+
`;
|
|
421
|
+
}
|
|
422
|
+
out += `
|
|
423
|
+
`;
|
|
424
|
+
}
|
|
425
|
+
if (byPriority.high.length > 0) {
|
|
426
|
+
out += `\u{1F7E0} LEITURA IMPORTANTE (${byPriority.high.length})
|
|
427
|
+
`;
|
|
428
|
+
out += ` Arquivos importados diretamente:
|
|
429
|
+
|
|
430
|
+
`;
|
|
431
|
+
for (const s of byPriority.high) {
|
|
432
|
+
const sIcon = categoryIcons[s.category];
|
|
433
|
+
out += ` ${sIcon} ${s.path}
|
|
434
|
+
`;
|
|
435
|
+
out += ` ${s.reason}
|
|
436
|
+
`;
|
|
437
|
+
}
|
|
438
|
+
out += `
|
|
439
|
+
`;
|
|
440
|
+
}
|
|
441
|
+
if (byPriority.medium.length > 0) {
|
|
442
|
+
out += `\u{1F7E1} LEITURA RECOMENDADA (${byPriority.medium.length})
|
|
443
|
+
`;
|
|
444
|
+
out += ` Arquivos que usam este arquivo:
|
|
445
|
+
|
|
446
|
+
`;
|
|
447
|
+
for (const s of byPriority.medium) {
|
|
448
|
+
const sIcon = categoryIcons[s.category];
|
|
449
|
+
out += ` ${sIcon} ${s.path}
|
|
450
|
+
`;
|
|
451
|
+
out += ` ${s.reason}
|
|
452
|
+
`;
|
|
453
|
+
}
|
|
454
|
+
out += `
|
|
455
|
+
`;
|
|
456
|
+
}
|
|
457
|
+
if (byPriority.low.length > 0) {
|
|
458
|
+
out += `\u{1F7E2} LEITURA OPCIONAL (${byPriority.low.length})
|
|
459
|
+
`;
|
|
460
|
+
out += ` Testes relacionados:
|
|
461
|
+
|
|
462
|
+
`;
|
|
463
|
+
for (const s of byPriority.low) {
|
|
464
|
+
const sIcon = categoryIcons[s.category];
|
|
465
|
+
out += ` ${sIcon} ${s.path}
|
|
466
|
+
`;
|
|
467
|
+
out += ` ${s.reason}
|
|
468
|
+
`;
|
|
469
|
+
}
|
|
470
|
+
out += `
|
|
471
|
+
`;
|
|
472
|
+
}
|
|
473
|
+
out += `\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\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
|
|
474
|
+
|
|
475
|
+
`;
|
|
476
|
+
out += `\u{1F4CA} RESUMO
|
|
477
|
+
`;
|
|
478
|
+
out += ` Total de arquivos sugeridos: ${result.suggestions.length}
|
|
479
|
+
`;
|
|
480
|
+
if (byPriority.critical.length > 0) {
|
|
481
|
+
out += ` \u{1F534} Criticos: ${byPriority.critical.length}
|
|
482
|
+
`;
|
|
483
|
+
}
|
|
484
|
+
if (byPriority.high.length > 0) {
|
|
485
|
+
out += ` \u{1F7E0} Importantes: ${byPriority.high.length}
|
|
486
|
+
`;
|
|
487
|
+
}
|
|
488
|
+
if (byPriority.medium.length > 0) {
|
|
489
|
+
out += ` \u{1F7E1} Recomendados: ${byPriority.medium.length}
|
|
490
|
+
`;
|
|
491
|
+
}
|
|
492
|
+
if (byPriority.low.length > 0) {
|
|
493
|
+
out += ` \u{1F7E2} Opcionais: ${byPriority.low.length}
|
|
494
|
+
`;
|
|
495
|
+
}
|
|
496
|
+
return out;
|
|
497
|
+
}
|
|
498
|
+
function formatContextText(result) {
|
|
499
|
+
let out = "";
|
|
500
|
+
out += `
|
|
501
|
+
`;
|
|
502
|
+
out += `\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
|
|
503
|
+
`;
|
|
504
|
+
out += `\u2551 \u{1F4C4} CONTEXT \u2551
|
|
505
|
+
`;
|
|
506
|
+
out += `\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
|
|
507
|
+
|
|
508
|
+
`;
|
|
509
|
+
const icon = categoryIcons[result.category];
|
|
510
|
+
out += `\u{1F4CD} ARQUIVO: ${result.file}
|
|
511
|
+
`;
|
|
512
|
+
out += ` ${icon} ${result.category}
|
|
513
|
+
|
|
514
|
+
`;
|
|
515
|
+
out += `\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\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
|
|
516
|
+
|
|
517
|
+
`;
|
|
518
|
+
if (result.imports.length > 0) {
|
|
519
|
+
out += `\u{1F4E5} IMPORTS (${result.imports.length})
|
|
520
|
+
|
|
521
|
+
`;
|
|
522
|
+
for (const imp of result.imports) {
|
|
523
|
+
const typeLabel = imp.isTypeOnly ? " [type]" : "";
|
|
524
|
+
out += ` ${imp.source}${typeLabel}
|
|
525
|
+
`;
|
|
526
|
+
if (imp.specifiers.length > 0) {
|
|
527
|
+
out += ` { ${imp.specifiers.join(", ")} }
|
|
528
|
+
`;
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
out += `
|
|
532
|
+
`;
|
|
533
|
+
}
|
|
534
|
+
if (result.exports.length > 0) {
|
|
535
|
+
out += `\u{1F4E4} EXPORTS (${result.exports.length})
|
|
536
|
+
`;
|
|
537
|
+
out += ` ${result.exports.join(", ")}
|
|
538
|
+
|
|
539
|
+
`;
|
|
540
|
+
}
|
|
541
|
+
if (result.types.length > 0) {
|
|
542
|
+
out += `\u{1F3F7}\uFE0F TYPES (${result.types.length})
|
|
543
|
+
|
|
544
|
+
`;
|
|
545
|
+
for (const t of result.types) {
|
|
546
|
+
const exported = t.isExported ? "export " : "";
|
|
547
|
+
out += ` ${exported}${t.kind} ${t.name}
|
|
548
|
+
`;
|
|
549
|
+
out += ` ${t.definition}
|
|
550
|
+
|
|
551
|
+
`;
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
if (result.functions.length > 0) {
|
|
555
|
+
out += `\u26A1 FUNCTIONS (${result.functions.length})
|
|
556
|
+
|
|
557
|
+
`;
|
|
558
|
+
for (const fn of result.functions) {
|
|
559
|
+
const exported = fn.isExported ? "export " : "";
|
|
560
|
+
const async = fn.isAsync ? "async " : "";
|
|
561
|
+
const arrow = fn.isArrowFunction ? " =>" : "";
|
|
562
|
+
const params = fn.params.map((p) => `${p.name}: ${p.type}`).join(", ");
|
|
563
|
+
out += ` ${exported}${async}${fn.name}(${params})${arrow}: ${fn.returnType}
|
|
564
|
+
`;
|
|
565
|
+
if (fn.jsdoc) {
|
|
566
|
+
out += ` /** ${fn.jsdoc} */
|
|
567
|
+
`;
|
|
568
|
+
}
|
|
569
|
+
out += `
|
|
570
|
+
`;
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
out += `\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\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
|
|
574
|
+
|
|
575
|
+
`;
|
|
576
|
+
out += `\u{1F4CA} RESUMO
|
|
577
|
+
`;
|
|
578
|
+
out += ` Imports: ${result.imports.length}
|
|
579
|
+
`;
|
|
580
|
+
out += ` Exports: ${result.exports.length}
|
|
581
|
+
`;
|
|
582
|
+
out += ` Types: ${result.types.length}
|
|
583
|
+
`;
|
|
584
|
+
out += ` Functions: ${result.functions.length}
|
|
585
|
+
`;
|
|
586
|
+
return out;
|
|
587
|
+
}
|
|
376
588
|
|
|
377
589
|
// src/cache/index.ts
|
|
378
590
|
import { existsSync, mkdirSync, readFileSync, writeFileSync, statSync, readdirSync } from "fs";
|
|
@@ -1010,8 +1222,617 @@ function levenshteinDistance(a, b) {
|
|
|
1010
1222
|
return matrix[b.length][a.length];
|
|
1011
1223
|
}
|
|
1012
1224
|
|
|
1225
|
+
// src/commands/suggest.ts
|
|
1226
|
+
import skott3 from "skott";
|
|
1227
|
+
async function suggest(target, options = {}) {
|
|
1228
|
+
const cwd = options.cwd || process.cwd();
|
|
1229
|
+
const format = options.format || "text";
|
|
1230
|
+
const useCache = options.cache !== false;
|
|
1231
|
+
const limit = options.limit || 10;
|
|
1232
|
+
if (!target) {
|
|
1233
|
+
throw new Error("Target e obrigatorio. Exemplo: ai-tool suggest src/components/Button.tsx");
|
|
1234
|
+
}
|
|
1235
|
+
try {
|
|
1236
|
+
let graph;
|
|
1237
|
+
let allFiles = [];
|
|
1238
|
+
let fromCache = false;
|
|
1239
|
+
if (useCache && isCacheValid(cwd)) {
|
|
1240
|
+
const cached = getCachedGraph(cwd);
|
|
1241
|
+
if (cached) {
|
|
1242
|
+
graph = cached.graph;
|
|
1243
|
+
allFiles = cached.files;
|
|
1244
|
+
fromCache = true;
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
if (!graph) {
|
|
1248
|
+
const { getStructure } = await skott3({
|
|
1249
|
+
cwd,
|
|
1250
|
+
includeBaseDir: false,
|
|
1251
|
+
dependencyTracking: {
|
|
1252
|
+
thirdParty: false,
|
|
1253
|
+
builtin: false,
|
|
1254
|
+
typeOnly: false
|
|
1255
|
+
},
|
|
1256
|
+
fileExtensions: [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"],
|
|
1257
|
+
tsConfigPath: "tsconfig.json"
|
|
1258
|
+
});
|
|
1259
|
+
const structure = getStructure();
|
|
1260
|
+
graph = structure.graph;
|
|
1261
|
+
allFiles = Object.keys(graph);
|
|
1262
|
+
if (useCache) {
|
|
1263
|
+
cacheGraph(cwd, {
|
|
1264
|
+
graph,
|
|
1265
|
+
files: allFiles,
|
|
1266
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1267
|
+
});
|
|
1268
|
+
updateCacheMeta(cwd);
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1271
|
+
const targetPath = findTargetFile2(target, allFiles);
|
|
1272
|
+
if (!targetPath) {
|
|
1273
|
+
return formatNotFound2(target, allFiles);
|
|
1274
|
+
}
|
|
1275
|
+
const suggestions = collectSuggestions(targetPath, graph, allFiles, limit);
|
|
1276
|
+
const result = {
|
|
1277
|
+
version: "1.0.0",
|
|
1278
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1279
|
+
target: targetPath,
|
|
1280
|
+
category: detectCategory(targetPath),
|
|
1281
|
+
suggestions
|
|
1282
|
+
};
|
|
1283
|
+
if (format === "json") {
|
|
1284
|
+
return JSON.stringify(result, null, 2);
|
|
1285
|
+
}
|
|
1286
|
+
const output = formatSuggestText(result);
|
|
1287
|
+
return fromCache ? output + "\n\n(grafo do cache)" : output;
|
|
1288
|
+
} catch (error) {
|
|
1289
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1290
|
+
throw new Error(`Erro ao executar suggest: ${message}`);
|
|
1291
|
+
}
|
|
1292
|
+
}
|
|
1293
|
+
function collectSuggestions(targetPath, graph, allFiles, limit) {
|
|
1294
|
+
const suggestions = [];
|
|
1295
|
+
const addedPaths = /* @__PURE__ */ new Set();
|
|
1296
|
+
const targetNode = graph[targetPath];
|
|
1297
|
+
if (!targetNode) {
|
|
1298
|
+
return suggestions;
|
|
1299
|
+
}
|
|
1300
|
+
for (const dep of targetNode.adjacentTo) {
|
|
1301
|
+
if (addedPaths.has(dep)) continue;
|
|
1302
|
+
addedPaths.add(dep);
|
|
1303
|
+
const category = detectCategory(dep);
|
|
1304
|
+
if (category === "type") {
|
|
1305
|
+
suggestions.push({
|
|
1306
|
+
path: dep,
|
|
1307
|
+
category,
|
|
1308
|
+
reason: "Define tipos usados",
|
|
1309
|
+
priority: "critical"
|
|
1310
|
+
});
|
|
1311
|
+
} else {
|
|
1312
|
+
suggestions.push({
|
|
1313
|
+
path: dep,
|
|
1314
|
+
category,
|
|
1315
|
+
reason: "Importado diretamente",
|
|
1316
|
+
priority: "high"
|
|
1317
|
+
});
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1320
|
+
const upstream = findUpstream(targetPath, graph);
|
|
1321
|
+
const upstreamLimited = upstream.slice(0, 5);
|
|
1322
|
+
for (const file of upstreamLimited) {
|
|
1323
|
+
if (addedPaths.has(file)) continue;
|
|
1324
|
+
addedPaths.add(file);
|
|
1325
|
+
suggestions.push({
|
|
1326
|
+
path: file,
|
|
1327
|
+
category: detectCategory(file),
|
|
1328
|
+
reason: "Usa este arquivo",
|
|
1329
|
+
priority: "medium"
|
|
1330
|
+
});
|
|
1331
|
+
}
|
|
1332
|
+
const relatedTests = findRelatedTests(targetPath, allFiles);
|
|
1333
|
+
for (const testFile of relatedTests) {
|
|
1334
|
+
if (addedPaths.has(testFile)) continue;
|
|
1335
|
+
addedPaths.add(testFile);
|
|
1336
|
+
suggestions.push({
|
|
1337
|
+
path: testFile,
|
|
1338
|
+
category: "test",
|
|
1339
|
+
reason: "Testa este arquivo",
|
|
1340
|
+
priority: "low"
|
|
1341
|
+
});
|
|
1342
|
+
}
|
|
1343
|
+
const priorityOrder = {
|
|
1344
|
+
critical: 0,
|
|
1345
|
+
high: 1,
|
|
1346
|
+
medium: 2,
|
|
1347
|
+
low: 3
|
|
1348
|
+
};
|
|
1349
|
+
suggestions.sort((a, b) => priorityOrder[a.priority] - priorityOrder[b.priority]);
|
|
1350
|
+
return suggestions.slice(0, limit);
|
|
1351
|
+
}
|
|
1352
|
+
function findUpstream(targetPath, graph) {
|
|
1353
|
+
const upstream = [];
|
|
1354
|
+
for (const [file, node] of Object.entries(graph)) {
|
|
1355
|
+
if (node.adjacentTo.includes(targetPath)) {
|
|
1356
|
+
upstream.push(file);
|
|
1357
|
+
}
|
|
1358
|
+
}
|
|
1359
|
+
return upstream;
|
|
1360
|
+
}
|
|
1361
|
+
function findRelatedTests(targetPath, allFiles) {
|
|
1362
|
+
const targetName = targetPath.split("/").pop() || "";
|
|
1363
|
+
const targetNameNoExt = targetName.replace(/\.(tsx?|jsx?|mjs|cjs)$/, "");
|
|
1364
|
+
const tests = [];
|
|
1365
|
+
for (const file of allFiles) {
|
|
1366
|
+
const fileName = file.split("/").pop() || "";
|
|
1367
|
+
if (fileName.includes(".test.") || fileName.includes(".spec.") || file.includes("/__tests__/")) {
|
|
1368
|
+
const testNameNoExt = fileName.replace(/\.(test|spec)\..*$/, "").replace(/\.(tsx?|jsx?|mjs|cjs)$/, "");
|
|
1369
|
+
if (testNameNoExt.toLowerCase() === targetNameNoExt.toLowerCase()) {
|
|
1370
|
+
tests.push(file);
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1374
|
+
return tests;
|
|
1375
|
+
}
|
|
1376
|
+
function findTargetFile2(target, allFiles) {
|
|
1377
|
+
const normalizedTarget = target.replace(/\\/g, "/");
|
|
1378
|
+
if (allFiles.includes(normalizedTarget)) {
|
|
1379
|
+
return normalizedTarget;
|
|
1380
|
+
}
|
|
1381
|
+
const targetName = normalizedTarget.split("/").pop()?.toLowerCase() || "";
|
|
1382
|
+
const targetNameNoExt = targetName.replace(/\.(tsx?|jsx?|mjs|cjs)$/, "");
|
|
1383
|
+
const matches = [];
|
|
1384
|
+
for (const file of allFiles) {
|
|
1385
|
+
const fileName = file.split("/").pop()?.toLowerCase() || "";
|
|
1386
|
+
const fileNameNoExt = fileName.replace(/\.(tsx?|jsx?|mjs|cjs)$/, "");
|
|
1387
|
+
if (fileNameNoExt === targetNameNoExt) {
|
|
1388
|
+
matches.unshift(file);
|
|
1389
|
+
} else if (file.toLowerCase().includes(normalizedTarget.toLowerCase())) {
|
|
1390
|
+
matches.push(file);
|
|
1391
|
+
}
|
|
1392
|
+
}
|
|
1393
|
+
if (matches.length === 1) {
|
|
1394
|
+
return matches[0];
|
|
1395
|
+
}
|
|
1396
|
+
if (matches.length > 1) {
|
|
1397
|
+
return matches[0];
|
|
1398
|
+
}
|
|
1399
|
+
return null;
|
|
1400
|
+
}
|
|
1401
|
+
function formatNotFound2(target, allFiles) {
|
|
1402
|
+
const normalizedTarget = target.toLowerCase();
|
|
1403
|
+
const similar = allFiles.filter((f) => {
|
|
1404
|
+
const fileName = f.split("/").pop()?.toLowerCase() || "";
|
|
1405
|
+
const fileNameNoExt = fileName.replace(/\.(tsx?|jsx?|mjs|cjs)$/, "");
|
|
1406
|
+
return fileName.includes(normalizedTarget) || fileNameNoExt.includes(normalizedTarget) || levenshteinDistance2(fileNameNoExt, normalizedTarget) <= 3;
|
|
1407
|
+
}).slice(0, 5);
|
|
1408
|
+
let out = `Arquivo nao encontrado no indice: "${target}"
|
|
1409
|
+
|
|
1410
|
+
`;
|
|
1411
|
+
out += `Total de arquivos indexados: ${allFiles.length}
|
|
1412
|
+
|
|
1413
|
+
`;
|
|
1414
|
+
if (similar.length > 0) {
|
|
1415
|
+
out += `Arquivos com nome similar:
|
|
1416
|
+
`;
|
|
1417
|
+
for (const s of similar) {
|
|
1418
|
+
out += ` - ${s}
|
|
1419
|
+
`;
|
|
1420
|
+
}
|
|
1421
|
+
out += `
|
|
1422
|
+
`;
|
|
1423
|
+
}
|
|
1424
|
+
out += `Dicas:
|
|
1425
|
+
`;
|
|
1426
|
+
out += ` - Use o caminho relativo: src/components/Header.tsx
|
|
1427
|
+
`;
|
|
1428
|
+
out += ` - Ou apenas o nome do arquivo: Header
|
|
1429
|
+
`;
|
|
1430
|
+
out += ` - Verifique se o arquivo esta em uma pasta incluida no scan
|
|
1431
|
+
`;
|
|
1432
|
+
return out;
|
|
1433
|
+
}
|
|
1434
|
+
function levenshteinDistance2(a, b) {
|
|
1435
|
+
const matrix = [];
|
|
1436
|
+
for (let i = 0; i <= b.length; i++) {
|
|
1437
|
+
matrix[i] = [i];
|
|
1438
|
+
}
|
|
1439
|
+
for (let j = 0; j <= a.length; j++) {
|
|
1440
|
+
matrix[0][j] = j;
|
|
1441
|
+
}
|
|
1442
|
+
for (let i = 1; i <= b.length; i++) {
|
|
1443
|
+
for (let j = 1; j <= a.length; j++) {
|
|
1444
|
+
if (b.charAt(i - 1) === a.charAt(j - 1)) {
|
|
1445
|
+
matrix[i][j] = matrix[i - 1][j - 1];
|
|
1446
|
+
} else {
|
|
1447
|
+
matrix[i][j] = Math.min(
|
|
1448
|
+
matrix[i - 1][j - 1] + 1,
|
|
1449
|
+
matrix[i][j - 1] + 1,
|
|
1450
|
+
matrix[i - 1][j] + 1
|
|
1451
|
+
);
|
|
1452
|
+
}
|
|
1453
|
+
}
|
|
1454
|
+
}
|
|
1455
|
+
return matrix[b.length][a.length];
|
|
1456
|
+
}
|
|
1457
|
+
|
|
1458
|
+
// src/commands/context.ts
|
|
1459
|
+
import { existsSync as existsSync2, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
|
|
1460
|
+
import { join as join2, resolve, basename, extname as extname2 } from "path";
|
|
1461
|
+
|
|
1462
|
+
// src/ts/extractor.ts
|
|
1463
|
+
import { Project, SyntaxKind } from "ts-morph";
|
|
1464
|
+
function createProject(cwd) {
|
|
1465
|
+
return new Project({
|
|
1466
|
+
tsConfigFilePath: `${cwd}/tsconfig.json`,
|
|
1467
|
+
skipAddingFilesFromTsConfig: true
|
|
1468
|
+
});
|
|
1469
|
+
}
|
|
1470
|
+
function addSourceFile(project, filePath) {
|
|
1471
|
+
return project.addSourceFileAtPath(filePath);
|
|
1472
|
+
}
|
|
1473
|
+
function extractImports(sourceFile) {
|
|
1474
|
+
const imports = [];
|
|
1475
|
+
for (const importDecl of sourceFile.getImportDeclarations()) {
|
|
1476
|
+
const specifiers = [];
|
|
1477
|
+
const defaultImport = importDecl.getDefaultImport();
|
|
1478
|
+
if (defaultImport) {
|
|
1479
|
+
specifiers.push(defaultImport.getText());
|
|
1480
|
+
}
|
|
1481
|
+
for (const namedImport of importDecl.getNamedImports()) {
|
|
1482
|
+
const alias = namedImport.getAliasNode();
|
|
1483
|
+
if (alias) {
|
|
1484
|
+
specifiers.push(`${namedImport.getName()} as ${alias.getText()}`);
|
|
1485
|
+
} else {
|
|
1486
|
+
specifiers.push(namedImport.getName());
|
|
1487
|
+
}
|
|
1488
|
+
}
|
|
1489
|
+
const namespaceImport = importDecl.getNamespaceImport();
|
|
1490
|
+
if (namespaceImport) {
|
|
1491
|
+
specifiers.push(`* as ${namespaceImport.getText()}`);
|
|
1492
|
+
}
|
|
1493
|
+
imports.push({
|
|
1494
|
+
source: importDecl.getModuleSpecifierValue(),
|
|
1495
|
+
specifiers,
|
|
1496
|
+
isTypeOnly: importDecl.isTypeOnly()
|
|
1497
|
+
});
|
|
1498
|
+
}
|
|
1499
|
+
return imports;
|
|
1500
|
+
}
|
|
1501
|
+
function getJsDocDescription(node) {
|
|
1502
|
+
const jsDocs = node.getChildrenOfKind(SyntaxKind.JSDoc);
|
|
1503
|
+
if (jsDocs.length === 0) return void 0;
|
|
1504
|
+
const firstJsDoc = jsDocs[0];
|
|
1505
|
+
const comment = firstJsDoc.getComment();
|
|
1506
|
+
if (typeof comment === "string") {
|
|
1507
|
+
return comment.trim() || void 0;
|
|
1508
|
+
}
|
|
1509
|
+
if (Array.isArray(comment)) {
|
|
1510
|
+
const text = comment.filter((c) => c !== void 0).map((c) => c.getText()).join("").trim();
|
|
1511
|
+
return text || void 0;
|
|
1512
|
+
}
|
|
1513
|
+
return void 0;
|
|
1514
|
+
}
|
|
1515
|
+
function extractParams(params) {
|
|
1516
|
+
return params.map((p) => ({
|
|
1517
|
+
name: p.getName(),
|
|
1518
|
+
type: simplifyType(p.getType().getText())
|
|
1519
|
+
}));
|
|
1520
|
+
}
|
|
1521
|
+
function simplifyType(typeText) {
|
|
1522
|
+
let simplified = typeText.replace(/import\([^)]+\)\./g, "");
|
|
1523
|
+
if (simplified.length > 80) {
|
|
1524
|
+
simplified = simplified.slice(0, 77) + "...";
|
|
1525
|
+
}
|
|
1526
|
+
return simplified;
|
|
1527
|
+
}
|
|
1528
|
+
function extractFunctions(sourceFile) {
|
|
1529
|
+
const functions = [];
|
|
1530
|
+
for (const func of sourceFile.getFunctions()) {
|
|
1531
|
+
const name = func.getName();
|
|
1532
|
+
if (!name) continue;
|
|
1533
|
+
functions.push({
|
|
1534
|
+
name,
|
|
1535
|
+
params: extractParams(func.getParameters()),
|
|
1536
|
+
returnType: simplifyType(func.getReturnType().getText()),
|
|
1537
|
+
isAsync: func.isAsync(),
|
|
1538
|
+
isExported: func.isExported(),
|
|
1539
|
+
isArrowFunction: false,
|
|
1540
|
+
jsdoc: getJsDocDescription(func)
|
|
1541
|
+
});
|
|
1542
|
+
}
|
|
1543
|
+
for (const varStatement of sourceFile.getVariableStatements()) {
|
|
1544
|
+
const isExported = varStatement.isExported();
|
|
1545
|
+
for (const varDecl of varStatement.getDeclarations()) {
|
|
1546
|
+
const init = varDecl.getInitializer();
|
|
1547
|
+
if (!init) continue;
|
|
1548
|
+
if (init.getKind() === SyntaxKind.ArrowFunction) {
|
|
1549
|
+
const arrowFunc = init.asKind(SyntaxKind.ArrowFunction);
|
|
1550
|
+
if (!arrowFunc) continue;
|
|
1551
|
+
functions.push({
|
|
1552
|
+
name: varDecl.getName(),
|
|
1553
|
+
params: extractParams(arrowFunc.getParameters()),
|
|
1554
|
+
returnType: simplifyType(arrowFunc.getReturnType().getText()),
|
|
1555
|
+
isAsync: arrowFunc.isAsync(),
|
|
1556
|
+
isExported,
|
|
1557
|
+
isArrowFunction: true,
|
|
1558
|
+
jsdoc: getJsDocDescription(varStatement)
|
|
1559
|
+
});
|
|
1560
|
+
}
|
|
1561
|
+
if (init.getKind() === SyntaxKind.FunctionExpression) {
|
|
1562
|
+
const funcExpr = init.asKind(SyntaxKind.FunctionExpression);
|
|
1563
|
+
if (!funcExpr) continue;
|
|
1564
|
+
functions.push({
|
|
1565
|
+
name: varDecl.getName(),
|
|
1566
|
+
params: extractParams(funcExpr.getParameters()),
|
|
1567
|
+
returnType: simplifyType(funcExpr.getReturnType().getText()),
|
|
1568
|
+
isAsync: funcExpr.isAsync(),
|
|
1569
|
+
isExported,
|
|
1570
|
+
isArrowFunction: false,
|
|
1571
|
+
jsdoc: getJsDocDescription(varStatement)
|
|
1572
|
+
});
|
|
1573
|
+
}
|
|
1574
|
+
}
|
|
1575
|
+
}
|
|
1576
|
+
return functions;
|
|
1577
|
+
}
|
|
1578
|
+
function extractTypes(sourceFile) {
|
|
1579
|
+
const types = [];
|
|
1580
|
+
for (const iface of sourceFile.getInterfaces()) {
|
|
1581
|
+
types.push({
|
|
1582
|
+
name: iface.getName(),
|
|
1583
|
+
kind: "interface",
|
|
1584
|
+
definition: formatInterfaceDefinition(iface),
|
|
1585
|
+
isExported: iface.isExported()
|
|
1586
|
+
});
|
|
1587
|
+
}
|
|
1588
|
+
for (const typeAlias of sourceFile.getTypeAliases()) {
|
|
1589
|
+
types.push({
|
|
1590
|
+
name: typeAlias.getName(),
|
|
1591
|
+
kind: "type",
|
|
1592
|
+
definition: simplifyType(typeAlias.getType().getText()),
|
|
1593
|
+
isExported: typeAlias.isExported()
|
|
1594
|
+
});
|
|
1595
|
+
}
|
|
1596
|
+
for (const enumDecl of sourceFile.getEnums()) {
|
|
1597
|
+
types.push({
|
|
1598
|
+
name: enumDecl.getName(),
|
|
1599
|
+
kind: "enum",
|
|
1600
|
+
definition: enumDecl.getMembers().map((m) => m.getName()).join(" | "),
|
|
1601
|
+
isExported: enumDecl.isExported()
|
|
1602
|
+
});
|
|
1603
|
+
}
|
|
1604
|
+
return types;
|
|
1605
|
+
}
|
|
1606
|
+
function formatInterfaceDefinition(iface) {
|
|
1607
|
+
const parts = [];
|
|
1608
|
+
const extendsClauses = iface.getExtends();
|
|
1609
|
+
if (extendsClauses.length > 0) {
|
|
1610
|
+
parts.push(`extends ${extendsClauses.map((e) => e.getText()).join(", ")}`);
|
|
1611
|
+
}
|
|
1612
|
+
const props = iface.getProperties();
|
|
1613
|
+
for (const prop of props) {
|
|
1614
|
+
const propType = simplifyType(prop.getType().getText());
|
|
1615
|
+
parts.push(`${prop.getName()}: ${propType}`);
|
|
1616
|
+
}
|
|
1617
|
+
const methods = iface.getMethods();
|
|
1618
|
+
for (const method of methods) {
|
|
1619
|
+
const returnType = simplifyType(method.getReturnType().getText());
|
|
1620
|
+
parts.push(`${method.getName()}(): ${returnType}`);
|
|
1621
|
+
}
|
|
1622
|
+
return parts.length > 0 ? `{ ${parts.join("; ")} }` : "{}";
|
|
1623
|
+
}
|
|
1624
|
+
function extractExports(sourceFile) {
|
|
1625
|
+
const exports = [];
|
|
1626
|
+
for (const exportDecl of sourceFile.getExportDeclarations()) {
|
|
1627
|
+
for (const namedExport of exportDecl.getNamedExports()) {
|
|
1628
|
+
exports.push(namedExport.getName());
|
|
1629
|
+
}
|
|
1630
|
+
}
|
|
1631
|
+
for (const func of sourceFile.getFunctions()) {
|
|
1632
|
+
if (func.isExported() && func.getName()) {
|
|
1633
|
+
exports.push(func.getName());
|
|
1634
|
+
}
|
|
1635
|
+
}
|
|
1636
|
+
for (const varStatement of sourceFile.getVariableStatements()) {
|
|
1637
|
+
if (varStatement.isExported()) {
|
|
1638
|
+
for (const decl of varStatement.getDeclarations()) {
|
|
1639
|
+
exports.push(decl.getName());
|
|
1640
|
+
}
|
|
1641
|
+
}
|
|
1642
|
+
}
|
|
1643
|
+
for (const iface of sourceFile.getInterfaces()) {
|
|
1644
|
+
if (iface.isExported()) {
|
|
1645
|
+
exports.push(iface.getName());
|
|
1646
|
+
}
|
|
1647
|
+
}
|
|
1648
|
+
for (const typeAlias of sourceFile.getTypeAliases()) {
|
|
1649
|
+
if (typeAlias.isExported()) {
|
|
1650
|
+
exports.push(typeAlias.getName());
|
|
1651
|
+
}
|
|
1652
|
+
}
|
|
1653
|
+
for (const enumDecl of sourceFile.getEnums()) {
|
|
1654
|
+
if (enumDecl.isExported()) {
|
|
1655
|
+
exports.push(enumDecl.getName());
|
|
1656
|
+
}
|
|
1657
|
+
}
|
|
1658
|
+
for (const classDecl of sourceFile.getClasses()) {
|
|
1659
|
+
if (classDecl.isExported() && classDecl.getName()) {
|
|
1660
|
+
exports.push(classDecl.getName());
|
|
1661
|
+
}
|
|
1662
|
+
}
|
|
1663
|
+
return [...new Set(exports)];
|
|
1664
|
+
}
|
|
1665
|
+
|
|
1666
|
+
// src/commands/context.ts
|
|
1667
|
+
async function context(target, options = {}) {
|
|
1668
|
+
const cwd = options.cwd || process.cwd();
|
|
1669
|
+
const format = options.format || "text";
|
|
1670
|
+
if (!target) {
|
|
1671
|
+
throw new Error("Target e obrigatorio. Exemplo: ai-tool context src/components/Button.tsx");
|
|
1672
|
+
}
|
|
1673
|
+
try {
|
|
1674
|
+
const targetPath = findTargetFile3(target, cwd);
|
|
1675
|
+
if (!targetPath) {
|
|
1676
|
+
return formatNotFound3(target, cwd);
|
|
1677
|
+
}
|
|
1678
|
+
const project = createProject(cwd);
|
|
1679
|
+
const absolutePath = resolve(cwd, targetPath);
|
|
1680
|
+
const sourceFile = addSourceFile(project, absolutePath);
|
|
1681
|
+
const imports = extractImports(sourceFile);
|
|
1682
|
+
const functions = extractFunctions(sourceFile);
|
|
1683
|
+
const types = extractTypes(sourceFile);
|
|
1684
|
+
const exports = extractExports(sourceFile);
|
|
1685
|
+
const result = {
|
|
1686
|
+
version: "1.0.0",
|
|
1687
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1688
|
+
file: targetPath,
|
|
1689
|
+
category: detectCategory(targetPath),
|
|
1690
|
+
imports,
|
|
1691
|
+
exports,
|
|
1692
|
+
functions,
|
|
1693
|
+
types
|
|
1694
|
+
};
|
|
1695
|
+
if (format === "json") {
|
|
1696
|
+
return JSON.stringify(result, null, 2);
|
|
1697
|
+
}
|
|
1698
|
+
return formatContextText(result);
|
|
1699
|
+
} catch (error) {
|
|
1700
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1701
|
+
throw new Error(`Erro ao executar context: ${message}`);
|
|
1702
|
+
}
|
|
1703
|
+
}
|
|
1704
|
+
var CODE_EXTENSIONS2 = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]);
|
|
1705
|
+
function findTargetFile3(target, cwd) {
|
|
1706
|
+
const normalizedTarget = target.replace(/\\/g, "/");
|
|
1707
|
+
const directPath = resolve(cwd, normalizedTarget);
|
|
1708
|
+
if (existsSync2(directPath) && isCodeFile2(directPath)) {
|
|
1709
|
+
return normalizedTarget;
|
|
1710
|
+
}
|
|
1711
|
+
for (const ext of CODE_EXTENSIONS2) {
|
|
1712
|
+
const withExt = directPath + ext;
|
|
1713
|
+
if (existsSync2(withExt)) {
|
|
1714
|
+
return normalizedTarget + ext;
|
|
1715
|
+
}
|
|
1716
|
+
}
|
|
1717
|
+
const targetName = basename(normalizedTarget).toLowerCase();
|
|
1718
|
+
const targetNameNoExt = targetName.replace(/\.(tsx?|jsx?|mjs|cjs)$/, "");
|
|
1719
|
+
const allFiles = getAllCodeFiles(cwd);
|
|
1720
|
+
const matches = [];
|
|
1721
|
+
for (const file of allFiles) {
|
|
1722
|
+
const fileName = basename(file).toLowerCase();
|
|
1723
|
+
const fileNameNoExt = fileName.replace(/\.(tsx?|jsx?|mjs|cjs)$/, "");
|
|
1724
|
+
if (fileNameNoExt === targetNameNoExt) {
|
|
1725
|
+
matches.unshift(file);
|
|
1726
|
+
} else if (file.toLowerCase().includes(normalizedTarget.toLowerCase())) {
|
|
1727
|
+
matches.push(file);
|
|
1728
|
+
}
|
|
1729
|
+
}
|
|
1730
|
+
if (matches.length > 0) {
|
|
1731
|
+
return matches[0];
|
|
1732
|
+
}
|
|
1733
|
+
return null;
|
|
1734
|
+
}
|
|
1735
|
+
function isCodeFile2(filePath) {
|
|
1736
|
+
const ext = extname2(filePath);
|
|
1737
|
+
return CODE_EXTENSIONS2.has(ext);
|
|
1738
|
+
}
|
|
1739
|
+
function getAllCodeFiles(dir, files = [], baseDir = dir) {
|
|
1740
|
+
try {
|
|
1741
|
+
const entries = readdirSync2(dir);
|
|
1742
|
+
for (const entry of entries) {
|
|
1743
|
+
const fullPath = join2(dir, entry);
|
|
1744
|
+
if (shouldIgnore(entry)) {
|
|
1745
|
+
continue;
|
|
1746
|
+
}
|
|
1747
|
+
try {
|
|
1748
|
+
const stat = statSync2(fullPath);
|
|
1749
|
+
if (stat.isDirectory()) {
|
|
1750
|
+
getAllCodeFiles(fullPath, files, baseDir);
|
|
1751
|
+
} else if (isCodeFile2(entry)) {
|
|
1752
|
+
const relativePath = fullPath.slice(baseDir.length + 1).replace(/\\/g, "/");
|
|
1753
|
+
files.push(relativePath);
|
|
1754
|
+
}
|
|
1755
|
+
} catch {
|
|
1756
|
+
}
|
|
1757
|
+
}
|
|
1758
|
+
} catch {
|
|
1759
|
+
}
|
|
1760
|
+
return files;
|
|
1761
|
+
}
|
|
1762
|
+
function shouldIgnore(name) {
|
|
1763
|
+
const ignoredDirs = [
|
|
1764
|
+
"node_modules",
|
|
1765
|
+
"dist",
|
|
1766
|
+
"build",
|
|
1767
|
+
".git",
|
|
1768
|
+
".next",
|
|
1769
|
+
".cache",
|
|
1770
|
+
"coverage",
|
|
1771
|
+
".turbo",
|
|
1772
|
+
".vercel"
|
|
1773
|
+
];
|
|
1774
|
+
return ignoredDirs.includes(name) || name.startsWith(".");
|
|
1775
|
+
}
|
|
1776
|
+
function formatNotFound3(target, cwd) {
|
|
1777
|
+
const normalizedTarget = target.toLowerCase();
|
|
1778
|
+
const allFiles = getAllCodeFiles(cwd);
|
|
1779
|
+
const similar = allFiles.filter((f) => {
|
|
1780
|
+
const fileName = basename(f).toLowerCase();
|
|
1781
|
+
const fileNameNoExt = fileName.replace(/\.(tsx?|jsx?|mjs|cjs)$/, "");
|
|
1782
|
+
return fileName.includes(normalizedTarget) || fileNameNoExt.includes(normalizedTarget) || levenshteinDistance3(fileNameNoExt, normalizedTarget) <= 3;
|
|
1783
|
+
}).slice(0, 5);
|
|
1784
|
+
let out = `Arquivo nao encontrado: "${target}"
|
|
1785
|
+
|
|
1786
|
+
`;
|
|
1787
|
+
out += `Total de arquivos no projeto: ${allFiles.length}
|
|
1788
|
+
|
|
1789
|
+
`;
|
|
1790
|
+
if (similar.length > 0) {
|
|
1791
|
+
out += `Arquivos com nome similar:
|
|
1792
|
+
`;
|
|
1793
|
+
for (const s of similar) {
|
|
1794
|
+
out += ` - ${s}
|
|
1795
|
+
`;
|
|
1796
|
+
}
|
|
1797
|
+
out += `
|
|
1798
|
+
`;
|
|
1799
|
+
}
|
|
1800
|
+
out += `Dicas:
|
|
1801
|
+
`;
|
|
1802
|
+
out += ` - Use o caminho relativo: src/components/Header.tsx
|
|
1803
|
+
`;
|
|
1804
|
+
out += ` - Ou apenas o nome do arquivo: Header
|
|
1805
|
+
`;
|
|
1806
|
+
out += ` - Verifique se o arquivo existe e e um arquivo .ts/.tsx/.js/.jsx
|
|
1807
|
+
`;
|
|
1808
|
+
return out;
|
|
1809
|
+
}
|
|
1810
|
+
function levenshteinDistance3(a, b) {
|
|
1811
|
+
const matrix = [];
|
|
1812
|
+
for (let i = 0; i <= b.length; i++) {
|
|
1813
|
+
matrix[i] = [i];
|
|
1814
|
+
}
|
|
1815
|
+
for (let j = 0; j <= a.length; j++) {
|
|
1816
|
+
matrix[0][j] = j;
|
|
1817
|
+
}
|
|
1818
|
+
for (let i = 1; i <= b.length; i++) {
|
|
1819
|
+
for (let j = 1; j <= a.length; j++) {
|
|
1820
|
+
if (b.charAt(i - 1) === a.charAt(j - 1)) {
|
|
1821
|
+
matrix[i][j] = matrix[i - 1][j - 1];
|
|
1822
|
+
} else {
|
|
1823
|
+
matrix[i][j] = Math.min(
|
|
1824
|
+
matrix[i - 1][j - 1] + 1,
|
|
1825
|
+
matrix[i][j - 1] + 1,
|
|
1826
|
+
matrix[i - 1][j] + 1
|
|
1827
|
+
);
|
|
1828
|
+
}
|
|
1829
|
+
}
|
|
1830
|
+
}
|
|
1831
|
+
return matrix[b.length][a.length];
|
|
1832
|
+
}
|
|
1833
|
+
|
|
1013
1834
|
// src/index.ts
|
|
1014
|
-
var VERSION = "0.
|
|
1835
|
+
var VERSION = "0.3.1";
|
|
1015
1836
|
|
|
1016
1837
|
export {
|
|
1017
1838
|
detectCategory,
|
|
@@ -1025,5 +1846,7 @@ export {
|
|
|
1025
1846
|
dead,
|
|
1026
1847
|
deadFix,
|
|
1027
1848
|
impact,
|
|
1849
|
+
suggest,
|
|
1850
|
+
context,
|
|
1028
1851
|
VERSION
|
|
1029
1852
|
};
|