@xagent/x-cli 1.2.11 → 1.2.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -12,6 +12,7 @@ import os__default from 'os';
12
12
  import { Client } from '@modelcontextprotocol/sdk/client/index.js';
13
13
  import { exec, execSync, spawn } from 'child_process';
14
14
  import { promisify } from 'util';
15
+ import { createTwoFilesPatch } from 'diff';
15
16
  import fs3, { writeFile } from 'fs/promises';
16
17
  import * as ops6 from 'fs-extra';
17
18
  import { parse } from '@typescript-eslint/typescript-estree';
@@ -1913,6 +1914,29 @@ STDERR: ${stderr}` : "");
1913
1914
  };
1914
1915
  }
1915
1916
  });
1917
+ function logToSession(message, data) {
1918
+ try {
1919
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
1920
+ const logEntry = `
1921
+ [${timestamp}] \u{1F527} TEXT EDITOR TOOL - ${message}
1922
+ =====================================
1923
+ ${data ? JSON.stringify(data, null, 2) : "No additional data"}
1924
+ =====================================
1925
+ `;
1926
+ const workingDir = process.cwd();
1927
+ const logsDir = path8.join(workingDir, "logs");
1928
+ const fs19 = fs7;
1929
+ if (fs19.existsSync(logsDir)) {
1930
+ const sessionFiles = fs19.readdirSync(logsDir).filter((f) => f.startsWith("session-") && f.endsWith(".log"));
1931
+ if (sessionFiles.length > 0) {
1932
+ const sessionFile = path8.join(logsDir, sessionFiles[sessionFiles.length - 1]);
1933
+ fs19.appendFileSync(sessionFile, logEntry);
1934
+ }
1935
+ }
1936
+ } catch (error) {
1937
+ console.log(`[TextEditorTool] ${message}`, data);
1938
+ }
1939
+ }
1916
1940
  var pathExists, TextEditorTool;
1917
1941
  var init_text_editor = __esm({
1918
1942
  "src/tools/text-editor.ts"() {
@@ -2045,7 +2069,7 @@ ${numberedLines}${additionalLinesMessage}`
2045
2069
  });
2046
2070
  const oldLines = content.split("\n");
2047
2071
  const newLines = newContent.split("\n");
