@justmpm/ai-tool 3.22.3 → 3.23.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.
@@ -20,7 +20,7 @@ import {
20
20
  readConfig,
21
21
  recoveryHint,
22
22
  simplifyType
23
- } from "./chunk-A56FYR42.js";
23
+ } from "./chunk-WTRV35AO.js";
24
24
 
25
25
  // src/commands/describe.ts
26
26
  var STOPWORDS = /* @__PURE__ */ new Set([
@@ -2002,6 +2002,24 @@ Use: ${hint("changes", ctx)} --file=<arquivo>
2002
2002
  `;
2003
2003
  }
2004
2004
  }
2005
+ if (file.astDiff && file.astDiff.astApplied) {
2006
+ out += `
2007
+ AST ANALYSIS (${file.astDiff.changeType})
2008
+ `;
2009
+ for (const category of file.astDiff.categories) {
2010
+ if (category.entries.length === 0) continue;
2011
+ const catIcon = formatAstCategoryIcon(category.name);
2012
+ out += ` ${catIcon} ${category.name} (${category.entries.length})
2013
+ `;
2014
+ for (const entry of category.entries) {
2015
+ const prefix = entry.type === "added" ? "+" : entry.type === "removed" ? "-" : "~";
2016
+ const detail = entry.detail ? ` - ${entry.detail}` : "";
2017
+ const count = entry.count && entry.count > 1 ? ` (x${entry.count})` : "";
2018
+ out += ` ${prefix} ${entry.name}${detail}${count}
2019
+ `;
2020
+ }
2021
+ }
2022
+ }
2005
2023
  }
2006
2024
  if (newFiles.length > 0) {
2007
2025
  out += `
@@ -2077,6 +2095,24 @@ function formatKindIcon(kind) {
2077
2095
  return "+";
2078
2096
  }
2079
2097
  }
2098
+ function formatAstCategoryIcon(name) {
2099
+ switch (name) {
2100
+ case "JSX COMPONENTS":
2101
+ return "<>";
2102
+ case "PROPS":
2103
+ return "{}";
2104
+ case "DECLARATIONS":
2105
+ return "fn";
2106
+ case "IMPORTS":
2107
+ return "->";
2108
+ case "STRINGS":
2109
+ return '""';
2110
+ case "STRUCTURAL":
2111
+ return "##";
2112
+ default:
2113
+ return "..";
2114
+ }
2115
+ }
2080
2116
  var MAX_EXAMPLES_PER_SYMBOL = 3;
2081
2117
  function formatUsageExamples(examples, indent = 2) {
2082
2118
  const prefix = " ".repeat(indent);
@@ -5286,7 +5322,7 @@ function enrichSuggestionsWithUsages(suggestions, index, cwd) {
5286
5322
  }
5287
5323
 
5288
5324
  // src/integrations/git.ts
5289
- import { existsSync as existsSync8 } from "fs";
5325
+ import { existsSync as existsSync8, readFileSync as readFileSync6 } from "fs";
5290
5326
  import { execSync as execSync2 } from "child_process";
5291
5327
  function hasGitRepo(cwd) {
5292
5328
  return existsSync8(cwd + "/.git");
@@ -5661,7 +5697,7 @@ async function suggest(target, options = {}) {
5661
5697
  }
5662
5698
 
5663
5699
  // src/commands/context.ts
5664
- import { existsSync as existsSync9, readFileSync as readFileSync6 } from "fs";
5700
+ import { existsSync as existsSync9, readFileSync as readFileSync7 } from "fs";
5665
5701
  import { resolve as resolve3, join as join9 } from "path";
5666
5702
  import { SyntaxKind as SyntaxKind3 } from "ts-morph";
5667
5703
 
@@ -6125,7 +6161,7 @@ function findUsageInFile2(symbolName, filePath, maxExamples, cwd) {
6125
6161
  return examples;
6126
6162
  }
6127
6163
  try {
6128
- const content = readFileSync6(absolutePath, "utf-8");
6164
+ const content = readFileSync7(absolutePath, "utf-8");
6129
6165
  const lines = content.split("\n");
6130
6166
  const usageRegex = new RegExp(`\\b${escapeRegex2(symbolName)}\\b`);
6131
6167
  for (let i = 0; i < lines.length; i++) {
@@ -7298,9 +7334,423 @@ function getTriggerIcon(trigger) {
7298
7334
 
7299
7335
  // src/commands/changes.ts
7300
7336
  import * as path from "path";
7301
- import { readFileSync as readFileSync7 } from "fs";
7337
+ import { readFileSync as readFileSync9 } from "fs";
7302
7338
  import parseGitDiff from "parse-git-diff";
7339
+
7340
+ // src/commands/ast-diff.ts
7341
+ import { execSync as execSync3 } from "child_process";
7342
+ import { existsSync as existsSync10, readFileSync as readFileSync8 } from "fs";
7343
+ import { join as join11, extname as extname3 } from "path";
7344
+ import {
7345
+ Project as Project3,
7346
+ SyntaxKind as SyntaxKind4
7347
+ } from "ts-morph";
7348
+ var MAX_ENTRIES_PER_CATEGORY = 100;
7349
+ var TS_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".tsx"]);
7350
+ var EMPTY_RESULT = (filePath) => ({
7351
+ filePath,
7352
+ changeType: "mixed",
7353
+ stats: { added: 0, removed: 0 },
7354
+ categories: []
7355
+ });
7356
+ function compareByName(oldItems, newItems, getName, getValue) {
7357
+ const entries = [];
7358
+ const oldMap = new Map(oldItems.map((item) => [getName(item), item]));
7359
+ const newMap = new Map(newItems.map((item) => [getName(item), item]));
7360
+ for (const [name] of oldMap) {
7361
+ if (!newMap.has(name)) {
7362
+ entries.push({ type: "removed", name });
7363
+ }
7364
+ }
7365
+ for (const [name] of newMap) {
7366
+ if (!oldMap.has(name)) {
7367
+ entries.push({ type: "added", name });
7368
+ }
7369
+ }
7370
+ if (getValue) {
7371
+ for (const [name, oldItem] of oldMap) {
7372
+ const newItem = newMap.get(name);
7373
+ if (newItem && getValue(oldItem) !== getValue(newItem)) {
7374
+ entries.push({ type: "modified", name });
7375
+ }
7376
+ }
7377
+ }
7378
+ return entries;
7379
+ }
7380
+ function classifyChangeType(stats, categories) {
7381
+ const totalLines = stats.added + stats.removed;
7382
+ const totalEntries = categories.reduce((sum, cat) => sum + cat.entries.length, 0);
7383
+ const jsxCount = categories.find((c) => c.name === "JSX COMPONENTS")?.entries.length ?? 0;
7384
+ const propsCount = categories.find((c) => c.name === "PROPS")?.entries.length ?? 0;
7385
+ const declCount = categories.find((c) => c.name === "DECLARATIONS")?.entries.length ?? 0;
7386
+ const addedEntries = categories.reduce(
7387
+ (sum, cat) => sum + cat.entries.filter((e) => e.type === "added").length,
7388
+ 0
7389
+ );
7390
+ const modifiedEntries = categories.reduce(
7391
+ (sum, cat) => sum + cat.entries.filter((e) => e.type === "modified").length,
7392
+ 0
7393
+ );
7394
+ if (totalLines > 100 && addedEntries < 3 && jsxCount + propsCount > 5) {
7395
+ return "structural";
7396
+ }
7397
+ if (addedEntries > 3 || declCount > 2 && addedEntries > 1) {
7398
+ return "new-feature";
7399
+ }
7400
+ if (totalLines < 50 && modifiedEntries > addedEntries && totalEntries < 20) {
7401
+ return "correction";
7402
+ }
7403
+ return "mixed";
7404
+ }
7405
+ function extractAttributesFromElement(el) {
7406
+ const props = /* @__PURE__ */ new Map();
7407
+ const attrs = el.getDescendantsOfKind(SyntaxKind4.JsxAttribute);
7408
+ for (const attr of attrs) {
7409
+ const name = attr.getNameNode().getText();
7410
+ const value = attr.getInitializer()?.getText() ?? "";
7411
+ props.set(name, value);
7412
+ }
7413
+ return props;
7414
+ }
7415
+ function extractImportsFromFile(sourceFile) {
7416
+ const imports = [];
7417
+ for (const decl of sourceFile.getImportDeclarations()) {
7418
+ const specifiers = [];
7419
+ const defaultImport = decl.getDefaultImport();
7420
+ if (defaultImport) {
7421
+ specifiers.push(defaultImport.getText());
7422
+ }
7423
+ for (const named of decl.getNamedImports()) {
7424
+ const alias = named.getAliasNode();
7425
+ if (alias) {
7426
+ specifiers.push(`${named.getName()} as ${alias.getText()}`);
7427
+ } else {
7428
+ specifiers.push(named.getName());
7429
+ }
7430
+ }
7431
+ const namespace = decl.getNamespaceImport();
7432
+ if (namespace) {
7433
+ specifiers.push(`* as ${namespace.getText()}`);
7434
+ }
7435
+ imports.push({
7436
+ module: decl.getModuleSpecifierValue(),
7437
+ specifiers: specifiers.join(", ")
7438
+ });
7439
+ }
7440
+ return imports;
7441
+ }
7442
+ function getOldContent(filePath, cwd, target) {
7443
+ try {
7444
+ const gitRef = target === "staged" ? `:${filePath}` : `HEAD:${filePath}`;
7445
+ return execSync3(`git show ${gitRef}`, { cwd, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
7446
+ } catch {
7447
+ return null;
7448
+ }
7449
+ }
7450
+ function countLineChanges(oldContent, newContent) {
7451
+ const oldLines = oldContent.split("\n");
7452
+ const newLines = newContent.split("\n");
7453
+ const oldSet = new Set(oldLines);
7454
+ const newSet = new Set(newLines);
7455
+ const added = newLines.filter((line) => !oldSet.has(line)).length;
7456
+ const removed = oldLines.filter((line) => !newSet.has(line)).length;
7457
+ return { added, removed };
7458
+ }
7459
+ function compareComponentProps(oldProps, newProps, componentName) {
7460
+ const entries = [];
7461
+ for (const [name] of oldProps) {
7462
+ if (!newProps.has(name)) {
7463
+ entries.push({
7464
+ type: "removed",
7465
+ name: `${componentName}.${name}`,
7466
+ detail: `${name}=${oldProps.get(name)}`
7467
+ });
7468
+ }
7469
+ }
7470
+ for (const [name, value] of newProps) {
7471
+ if (!oldProps.has(name)) {
7472
+ entries.push({
7473
+ type: "added",
7474
+ name: `${componentName}.${name}`,
7475
+ detail: `${name}=${value}`
7476
+ });
7477
+ }
7478
+ }
7479
+ for (const [name, oldValue] of oldProps) {
7480
+ const newValue = newProps.get(name);
7481
+ if (newValue !== void 0 && oldValue !== newValue) {
7482
+ entries.push({
7483
+ type: "modified",
7484
+ name: `${componentName}.${name}`,
7485
+ detail: `${oldValue} -> ${newValue}`
7486
+ });
7487
+ }
7488
+ }
7489
+ return entries;
7490
+ }
7491
+ function detectStructuralChanges(oldContent, newContent, detectedChanges) {
7492
+ const entries = [];
7493
+ const lineStats = countLineChanges(oldContent, newContent);
7494
+ const unexplainedLines = lineStats.added + lineStats.removed - detectedChanges * 2;
7495
+ if (unexplainedLines > 20) {
7496
+ entries.push({
7497
+ type: "modified",
7498
+ name: "layout/structure",
7499
+ detail: `~${unexplainedLines} linhas nao explicadas por declaracoes individuais`
7500
+ });
7501
+ }
7502
+ const stylePatterns = [/\bsx=\{/, /\bclassName=/, /\bstyle=\{/];
7503
+ const newStyleMatches = stylePatterns.reduce(
7504
+ (count, pattern) => count + (newContent.match(pattern)?.length ?? 0),
7505
+ 0
7506
+ );
7507
+ const oldStyleMatches = stylePatterns.reduce(
7508
+ (count, pattern) => count + (oldContent.match(pattern)?.length ?? 0),
7509
+ 0
7510
+ );
7511
+ if (newStyleMatches !== oldStyleMatches) {
7512
+ const diff = newStyleMatches - oldStyleMatches;
7513
+ if (diff > 0) {
7514
+ entries.push({
7515
+ type: "added",
7516
+ name: "inline-styles",
7517
+ detail: `${diff} atributo(s) de estilo adicionado(s)`
7518
+ });
7519
+ } else {
7520
+ entries.push({
7521
+ type: "removed",
7522
+ name: "inline-styles",
7523
+ detail: `${Math.abs(diff)} atributo(s) de estilo removido(s)`
7524
+ });
7525
+ }
7526
+ }
7527
+ return entries;
7528
+ }
7529
+ async function astDiff(options) {
7530
+ const { filePath, cwd, target } = options;
7531
+ const fullPath = join11(cwd, filePath);
7532
+ const ext = extname3(filePath).toLowerCase();
7533
+ if (!TS_EXTENSIONS.has(ext)) {
7534
+ return EMPTY_RESULT(filePath);
7535
+ }
7536
+ if (!existsSync10(fullPath)) {
7537
+ return EMPTY_RESULT(filePath);
7538
+ }
7539
+ let newContent;
7540
+ try {
7541
+ newContent = readFileSync8(fullPath, "utf-8");
7542
+ } catch {
7543
+ return EMPTY_RESULT(filePath);
7544
+ }
7545
+ const oldContent = getOldContent(filePath, cwd, target);
7546
+ if (oldContent === null) {
7547
+ return {
7548
+ filePath,
7549
+ changeType: "new-feature",
7550
+ stats: { added: newContent.split("\n").length, removed: 0 },
7551
+ categories: []
7552
+ };
7553
+ }
7554
+ if (oldContent === newContent) {
7555
+ return EMPTY_RESULT(filePath);
7556
+ }
7557
+ try {
7558
+ const project = new Project3({ useInMemoryFileSystem: true });
7559
+ const oldFile = project.createSourceFile("old.tsx", oldContent);
7560
+ const newFile = project.createSourceFile("new.tsx", newContent);
7561
+ const categories = [];
7562
+ categories.push(compareJsxComponents(oldFile, newFile));
7563
+ categories.push(compareJsxProps(oldFile, newFile));
7564
+ categories.push(compareDeclarations(oldFile, newFile));
7565
+ categories.push(compareImports(oldFile, newFile));
7566
+ categories.push(compareStringLiterals(oldFile, newFile));
7567
+ const detectedChanges = categories.reduce((sum, cat) => sum + cat.entries.length, 0);
7568
+ const structuralEntries = detectStructuralChanges(oldContent, newContent, detectedChanges);
7569
+ if (structuralEntries.length > 0) {
7570
+ categories.push({ name: "STRUCTURAL", entries: structuralEntries });
7571
+ }
7572
+ for (const cat of categories) {
7573
+ if (cat.entries.length > MAX_ENTRIES_PER_CATEGORY) {
7574
+ cat.entries = cat.entries.slice(0, MAX_ENTRIES_PER_CATEGORY);
7575
+ }
7576
+ }
7577
+ const stats = countLineChanges(oldContent, newContent);
7578
+ const changeType = classifyChangeType(stats, categories);
7579
+ return { filePath, changeType, stats, categories };
7580
+ } catch {
7581
+ const stats = countLineChanges(oldContent, newContent);
7582
+ return {
7583
+ filePath,
7584
+ changeType: "mixed",
7585
+ stats,
7586
+ categories: []
7587
+ };
7588
+ }
7589
+ }
7590
+ function compareJsxComponents(oldFile, newFile) {
7591
+ const oldComponents = extractJsxComponentsFromSource(oldFile);
7592
+ const newComponents = extractJsxComponentsFromSource(newFile);
7593
+ const entries = [];
7594
+ for (const [name] of oldComponents) {
7595
+ if (!newComponents.has(name)) {
7596
+ entries.push({ type: "removed", name });
7597
+ }
7598
+ }
7599
+ for (const [name] of newComponents) {
7600
+ if (!oldComponents.has(name)) {
7601
+ entries.push({ type: "added", name });
7602
+ }
7603
+ }
7604
+ for (const [name, oldInfo] of oldComponents) {
7605
+ const newInfo = newComponents.get(name);
7606
+ if (newInfo) {
7607
+ const propChanges = compareComponentProps(oldInfo.props, newInfo.props, name);
7608
+ const modifiedProps = propChanges.filter((p) => p.type === "modified");
7609
+ if (modifiedProps.length > 0) {
7610
+ entries.push({
7611
+ type: "modified",
7612
+ name,
7613
+ detail: modifiedProps.map((p) => p.detail).join(", ")
7614
+ });
7615
+ }
7616
+ }
7617
+ }
7618
+ return { name: "JSX COMPONENTS", entries };
7619
+ }
7620
+ function compareJsxProps(oldFile, newFile) {
7621
+ const oldComponents = extractJsxComponentsFromSource(oldFile);
7622
+ const newComponents = extractJsxComponentsFromSource(newFile);
7623
+ const entries = [];
7624
+ for (const [name, newInfo] of newComponents) {
7625
+ const oldInfo = oldComponents.get(name);
7626
+ if (oldInfo) {
7627
+ const propChanges = compareComponentProps(oldInfo.props, newInfo.props, name);
7628
+ const addedRemoved = propChanges.filter((p) => p.type !== "modified");
7629
+ entries.push(...addedRemoved);
7630
+ }
7631
+ }
7632
+ return { name: "PROPS", entries };
7633
+ }
7634
+ function compareDeclarations(oldFile, newFile) {
7635
+ const oldDecls = extractDeclarationsFromSource(oldFile);
7636
+ const newDecls = extractDeclarationsFromSource(newFile);
7637
+ const entries = compareByName(
7638
+ oldDecls,
7639
+ newDecls,
7640
+ (d) => d.name,
7641
+ (d) => `${d.kind}:${d.name}`
7642
+ );
7643
+ for (const entry of entries) {
7644
+ const isNew = entry.type === "added";
7645
+ const source = isNew ? newDecls : oldDecls;
7646
+ const decl = source.find((d) => d.name === entry.name);
7647
+ if (decl) {
7648
+ entry.detail = `(${decl.kind})`;
7649
+ }
7650
+ }
7651
+ return { name: "DECLARATIONS", entries };
7652
+ }
7653
+ function compareImports(oldFile, newFile) {
7654
+ const oldImports = extractImportsFromFile(oldFile);
7655
+ const newImports = extractImportsFromFile(newFile);
7656
+ const entries = compareByName(
7657
+ oldImports,
7658
+ newImports,
7659
+ (i) => i.module,
7660
+ (i) => i.specifiers
7661
+ );
7662
+ return { name: "IMPORTS", entries };
7663
+ }
7664
+ function compareStringLiterals(oldFile, newFile) {
7665
+ const oldStrings = extractStringsFromSource(oldFile);
7666
+ const newStrings = extractStringsFromSource(newFile);
7667
+ const oldMap = /* @__PURE__ */ new Map();
7668
+ for (const s of oldStrings) {
7669
+ oldMap.set(s, (oldMap.get(s) ?? 0) + 1);
7670
+ }
7671
+ const newMap = /* @__PURE__ */ new Map();
7672
+ for (const s of newStrings) {
7673
+ newMap.set(s, (newMap.get(s) ?? 0) + 1);
7674
+ }
7675
+ const entries = [];
7676
+ for (const [str, oldCount] of oldMap) {
7677
+ const newCount = newMap.get(str) ?? 0;
7678
+ if (newCount < oldCount) {
7679
+ entries.push({
7680
+ type: "removed",
7681
+ name: `"${str}"`,
7682
+ count: oldCount - newCount
7683
+ });
7684
+ }
7685
+ }
7686
+ for (const [str, newCount] of newMap) {
7687
+ const oldCount = oldMap.get(str) ?? 0;
7688
+ if (newCount > oldCount) {
7689
+ entries.push({
7690
+ type: "added",
7691
+ name: `"${str}"`,
7692
+ count: newCount - oldCount
7693
+ });
7694
+ }
7695
+ }
7696
+ return { name: "STRINGS", entries };
7697
+ }
7698
+ function extractJsxComponentsFromSource(sourceFile) {
7699
+ const components = /* @__PURE__ */ new Map();
7700
+ const selfClosing = sourceFile.getDescendantsOfKind(SyntaxKind4.JsxSelfClosingElement);
7701
+ const opening = sourceFile.getDescendantsOfKind(SyntaxKind4.JsxOpeningElement);
7702
+ for (const el of [...selfClosing, ...opening]) {
7703
+ const name = el.getTagNameNode().getText();
7704
+ const props = extractAttributesFromElement(el);
7705
+ components.set(name, { name, props });
7706
+ }
7707
+ return components;
7708
+ }
7709
+ function extractDeclarationsFromSource(sourceFile) {
7710
+ const declarations = [];
7711
+ for (const fn of sourceFile.getFunctions()) {
7712
+ const name = fn.getName();
7713
+ if (name) {
7714
+ declarations.push({ name, kind: "function" });
7715
+ }
7716
+ }
7717
+ for (const v of sourceFile.getVariableDeclarations()) {
7718
+ const name = v.getName();
7719
+ if (name) {
7720
+ declarations.push({ name, kind: "const" });
7721
+ }
7722
+ }
7723
+ for (const iface of sourceFile.getInterfaces()) {
7724
+ declarations.push({ name: iface.getName(), kind: "interface" });
7725
+ }
7726
+ for (const t of sourceFile.getTypeAliases()) {
7727
+ declarations.push({ name: t.getName(), kind: "type" });
7728
+ }
7729
+ for (const e of sourceFile.getEnums()) {
7730
+ declarations.push({ name: e.getName(), kind: "enum" });
7731
+ }
7732
+ return declarations;
7733
+ }
7734
+ function extractStringsFromSource(sourceFile) {
7735
+ const literals = [];
7736
+ const strings = sourceFile.getDescendantsOfKind(SyntaxKind4.StringLiteral);
7737
+ for (const str of strings) {
7738
+ const parent = str.getParent();
7739
+ if (parent && (parent.getKind() === SyntaxKind4.ImportDeclaration || parent.getKind() === SyntaxKind4.ExportDeclaration)) {
7740
+ continue;
7741
+ }
7742
+ literals.push(str.getLiteralValue());
7743
+ }
7744
+ return literals;
7745
+ }
7746
+
7747
+ // src/commands/changes.ts
7303
7748
  var UNTRACKED_PREVIEW_LINES = 20;
7749
+ var AST_DIFF_THRESHOLD = 50;
7750
+ function extIsTsOrTsx(filePath) {
7751
+ const ext = filePath.split(".").pop()?.toLowerCase();
7752
+ return ext === "ts" || ext === "tsx";
7753
+ }
7304
7754
  async function changes(options = {}) {
7305
7755
  const { cwd, format } = parseCommandOptions(options);
7306
7756
  const ctx = options.ctx || "cli";
@@ -7350,6 +7800,19 @@ async function changes(options = {}) {
7350
7800
  if (file.type === "DeletedFile") totalDeletedFiles++;
7351
7801
  totalAdded += added;
7352
7802
  totalRemoved += removed;
7803
+ if (options.file && extIsTsOrTsx(filePath) && added + removed > AST_DIFF_THRESHOLD) {
7804
+ try {
7805
+ const astResult = await astDiff({
7806
+ filePath,
7807
+ cwd,
7808
+ target
7809
+ });
7810
+ if (astResult.categories.length > 0) {
7811
+ change.astDiff = { ...astResult, astApplied: true };
7812
+ }
7813
+ } catch {
7814
+ }
7815
+ }
7353
7816
  fileChanges.push(change);
7354
7817
  }
7355
7818
  }
@@ -7362,7 +7825,7 @@ async function changes(options = {}) {
7362
7825
  const fullFilePath = path.resolve(cwd, filePath);
7363
7826
  let lineCount = 0;
7364
7827
  try {
7365
- const content = readFileSync7(fullFilePath, "utf-8");
7828
+ const content = readFileSync9(fullFilePath, "utf-8");
7366
7829
  const lines = content.split("\n");
7367
7830
  lineCount = lines.length;
7368
7831
  const syntheticDiff = lines.slice(0, UNTRACKED_PREVIEW_LINES).map((line) => `+${line}`).join("\n");
@@ -7964,8 +8427,8 @@ function extractGenericDiffPreview(diffText, filePath) {
7964
8427
  }
7965
8428
 
7966
8429
  // src/commands/find.ts
7967
- import { existsSync as existsSync10, readFileSync as readFileSync8 } from "fs";
7968
- import { join as join11 } from "path";
8430
+ import { existsSync as existsSync11, readFileSync as readFileSync10 } from "fs";
8431
+ import { join as join12 } from "path";
7969
8432
 
7970
8433
  // src/cache/gitHistory.ts
7971
8434
  var memoryCache = /* @__PURE__ */ new Map();
@@ -8160,11 +8623,11 @@ function searchInIndex(index, query, filterType, allowedFiles, cwd) {
8160
8623
  const baseCwd = cwd || "";
8161
8624
  for (const filePath of importFiles) {
8162
8625
  try {
8163
- const fullPath = baseCwd ? join11(baseCwd, filePath) : filePath;
8164
- if (!existsSync10(fullPath)) {
8626
+ const fullPath = baseCwd ? join12(baseCwd, filePath) : filePath;
8627
+ if (!existsSync11(fullPath)) {
8165
8628
  continue;
8166
8629
  }
8167
- const content = readFileSync8(fullPath, "utf-8");
8630
+ const content = readFileSync10(fullPath, "utf-8");
8168
8631
  const lines = content.split("\n");
8169
8632
  usageFilesScanned++;
8170
8633
  const usageRegex = new RegExp(`\\b${escapeRegex3(query)}\\b`);
package/dist/cli.js CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  depsInfo,
5
5
  depsSearch,
6
6
  describe
7
- } from "./chunk-WSOR2KFZ.js";
7
+ } from "./chunk-4Z6PUAFR.js";
8
8
  import {
9
9
  VERSION,
10
10
  area,
@@ -20,7 +20,7 @@ import {
20
20
  impact,
21
21
  map,
22
22
  suggest
23
- } from "./chunk-A56FYR42.js";
23
+ } from "./chunk-WTRV35AO.js";
24
24
 
25
25
  // src/cli.ts
26
26
  import { resolve } from "path";
@@ -131,7 +131,7 @@ async function main() {
131
131
  }
132
132
  }
133
133
  if (flags.mcp) {
134
- const { startMcpServer } = await import("./server-R46RC5Y2.js");
134
+ const { startMcpServer } = await import("./server-3HLEATUD.js");
135
135
  await startMcpServer();
136
136
  return;
137
137
  }
package/dist/index.d.ts CHANGED
@@ -406,6 +406,8 @@ interface FileChange {
406
406
  renamedFrom?: string;
407
407
  /** Tipo de mudança do arquivo no git */
408
408
  fileType?: "added" | "deleted" | "changed" | "renamed";
409
+ /** Resultado AST diff quando disponível (só para --file com diff grande) */
410
+ astDiff?: AstDiffResult;
409
411
  }
410
412
  interface ChangesOptions extends CommandOptions {
411
413
  target?: "staged" | "unstaged" | "all";
@@ -452,6 +454,39 @@ interface StabilityInfo {
452
454
  /** Resumo de cambios recientes (máx 3) */
453
455
  lastChanges: string[];
454
456
  }
457
+ /** Tipo de mudança predominante detectada pelo AST diff */
458
+ type AstChangeType = "structural" | "new-feature" | "correction" | "mixed";
459
+ /** Categoria de mudança AST */
460
+ type AstDiffCategoryName = "JSX COMPONENTS" | "PROPS" | "DECLARATIONS" | "IMPORTS" | "STRINGS" | "STRUCTURAL";
461
+ /** Entry individual do diff AST */
462
+ interface AstDiffEntry {
463
+ type: "added" | "modified" | "removed";
464
+ name: string;
465
+ detail?: string;
466
+ line?: number;
467
+ /** Quantas ocorrências da mudança */
468
+ count?: number;
469
+ }
470
+ /** Categoria com entries agrupadas */
471
+ interface AstDiffCategory {
472
+ name: AstDiffCategoryName;
473
+ entries: AstDiffEntry[];
474
+ }
475
+ /** Resultado do AST diff */
476
+ interface AstDiffResult {
477
+ filePath: string;
478
+ /** Classificação do tipo de mudança predominante */
479
+ changeType: AstChangeType;
480
+ /** Total de linhas mudadas */
481
+ stats: {
482
+ added: number;
483
+ removed: number;
484
+ };
485
+ /** Entradas agrupadas por categoria */
486
+ categories: AstDiffCategory[];
487
+ /** Indica se AST diff foi aplicado (false = fallback para regex) */
488
+ astApplied: boolean;
489
+ }
455
490
 
456
491
  /**
457
492
  * Comando MAP - Mapa do projeto usando Skott
package/dist/index.js CHANGED
@@ -47,7 +47,7 @@ import {
47
47
  setFileDescription,
48
48
  suggest,
49
49
  writeConfig
50
- } from "./chunk-A56FYR42.js";
50
+ } from "./chunk-WTRV35AO.js";
51
51
  export {
52
52
  VERSION,
53
53
  area,
@@ -3,7 +3,7 @@ import {
3
3
  depsInfo,
4
4
  depsSearch,
5
5
  describe
6
- } from "./chunk-WSOR2KFZ.js";
6
+ } from "./chunk-4Z6PUAFR.js";
7
7
  import {
8
8
  VERSION,
9
9
  area,
@@ -19,7 +19,7 @@ import {
19
19
  map,
20
20
  recoveryHint,
21
21
  suggest
22
- } from "./chunk-A56FYR42.js";
22
+ } from "./chunk-WTRV35AO.js";
23
23
 
24
24
  // src/mcp/server.ts
25
25
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@justmpm/ai-tool",
3
- "version": "3.22.3",
3
+ "version": "3.23.0",
4
4
  "description": "Ferramenta de análise de dependências e impacto para projetos TypeScript/JavaScript. Usa Skott + Knip internamente. Inclui busca por descrição, integração Git e testes inteligentes.",
5
5
  "keywords": [
6
6
  "dependency-analysis",