@justmpm/ai-tool 3.22.1 → 3.22.3

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.
@@ -1832,10 +1832,22 @@ function formatChangesText(result, ctx = "cli") {
1832
1832
  return out;
1833
1833
  }
1834
1834
  const targetLabel = result.target === "staged" ? "staged" : result.target === "unstaged" ? "unstaged" : "unstaged + staged";
1835
- let summaryLine = `Files: ${result.summary.totalFiles} modified (${targetLabel}) | +${result.summary.totalAdded} -${result.summary.totalRemoved}`;
1835
+ const parts = [];
1836
+ parts.push(`Files: ${result.summary.totalFiles} modified (${targetLabel})`);
1837
+ parts.push(`+${result.summary.totalAdded} -${result.summary.totalRemoved}`);
1836
1838
  if (result.summary.totalNewFiles > 0) {
1837
- summaryLine += ` | ${result.summary.totalNewFiles} new (untracked)`;
1839
+ parts.push(`${result.summary.totalNewFiles} new (untracked, ${result.summary.totalUntrackedLines} lines)`);
1840
+ }
1841
+ if (result.summary.totalRenames > 0) {
1842
+ parts.push(`${result.summary.totalRenames} renamed`);
1843
+ }
1844
+ if (result.summary.totalAddedFiles > 0) {
1845
+ parts.push(`${result.summary.totalAddedFiles} added`);
1838
1846
  }
1847
+ if (result.summary.totalDeletedFiles > 0) {
1848
+ parts.push(`${result.summary.totalDeletedFiles} deleted`);
1849
+ }
1850
+ let summaryLine = parts.join(" | ");
1839
1851
  out += `${summaryLine}
1840
1852
  `;
1841
1853
  const kindCounts = {};
@@ -1859,11 +1871,11 @@ function formatChangesText(result, ctx = "cli") {
1859
1871
  };
1860
1872
  const kindParts = [];
1861
1873
  for (const [kind, counts] of Object.entries(kindCounts)) {
1862
- const parts = [];
1863
- if (counts.added > 0) parts.push(`+${counts.added}`);
1864
- if (counts.modified > 0) parts.push(`~${counts.modified}`);
1865
- if (counts.removed > 0) parts.push(`-${counts.removed}`);
1866
- if (parts.length > 0) kindParts.push(`${kindLabels[kind]}: ${parts.join(" ")}`);
1874
+ const parts2 = [];
1875
+ if (counts.added > 0) parts2.push(`+${counts.added}`);
1876
+ if (counts.modified > 0) parts2.push(`~${counts.modified}`);
1877
+ if (counts.removed > 0) parts2.push(`-${counts.removed}`);
1878
+ if (parts2.length > 0) kindParts.push(`${kindLabels[kind]}: ${parts2.join(" ")}`);
1867
1879
  }
1868
1880
  if (kindParts.length > 0) {
1869
1881
  out += `${kindParts.join(" | ")}
@@ -1891,10 +1903,12 @@ COMPACT MODE: Too many files. Use --file=<path> for details.
1891
1903
 
1892
1904
  `;
1893
1905
  const modifiedCompact = result.files.filter((f) => !f.newFile);
1894
- if (modifiedCompact.length > 0) {
1906
+ const renamedCompact = modifiedCompact.filter((f) => f.renamed);
1907
+ const otherModifiedCompact = modifiedCompact.filter((f) => !f.renamed);
1908
+ if (otherModifiedCompact.length > 0) {
1895
1909
  out += `Modified files:
1896
1910
  `;
1897
- for (const file of modifiedCompact) {
1911
+ for (const file of otherModifiedCompact) {
1898
1912
  const changeCount = file.changes.length;
1899
1913
  out += ` ${file.path} (+${file.stats.added} -${file.stats.removed})`;
1900
1914
  if (changeCount > 0) {
@@ -1903,6 +1917,20 @@ COMPACT MODE: Too many files. Use --file=<path> for details.
1903
1917
  out += ` - (no semantic changes)`;
1904
1918
  }
1905
1919
  out += `
1920
+ `;
1921
+ }
1922
+ }
1923
+ if (renamedCompact.length > 0) {
1924
+ out += `
1925
+ Renamed files:
1926
+ `;
1927
+ for (const file of renamedCompact) {
1928
+ out += ` ${file.renamedFrom} -> ${file.path} (+${file.stats.added} -${file.stats.removed})`;
1929
+ const changeCount = file.changes.length;
1930
+ if (changeCount > 0) {
1931
+ out += ` - ${changeCount} changes`;
1932
+ }
1933
+ out += `
1906
1934
  `;
1907
1935
  }
1908
1936
  }
@@ -2000,6 +2028,24 @@ NEW FILES (${newFiles.length}, untracked)
2000
2028
  }
2001
2029
  }
2002
2030
  }