2048
- const diff = this.generateDiff(oldLines, newLines, filePath);
2072
+ const diff = this.generateColoredDiff(oldLines, newLines, filePath);
2049
2073
  return {
2050
2074
  success: true,
2051
2075
  output: diff
@@ -2434,6 +2458,134 @@ ${numberedLines}${additionalLinesMessage}`
2434
2458
  }
2435
2459
  return diff.trim();
2436
2460
  }
2461
+ /**
2462
+ * Generate colored diff with ANSI color codes for terminal display
2463
+ */
2464
+ generateColoredDiff(oldLines, newLines, filePath) {
2465
+ logToSession("generateColoredDiff called", { filePath, oldLinesCount: oldLines.length, newLinesCount: newLines.length });
2466
+ if (oldLines.length === 0 && newLines.length === 0) {
2467
+ return `No changes in ${filePath}`;
2468
+ }
2469
+ if (oldLines.length === 0 && newLines.length > 0) {
2470
+ logToSession("Handling new file creation");
2471
+ const addedCount = newLines.length;
2472
+ let diff = `\x1B[32m\u2705 Created ${filePath} with ${addedCount} line${addedCount !== 1 ? "s" : ""}\x1B[0m
2473
+
2474
+ `;
2475
+ diff += `\x1B[36m--- /dev/null\x1B[0m
2476
+ `;
2477
+ diff += `\x1B[36m+++ b/${filePath}\x1B[0m
2478
+ `;
2479
+ diff += `\x1B[36m@@ -0,0 +1,${addedCount} @@\x1B[0m
2480
+ `;
2481
+ newLines.forEach((line) => {
2482
+ diff += `\x1B[32m+${line}\x1B[0m
2483
+ `;
2484
+ });
2485
+ return diff.trim();
2486
+ }
2487
+ try {
2488
+ logToSession("Generating unified diff with createTwoFilesPatch");
2489
+ const patch = createTwoFilesPatch(
2490
+ `a/${filePath}`,
2491
+ `b/${filePath}`,
2492
+ oldLines.join("\n"),
2493
+ newLines.join("\n"),
2494
+ "",
2495
+ // old header
2496
+ "",
2497
+ // new header
2498
+ { context: 3 }
2499
+ );
2500
+ logToSession("createTwoFilesPatch completed", { patchLength: patch.length });
2501
+ return this.colorizeUnifiedDiff(patch, filePath);
2502
+ } catch (error) {
2503
+ logToSession("CRITICAL: createTwoFilesPatch failed", {
2504
+ error: error.message,
2505
+ errorName: error.name,
2506
+ errorStack: error.stack?.substring(0, 500),
2507
+ filePath
2508
+ });
2509
+ return `\x1B[32m\u2705 Updated ${filePath}\x1B[0m
2510
+ \x1B[32m+ (diff generation failed - ${error.message})\x1B[0m`;
2511
+ }
2512
+ }
2513
+ /**
2514
+ * Colorize unified diff output with ANSI color codes
2515
+ */
2516
+ colorizeUnifiedDiff(diff, filePath) {
2517
+ logToSession("colorizeUnifiedDiff called", { diffLength: diff.length });
2518
+ try {
2519
+ const lines = diff.split("\n");
2520
+ logToSession("Split into lines", { lineCount: lines.length });
2521
+ let colorizedLines = [];
2522
+ let addedLines = 0;
2523
+ let removedLines = 0;
2524
+ for (const line of lines) {
2525
+ if (line.startsWith("+") && !line.startsWith("+++")) addedLines++;
2526
+ if (line.startsWith("-") && !line.startsWith("---")) removedLines++;
2527
+ }
2528
+ logToSession("Change counts", { added: addedLines, removed: removedLines });
2529
+ if (addedLines > 0 || removedLines > 0) {
2530
+ let summary = `\x1B[32m\u2705 Updated ${filePath}\x1B[0m`;
2531
+ if (addedLines > 0 && removedLines > 0) {
2532
+ summary += ` with \x1B[32m${addedLines} addition${addedLines !== 1 ? "s" : ""}\x1B[0m and \x1B[31m${removedLines} removal${removedLines !== 1 ? "s" : ""}\x1B[0m`;
2533
+ } else if (addedLines > 0) {
2534
+ summary += ` with \x1B[32m${addedLines} addition${addedLines !== 1 ? "s" : ""}\x1B[0m`;
2535
+ } else if (removedLines > 0) {
2536
+ summary += ` with \x1B[31m${removedLines} removal${removedLines !== 1 ? "s" : ""}\x1B[0m`;
2537
+ }
2538
+ colorizedLines.push(summary);
2539
+ colorizedLines.push("");
2540
+ }
2541
+ let oldLineNum = 1;
2542
+ let newLineNum = 1;
2543
+ for (const line of lines) {
2544
+ if (line.startsWith("---") || line.startsWith("+++")) {
2545
+ continue;
2546
+ } else if (line.startsWith("@@")) {
2547
+ logToSession("Processing hunk header", { header: line });
2548
+ const hunkMatch = line.match(/@@ -(\d+),?\d* \+(\d+),?\d* @@/);
2549
+ if (hunkMatch) {
2550
+ oldLineNum = parseInt(hunkMatch[1]);
2551
+ newLineNum = parseInt(hunkMatch[2]);
2552
+ logToSession("Parsed line numbers", { old: oldLineNum, new: newLineNum });
2553
+ } else {
2554
+ logToSession("Failed to parse hunk header", { header: line });
2555
+ }
2556
+ continue;
2557
+ } else if (line.startsWith("+")) {
2558
+ const content = line.substring(1);
2559
+ colorizedLines.push(` \x1B[32m${newLineNum.toString().padStart(3)} +\x1B[0m ${content}`);
2560
+ newLineNum++;
2561
+ } else if (line.startsWith("-")) {
2562
+ const content = line.substring(1);
2563
+ colorizedLines.push(` \x1B[31m${oldLineNum.toString().padStart(3)} -\x1B[0m ${content}`);
2564
+ oldLineNum++;
2565
+ } else if (line.startsWith(" ")) {
2566
+ const content = line.substring(1);
2567
+ colorizedLines.push(` ${oldLineNum.toString().padStart(3)} ${content}`);
2568
+ oldLineNum++;
2569
+ newLineNum++;
2570
+ } else if (line.trim() === "") {
2571
+ continue;
2572
+ }
2573
+ }
2574
+ const result = colorizedLines.join("\n");
2575
+ logToSession("colorizeUnifiedDiff completed", { resultLength: result.length });
2576
+ return result;
2577
+ } catch (error) {
2578
+ logToSession("CRITICAL: colorizeUnifiedDiff failed", {
2579
+ error: error.message,
2580
+ errorName: error.name,
2581
+ errorStack: error.stack?.substring(0, 500),
2582
+ filePath,
2583
+ diffPreview: diff.substring(0, 500)
2584
+ });
2585
+ return `\x1B[32m\u2705 Updated ${filePath}\x1B[0m
2586
+ \x1B[32m+ (diff colorization failed - ${error.message})\x1B[0m`;
2587
+ }
2588
+ }
2437
2589
  getEditHistory() {
2438
2590
  return [...this.editHistory];
2439
2591
  }
@@ -7562,10 +7714,10 @@ var init_dependency_analyzer = __esm({
7562
7714
  const circularDeps = [];
7563
7715
  const visited = /* @__PURE__ */ new Set();
7564
7716
  const visiting = /* @__PURE__ */ new Set();
7565
- const dfs = (filePath, path39) => {
7717
+ const dfs = (filePath, path41) => {
7566
7718
  if (visiting.has(filePath)) {
7567
- const cycleStart = path39.indexOf(filePath);
7568
- const cycle = path39.slice(cycleStart).concat([filePath]);
7719
+ const cycleStart = path41.indexOf(filePath);
7720
+ const cycle = path41.slice(cycleStart).concat([filePath]);
7569
7721
  circularDeps.push({
7570
7722
  cycle: cycle.map((fp) => graph.nodes.get(fp)?.filePath || fp),
7571
7723
  severity: cycle.length <= 2 ? "error" : "warning",
@@ -7581,7 +7733,7 @@ var init_dependency_analyzer = __esm({
7581
7733
  if (node) {
7582
7734
  for (const dependency of node.dependencies) {
7583
7735
  if (graph.nodes.has(dependency)) {
7584
- dfs(dependency, [...path39, filePath]);
7736
+ dfs(dependency, [...path41, filePath]);
7585
7737
  }
7586
7738
  }
7587
7739
  }
@@ -12328,6 +12480,184 @@ ${option.id}) ${option.title}`);
12328
12480
  }
12329
12481
  });
12330
12482
 
12483
+ // src/utils/regex-helper.ts
12484
+ var RegexHelper;
12485
+ var init_regex_helper = __esm({
12486
+ "src/utils/regex-helper.ts"() {
12487
+ RegexHelper = class {
12488
+ /**
12489
+ * Sanitize input strings for safe regex construction
12490
+ * Escapes all regex special characters to prevent pattern errors
12491
+ */
12492
+ static sanitizeForRegex(input) {
12493
+ if (typeof input !== "string") {
12494
+ console.log("RegexHelper.sanitizeForRegex: input is not a string", { input });
12495
+ return "";
12496
+ }
12497
+ const sanitized = input.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
12498
+ console.log("RegexHelper.sanitizeForRegex completed", {
12499
+ originalLength: input.length,
12500
+ sanitizedLength: sanitized.length
12501
+ });
12502
+ return sanitized;
12503
+ }
12504
+ /**
12505
+ * Create a RegExp safely with error handling
12506
+ * Returns null if regex construction fails
12507
+ */
12508
+ static createSafeRegex(pattern, flags = "i") {
12509
+ try {
12510
+ const regex = new RegExp(pattern, flags);
12511
+ console.log("RegexHelper.createSafeRegex success", {
12512
+ pattern: pattern.substring(0, 50),
12513
+ flags
12514
+ });
12515
+ return regex;
12516
+ } catch (error) {
12517
+ console.log("RegexHelper.createSafeRegex failed", {
12518
+ pattern: pattern.substring(0, 50),
12519
+ flags,
12520
+ error: error.message,
12521
+ errorName: error.name
12522
+ });
12523
+ return null;
12524
+ }
12525
+ }
12526
+ /**
12527
+ * Find fuzzy match with multiple fallback strategies
12528
+ * Progressive fallbacks: exact match -> sanitized regex -> Levenshtein distance
12529
+ */
12530
+ static findFuzzyMatch(content, searchStr) {
12531
+ if (typeof content !== "string" || typeof searchStr !== "string") {
12532
+ console.log("RegexHelper.findFuzzyMatch: invalid input types", {
12533
+ contentType: typeof content,
12534
+ searchStrType: typeof searchStr
12535
+ });
12536
+ return null;
12537
+ }
12538
+ console.log("RegexHelper.findFuzzyMatch started", {
12539
+ contentLength: content.length,
12540
+ searchStrLength: searchStr.length,
12541
+ searchStrPreview: searchStr.substring(0, 50)
12542
+ });
12543
+ try {
12544
+ if (content.includes(searchStr)) {
12545
+ console.log("RegexHelper.findFuzzyMatch: exact match found");
12546
+ return searchStr;
12547
+ }
12548
+ const sanitized = this.sanitizeForRegex(searchStr);
12549
+ const fuzzyPattern = sanitized.split(/\s+/).map((word) => this.sanitizeForRegex(word)).join("\\s*");
12550
+ const regex = this.createSafeRegex(`\\b${fuzzyPattern}\\b`, "i");
12551
+ if (regex) {
12552
+ const match = content.match(regex);
12553
+ if (match) {
12554
+ console.log("RegexHelper.findFuzzyMatch: fuzzy regex match found", {
12555
+ matched: match[0].substring(0, 50)
12556
+ });
12557
+ return match[0];
12558
+ }
12559
+ }
12560
+ const lines = content.split("\n");
12561
+ let bestMatch = null;
12562
+ let bestDistance = Infinity;
12563
+ const maxDistance = Math.min(searchStr.length / 2, 5);
12564
+ for (const line of lines) {
12565
+ const trimmed = line.trim();
12566
+ if (trimmed.length === 0) continue;
12567
+ const distance = this.levenshteinDistance(trimmed, searchStr);
12568
+ if (distance < bestDistance && distance <= maxDistance) {
12569
+ bestDistance = distance;
12570
+ bestMatch = trimmed;
12571
+ }
12572
+ }
12573
+ if (bestMatch) {
12574
+ console.log("RegexHelper.findFuzzyMatch: Levenshtein match found", {
12575
+ distance: bestDistance,
12576
+ matched: bestMatch.substring(0, 50)
12577
+ });
12578
+ } else {
12579
+ console.log("RegexHelper.findFuzzyMatch: no match found");
12580
+ }
12581
+ return bestMatch;
12582
+ } catch (error) {
12583
+ console.log("RegexHelper.findFuzzyMatch critical error", {
12584
+ error: error.message,
12585
+ errorName: error.name,
12586
+ errorStack: error.stack?.substring(0, 500),
12587
+ searchStrPreview: searchStr.substring(0, 50)
12588
+ });
12589
+ return null;
12590
+ }
12591
+ }
12592
+ /**
12593
+ * Calculate Levenshtein distance between two strings
12594
+ * Used as fallback when regex fails
12595
+ */
12596
+ static levenshteinDistance(a, b) {
12597
+ if (a.length === 0) return b.length;
12598
+ if (b.length === 0) return a.length;
12599
+ const matrix = [];
12600
+ for (let i = 0; i <= b.length; i++) {
12601
+ matrix[i] = [i];
12602
+ }
12603
+ for (let j = 0; j <= a.length; j++) {
12604
+ matrix[0][j] = j;
12605
+ }
12606
+ for (let i = 1; i <= b.length; i++) {
12607
+ for (let j = 1; j <= a.length; j++) {
12608
+ if (b.charAt(i - 1) === a.charAt(j - 1)) {
12609
+ matrix[i][j] = matrix[i - 1][j - 1];
12610
+ } else {
12611
+ matrix[i][j] = Math.min(
12612
+ matrix[i - 1][j - 1] + 1,
12613
+ // substitution
12614
+ matrix[i][j - 1] + 1,
12615
+ // insertion
12616
+ matrix[i - 1][j] + 1
12617
+ // deletion
12618
+ );
12619
+ }
12620
+ }
12621
+ }
12622
+ return matrix[b.length][a.length];
12623
+ }
12624
+ /**
12625
+ * Validate if a string contains potentially dangerous regex patterns
12626
+ * Useful for input validation before processing
12627
+ */
12628
+ static hasDangerousPatterns(input) {
12629
+ const openBrackets = (input.match(/\[/g) || []).length;
12630
+ const closeBrackets = (input.match(/\]/g) || []).length;
12631
+ if (openBrackets !== closeBrackets) {
12632
+ console.log("RegexHelper.hasDangerousPatterns: unbalanced brackets detected", {
12633
+ open: openBrackets,
12634
+ close: closeBrackets
12635
+ });
12636
+ return true;
12637
+ }
12638
+ const dangerousPatterns = [
12639
+ /\\[dws]/,
12640
+ // Incomplete escape sequences
12641
+ /\(\?/,
12642
+ // Incomplete lookbehind/lookahead
12643
+ /\{[^}]*$/
12644
+ // Unclosed quantifier
12645
+ ];
12646
+ for (const pattern of dangerousPatterns) {
12647
+ if (pattern.test(input)) {
12648
+ console.log("RegexHelper.hasDangerousPatterns: dangerous pattern detected", {
12649
+ pattern: pattern.source,
12650
+ inputPreview: input.substring(0, 50)
12651
+ });
12652
+ return true;
12653
+ }
12654
+ }
12655
+ return false;
12656
+ }
12657
+ };
12658
+ }
12659
+ });
12660
+
12331
12661
  // node_modules/zod/v3/helpers/util.js
12332
12662
  var util, objectUtil, ZodParsedType, getParsedType;
12333
12663
  var init_util = __esm({
@@ -12740,8 +13070,8 @@ var init_parseUtil = __esm({
12740
13070
  init_errors();
12741
13071
  init_en();
12742
13072
  makeIssue = (params) => {
12743
- const { data, path: path39, errorMaps, issueData } = params;
12744
- const fullPath = [...path39, ...issueData.path || []];
13073
+ const { data, path: path41, errorMaps, issueData } = params;
13074
+ const fullPath = [...path41, ...issueData.path || []];
12745
13075
  const fullIssue = {
12746
13076
  ...issueData,
12747
13077
  path: fullPath
@@ -13049,11 +13379,11 @@ var init_types = __esm({
13049
13379
  init_parseUtil();
13050
13380
  init_util();
13051
13381
  ParseInputLazyPath = class {
13052
- constructor(parent, value, path39, key) {
13382
+ constructor(parent, value, path41, key) {
13053
13383
  this._cachedPath = [];
13054
13384
  this.parent = parent;
13055
13385
  this.data = value;
13056
- this._path = path39;
13386
+ this._path = path41;
13057
13387
  this._key = key;
13058
13388
  }
13059
13389
  get path() {
@@ -18371,6 +18701,7 @@ var init_grok_agent = __esm({
18371
18701
  init_settings_manager();
18372
18702
  init_research_recommend();
18373
18703
  init_execution_orchestrator();
18704
+ init_regex_helper();
18374
18705
  GrokAgent = class extends EventEmitter {
18375
18706
  constructor(apiKey, baseURL, model, maxToolRounds, contextPack, verbosityLevel, explainLevel) {
18376
18707
  super();
@@ -18519,6 +18850,12 @@ IMPORTANT RESPONSE GUIDELINES:
18519
18850
  - Keep responses concise and focused on the actual work being done
18520
18851
  - If a tool execution completes the user's request, you can remain silent or give a brief confirmation
18521
18852
 
18853
+ \u{1F3A8} COLORED DIFF OUTPUT RULES:
18854
+ - When str_replace_editor returns colored diff output with ANSI escape codes, ALWAYS show the full tool output directly
18855
+ - NEVER summarize or paraphrase tool results that contain colored diffs - show the complete output
18856
+ - If the tool output contains ANSI color codes (\\x1b[), display it exactly as returned by the tool
18857
+ - Show the full colored diff with line numbers, not just a summary like "\u2705 Changed X to Y"
18858
+
18522
18859
  Current working directory: ${process.cwd()}`
18523
18860
  });
18524
18861
  }
@@ -19098,10 +19435,10 @@ Current working directory: ${process.cwd()}`
19098
19435
  return await this.textEditor.view(args.path, range);
19099
19436
  } catch (error) {
19100
19437
  console.warn(`view_file tool failed, falling back to bash: ${error.message}`);
19101
- const path39 = args.path;
19102
- let command = `cat "${path39}"`;
19438
+ const path41 = args.path;
19439
+ let command = `cat "${path41}"`;
19103
19440
  if (args.start_line && args.end_line) {
19104
- command = `sed -n '${args.start_line},${args.end_line}p' "${path39}"`;
19441
+ command = `sed -n '${args.start_line},${args.end_line}p' "${path41}"`;
19105
19442
  }
19106
19443
  return await this.bash.execute(command);
19107
19444
  }
@@ -19128,8 +19465,8 @@ EOF`;
19128
19465
  return await this.wrapWithChainValidation(toolCall, result);
19129
19466
  } catch (error) {
19130
19467
  console.warn(`str_replace_editor tool failed, falling back to bash: ${error.message}`);
19131
- const escapedOld = args.old_str.replace(/[\/&]/g, "\\$&");
19132
- const escapedNew = args.new_str.replace(/[\/&]/g, "\\$&");
19468
+ const escapedOld = RegexHelper.sanitizeForRegex(args.old_str);
19469
+ const escapedNew = RegexHelper.sanitizeForRegex(args.new_str);
19133
19470
  const sedCommand = args.replace_all ? `sed -i 's/${escapedOld}/${escapedNew}/g' "${args.path}"` : `sed -i '0,/${escapedOld}/s/${escapedOld}/${escapedNew}/' "${args.path}"`;
19134
19471
  const result = await this.bash.execute(sedCommand);
19135
19472
  return await this.wrapWithChainValidation(toolCall, result);
@@ -27050,7 +27387,7 @@ var init_package = __esm({
27050
27387
  package_default = {
27051
27388
  type: "module",
27052
27389
  name: "@xagent/one-shot",
27053
- version: "1.2.11",
27390
+ version: "1.2.12",
27054
27391
  description: "An open-source AI agent that brings advanced AI capabilities directly into your terminal with automatic documentation updates.",
27055
27392
  main: "dist/index.js",
27056
27393
  module: "dist/index.js",
@@ -27137,6 +27474,7 @@ var init_package = __esm({
27137
27474
  chokidar: "^4.0.3",
27138
27475
  "cli-highlight": "^2.1.11",
27139
27476
  commander: "^12.0.0",
27477
+ diff: "^8.0.2",
27140
27478
  dotenv: "^16.4.0",
27141
27479
  enquirer: "^2.4.1",
27142
27480
  "fs-extra": "^11.2.0",
@@ -30513,8 +30851,123 @@ var init_user_message_entry = __esm({
30513
30851
  };
30514
30852
  }
30515
30853
  });
30854
+ function logToSession2(message, data) {
30855
+ try {
30856
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
30857
+ const logEntry = `
30858
+ [${timestamp}] \u{1F3A8} COLORED DIFF RENDERER - ${message}
30859
+ =====================================
30860
+ ${data ? JSON.stringify(data, null, 2) : "No additional data"}
30861
+ =====================================
30862
+ `;
30863
+ const workingDir = process.cwd();
30864
+ const logsDir = path8__default.join(workingDir, "logs");
30865
+ const sessionFiles = fs7__default.readdirSync(logsDir).filter((f) => f.startsWith("session-") && f.endsWith(".log"));
30866
+ if (sessionFiles.length > 0) {
30867
+ const sessionFile = path8__default.join(logsDir, sessionFiles[sessionFiles.length - 1]);
30868
+ fs7__default.appendFileSync(sessionFile, logEntry);
30869
+ } else {
30870
+ console.log(`[ColoredDiffRenderer] ${message}`, data);
30871
+ }
30872
+ } catch (error) {
30873
+ console.log(`[ColoredDiffRenderer] ${message}`, data);
30874
+ console.error("[ColoredDiffRenderer] Session logging failed:", error);
30875
+ }
30876
+ }
30877
+ function ColoredDiffRenderer({ content }) {
30878
+ try {
30879
+ logToSession2("Rendering content with ANSI codes", {
30880
+ contentLength: content.length,
30881
+ contentPreview: content.substring(0, 200),
30882
+ ansiCodes: [],
30883
+ // ANSI codes detected in content
30884
+ lineCount: content.split("\n").length
30885
+ });
30886
+ const parseAnsiAndRender = (text) => {
30887
+ const parts = [];
30888
+ const lines = text.split("\n");
30889
+ lines.forEach((line, lineIndex) => {
30890
+ if (lineIndex > 0) {
30891
+ parts.push("\n");
30892
+ }
30893
+ if (line.includes("\x1B[32m") && line.includes(" +")) {
30894
+ const ansiEscape = String.fromCharCode(27);
30895
+ const cleanLine = line.replace(new RegExp(`${ansiEscape.replace(/[.*+?^${}()|\\[\\]\\\\]/g, "\\$&")}\\[\\d+m`, "g"), "");
30896
+ parts.push(/* @__PURE__ */ jsx(Text, { color: "green", children: cleanLine }, `${lineIndex}-green`));
30897
+ } else if (line.includes("\x1B[31m") && line.includes(" -")) {
30898
+ const ansiEscape = String.fromCharCode(27);
30899
+ const cleanLine = line.replace(new RegExp(`${ansiEscape.replace(/[.*+?^${}()|\\[\\]\\\\]/g, "\\$&")}\\[\\d+m`, "g"), "");
30900
+ parts.push(/* @__PURE__ */ jsx(Text, { color: "red", children: cleanLine }, `${lineIndex}-red`));
30901
+ } else if (line.includes("\x1B[32m") && line.includes("\u2705")) {
30902
+ const ansiEscape = String.fromCharCode(27);
30903
+ const cleanLine = line.replace(new RegExp(`${ansiEscape.replace(/[.*+?^${}()|\\[\\]\\\\]/g, "\\$&")}\\[\\d+m`, "g"), "");
30904
+ parts.push(/* @__PURE__ */ jsx(Text, { color: "green", children: cleanLine }, `${lineIndex}-summary`));
30905
+ } else if (line.includes("\x1B[36m")) {
30906
+ const ansiEscape = String.fromCharCode(27);
30907
+ const cleanLine = line.replace(new RegExp(`${ansiEscape.replace(/[.*+?^${}()|\\[\\]\\\\]/g, "\\$&")}\\[\\d+m`, "g"), "");
30908
+ parts.push(/* @__PURE__ */ jsx(Text, { color: "cyan", children: cleanLine }, `${lineIndex}-cyan`));
30909
+ } else {
30910
+ const ansiEscape = String.fromCharCode(27);
30911
+ const cleanLine = line.replace(new RegExp(`${ansiEscape.replace(/[.*+?^${}()|\\[\\]\\\\]/g, "\\$&")}\\[\\d+m`, "g"), "");
30912
+ parts.push(/* @__PURE__ */ jsx(Text, { children: cleanLine }, `${lineIndex}-context`));
30913
+ }
30914
+ });
30915
+ return parts;
30916
+ };
30917
+ return /* @__PURE__ */ jsx(Text, { wrap: "wrap", dimColor: false, children: parseAnsiAndRender(content) });
30918
+ } catch (error) {
30919
+ return /* @__PURE__ */ jsxs(Text, { wrap: "wrap", dimColor: false, color: "red", children: [
30920
+ "Error rendering colored diff: ",
30921
+ error.message,
30922
+ "\n",
30923
+ content
30924
+ ] });
30925
+ }
30926
+ }
30927
+ var init_colored_diff_renderer = __esm({
30928
+ "src/ui/components/colored-diff-renderer.tsx"() {
30929
+ }
30930
+ });
30931
+ function logToSession3(message, data) {
30932
+ try {
30933
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
30934
+ const logEntry = `
30935
+ [${timestamp}] \u{1F3A8} MARKDOWN RENDERER - ${message}
30936
+ =====================================
30937
+ ${data ? JSON.stringify(data, null, 2) : "No additional data"}
30938
+ =====================================
30939
+ `;
30940
+ const workingDir = process.cwd();
30941
+ const logsDir = path8__default.join(workingDir, "logs");
30942
+ const sessionFiles = fs7__default.readdirSync(logsDir).filter((f) => f.startsWith("session-") && f.endsWith(".log"));
30943
+ if (sessionFiles.length > 0) {
30944
+ const sessionFile = path8__default.join(logsDir, sessionFiles[sessionFiles.length - 1]);
30945
+ fs7__default.appendFileSync(sessionFile, logEntry);
30946
+ } else {
30947
+ console.log(`[MarkdownRenderer] ${message}`, data);
30948
+ }
30949
+ } catch (error) {
30950
+ console.log(`[MarkdownRenderer] ${message}`, data);
30951
+ console.error("[MarkdownRenderer] Session logging failed:", error);
30952
+ }
30953
+ }
30516
30954
  function MarkdownRenderer({ content }) {
30517
30955
  try {
30956
+ const ansiEscape = String.fromCharCode(27);
30957
+ const hasAnsiColors = new RegExp(`${ansiEscape.replace(/[.*+?^${}()|\\[\\]\\\\]/g, "\\$&")}\\[\\d+m`).test(content);
30958
+ const hasDiffMarkers = /^[\+\-\s].*$/m.test(content);
30959
+ logToSession3("Content analysis", {
30960
+ contentLength: content.length,
30961
+ contentPreview: content.substring(0, 200),
30962
+ hasAnsiColors,
30963
+ hasDiffMarkers,
30964
+ ansiEscapeUsed: ansiEscape.charCodeAt(0),
30965
+ regexPattern: `${ansiEscape.replace(/[.*+?^${}()|\\[\\]\\\\]/g, "\\$&")}\\[\\d+m`
30966
+ });
30967
+ if (hasAnsiColors && hasDiffMarkers) {
30968
+ logToSession3("Using ColoredDiffRenderer for ANSI + diff content");
30969
+ return /* @__PURE__ */ jsx(ColoredDiffRenderer, { content });
30970
+ }
30518
30971
  return /* @__PURE__ */ jsx(InlineMarkdown, { content });
30519
30972
  } catch (error) {
30520
30973
  console.error("Markdown rendering error:", error);
@@ -30673,6 +31126,7 @@ function parseInlineMarkdown(content) {
30673
31126
  }
30674
31127
  var init_markdown_renderer = __esm({
30675
31128
  "src/ui/utils/markdown-renderer.tsx"() {
31129
+ init_colored_diff_renderer();
30676
31130
  }
30677
31131
  });
30678
31132
  function AssistantMessageEntry({ entry, verbosityLevel: _verbosityLevel }) {
@@ -30958,13 +31412,18 @@ var init_tool_brevity_service = __esm({
30958
31412
  const normalizedToolName = this.normalizeToolName(toolName);
30959
31413
  const metadata = this.extractMetadata(normalizedToolName, result);
30960
31414
  const summary = this.generateSummary(normalizedToolName, result, metadata);
31415
+ const hasColoredDiff = this.hasColoredDiffContent(result);
30961
31416
  return {
30962
31417
  toolName: normalizedToolName,
30963
31418
  summary,
30964
- expansionHint: result.length > 0 ? "(ctrl+r to expand)" : "",
31419
+ expansionHint: result.length > 0 ? hasColoredDiff ? "(ctrl+r to expand diff)" : "(ctrl+r to expand)" : "",
30965
31420
  hasContent: result.length > 0,
30966
31421
  originalContent: result,
30967
- metadata
31422
+ metadata: {
31423
+ ...metadata,
31424
+ isColoredDiff: hasColoredDiff
31425
+ // Flag for UI rendering
31426
+ }
30968
31427
  };
30969
31428
  }
30970
31429
  /**
@@ -31025,6 +31484,9 @@ var init_tool_brevity_service = __esm({
31025
31484
  if (!content || content.trim().length === 0) {
31026
31485
  return `${this.capitalizeFirst(tool)} (no output)`;
31027
31486
  }
31487
+ if (this.hasColoredDiffContent(content)) {
31488
+ return this.extractDiffSummary(content);
31489
+ }
31028
31490
  switch (tool) {
31029
31491
  case "read":
31030
31492
  return `Read ${metadata.lineCount || 0} lines`;
@@ -31129,6 +31591,42 @@ var init_tool_brevity_service = __esm({
31129
31591
  const resultMatches = content.match(/result\s+\d+/gi);
31130
31592
  return resultMatches ? resultMatches.length : 1;
31131
31593
  }
31594
+ /**
31595
+ * Check if content contains colored diff output from enhanced TextEditorTool
31596
+ */
31597
+ static hasColoredDiffContent(content) {
31598
+ const ansiEscape = String.fromCharCode(27);
31599
+ const hasAnsiColors = new RegExp(`${ansiEscape.replace(/[.*+?^${}()|\\[\\]\\\\]/g, "\\$&")}\\[\\d+m`).test(content);
31600
+ const hasDiffMarkers = /^[\+\-\s].*$/m.test(content);
31601
+ const hasDiffSummary = /✅\s+(Updated|Created)/.test(content);
31602
+ return hasAnsiColors && (hasDiffMarkers || hasDiffSummary);
31603
+ }
31604
+ /**
31605
+ * Extract summary from colored diff content
31606
+ */
31607
+ static extractDiffSummary(content) {
31608
+ const summaryMatch = content.match(/✅\s+((?:Updated|Created)[^✓\n\r]*)/);
31609
+ if (summaryMatch) {
31610
+ const ansiEscape = String.fromCharCode(27);
31611
+ return summaryMatch[1].replace(new RegExp(`${ansiEscape.replace(/[.*+?^${}()|\\[\\]\\\\]/g, "\\$&")}\\[\\d+m`, "g"), "").trim();
31612
+ }
31613
+ const fileMatch = content.match(/\+\+\+\s+b\/([^\s\n]+)/);
31614
+ if (fileMatch) {
31615
+ const filename = fileMatch[1];
31616
+ const additionCount = (content.match(/^\+[^+]/gm) || []).length;
31617
+ const deletionCount = (content.match(/^-[^-]/gm) || []).length;
31618
+ if (additionCount > 0 && deletionCount > 0) {
31619
+ return `Updated ${filename} with ${additionCount} additions and ${deletionCount} deletions`;
31620
+ } else if (additionCount > 0) {
31621
+ return `Updated ${filename} with ${additionCount} additions`;
31622
+ } else if (deletionCount > 0) {
31623
+ return `Updated ${filename} with ${deletionCount} deletions`;
31624
+ } else {
31625
+ return `Updated ${filename}`;
31626
+ }
31627
+ }
31628
+ return "File updated";
31629
+ }
31132
31630
  /**
31133
31631
  * Capitalize first letter of string
31134
31632
  */
@@ -31277,7 +31775,8 @@ function ToolCallEntry({ entry, verbosityLevel, explainLevel }) {
31277
31775
  );
31278
31776
  const explanation = getExplanation(toolName, filePath);
31279
31777
  const { preview, hasMore, totalLines } = truncateToClaudeStyle(entry.content || "");
31280
- const useClaudeCodeFormat = verbosityLevel === "quiet" && brevitySummary.hasContent;
31778
+ const useClaudeCodeFormat = verbosityLevel === "quiet" && brevitySummary.hasContent && !brevitySummary.metadata.isColoredDiff;
31779
+ const forceShowColoredDiff = brevitySummary.metadata.isColoredDiff;
31281
31780
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
31282
31781
  /* @__PURE__ */ jsxs(Box, { children: [
31283
31782
  /* @__PURE__ */ jsx(Text, { color: "gray", children: "\u23BF" }),
@@ -31311,8 +31810,8 @@ function ToolCallEntry({ entry, verbosityLevel, explainLevel }) {
31311
31810
  ] }) : /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
31312
31811
  /* @__PURE__ */ jsx(Text, { color: "gray", children: "\u23BF File contents:" }),
31313
31812
  /* @__PURE__ */ jsx(Box, { marginLeft: 2, flexDirection: "column", children: /* @__PURE__ */ jsx(FileContentRenderer, { content: entry.content }) })
31314
- ] }) }) : shouldShowDiff && shouldShowFullContent ? (
31315
- // For diff results, show only the summary line, not the raw content
31813
+ ] }) }) : shouldShowDiff && shouldShowFullContent && !brevitySummary.metadata.isColoredDiff ? (
31814
+ // For diff results, show only the summary line, not the raw content (unless it's a colored diff)
31316
31815
  /* @__PURE__ */ jsxs(Text, { color: "gray", children: [
31317
31816
  "\u23BF ",
31318
31817
  entry.content.split("\n")[0]
@@ -31334,7 +31833,7 @@ function ToolCallEntry({ entry, verbosityLevel, explainLevel }) {
31334
31833
  /* @__PURE__ */ jsx(Text, { color: "gray", children: "\u23BF " }),
31335
31834
  /* @__PURE__ */ jsx(Box, { flexDirection: "column", width: "100%", children: /* @__PURE__ */ jsx(MarkdownRenderer, { content: formatToolContent(entry.content, toolName) }) })
31336
31835
  ] }) }),
31337
- shouldShowDiff && !isExecuting && shouldShowFullContent && !useClaudeCodeFormat && /* @__PURE__ */ jsx(Box, { marginLeft: 4, flexDirection: "column", children: /* @__PURE__ */ jsx(
31836
+ (shouldShowDiff && !isExecuting && shouldShowFullContent && !useClaudeCodeFormat || forceShowColoredDiff && !isExecuting) && /* @__PURE__ */ jsx(Box, { marginLeft: 4, flexDirection: "column", children: forceShowColoredDiff ? /* @__PURE__ */ jsx(ColoredDiffRenderer, { content: entry.content }) : /* @__PURE__ */ jsx(
31338
31837
  DiffRenderer,
31339
31838
  {
31340
31839
  diffContent: entry.content,
@@ -31348,6 +31847,7 @@ var truncateContent2;
31348
31847
  var init_tool_call_entry = __esm({
31349
31848
  "src/ui/components/chat-entries/tool-call-entry.tsx"() {
31350
31849
  init_diff_renderer();
31850
+ init_colored_diff_renderer();
31351
31851
  init_file_content_renderer();
31352
31852
  init_tool_brevity_service();
31353
31853
  init_markdown_renderer();
@@ -32387,10 +32887,11 @@ var init_session_logger = __esm({
32387
32887
  SessionLogger = class {
32388
32888
  constructor() {
32389
32889
  this.sessionId = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
32390
- this.logPath = path8__default.join(process.cwd(), `session-${this.sessionId}.log`);
32890
+ this.logPath = path8__default.join(process.cwd(), "logs", `session-${this.sessionId}.log`);
32391
32891
  this.init();
32392
32892
  }
32393
32893
  init() {
32894
+ fs7__default.mkdirSync(path8__default.dirname(this.logPath), { recursive: true });
32394
32895
  const header = `
32395
32896
  =================================================================
32396
32897
  \u{1F9EA} GROK ONE-SHOT TESTING SESSION
@@ -32899,7 +33400,7 @@ var require_package = __commonJS({
32899
33400
  module.exports = {
32900
33401
  type: "module",
32901
33402
  name: "@xagent/one-shot",
32902
- version: "1.2.11",
33403
+ version: "1.2.12",
32903
33404
  description: "An open-source AI agent that brings advanced AI capabilities directly into your terminal with automatic documentation updates.",
32904
33405
  main: "dist/index.js",
32905
33406
  module: "dist/index.js",
@@ -32986,6 +33487,7 @@ var require_package = __commonJS({
32986
33487
  chokidar: "^4.0.3",
32987
33488
  "cli-highlight": "^2.1.11",
32988
33489
  commander: "^12.0.0",
33490
+ diff: "^8.0.2",
32989
33491
  dotenv: "^16.4.0",
32990
33492
  enquirer: "^2.4.1",
32991
33493
  "fs-extra": "^11.2.0",