@justmpm/ai-tool 3.22.1 → 3.22.2

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-4K4LZPXY.js";
23
+ } from "./chunk-WESIQAV2.js";
24
24
 
25
25
  // src/commands/describe.ts
26
26
  var STOPWORDS = /* @__PURE__ */ new Set([
@@ -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,26 +5347,12 @@ 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 "";
5321
5352
  try {
5322
5353
  const diffArgs = getDiffArgs(target);
5323
- const cmd = `git diff -U3 ${diffArgs.join(" ")} -- "${filePath}"`;
5354
+ const fileArg = filePath ? ` -- "${filePath}"` : "";
5355
+ const cmd = `git diff --unified=10${fileArg} ${diffArgs.join(" ")}`;
5324
5356
  const output = execSync2(cmd, {
5325
5357
  cwd,
5326
5358
  encoding: "utf-8",
@@ -5331,28 +5363,6 @@ function getDiffForFile(filePath, target, cwd) {
5331
5363
  return "";
5332
5364
  }
5333
5365
  }
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
5366
 
5357
5367
  // src/commands/impact.ts
5358
5368
  async function impact(target, options = {}) {
@@ -7266,6 +7276,7 @@ function getTriggerIcon(trigger) {
7266
7276
  // src/commands/changes.ts
7267
7277
  import * as path from "path";
7268
7278
  import { readFileSync as readFileSync7 } from "fs";
7279
+ import parseGitDiff from "parse-git-diff";
7269
7280
  var UNTRACKED_PREVIEW_LINES = 20;
7270
7281
  async function changes(options = {}) {
7271
7282
  const { cwd, format } = parseCommandOptions(options);
@@ -7275,59 +7286,55 @@ async function changes(options = {}) {
7275
7286
  throw new Error("Nao e um repositorio Git. Execute este comando em um projeto com Git.");
7276
7287
  }
7277
7288
  try {
7278
- const changedFiles = getChangedFiles(target, cwd);
7289
+ const diffText = getFullDiffText(target, cwd, options.file || void 0);
7279
7290
  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
7291
  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;
7292
+ let parsed = null;
7293
+ if (diffText.trim()) {
7294
+ parsed = parseGitDiff(diffText);
7295
+ }
7308
7296
  const fileChanges = [];
7309
7297
  let totalAdded = 0;
7310
7298
  let totalRemoved = 0;
7299
+ let totalRenames = 0;
7300
+ let totalAddedFiles = 0;
7301
+ let totalDeletedFiles = 0;
7311
7302
  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
- });
7303
+ if (parsed && parsed.files.length > 0) {
7304
+ for (const file of parsed.files) {
7305
+ const filePath = resolveFilePath(file);
7306
+ if (options.file) {
7307
+ if (!filePath.toLowerCase().includes(options.file.toLowerCase())) continue;
7308
+ }
7309
+ if (DEFAULT_IGNORE.some((p) => filePath.includes(p))) continue;
7310
+ const chunks = extractChunks(file);
7311
+ const { added, removed, entries } = processChunks(chunks, filePath, symbolsIndex);
7312
+ const fileType = resolveFileType(file);
7313
+ const change = {
7314
+ path: filePath,
7315
+ category: detectCategory(filePath),
7316
+ stats: { added, removed },
7317
+ changes: entries,
7318
+ fileType
7319
+ };
7320
+ if (file.type === "RenamedFile") {
7321
+ const renamedFile = file;
7322
+ change.renamed = true;
7323
+ change.renamedFrom = renamedFile.pathBefore;
7324
+ totalRenames++;
7325
+ }
7326
+ if (file.type === "AddedFile") totalAddedFiles++;
7327
+ if (file.type === "DeletedFile") totalDeletedFiles++;
7328
+ totalAdded += added;
7329
+ totalRemoved += removed;
7330
+ fileChanges.push(change);
7331
+ }
7330
7332
  }
7333
+ let totalUntrackedLines = 0;
7334
+ const untrackedFiltered = untrackedFiles.filter(
7335
+ (f) => !DEFAULT_IGNORE.some((p) => f.includes(p))
7336
+ );
7337
+ const untrackedToProcess = options.file ? untrackedFiltered.filter((f) => f.toLowerCase().includes(options.file.toLowerCase())) : untrackedFiltered;
7331
7338
  for (const filePath of untrackedToProcess) {
7332
7339
  const fullFilePath = path.resolve(cwd, filePath);
7333
7340
  let lineCount = 0;
@@ -7349,31 +7356,36 @@ async function changes(options = {}) {
7349
7356
  category: detectCategory(filePath),
7350
7357
  stats: { added: lineCount, removed: 0 },
7351
7358
  changes: enrichedEntries,
7352
- newFile: true
7359
+ newFile: true,
7360
+ fileType: "added"
7353
7361
  });
7354
- totalAdded += lineCount;
7362
+ totalUntrackedLines += lineCount;
7355
7363
  } catch {
7356
7364
  fileChanges.push({
7357
7365
  path: filePath,
7358
7366
  category: detectCategory(filePath),
7359
7367
  stats: { added: 0, removed: 0 },
7360
7368
  changes: [],
7361
- newFile: true
7369
+ newFile: true,
7370
+ fileType: "added"
7362
7371
  });
7363
7372
  }
7364
7373
  }
7365
7374
  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;
7375
+ const trackedFiles = fileChanges.filter((f) => !f.newFile);
7368
7376
  const result = {
7369
7377
  version: "1.0.0",
7370
7378
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
7371
7379
  target,
7372
7380
  summary: {
7373
- totalFiles: totalForSummary,
7374
- totalNewFiles: totalNewForSummary,
7381
+ totalFiles: trackedFiles.length,
7382
+ totalNewFiles: untrackedToProcess.length,
7375
7383
  totalAdded,
7376
- totalRemoved
7384
+ totalRemoved,
7385
+ totalUntrackedLines,
7386
+ totalRenames,
7387
+ totalAddedFiles,
7388
+ totalDeletedFiles
7377
7389
  },
7378
7390
  files: fileChanges,
7379
7391
  affectedAreas: affectedAreas.length > 0 ? affectedAreas : void 0
@@ -7384,6 +7396,142 @@ async function changes(options = {}) {
7384
7396
  throw new Error(`Erro ao executar changes: ${message}`);
7385
7397
  }
7386
7398
  }
7399
+ function resolveFilePath(file) {
7400
+ if (file.type === "RenamedFile") return file.pathAfter;
7401
+ return file.path;
7402
+ }
7403
+ function extractChunks(file) {
7404
+ if ("chunks" in file) {
7405
+ return file.chunks.filter((c) => c.type === "Chunk");
7406
+ }
7407
+ return [];
7408
+ }
7409
+ function resolveFileType(file) {
7410
+ switch (file.type) {
7411
+ case "AddedFile":
7412
+ return "added";
7413
+ case "DeletedFile":
7414
+ return "deleted";
7415
+ case "RenamedFile":
7416
+ return "renamed";
7417
+ case "ChangedFile":
7418
+ return "changed";
7419
+ }
7420
+ }
7421
+ function processChunks(chunks, filePath, symbolsIndex) {
7422
+ let added = 0;
7423
+ let removed = 0;
7424
+ const allAddedLines = [];
7425
+ const allDeletedLines = [];
7426
+ for (const chunk of chunks) {
7427
+ for (const change of chunk.changes) {
7428
+ if (change.type === "AddedLine") {
7429
+ added++;
7430
+ allAddedLines.push(change.content);
7431
+ } else if (change.type === "DeletedLine") {
7432
+ removed++;
7433
+ allDeletedLines.push(change.content);
7434
+ }
7435
+ }
7436
+ }
7437
+ const entries = classifyFromStructuredLines(allAddedLines, allDeletedLines);
7438
+ if (entries.length === 0 && added + removed > 0) {
7439
+ const previewText = buildSyntheticDiff(allAddedLines, allDeletedLines);
7440
+ const genericEntries = extractGenericDiffPreview(previewText, filePath);
7441
+ entries.push(...genericEntries);
7442
+ }
7443
+ const enrichedEntries = enrichWithJsDoc(entries, filePath, symbolsIndex);
7444
+ return { added, removed, entries: enrichedEntries };
7445
+ }
7446
+ function buildSyntheticDiff(addedLines, deletedLines) {
7447
+ const lines = [];
7448
+ const maxPreview = 10;
7449
+ for (const line of deletedLines.slice(0, maxPreview)) {
7450
+ lines.push(`-${line}`);
7451
+ }
7452
+ for (const line of addedLines.slice(0, maxPreview)) {
7453
+ lines.push(`+${line}`);
7454
+ }
7455
+ return lines.join("\n");
7456
+ }
7457
+ function classifyFromStructuredLines(addedLines, deletedLines) {
7458
+ const entries = [];
7459
+ const removedSymbols = /* @__PURE__ */ new Map();
7460
+ for (const line of deletedLines) {
7461
+ const multiReexport = extractMultipleReexports(line);
7462
+ if (multiReexport) {
7463
+ for (const name of multiReexport.names) {
7464
+ if (!removedSymbols.has(name)) {
7465
+ removedSymbols.set(name, { kind: multiReexport.kind, content: line });
7466
+ }
7467
+ }
7468
+ continue;
7469
+ }
7470
+ const info = extractSymbolInfo(line);
7471
+ if (info && info.name && isRelevantSymbol(info)) {
7472
+ if (!removedSymbols.has(info.name)) {
7473
+ removedSymbols.set(info.name, { kind: info.kind, content: line });
7474
+ }
7475
+ }
7476
+ }
7477
+ const addedSymbols = /* @__PURE__ */ new Map();
7478
+ for (const line of addedLines) {
7479
+ const multiReexport = extractMultipleReexports(line);
7480
+ if (multiReexport) {
7481
+ for (const name of multiReexport.names) {
7482
+ if (!addedSymbols.has(name)) {
7483
+ addedSymbols.set(name, { kind: multiReexport.kind, content: line });
7484
+ }
7485
+ }
7486
+ continue;
7487
+ }
7488
+ const info = extractSymbolInfo(line);
7489
+ if (info && info.name && isRelevantSymbol(info)) {
7490
+ if (!addedSymbols.has(info.name)) {
7491
+ addedSymbols.set(info.name, { kind: info.kind, content: line });
7492
+ }
7493
+ }
7494
+ }
7495
+ const modifiedNames = /* @__PURE__ */ new Set();
7496
+ for (const name of removedSymbols.keys()) {
7497
+ if (addedSymbols.has(name)) modifiedNames.add(name);
7498
+ }
7499
+ for (const [name, { kind }] of removedSymbols) {
7500
+ if (!modifiedNames.has(name)) {
7501
+ entries.push({ kind, type: "removed", name });
7502
+ }
7503
+ }
7504
+ for (const name of modifiedNames) {
7505
+ const removed = removedSymbols.get(name);
7506
+ const added = addedSymbols.get(name);
7507
+ const detail = generateModifiedDetail(removed.content, added.content, added.kind);
7508
+ entries.push({ kind: added.kind, type: "modified", name, detail });
7509
+ }
7510
+ for (const [name, { kind, content }] of addedSymbols) {
7511
+ if (!modifiedNames.has(name)) {
7512
+ const previewLines = buildPreviewFromLines([content], "added");
7513
+ entries.push({
7514
+ kind,
7515
+ type: "added",
7516
+ name,
7517
+ previewLines: previewLines.length > 0 ? previewLines : void 0
7518
+ });
7519
+ }
7520
+ }
7521
+ return entries;
7522
+ }
7523
+ function buildPreviewFromLines(lines, type) {
7524
+ const preview = [];
7525
+ const limit = type === "added" ? 2 : 4;
7526
+ for (const line of lines) {
7527
+ if (preview.length >= limit) break;
7528
+ const trimmed = line.trim();
7529
+ if (trimmed && !trimmed.startsWith("//") && !trimmed.startsWith("/*") && !/^[})\];,]*$/.test(trimmed)) {
7530
+ preview.push(type === "added" ? `[+] ${line}` : `[+] ${line}`);
7531
+ }
7532
+ }
7533
+ return preview.slice(0, limit);
7534
+ }
7387
7535
  function enrichWithJsDoc(entries, filePath, cachedIndex) {
7388
7536
  const needsEnrichment = (e) => e.kind !== "import" && e.kind !== "config" && !e.detail;
7389
7537
  if (!entries.some(needsEnrichment)) {
@@ -7496,13 +7644,9 @@ function classifyChanges(diffText) {
7496
7644
  if (multiReexport) {
7497
7645
  for (const name of multiReexport.names) {
7498
7646
  if (line.prefix === "remove") {
7499
- if (!removedSymbols.has(name)) {
7500
- removedSymbols.set(name, line);
7501
- }
7647
+ if (!removedSymbols.has(name)) removedSymbols.set(name, line);
7502
7648
  } else {
7503
- if (!addedSymbols.has(name)) {
7504
- addedSymbols.set(name, line);
7505
- }
7649
+ if (!addedSymbols.has(name)) addedSymbols.set(name, line);
7506
7650
  }
7507
7651
  }
7508
7652
  continue;
@@ -7510,32 +7654,22 @@ function classifyChanges(diffText) {
7510
7654
  const info = extractSymbolInfo(line.content);
7511
7655
  if (info && info.name && isRelevantSymbol(info)) {
7512
7656
  if (line.prefix === "remove") {
7513
- if (!removedSymbols.has(info.name)) {
7514
- removedSymbols.set(info.name, line);
7515
- }
7657
+ if (!removedSymbols.has(info.name)) removedSymbols.set(info.name, line);
7516
7658
  } else {
7517
- if (!addedSymbols.has(info.name)) {
7518
- addedSymbols.set(info.name, line);
7519
- }
7659
+ if (!addedSymbols.has(info.name)) addedSymbols.set(info.name, line);
7520
7660
  }
7521
7661
  }
7522
7662
  }
7523
7663
  const modifiedNames = /* @__PURE__ */ new Set();
7524
7664
  for (const name of removedSymbols.keys()) {
7525
- if (addedSymbols.has(name)) {
7526
- modifiedNames.add(name);
7527
- }
7665
+ if (addedSymbols.has(name)) modifiedNames.add(name);
7528
7666
  }
7529
7667
  for (const [name, line] of removedSymbols) {
7530
7668
  if (!modifiedNames.has(name)) {
7531
7669
  const info = extractSymbolInfo(line.content);
7532
7670
  if (info) {
7533
7671
  processed.add(line.line);
7534
- entries.push({
7535
- kind: info.kind,
7536
- type: "removed",
7537
- name
7538
- });
7672
+ entries.push({ kind: info.kind, type: "removed", name });
7539
7673
  }
7540
7674
  }
7541
7675
  }
@@ -7548,15 +7682,7 @@ function classifyChanges(diffText) {
7548
7682
  processed.add(removedLine.line);
7549
7683
  processed.add(addedLine.line);
7550
7684
  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
- });
7685
+ entries.push({ kind: addedInfo.kind, type: "modified", name, detail });
7560
7686
  }
7561
7687
  }
7562
7688
  for (const [name, line] of addedSymbols) {
@@ -7564,13 +7690,7 @@ function classifyChanges(diffText) {
7564
7690
  const info = extractSymbolInfo(line.content);
7565
7691
  if (info) {
7566
7692
  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
- });
7693
+ entries.push({ kind: info.kind, type: "added", name });
7574
7694
  }
7575
7695
  }
7576
7696
  }
@@ -7787,52 +7907,6 @@ function extractTypeBody(line) {
7787
7907
  const eqIndex = line.indexOf("=");
7788
7908
  return eqIndex >= 0 ? line.slice(eqIndex + 1).trim() : line.trim();
7789
7909
  }
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
7910
  var GENERIC_PREVIEW_LINES_LIMIT = 5;
7837
7911
  function extractGenericDiffPreview(diffText, filePath) {
7838
7912
  if (!diffText.trim()) return [];
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-4AFO5NLB.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-WESIQAV2.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-XCWCN2RZ.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-WESIQAV2.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-4AFO5NLB.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-WESIQAV2.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.2",
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"