2031
+ const renamedFiles = semanticFiles.filter((f) => f.renamed);
2032
+ if (renamedFiles.length > 0) {
2033
+ out += `
2034
+ RENAMED FILES (${renamedFiles.length})
2035
+ `;
2036
+ for (const file of renamedFiles) {
2037
+ out += ` ${file.renamedFrom} -> ${file.path} (+${file.stats.added} -${file.stats.removed})
2038
+ `;
2039
+ if (file.changes.length > 0) {
2040
+ for (const entry of file.changes) {
2041
+ const prefix = formatKindIcon(entry.kind);
2042
+ const detail = entry.detail ? ` - ${entry.detail}` : "";
2043
+ out += ` ${prefix} ${entry.name}${detail}
2044
+ `;
2045
+ }
2046
+ }
2047
+ }
2048
+ }
2003
2049
  if (otherFiles.length > 0) {
2004
2050
  out += `
2005
2051
  OTHER FILES (${otherFiles.length}):
@@ -5301,58 +5347,45 @@ function getUntrackedFiles(cwd) {
5301
5347
  return [];
5302
5348
  }
5303
5349
  }
5304
- function getChangedFiles(target, cwd) {
5305
- if (!hasGitRepo(cwd)) return [];
5306
- try {
5307
- const diffArgs = getDiffArgs(target);
5308
- const cmd = `git diff --name-only ${diffArgs.join(" ")}`;
5309
- const output = execSync2(cmd, {
5310
- cwd,
5311
- encoding: "utf-8",
5312
- stdio: ["pipe", "pipe", "pipe"]
5313
- });
5314
- return output.trim().split("\n").filter((line) => line.trim() !== "").map((line) => line.trim());
5315
- } catch {
5316
- return [];
5317
- }
5318
- }
5319
- function getDiffForFile(filePath, target, cwd) {
5350
+ function getFullDiffText(target, cwd, filePath) {
5320
5351
  if (!hasGitRepo(cwd)) return "";
5352
+ const fileArg = filePath ? ` -- "${filePath}"` : "";
5321
5353
  try {
5354
+ if (target === "all") {
5355
+ let output2 = "";
5356
+ try {
5357
+ output2 += execSync2(`git diff --unified=10${fileArg} --cached`, {
5358
+ cwd,
5359
+ encoding: "utf-8",
5360
+ stdio: ["pipe", "pipe", "pipe"],
5361
+ maxBuffer: 10 * 1024 * 1024
5362
+ });
5363
+ } catch {
5364
+ }
5365
+ try {
5366
+ output2 += execSync2(`git diff --unified=10${fileArg}`, {
5367
+ cwd,
5368
+ encoding: "utf-8",
5369
+ stdio: ["pipe", "pipe", "pipe"],
5370
+ maxBuffer: 10 * 1024 * 1024
5371
+ });
5372
+ } catch {
5373
+ }
5374
+ return output2;
5375
+ }
5322
5376
  const diffArgs = getDiffArgs(target);
5323
- const cmd = `git diff -U3 ${diffArgs.join(" ")} -- "${filePath}"`;
5377
+ const cmd = `git diff --unified=10${fileArg} ${diffArgs.join(" ")}`;
5324
5378
  const output = execSync2(cmd, {
5325
5379
  cwd,
5326
5380
  encoding: "utf-8",
5327
- stdio: ["pipe", "pipe", "pipe"]
5381
+ stdio: ["pipe", "pipe", "pipe"],
5382
+ maxBuffer: 10 * 1024 * 1024
5328
5383
  });
5329
5384
  return output;
5330
5385
  } catch {
5331
5386
  return "";
5332
5387
  }
5333
5388
  }
5334
- function getDiffStats(filePath, target, cwd) {
5335
- if (!hasGitRepo(cwd)) return null;
5336
- try {
5337
- const diffArgs = getDiffArgs(target);
5338
- const cmd = `git diff --numstat ${diffArgs.join(" ")} -- "${filePath}"`;
5339
- const output = execSync2(cmd, {
5340
- cwd,
5341
- encoding: "utf-8",
5342
- stdio: ["pipe", "pipe", "pipe"]
5343
- });
5344
- const line = output.trim().split("\n")[0];
5345
- if (!line) return null;
5346
- const parts = line.split(" ");
5347
- if (parts.length < 2) return null;
5348
- const added = parts[0] === "-" ? 0 : parseInt(parts[0], 10);
5349
- const removed = parts[1] === "-" ? 0 : parseInt(parts[1], 10);
5350
- if (Number.isNaN(added) || Number.isNaN(removed)) return null;
5351
- return { added, removed };
5352
- } catch {
5353
- return null;
5354
- }
5355
- }
5356
5389
 
5357
5390
  // src/commands/impact.ts
5358
5391
  async function impact(target, options = {}) {
@@ -7266,6 +7299,7 @@ function getTriggerIcon(trigger) {
7266
7299
  // src/commands/changes.ts
7267
7300
  import * as path from "path";
7268
7301
  import { readFileSync as readFileSync7 } from "fs";
7302
+ import parseGitDiff from "parse-git-diff";
7269
7303
  var UNTRACKED_PREVIEW_LINES = 20;
7270
7304
  async function changes(options = {}) {
7271
7305
  const { cwd, format } = parseCommandOptions(options);
@@ -7275,59 +7309,55 @@ async function changes(options = {}) {
7275
7309
  throw new Error("Nao e um repositorio Git. Execute este comando em um projeto com Git.");
7276
7310
  }
7277
7311
  try {
7278
- const changedFiles = getChangedFiles(target, cwd);
7312
+ const diffText = getFullDiffText(target, cwd, options.file || void 0);
7279
7313
  const untrackedFiles = getUntrackedFiles(cwd);
7280
- if (changedFiles.length === 0 && untrackedFiles.length === 0) {
7281
- const result2 = {
7282
- version: "1.0.0",
7283
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
7284
- target,
7285
- summary: {
7286
- totalFiles: 0,
7287
- totalNewFiles: 0,
7288
- totalAdded: 0,
7289
- totalRemoved: 0
7290
- },
7291
- files: []
7292
- };
7293
- return formatOutput(result2, format, (r) => formatChangesText(r, ctx));
7294
- }
7295
7314
  const DEFAULT_IGNORE = [".analyze/"];
7296
- const filesFiltered = changedFiles.filter(
7297
- (f) => !DEFAULT_IGNORE.some((pattern) => f.includes(pattern))
7298
- );
7299
- const untrackedFiltered = untrackedFiles.filter(
7300
- (f) => !DEFAULT_IGNORE.some((pattern) => f.includes(pattern))
7301
- );
7302
- const filesToProcess = options.file ? filesFiltered.filter(
7303
- (f) => f.toLowerCase().includes(options.file.toLowerCase())
7304
- ) : filesFiltered;
7305
- const untrackedToProcess = options.file ? untrackedFiltered.filter(
7306
- (f) => f.toLowerCase().includes(options.file.toLowerCase())
7307
- ) : untrackedFiltered;
7315
+ let parsed = null;
7316
+ if (diffText.trim()) {
7317
+ parsed = parseGitDiff(diffText);
7318
+ }
7308
7319
  const fileChanges = [];
7309
7320
  let totalAdded = 0;
7310
7321
  let totalRemoved = 0;
7322
+ let totalRenames = 0;
7323
+ let totalAddedFiles = 0;
7324
+ let totalDeletedFiles = 0;
7311
7325
  const symbolsIndex = await loadCachedSymbolsIndex(cwd);
7312
- for (const filePath of filesToProcess) {
7313
- const stats = getDiffStats(filePath, target, cwd);
7314
- const diffText = getDiffForFile(filePath, target, cwd);
7315
- let entries = classifyChanges(diffText);
7316
- if (stats) {
7317
- totalAdded += stats.added;
7318
- totalRemoved += stats.removed;
7319
- }
7320
- if (entries.length === 0 && stats && stats.added + stats.removed > 0) {
7321
- entries = extractGenericDiffPreview(diffText, filePath);
7322
- }
7323
- const enrichedEntries = enrichWithJsDoc(entries, filePath, symbolsIndex);
7324
- fileChanges.push({
7325
- path: filePath,
7326
- category: detectCategory(filePath),
7327
- stats: stats || { added: 0, removed: 0 },
7328
- changes: enrichedEntries
7329
- });
7326
+ if (parsed && parsed.files.length > 0) {
7327
+ for (const file of parsed.files) {
7328
+ const filePath = resolveFilePath(file);
7329
+ if (options.file) {
7330
+ if (!filePath.toLowerCase().includes(options.file.toLowerCase())) continue;
7331
+ }
7332
+ if (DEFAULT_IGNORE.some((p) => filePath.includes(p))) continue;
7333
+ const chunks = extractChunks(file);
7334
+ const { added, removed, entries } = processChunks(chunks, filePath, symbolsIndex);
7335
+ const fileType = resolveFileType(file);
7336
+ const change = {
7337
+ path: filePath,
7338
+ category: detectCategory(filePath),
7339
+ stats: { added, removed },
7340
+ changes: entries,
7341
+ fileType
7342
+ };
7343
+ if (file.type === "RenamedFile") {
7344
+ const renamedFile = file;
7345
+ change.renamed = true;
7346
+ change.renamedFrom = renamedFile.pathBefore;
7347
+ totalRenames++;
7348
+ }
7349
+ if (file.type === "AddedFile") totalAddedFiles++;
7350
+ if (file.type === "DeletedFile") totalDeletedFiles++;
7351
+ totalAdded += added;
7352
+ totalRemoved += removed;
7353
+ fileChanges.push(change);
7354
+ }
7330
7355
  }
7356
+ let totalUntrackedLines = 0;
7357
+ const untrackedFiltered = untrackedFiles.filter(
7358
+ (f) => !DEFAULT_IGNORE.some((p) => f.includes(p))
7359
+ );
7360
+ const untrackedToProcess = options.file ? untrackedFiltered.filter((f) => f.toLowerCase().includes(options.file.toLowerCase())) : untrackedFiltered;
7331
7361
  for (const filePath of untrackedToProcess) {
7332
7362
  const fullFilePath = path.resolve(cwd, filePath);
7333
7363
  let lineCount = 0;
@@ -7349,31 +7379,36 @@ async function changes(options = {}) {
7349
7379
  category: detectCategory(filePath),
7350
7380
  stats: { added: lineCount, removed: 0 },
7351
7381
  changes: enrichedEntries,
7352
- newFile: true
7382
+ newFile: true,
7383
+ fileType: "added"
7353
7384
  });
7354
- totalAdded += lineCount;
7385
+ totalUntrackedLines += lineCount;
7355
7386
  } catch {
7356
7387
  fileChanges.push({
7357
7388
  path: filePath,
7358
7389
  category: detectCategory(filePath),
7359
7390
  stats: { added: 0, removed: 0 },
7360
7391
  changes: [],
7361
- newFile: true
7392
+ newFile: true,
7393
+ fileType: "added"
7362
7394
  });
7363
7395
  }
7364
7396
  }
7365
7397
  const affectedAreas = buildAffectedAreas(fileChanges, cwd);
7366
- const totalForSummary = options.file ? fileChanges.length : changedFiles.length;
7367
- const totalNewForSummary = options.file ? fileChanges.filter((f) => f.newFile).length : untrackedFiles.length;
7398
+ const trackedFiles = fileChanges.filter((f) => !f.newFile);
7368
7399
  const result = {
7369
7400
  version: "1.0.0",
7370
7401
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
7371
7402
  target,
7372
7403
  summary: {
7373
- totalFiles: totalForSummary,
7374
- totalNewFiles: totalNewForSummary,
7404
+ totalFiles: trackedFiles.length,
7405
+ totalNewFiles: untrackedToProcess.length,
7375
7406
  totalAdded,
7376
- totalRemoved
7407
+ totalRemoved,
7408
+ totalUntrackedLines,
7409
+ totalRenames,
7410
+ totalAddedFiles,
7411
+ totalDeletedFiles
7377
7412
  },
7378
7413
  files: fileChanges,
7379
7414
  affectedAreas: affectedAreas.length > 0 ? affectedAreas : void 0
@@ -7384,6 +7419,142 @@ async function changes(options = {}) {
7384
7419
  throw new Error(`Erro ao executar changes: ${message}`);
7385
7420
  }
7386
7421
  }
7422
+ function resolveFilePath(file) {
7423
+ if (file.type === "RenamedFile") return file.pathAfter;
7424
+ return file.path;
7425
+ }
7426
+ function extractChunks(file) {
7427
+ if ("chunks" in file) {
7428
+ return file.chunks.filter((c) => c.type === "Chunk");
7429
+ }
7430
+ return [];
7431
+ }
7432
+ function resolveFileType(file) {
7433
+ switch (file.type) {
7434
+ case "AddedFile":
7435
+ return "added";
7436
+ case "DeletedFile":
7437
+ return "deleted";
7438
+ case "RenamedFile":
7439
+ return "renamed";
7440
+ case "ChangedFile":
7441
+ return "changed";
7442
+ }
7443
+ }
7444
+ function processChunks(chunks, filePath, symbolsIndex) {
7445
+ let added = 0;
7446
+ let removed = 0;
7447
+ const allAddedLines = [];
7448
+ const allDeletedLines = [];
7449
+ for (const chunk of chunks) {
7450
+ for (const change of chunk.changes) {
7451
+ if (change.type === "AddedLine") {
7452
+ added++;
7453
+ allAddedLines.push(change.content);
7454
+ } else if (change.type === "DeletedLine") {
7455
+ removed++;
7456
+ allDeletedLines.push(change.content);
7457
+ }
7458
+ }
7459
+ }
7460
+ const entries = classifyFromStructuredLines(allAddedLines, allDeletedLines);
7461
+ if (entries.length === 0 && added + removed > 0) {
7462
+ const previewText = buildSyntheticDiff(allAddedLines, allDeletedLines);
7463
+ const genericEntries = extractGenericDiffPreview(previewText, filePath);
7464
+ entries.push(...genericEntries);
7465
+ }
7466
+ const enrichedEntries = enrichWithJsDoc(entries, filePath, symbolsIndex);
7467
+ return { added, removed, entries: enrichedEntries };
7468
+ }
7469
+ function buildSyntheticDiff(addedLines, deletedLines) {
7470
+ const lines = [];
7471
+ const maxPreview = 10;
7472
+ for (const line of deletedLines.slice(0, maxPreview)) {
7473
+ lines.push(`-${line}`);
7474
+ }
7475
+ for (const line of addedLines.slice(0, maxPreview)) {
7476
+ lines.push(`+${line}`);
7477
+ }
7478
+ return lines.join("\n");
7479
+ }
7480
+ function classifyFromStructuredLines(addedLines, deletedLines) {
7481
+ const entries = [];
7482
+ const removedSymbols = /* @__PURE__ */ new Map();
7483
+ for (const line of deletedLines) {
7484
+ const multiReexport = extractMultipleReexports(line);
7485
+ if (multiReexport) {
7486
+ for (const name of multiReexport.names) {
7487
+ if (!removedSymbols.has(name)) {
7488
+ removedSymbols.set(name, { kind: multiReexport.kind, content: line });
7489
+ }
7490
+ }
7491
+ continue;
7492
+ }
7493
+ const info = extractSymbolInfo(line);
7494
+ if (info && info.name && isRelevantSymbol(info)) {
7495
+ if (!removedSymbols.has(info.name)) {
7496
+ removedSymbols.set(info.name, { kind: info.kind, content: line });
7497
+ }
7498
+ }
7499
+ }
7500
+ const addedSymbols = /* @__PURE__ */ new Map();
7501
+ for (const line of addedLines) {
7502
+ const multiReexport = extractMultipleReexports(line);
7503
+ if (multiReexport) {
7504
+ for (const name of multiReexport.names) {
7505
+ if (!addedSymbols.has(name)) {
7506
+ addedSymbols.set(name, { kind: multiReexport.kind, content: line });
7507
+ }
7508
+ }
7509
+ continue;
7510
+ }
7511
+ const info = extractSymbolInfo(line);
7512
+ if (info && info.name && isRelevantSymbol(info)) {
7513
+ if (!addedSymbols.has(info.name)) {
7514
+ addedSymbols.set(info.name, { kind: info.kind, content: line });
7515
+ }
7516
+ }
7517
+ }
7518
+ const modifiedNames = /* @__PURE__ */ new Set();
7519
+ for (const name of removedSymbols.keys()) {
7520
+ if (addedSymbols.has(name)) modifiedNames.add(name);
7521
+ }
7522
+ for (const [name, { kind }] of removedSymbols) {
7523
+ if (!modifiedNames.has(name)) {
7524
+ entries.push({ kind, type: "removed", name });
7525
+ }
7526
+ }
7527
+ for (const name of modifiedNames) {
7528
+ const removed = removedSymbols.get(name);
7529
+ const added = addedSymbols.get(name);
7530
+ const detail = generateModifiedDetail(removed.content, added.content, added.kind);
7531
+ entries.push({ kind: added.kind, type: "modified", name, detail });
7532
+ }
7533
+ for (const [name, { kind, content }] of addedSymbols) {
7534
+ if (!modifiedNames.has(name)) {
7535
+ const previewLines = buildPreviewFromLines([content], "added");
7536
+ entries.push({
7537
+ kind,
7538
+ type: "added",
7539
+ name,
7540
+ previewLines: previewLines.length > 0 ? previewLines : void 0
7541
+ });
7542
+ }
7543
+ }
7544
+ return entries;
7545
+ }
7546
+ function buildPreviewFromLines(lines, type) {
7547
+ const preview = [];
7548
+ const limit = type === "added" ? 2 : 4;
7549
+ for (const line of lines) {
7550
+ if (preview.length >= limit) break;
7551
+ const trimmed = line.trim();
7552
+ if (trimmed && !trimmed.startsWith("//") && !trimmed.startsWith("/*") && !/^[})\];,]*$/.test(trimmed)) {
7553
+ preview.push(type === "added" ? `[+] ${line}` : `[+] ${line}`);
7554
+ }
7555
+ }
7556
+ return preview.slice(0, limit);
7557
+ }
7387
7558
  function enrichWithJsDoc(entries, filePath, cachedIndex) {
7388
7559
  const needsEnrichment = (e) => e.kind !== "import" && e.kind !== "config" && !e.detail;
7389
7560
  if (!entries.some(needsEnrichment)) {
@@ -7496,13 +7667,9 @@ function classifyChanges(diffText) {
7496
7667
  if (multiReexport) {
7497
7668
  for (const name of multiReexport.names) {
7498
7669
  if (line.prefix === "remove") {
7499
- if (!removedSymbols.has(name)) {
7500
- removedSymbols.set(name, line);
7501
- }
7670
+ if (!removedSymbols.has(name)) removedSymbols.set(name, line);
7502
7671
  } else {
7503
- if (!addedSymbols.has(name)) {
7504
- addedSymbols.set(name, line);
7505
- }
7672
+ if (!addedSymbols.has(name)) addedSymbols.set(name, line);
7506
7673
  }
7507
7674
  }
7508
7675
  continue;
@@ -7510,32 +7677,22 @@ function classifyChanges(diffText) {
7510
7677
  const info = extractSymbolInfo(line.content);
7511
7678
  if (info && info.name && isRelevantSymbol(info)) {
7512
7679
  if (line.prefix === "remove") {
7513
- if (!removedSymbols.has(info.name)) {
7514
- removedSymbols.set(info.name, line);
7515
- }
7680
+ if (!removedSymbols.has(info.name)) removedSymbols.set(info.name, line);
7516
7681
  } else {
7517
- if (!addedSymbols.has(info.name)) {
7518
- addedSymbols.set(info.name, line);
7519
- }
7682
+ if (!addedSymbols.has(info.name)) addedSymbols.set(info.name, line);
7520
7683
  }
7521
7684
  }
7522
7685
  }
7523
7686
  const modifiedNames = /* @__PURE__ */ new Set();
7524
7687
  for (const name of removedSymbols.keys()) {
7525
- if (addedSymbols.has(name)) {
7526
- modifiedNames.add(name);
7527
- }
7688
+ if (addedSymbols.has(name)) modifiedNames.add(name);
7528
7689
  }
7529
7690
  for (const [name, line] of removedSymbols) {
7530
7691
  if (!modifiedNames.has(name)) {
7531
7692
  const info = extractSymbolInfo(line.content);
7532
7693
  if (info) {
7533
7694
  processed.add(line.line);
7534
- entries.push({
7535
- kind: info.kind,
7536
- type: "removed",
7537
- name
7538
- });
7695
+ entries.push({ kind: info.kind, type: "removed", name });
7539
7696
  }
7540
7697
  }
7541
7698
  }
@@ -7548,15 +7705,7 @@ function classifyChanges(diffText) {
7548
7705
  processed.add(removedLine.line);
7549
7706
  processed.add(addedLine.line);
7550
7707
  const detail = generateModifiedDetail(removedLine.content, addedLine.content, addedInfo.kind);
7551
- const lineNumbers = [removedLine.line, addedLine.line];
7552
- const previewLines = extractPreviewLines(diffText, lineNumbers, "modified");
7553
- entries.push({
7554
- kind: addedInfo.kind,
7555
- type: "modified",
7556
- name,
7557
- detail,
7558
- previewLines: previewLines.length > 0 ? previewLines : void 0
7559
- });
7708
+ entries.push({ kind: addedInfo.kind, type: "modified", name, detail });
7560
7709
  }
7561
7710
  }
7562
7711
  for (const [name, line] of addedSymbols) {
@@ -7564,13 +7713,7 @@ function classifyChanges(diffText) {
7564
7713
  const info = extractSymbolInfo(line.content);
7565
7714
  if (info) {
7566
7715
  processed.add(line.line);
7567
- const previewLines = extractPreviewLines(diffText, [line.line], "added");
7568
- entries.push({
7569
- kind: info.kind,
7570
- type: "added",
7571
- name,
7572
- previewLines: previewLines.length > 0 ? previewLines : void 0
7573
- });
7716
+ entries.push({ kind: info.kind, type: "added", name });
7574
7717
  }
7575
7718
  }
7576
7719
  }
@@ -7787,52 +7930,6 @@ function extractTypeBody(line) {
7787
7930
  const eqIndex = line.indexOf("=");
7788
7931
  return eqIndex >= 0 ? line.slice(eqIndex + 1).trim() : line.trim();
7789
7932
  }
7790
- var PREVIEW_LINES_LIMIT = 3;
7791
- function extractPreviewLines(diffText, lineNumbers, type) {
7792
- if (lineNumbers.length === 0) return [];
7793
- const lines = diffText.split("\n");
7794
- const previewLines = [];
7795
- let foundSymbol = false;
7796
- for (const targetLine of lineNumbers) {
7797
- if (previewLines.length >= PREVIEW_LINES_LIMIT) break;
7798
- const searchStart = Math.max(0, targetLine - 3);
7799
- const searchEnd = Math.min(lines.length, targetLine + 2);
7800
- for (let i = searchStart; i < searchEnd; i++) {
7801
- if (previewLines.length >= PREVIEW_LINES_LIMIT) break;
7802
- const line = lines[i];
7803
- if (!line) continue;
7804
- if (line.startsWith("@@") || line.startsWith("diff ") || line.startsWith("index ")) {
7805
- continue;
7806
- }
7807
- const isAdded = line.startsWith("+");
7808
- const isRemoved = line.startsWith("-");
7809
- if (type === "added" && isAdded) {
7810
- const content = line.slice(1);
7811
- const trimmed = content.trim();
7812
- if (trimmed && !trimmed.startsWith("//") && !trimmed.startsWith("/*") && !/^[})\];,\s]*$/.test(trimmed)) {
7813
- previewLines.push(`[+] ${content}`);
7814
- foundSymbol = true;
7815
- }
7816
- } else if (type === "modified") {
7817
- if (isRemoved) {
7818
- const content = line.slice(1);
7819
- const trimmed = content.trim();
7820
- if (trimmed && !trimmed.startsWith("//") && !trimmed.startsWith("/*") && !/^[})\];,\s]*$/.test(trimmed)) {
7821
- previewLines.push(`[-] ${content}`);
7822
- }
7823
- } else if (isAdded) {
7824
- const content = line.slice(1);
7825
- const trimmed = content.trim();
7826
- if (trimmed && !trimmed.startsWith("//") && !trimmed.startsWith("/*") && !/^[})\];,\s]*$/.test(trimmed)) {
7827
- previewLines.push(`[+] ${content}`);
7828
- }
7829
- }
7830
- }
7831
- }
7832
- if (foundSymbol && type === "added") break;
7833
- }
7834
- return previewLines.slice(0, PREVIEW_LINES_LIMIT);
7835
- }
7836
7933
  var GENERIC_PREVIEW_LINES_LIMIT = 5;
7837
7934
  function extractGenericDiffPreview(diffText, filePath) {
7838
7935
  if (!diffText.trim()) return [];
@@ -20,7 +20,7 @@ import {
20
20
  readConfig,
21
21
  recoveryHint,
22
22
  simplifyType
23
- } from "./chunk-4K4LZPXY.js";
23
+ } from "./chunk-A56FYR42.js";
24
24
 
25
25
  // src/commands/describe.ts
26
26
  var STOPWORDS = /* @__PURE__ */ new Set([
package/dist/cli.js CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  depsInfo,
5
5
  depsSearch,
6
6
  describe
7
- } from "./chunk-YDDQL2IM.js";
7
+ } from "./chunk-WSOR2KFZ.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-4K4LZPXY.js";
23
+ } from "./chunk-A56FYR42.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-4AYOHM6T.js");
134
+ const { startMcpServer } = await import("./server-R46RC5Y2.js");
135
135
  await startMcpServer();
136
136
  return;
137
137
  }
package/dist/index.d.ts CHANGED
@@ -400,6 +400,12 @@ interface FileChange {
400
400
  changes: ChangeEntry[];
401
401
  /** Arquivo untracked (nunca commitado) */
402
402
  newFile?: boolean;
403
+ /** Arquivo foi renomeado */
404
+ renamed?: boolean;
405
+ /** Path original antes do rename */
406
+ renamedFrom?: string;
407
+ /** Tipo de mudança do arquivo no git */
408
+ fileType?: "added" | "deleted" | "changed" | "renamed";
403
409
  }
404
410
  interface ChangesOptions extends CommandOptions {
405
411
  target?: "staged" | "unstaged" | "all";
@@ -421,6 +427,14 @@ interface ChangesResult {
421
427
  totalNewFiles: number;
422
428
  totalAdded: number;
423
429
  totalRemoved: number;
430
+ /** Linhas de arquivos untracked (separado do total de tracked files) */
431
+ totalUntrackedLines: number;
432
+ /** Quantidade de arquivos renomeados */
433
+ totalRenames: number;
434
+ /** Arquivos completamente novos (added no git) */
435
+ totalAddedFiles: number;
436
+ /** Arquivos completamente deletados */
437
+ totalDeletedFiles: number;
424
438
  };
425
439
  files: FileChange[];
426
440
  affectedAreas?: AffectedArea[];
@@ -625,9 +639,10 @@ declare function functions(options?: FunctionsOptions): Promise<string>;
625
639
  /**
626
640
  * Comando CHANGES - Resumo semantico de mudancas git
627
641
  *
628
- * Analisa arquivos modificados via git diff, classifica mudancas por tipo
629
- * (added/modified/removed) e categoria (function/type/import/const/component/hook)
630
- * usando pattern matching deterministico, e retorna um resumo estruturado.
642
+ * Analisa arquivos modificados via git diff usando parse-git-diff como parser,
643
+ * classifica mudancas por tipo (added/modified/removed) e categoria
644
+ * (function/type/import/const/component/hook) usando pattern matching deterministico,
645
+ * e retorna um resumo estruturado.
631
646
  */
632
647
 
633
648
  /**
@@ -635,6 +650,7 @@ declare function functions(options?: FunctionsOptions): Promise<string>;
635
650
  *
636
651
  * Retorna um resumo semantico de mudancas git otimizado para consumo de IA.
637
652
  * Ao inves de mostrar diff bruto, classifica mudancas por tipo e categoria.
653
+ * Usa parse-git-diff como parser principal para maior confiabilidade.
638
654
  */
639
655
  declare function changes(options?: ChangesOptions): Promise<string>;
640
656
 
package/dist/index.js CHANGED
@@ -47,7 +47,7 @@ import {
47
47
  setFileDescription,
48
48
  suggest,
49
49
  writeConfig
50
- } from "./chunk-4K4LZPXY.js";
50
+ } from "./chunk-A56FYR42.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-YDDQL2IM.js";
6
+ } from "./chunk-WSOR2KFZ.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-4K4LZPXY.js";
22
+ } from "./chunk-A56FYR42.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.1",
3
+ "version": "3.22.3",
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",
@@ -46,6 +46,7 @@
46
46
  "@modelcontextprotocol/sdk": "^1.25.3",
47
47
  "knip": "^5.44.0",
48
48
  "minimatch": "^10.0.1",
49
+ "parse-git-diff": "^0.0.20",
49
50
  "skott": "^0.35.8",
50
51
  "ts-morph": "^27.0.2",
51
52
  "zod": "^3.25.76"