@xagent/x-cli 1.2.10 → 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.10",
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",
@@ -27086,6 +27423,9 @@ var init_package = __esm({
27086
27423
  "test:tools": "./scripts/test-all-tools.sh",
27087
27424
  "test:tools:quick": "./scripts/test-tools-quick.sh",
27088
27425
  "test:tools:validate": "./scripts/validate-tool-code.sh",
27426
+ "security:review": "npm audit && npm outdated",
27427
+ "security:critical": "npm audit --audit-level=critical",
27428
+ "security:report": "npm audit --json > security-report.json || true",
27089
27429
  prepare: "husky install",
27090
27430
  "dev:site": "cd apps/site && npm run start",
27091
27431
  "build:site": "cd apps/site && npm run build",
@@ -27134,6 +27474,7 @@ var init_package = __esm({
27134
27474
  chokidar: "^4.0.3",
27135
27475
  "cli-highlight": "^2.1.11",
27136
27476
  commander: "^12.0.0",
27477
+ diff: "^8.0.2",
27137
27478
  dotenv: "^16.4.0",
27138
27479
  enquirer: "^2.4.1",
27139
27480
  "fs-extra": "^11.2.0",
@@ -30510,8 +30851,123 @@ var init_user_message_entry = __esm({
30510
30851
  };
30511
30852
  }
30512
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
+ }
30513
30954
  function MarkdownRenderer({ content }) {
30514
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
+ }
30515
30971
  return /* @__PURE__ */ jsx(InlineMarkdown, { content });
30516
30972
  } catch (error) {
30517
30973
  console.error("Markdown rendering error:", error);
@@ -30670,6 +31126,7 @@ function parseInlineMarkdown(content) {
30670
31126
  }
30671
31127
  var init_markdown_renderer = __esm({
30672
31128
  "src/ui/utils/markdown-renderer.tsx"() {
31129
+ init_colored_diff_renderer();
30673
31130
  }
30674
31131
  });
30675
31132
  function AssistantMessageEntry({ entry, verbosityLevel: _verbosityLevel }) {
@@ -30955,13 +31412,18 @@ var init_tool_brevity_service = __esm({
30955
31412
  const normalizedToolName = this.normalizeToolName(toolName);
30956
31413
  const metadata = this.extractMetadata(normalizedToolName, result);
30957
31414
  const summary = this.generateSummary(normalizedToolName, result, metadata);
31415
+ const hasColoredDiff = this.hasColoredDiffContent(result);
30958
31416
  return {
30959
31417
  toolName: normalizedToolName,
30960
31418
  summary,
30961
- expansionHint: result.length > 0 ? "(ctrl+r to expand)" : "",
31419
+ expansionHint: result.length > 0 ? hasColoredDiff ? "(ctrl+r to expand diff)" : "(ctrl+r to expand)" : "",
30962
31420
  hasContent: result.length > 0,
30963
31421
  originalContent: result,
30964
- metadata
31422
+ metadata: {
31423
+ ...metadata,
31424
+ isColoredDiff: hasColoredDiff
31425
+ // Flag for UI rendering
31426
+ }
30965
31427
  };
30966
31428
  }
30967
31429
  /**
@@ -31022,6 +31484,9 @@ var init_tool_brevity_service = __esm({
31022
31484
  if (!content || content.trim().length === 0) {
31023
31485
  return `${this.capitalizeFirst(tool)} (no output)`;
31024
31486
  }
31487
+ if (this.hasColoredDiffContent(content)) {
31488
+ return this.extractDiffSummary(content);
31489
+ }
31025
31490
  switch (tool) {
31026
31491
  case "read":
31027
31492
  return `Read ${metadata.lineCount || 0} lines`;
@@ -31126,6 +31591,42 @@ var init_tool_brevity_service = __esm({
31126
31591
  const resultMatches = content.match(/result\s+\d+/gi);
31127
31592
  return resultMatches ? resultMatches.length : 1;
31128
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
+ }
31129
31630
  /**
31130
31631
  * Capitalize first letter of string
31131
31632
  */
@@ -31274,7 +31775,8 @@ function ToolCallEntry({ entry, verbosityLevel, explainLevel }) {
31274
31775
  );
31275
31776
  const explanation = getExplanation(toolName, filePath);
31276
31777
  const { preview, hasMore, totalLines } = truncateToClaudeStyle(entry.content || "");
31277
- const useClaudeCodeFormat = verbosityLevel === "quiet" && brevitySummary.hasContent;
31778
+ const useClaudeCodeFormat = verbosityLevel === "quiet" && brevitySummary.hasContent && !brevitySummary.metadata.isColoredDiff;
31779
+ const forceShowColoredDiff = brevitySummary.metadata.isColoredDiff;
31278
31780
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
31279
31781
  /* @__PURE__ */ jsxs(Box, { children: [
31280
31782
  /* @__PURE__ */ jsx(Text, { color: "gray", children: "\u23BF" }),
@@ -31308,8 +31810,8 @@ function ToolCallEntry({ entry, verbosityLevel, explainLevel }) {
31308
31810
  ] }) : /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
31309
31811
  /* @__PURE__ */ jsx(Text, { color: "gray", children: "\u23BF File contents:" }),
31310
31812
  /* @__PURE__ */ jsx(Box, { marginLeft: 2, flexDirection: "column", children: /* @__PURE__ */ jsx(FileContentRenderer, { content: entry.content }) })
31311
- ] }) }) : shouldShowDiff && shouldShowFullContent ? (
31312
- // 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)
31313
31815
  /* @__PURE__ */ jsxs(Text, { color: "gray", children: [
31314
31816
  "\u23BF ",
31315
31817
  entry.content.split("\n")[0]
@@ -31331,7 +31833,7 @@ function ToolCallEntry({ entry, verbosityLevel, explainLevel }) {
31331
31833
  /* @__PURE__ */ jsx(Text, { color: "gray", children: "\u23BF " }),
31332
31834
  /* @__PURE__ */ jsx(Box, { flexDirection: "column", width: "100%", children: /* @__PURE__ */ jsx(MarkdownRenderer, { content: formatToolContent(entry.content, toolName) }) })
31333
31835
  ] }) }),
31334
- 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(
31335
31837
  DiffRenderer,
31336
31838
  {
31337
31839
  diffContent: entry.content,
@@ -31345,6 +31847,7 @@ var truncateContent2;
31345
31847
  var init_tool_call_entry = __esm({
31346
31848
  "src/ui/components/chat-entries/tool-call-entry.tsx"() {
31347
31849
  init_diff_renderer();
31850
+ init_colored_diff_renderer();
31348
31851
  init_file_content_renderer();
31349
31852
  init_tool_brevity_service();
31350
31853
  init_markdown_renderer();
@@ -32384,10 +32887,11 @@ var init_session_logger = __esm({
32384
32887
  SessionLogger = class {
32385
32888
  constructor() {
32386
32889
  this.sessionId = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
32387
- this.logPath = path8__default.join(process.cwd(), `session-${this.sessionId}.log`);
32890
+ this.logPath = path8__default.join(process.cwd(), "logs", `session-${this.sessionId}.log`);
32388
32891
  this.init();
32389
32892
  }
32390
32893
  init() {
32894
+ fs7__default.mkdirSync(path8__default.dirname(this.logPath), { recursive: true });
32391
32895
  const header = `
32392
32896
  =================================================================
32393
32897
  \u{1F9EA} GROK ONE-SHOT TESTING SESSION
@@ -32896,7 +33400,7 @@ var require_package = __commonJS({
32896
33400
  module.exports = {
32897
33401
  type: "module",
32898
33402
  name: "@xagent/one-shot",
32899
- version: "1.2.10",
33403
+ version: "1.2.12",
32900
33404
  description: "An open-source AI agent that brings advanced AI capabilities directly into your terminal with automatic documentation updates.",
32901
33405
  main: "dist/index.js",
32902
33406
  module: "dist/index.js",
@@ -32932,6 +33436,9 @@ var require_package = __commonJS({
32932
33436
  "test:tools": "./scripts/test-all-tools.sh",
32933
33437
  "test:tools:quick": "./scripts/test-tools-quick.sh",
32934
33438
  "test:tools:validate": "./scripts/validate-tool-code.sh",
33439
+ "security:review": "npm audit && npm outdated",
33440
+ "security:critical": "npm audit --audit-level=critical",
33441
+ "security:report": "npm audit --json > security-report.json || true",
32935
33442
  prepare: "husky install",
32936
33443
  "dev:site": "cd apps/site && npm run start",
32937
33444
  "build:site": "cd apps/site && npm run build",
@@ -32980,6 +33487,7 @@ var require_package = __commonJS({
32980
33487
  chokidar: "^4.0.3",
32981
33488
  "cli-highlight": "^2.1.11",
32982
33489
  commander: "^12.0.0",
33490
+ diff: "^8.0.2",
32983
33491
  dotenv: "^16.4.0",
32984
33492
  enquirer: "^2.4.1",
32985
33493
  "fs-extra": "^11.2.0",