@justmpm/ai-tool 3.22.0 → 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-ZNVZNLRQ.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,11 +2028,33 @@ 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
- OTHER FILES (${otherFiles.length}): `;
2006
- out += otherFiles.map((f) => f.path.split("/").pop()).join(", ");
2007
- out += "\n";
2051
+ OTHER FILES (${otherFiles.length}):
2052
+ `;
2053
+ for (const f of otherFiles) {
2054
+ const fileName = f.path.split("/").pop() || f.path;
2055
+ out += ` ${fileName} (+${f.stats.added} -${f.stats.removed})
2056
+ `;
2057
+ }
2008
2058
  }
2009
2059
  out += nextSteps("changes", ctx);
2010
2060
  return out;
@@ -5297,26 +5347,12 @@ function getUntrackedFiles(cwd) {
5297
5347
  return [];
5298
5348
  }
5299
5349
  }
5300
- function getChangedFiles(target, cwd) {
5301
- if (!hasGitRepo(cwd)) return [];
5302
- try {
5303
- const diffArgs = getDiffArgs(target);
5304
- const cmd = `git diff --name-only ${diffArgs.join(" ")}`;
5305
- const output = execSync2(cmd, {
5306
- cwd,
5307
- encoding: "utf-8",
5308
- stdio: ["pipe", "pipe", "pipe"]
5309
- });
5310
- return output.trim().split("\n").filter((line) => line.trim() !== "").map((line) => line.trim());
5311
- } catch {
5312
- return [];
5313
- }
5314
- }
5315
- function getDiffForFile(filePath, target, cwd) {
5350
+ function getFullDiffText(target, cwd, filePath) {
5316
5351
  if (!hasGitRepo(cwd)) return "";
5317
5352
  try {
5318
5353
  const diffArgs = getDiffArgs(target);
5319
- const cmd = `git diff -U0 ${diffArgs.join(" ")} -- "${filePath}"`;
5354
+ const fileArg = filePath ? ` -- "${filePath}"` : "";
5355
+ const cmd = `git diff --unified=10${fileArg} ${diffArgs.join(" ")}`;
5320
5356
  const output = execSync2(cmd, {
5321
5357
  cwd,
5322
5358
  encoding: "utf-8",
@@ -5327,28 +5363,6 @@ function getDiffForFile(filePath, target, cwd) {
5327
5363
  return "";
5328
5364
  }
5329
5365
  }
5330
- function getDiffStats(filePath, target, cwd) {
5331
- if (!hasGitRepo(cwd)) return null;
5332
- try {
5333
- const diffArgs = getDiffArgs(target);
5334
- const cmd = `git diff --numstat ${diffArgs.join(" ")} -- "${filePath}"`;
5335
- const output = execSync2(cmd, {
5336
- cwd,
5337
- encoding: "utf-8",
5338
- stdio: ["pipe", "pipe", "pipe"]
5339
- });
5340
- const line = output.trim().split("\n")[0];
5341
- if (!line) return null;
5342
- const parts = line.split(" ");
5343
- if (parts.length < 2) return null;
5344
- const added = parts[0] === "-" ? 0 : parseInt(parts[0], 10);
5345
- const removed = parts[1] === "-" ? 0 : parseInt(parts[1], 10);
5346
- if (Number.isNaN(added) || Number.isNaN(removed)) return null;
5347
- return { added, removed };
5348
- } catch {
5349
- return null;
5350
- }
5351
- }
5352
5366
 
5353
5367
  // src/commands/impact.ts
5354
5368
  async function impact(target, options = {}) {
@@ -7262,6 +7276,7 @@ function getTriggerIcon(trigger) {
7262
7276
  // src/commands/changes.ts
7263
7277
  import * as path from "path";
7264
7278
  import { readFileSync as readFileSync7 } from "fs";
7279
+ import parseGitDiff from "parse-git-diff";
7265
7280
  var UNTRACKED_PREVIEW_LINES = 20;
7266
7281
  async function changes(options = {}) {
7267
7282
  const { cwd, format } = parseCommandOptions(options);
@@ -7271,62 +7286,55 @@ async function changes(options = {}) {
7271
7286
  throw new Error("Nao e um repositorio Git. Execute este comando em um projeto com Git.");
7272
7287
  }
7273
7288
  try {
7274
- const changedFiles = getChangedFiles(target, cwd);
7289
+ const diffText = getFullDiffText(target, cwd, options.file || void 0);
7275
7290
  const untrackedFiles = getUntrackedFiles(cwd);
7276
- if (changedFiles.length === 0 && untrackedFiles.length === 0) {
7277
- const result2 = {
7278
- version: "1.0.0",
7279
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
7280
- target,
7281
- summary: {
7282
- totalFiles: 0,
7283
- totalNewFiles: 0,
7284
- totalAdded: 0,
7285
- totalRemoved: 0
7286
- },
7287
- files: []
7288
- };
7289
- return formatOutput(result2, format, (r) => formatChangesText(r, ctx));
7290
- }
7291
7291
  const DEFAULT_IGNORE = [".analyze/"];
7292
- const filesFiltered = changedFiles.filter(
7293
- (f) => !DEFAULT_IGNORE.some((pattern) => f.includes(pattern))
7294
- );
7295
- const untrackedFiltered = untrackedFiles.filter(
7296
- (f) => !DEFAULT_IGNORE.some((pattern) => f.includes(pattern))
7297
- );
7298
- const filesToProcess = options.file ? filesFiltered.filter(
7299
- (f) => f.toLowerCase().includes(options.file.toLowerCase())
7300
- ) : filesFiltered;
7301
- const untrackedToProcess = options.file ? untrackedFiltered.filter(
7302
- (f) => f.toLowerCase().includes(options.file.toLowerCase())
7303
- ) : untrackedFiltered;
7292
+ let parsed = null;
7293
+ if (diffText.trim()) {
7294
+ parsed = parseGitDiff(diffText);
7295
+ }
7304
7296
  const fileChanges = [];
7305
7297
  let totalAdded = 0;
7306
7298
  let totalRemoved = 0;
7299
+ let totalRenames = 0;
7300
+ let totalAddedFiles = 0;
7301
+ let totalDeletedFiles = 0;
7307
7302
  const symbolsIndex = await loadCachedSymbolsIndex(cwd);
7308
- for (const filePath of filesToProcess) {
7309
- const stats = getDiffStats(filePath, target, cwd);
7310
- const diffText = getDiffForFile(filePath, target, cwd);
7311
- let entries = classifyChanges(diffText);
7312
- if (stats) {
7313
- totalAdded += stats.added;
7314
- totalRemoved += stats.removed;
7315
- }
7316
- if (entries.length === 0 && stats && stats.added + stats.removed > 0) {
7317
- const ext = path.extname(filePath).toLowerCase();
7318
- if (!CODE_EXTENSIONS2.has(ext)) {
7319
- entries = extractGenericDiffPreview(diffText, filePath);
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;
7320
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);
7321
7331
  }
7322
- const enrichedEntries = enrichWithJsDoc(entries, filePath, symbolsIndex);
7323
- fileChanges.push({
7324
- path: filePath,
7325
- category: detectCategory(filePath),
7326
- stats: stats || { added: 0, removed: 0 },
7327
- changes: enrichedEntries
7328
- });
7329
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;
7330
7338
  for (const filePath of untrackedToProcess) {
7331
7339
  const fullFilePath = path.resolve(cwd, filePath);
7332
7340
  let lineCount = 0;
@@ -7348,31 +7356,36 @@ async function changes(options = {}) {
7348
7356
  category: detectCategory(filePath),
7349
7357
  stats: { added: lineCount, removed: 0 },
7350
7358
  changes: enrichedEntries,
7351
- newFile: true
7359
+ newFile: true,
7360
+ fileType: "added"
7352
7361
  });
7353
- totalAdded += lineCount;
7362
+ totalUntrackedLines += lineCount;
7354
7363
  } catch {
7355
7364
  fileChanges.push({
7356
7365
  path: filePath,
7357
7366
  category: detectCategory(filePath),
7358
7367
  stats: { added: 0, removed: 0 },
7359
7368
  changes: [],
7360
- newFile: true
7369
+ newFile: true,
7370
+ fileType: "added"
7361
7371
  });
7362
7372
  }
7363
7373
  }
7364
7374
  const affectedAreas = buildAffectedAreas(fileChanges, cwd);
7365
- const totalForSummary = options.file ? fileChanges.length : changedFiles.length;
7366
- const totalNewForSummary = options.file ? fileChanges.filter((f) => f.newFile).length : untrackedFiles.length;
7375
+ const trackedFiles = fileChanges.filter((f) => !f.newFile);
7367
7376
  const result = {
7368
7377
  version: "1.0.0",
7369
7378
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
7370
7379
  target,
7371
7380
  summary: {
7372
- totalFiles: totalForSummary,
7373
- totalNewFiles: totalNewForSummary,
7381
+ totalFiles: trackedFiles.length,
7382
+ totalNewFiles: untrackedToProcess.length,
7374
7383
  totalAdded,
7375
- totalRemoved
7384
+ totalRemoved,
7385
+ totalUntrackedLines,
7386
+ totalRenames,
7387
+ totalAddedFiles,
7388
+ totalDeletedFiles
7376
7389
  },
7377
7390
  files: fileChanges,
7378
7391
  affectedAreas: affectedAreas.length > 0 ? affectedAreas : void 0
@@ -7383,6 +7396,142 @@ async function changes(options = {}) {
7383
7396
  throw new Error(`Erro ao executar changes: ${message}`);
7384
7397
  }
7385
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
+ }
7386
7535
  function enrichWithJsDoc(entries, filePath, cachedIndex) {
7387
7536
  const needsEnrichment = (e) => e.kind !== "import" && e.kind !== "config" && !e.detail;
7388
7537
  if (!entries.some(needsEnrichment)) {
@@ -7495,13 +7644,9 @@ function classifyChanges(diffText) {
7495
7644
  if (multiReexport) {
7496
7645
  for (const name of multiReexport.names) {
7497
7646
  if (line.prefix === "remove") {
7498
- if (!removedSymbols.has(name)) {
7499
- removedSymbols.set(name, line);
7500
- }
7647
+ if (!removedSymbols.has(name)) removedSymbols.set(name, line);
7501
7648
  } else {
7502
- if (!addedSymbols.has(name)) {
7503
- addedSymbols.set(name, line);
7504
- }
7649
+ if (!addedSymbols.has(name)) addedSymbols.set(name, line);
7505
7650
  }
7506
7651
  }
7507
7652
  continue;
@@ -7509,32 +7654,22 @@ function classifyChanges(diffText) {
7509
7654
  const info = extractSymbolInfo(line.content);
7510
7655
  if (info && info.name && isRelevantSymbol(info)) {
7511
7656
  if (line.prefix === "remove") {
7512
- if (!removedSymbols.has(info.name)) {
7513
- removedSymbols.set(info.name, line);
7514
- }
7657
+ if (!removedSymbols.has(info.name)) removedSymbols.set(info.name, line);
7515
7658
  } else {
7516
- if (!addedSymbols.has(info.name)) {
7517
- addedSymbols.set(info.name, line);
7518
- }
7659
+ if (!addedSymbols.has(info.name)) addedSymbols.set(info.name, line);
7519
7660
  }
7520
7661
  }
7521
7662
  }
7522
7663
  const modifiedNames = /* @__PURE__ */ new Set();
7523
7664
  for (const name of removedSymbols.keys()) {
7524
- if (addedSymbols.has(name)) {
7525
- modifiedNames.add(name);
7526
- }
7665
+ if (addedSymbols.has(name)) modifiedNames.add(name);
7527
7666
  }
7528
7667
  for (const [name, line] of removedSymbols) {
7529
7668
  if (!modifiedNames.has(name)) {
7530
7669
  const info = extractSymbolInfo(line.content);
7531
7670
  if (info) {
7532
7671
  processed.add(line.line);
7533
- entries.push({
7534
- kind: info.kind,
7535
- type: "removed",
7536
- name
7537
- });
7672
+ entries.push({ kind: info.kind, type: "removed", name });
7538
7673
  }
7539
7674
  }
7540
7675
  }
@@ -7547,15 +7682,7 @@ function classifyChanges(diffText) {
7547
7682
  processed.add(removedLine.line);
7548
7683
  processed.add(addedLine.line);
7549
7684
  const detail = generateModifiedDetail(removedLine.content, addedLine.content, addedInfo.kind);
7550
- const lineNumbers = [removedLine.line, addedLine.line];
7551
- const previewLines = extractPreviewLines(diffText, lineNumbers, "modified");
7552
- entries.push({
7553
- kind: addedInfo.kind,
7554
- type: "modified",
7555
- name,
7556
- detail,
7557
- previewLines: previewLines.length > 0 ? previewLines : void 0
7558
- });
7685
+ entries.push({ kind: addedInfo.kind, type: "modified", name, detail });
7559
7686
  }
7560
7687
  }
7561
7688
  for (const [name, line] of addedSymbols) {
@@ -7563,13 +7690,7 @@ function classifyChanges(diffText) {
7563
7690
  const info = extractSymbolInfo(line.content);
7564
7691
  if (info) {
7565
7692
  processed.add(line.line);
7566
- const previewLines = extractPreviewLines(diffText, [line.line], "added");
7567
- entries.push({
7568
- kind: info.kind,
7569
- type: "added",
7570
- name,
7571
- previewLines: previewLines.length > 0 ? previewLines : void 0
7572
- });
7693
+ entries.push({ kind: info.kind, type: "added", name });
7573
7694
  }
7574
7695
  }
7575
7696
  }
@@ -7786,52 +7907,6 @@ function extractTypeBody(line) {
7786
7907
  const eqIndex = line.indexOf("=");
7787
7908
  return eqIndex >= 0 ? line.slice(eqIndex + 1).trim() : line.trim();
7788
7909
  }
7789
- var PREVIEW_LINES_LIMIT = 3;
7790
- function extractPreviewLines(diffText, lineNumbers, type) {
7791
- if (lineNumbers.length === 0) return [];
7792
- const lines = diffText.split("\n");
7793
- const previewLines = [];
7794
- let foundSymbol = false;
7795
- for (const targetLine of lineNumbers) {
7796
- if (previewLines.length >= PREVIEW_LINES_LIMIT) break;
7797
- const searchStart = Math.max(0, targetLine - 3);
7798
- const searchEnd = Math.min(lines.length, targetLine + 2);
7799
- for (let i = searchStart; i < searchEnd; i++) {
7800
- if (previewLines.length >= PREVIEW_LINES_LIMIT) break;
7801
- const line = lines[i];
7802
- if (!line) continue;
7803
- if (line.startsWith("@@") || line.startsWith("diff ") || line.startsWith("index ")) {
7804
- continue;
7805
- }
7806
- const isAdded = line.startsWith("+");
7807
- const isRemoved = line.startsWith("-");
7808
- if (type === "added" && isAdded) {
7809
- const content = line.slice(1);
7810
- const trimmed = content.trim();
7811
- if (trimmed && !trimmed.startsWith("//") && !trimmed.startsWith("/*") && !/^[})\];,\s]*$/.test(trimmed)) {
7812
- previewLines.push(`[+] ${content}`);
7813
- foundSymbol = true;
7814
- }
7815
- } else if (type === "modified") {
7816
- if (isRemoved) {
7817
- const content = line.slice(1);
7818
- const trimmed = content.trim();
7819
- if (trimmed && !trimmed.startsWith("//") && !trimmed.startsWith("/*") && !/^[})\];,\s]*$/.test(trimmed)) {
7820
- previewLines.push(`[-] ${content}`);
7821
- }
7822
- } else if (isAdded) {
7823
- const content = line.slice(1);
7824
- const trimmed = content.trim();
7825
- if (trimmed && !trimmed.startsWith("//") && !trimmed.startsWith("/*") && !/^[})\];,\s]*$/.test(trimmed)) {
7826
- previewLines.push(`[+] ${content}`);
7827
- }
7828
- }
7829
- }
7830
- }
7831
- if (foundSymbol && type === "added") break;
7832
- }
7833
- return previewLines.slice(0, PREVIEW_LINES_LIMIT);
7834
- }
7835
7910
  var GENERIC_PREVIEW_LINES_LIMIT = 5;
7836
7911
  function extractGenericDiffPreview(diffText, filePath) {
7837
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-CFJB2JDZ.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-ZNVZNLRQ.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-QI5WOPV5.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-ZNVZNLRQ.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-CFJB2JDZ.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-ZNVZNLRQ.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.0",
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"