@xagent/x-cli 1.1.74 → 1.1.75

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
@@ -16,12 +16,13 @@ import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js'
16
16
  import axios from 'axios';
17
17
  import { exec, execSync, spawn } from 'child_process';
18
18
  import { promisify } from 'util';
19
- import fs4, { writeFile } from 'fs/promises';
19
+ import fs6, { writeFile } from 'fs/promises';
20
20
  import * as ops6 from 'fs-extra';
21
21
  import { parse } from '@typescript-eslint/typescript-estree';
22
22
  import Fuse from 'fuse.js';
23
23
  import { glob } from 'glob';
24
24
  import { encoding_for_model, get_encoding } from 'tiktoken';
25
+ import * as readline from 'readline';
25
26
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
26
27
  import { marked } from 'marked';
27
28
  import TerminalRenderer from 'marked-terminal';
@@ -336,6 +337,131 @@ var init_config = __esm({
336
337
  PREDEFINED_SERVERS = {};
337
338
  }
338
339
  });
340
+
341
+ // src/utils/context-loader.ts
342
+ var context_loader_exports = {};
343
+ __export(context_loader_exports, {
344
+ formatContextStatus: () => formatContextStatus,
345
+ loadContext: () => loadContext
346
+ });
347
+ function loadMarkdownDirectory(dirPath) {
348
+ if (!fs__default.existsSync(dirPath)) {
349
+ return "";
350
+ }
351
+ const files = fs__default.readdirSync(dirPath).filter((file) => file.endsWith(".md")).sort();
352
+ let content = "";
353
+ for (const file of files) {
354
+ const filePath = path7__default.join(dirPath, file);
355
+ try {
356
+ const fileContent = fs__default.readFileSync(filePath, "utf-8");
357
+ content += `
358
+
359
+ === ${file} ===
360
+
361
+ ${fileContent}`;
362
+ } catch (error) {
363
+ console.warn(`Failed to read ${filePath}:`, error);
364
+ }
365
+ }
366
+ return content;
367
+ }
368
+ function extractDateFromFilename(filename) {
369
+ const match = filename.match(/^(\d{4}-\d{2}-\d{2})/);
370
+ if (match) {
371
+ return new Date(match[1]);
372
+ }
373
+ return /* @__PURE__ */ new Date(0);
374
+ }
375
+ function summarizeContent(content, maxLength = MAX_SUMMARY_LENGTH) {
376
+ if (content.length <= maxLength) {
377
+ return content;
378
+ }
379
+ const truncated = content.substring(0, maxLength);
380
+ const lastNewline = truncated.lastIndexOf("\n\n");
381
+ if (lastNewline > maxLength * 0.8) {
382
+ return truncated.substring(0, lastNewline);
383
+ }
384
+ return truncated + "\n\n[...content truncated for context budget...]";
385
+ }
386
+ function loadTaskFiles(tasksDir, maxBudget) {
387
+ if (!fs__default.existsSync(tasksDir)) {
388
+ return [];
389
+ }
390
+ const files = fs__default.readdirSync(tasksDir).filter((file) => file.endsWith(".md")).map((filename) => {
391
+ const filePath = path7__default.join(tasksDir, filename);
392
+ const content = fs__default.readFileSync(filePath, "utf-8");
393
+ return {
394
+ filename,
395
+ content,
396
+ size: Buffer.byteLength(content, "utf-8"),
397
+ date: extractDateFromFilename(filename),
398
+ isSummarized: false
399
+ };
400
+ }).sort((a, b) => b.date.getTime() - a.date.getTime());
401
+ const result = [];
402
+ let usedBudget = 0;
403
+ for (const file of files) {
404
+ let finalContent = file.content;
405
+ let isSummarized = false;
406
+ if (usedBudget + file.size > maxBudget) {
407
+ finalContent = summarizeContent(file.content);
408
+ const summarizedSize = Buffer.byteLength(finalContent, "utf-8");
409
+ if (usedBudget + summarizedSize > maxBudget) {
410
+ continue;
411
+ }
412
+ usedBudget += summarizedSize;
413
+ isSummarized = true;
414
+ } else {
415
+ usedBudget += file.size;
416
+ }
417
+ result.push({
418
+ ...file,
419
+ content: finalContent,
420
+ isSummarized
421
+ });
422
+ }
423
+ return result;
424
+ }
425
+ function loadContext(agentDir = ".agent") {
426
+ const systemContent = loadMarkdownDirectory(path7__default.join(agentDir, "system"));
427
+ const sopContent = loadMarkdownDirectory(path7__default.join(agentDir, "sop"));
428
+ const systemSize = Buffer.byteLength(systemContent, "utf-8");
429
+ const sopSize = Buffer.byteLength(sopContent, "utf-8");
430
+ const taskBudget = Math.max(0, CONTEXT_BUDGET_BYTES - systemSize - sopSize);
431
+ const tasks = loadTaskFiles(path7__default.join(agentDir, "tasks"), taskBudget);
432
+ const totalSize = systemSize + sopSize + tasks.reduce((sum, task) => sum + Buffer.byteLength(task.content, "utf-8"), 0);
433
+ const warnings = [];
434
+ if (totalSize > CONTEXT_BUDGET_BYTES) {
435
+ warnings.push(`Context size (${(totalSize / 1024).toFixed(1)}KB) exceeds budget (${CONTEXT_BUDGET_BYTES / 1024}KB)`);
436
+ }
437
+ return {
438
+ system: systemContent,
439
+ sop: sopContent,
440
+ tasks,
441
+ totalSize,
442
+ warnings
443
+ };
444
+ }
445
+ function formatContextStatus(pack) {
446
+ const taskCount = pack.tasks.length;
447
+ const summarizedCount = pack.tasks.filter((t) => t.isSummarized).length;
448
+ const sizeKB = (pack.totalSize / 1024).toFixed(1);
449
+ let status = `[x-cli] Context: loaded system docs, sop docs, ${taskCount} task docs (~${sizeKB} KB).`;
450
+ if (summarizedCount > 0) {
451
+ status += ` Summarized ${summarizedCount} older tasks for context budget.`;
452
+ }
453
+ if (pack.warnings.length > 0) {
454
+ status += ` Warnings: ${pack.warnings.join("; ")}`;
455
+ }
456
+ return status;
457
+ }
458
+ var CONTEXT_BUDGET_BYTES, MAX_SUMMARY_LENGTH;
459
+ var init_context_loader = __esm({
460
+ "src/utils/context-loader.ts"() {
461
+ CONTEXT_BUDGET_BYTES = 280 * 1024;
462
+ MAX_SUMMARY_LENGTH = 2e3;
463
+ }
464
+ });
339
465
  var GrokClient = class {
340
466
  constructor(apiKey, model, baseURL) {
341
467
  this.currentModel = "grok-code-fast-1";
@@ -629,7 +755,7 @@ var MCPManager = class extends EventEmitter {
629
755
  this.transports.set(config2.name, transport);
630
756
  const client = new Client(
631
757
  {
632
- name: "grok-cli",
758
+ name: "x-cli",
633
759
  version: "1.0.0"
634
760
  },
635
761
  {
@@ -5300,7 +5426,7 @@ var OperationHistoryTool = class {
5300
5426
  files: fileSnapshots
5301
5427
  },
5302
5428
  metadata: {
5303
- tool: "grok-cli",
5429
+ tool: "x-cli",
5304
5430
  filesAffected: files,
5305
5431
  operationSize: this.determineOperationSize(files, rollbackData),
5306
5432
  ...metadata
@@ -5585,7 +5711,7 @@ This action cannot be undone.`
5585
5711
  }
5586
5712
  }
5587
5713
  snapshots.push(snapshot);
5588
- } catch (error) {
5714
+ } catch (_error) {
5589
5715
  snapshots.push({
5590
5716
  filePath: path7.resolve(filePath),
5591
5717
  existed: false
@@ -5676,7 +5802,7 @@ This action cannot be undone.`
5676
5802
  /**
5677
5803
  * Perform redo operation
5678
5804
  */
5679
- async performRedo(entry) {
5805
+ async performRedo(_entry) {
5680
5806
  return {
5681
5807
  success: false,
5682
5808
  error: "Redo functionality requires storing forward changes - not yet implemented"
@@ -5735,7 +5861,7 @@ ${errors.join("\n")}`;
5735
5861
  /**
5736
5862
  * Undo refactor operation
5737
5863
  */
5738
- async undoRefactorOperation(fileSnapshots, customData) {
5864
+ async undoRefactorOperation(fileSnapshots, _customData) {
5739
5865
  return await this.undoFileOperations(fileSnapshots);
5740
5866
  }
5741
5867
  /**
@@ -5808,7 +5934,7 @@ ${errors.join("\n")}`;
5808
5934
  /**
5809
5935
  * Determine operation size
5810
5936
  */
5811
- determineOperationSize(files, rollbackData) {
5937
+ determineOperationSize(files, _rollbackData) {
5812
5938
  if (files.length <= 3) return "small";
5813
5939
  if (files.length <= 10) return "medium";
5814
5940
  return "large";
@@ -5854,7 +5980,7 @@ ${errors.join("\n")}`;
5854
5980
  }));
5855
5981
  this.currentPosition = parsed.currentPosition || this.history.length - 1;
5856
5982
  }
5857
- } catch (error) {
5983
+ } catch (_error) {
5858
5984
  this.history = [];
5859
5985
  this.currentPosition = -1;
5860
5986
  }
@@ -5871,7 +5997,7 @@ ${errors.join("\n")}`;
5871
5997
  lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
5872
5998
  };
5873
5999
  await ops6.promises.writeFile(this.historyFile, JSON.stringify(data, null, 2), "utf-8");
5874
- } catch (error) {
6000
+ } catch (_error) {
5875
6001
  }
5876
6002
  }
5877
6003
  /**
@@ -7040,10 +7166,10 @@ var DependencyAnalyzerTool = class {
7040
7166
  const circularDeps = [];
7041
7167
  const visited = /* @__PURE__ */ new Set();
7042
7168
  const visiting = /* @__PURE__ */ new Set();
7043
- const dfs = (filePath, path31) => {
7169
+ const dfs = (filePath, path32) => {
7044
7170
  if (visiting.has(filePath)) {
7045
- const cycleStart = path31.indexOf(filePath);
7046
- const cycle = path31.slice(cycleStart).concat([filePath]);
7171
+ const cycleStart = path32.indexOf(filePath);
7172
+ const cycle = path32.slice(cycleStart).concat([filePath]);
7047
7173
  circularDeps.push({
7048
7174
  cycle: cycle.map((fp) => graph.nodes.get(fp)?.filePath || fp),
7049
7175
  severity: cycle.length <= 2 ? "error" : "warning",
@@ -7059,7 +7185,7 @@ var DependencyAnalyzerTool = class {
7059
7185
  if (node) {
7060
7186
  for (const dependency of node.dependencies) {
7061
7187
  if (graph.nodes.has(dependency)) {
7062
- dfs(dependency, [...path31, filePath]);
7188
+ dfs(dependency, [...path32, filePath]);
7063
7189
  }
7064
7190
  }
7065
7191
  }
@@ -8435,89 +8561,955 @@ ${body}
8435
8561
  default: true
8436
8562
  }
8437
8563
  },
8438
- required: ["operation"]
8564
+ required: ["operation"]
8565
+ };
8566
+ }
8567
+ };
8568
+ var TokenCounter = class {
8569
+ constructor(model = "gpt-4") {
8570
+ try {
8571
+ this.encoder = encoding_for_model(model);
8572
+ } catch {
8573
+ this.encoder = get_encoding("cl100k_base");
8574
+ }
8575
+ }
8576
+ /**
8577
+ * Count tokens in a string
8578
+ */
8579
+ countTokens(text) {
8580
+ if (!text) return 0;
8581
+ return this.encoder.encode(text).length;
8582
+ }
8583
+ /**
8584
+ * Count tokens in messages array (for chat completions)
8585
+ */
8586
+ countMessageTokens(messages) {
8587
+ let totalTokens = 0;
8588
+ for (const message of messages) {
8589
+ totalTokens += 3;
8590
+ if (message.content && typeof message.content === "string") {
8591
+ totalTokens += this.countTokens(message.content);
8592
+ }
8593
+ if (message.role) {
8594
+ totalTokens += this.countTokens(message.role);
8595
+ }
8596
+ if (message.tool_calls) {
8597
+ totalTokens += this.countTokens(JSON.stringify(message.tool_calls));
8598
+ }
8599
+ }
8600
+ totalTokens += 3;
8601
+ return totalTokens;
8602
+ }
8603
+ /**
8604
+ * Estimate tokens for streaming content
8605
+ * This is an approximation since we don't have the full response yet
8606
+ */
8607
+ estimateStreamingTokens(accumulatedContent) {
8608
+ return this.countTokens(accumulatedContent);
8609
+ }
8610
+ /**
8611
+ * Clean up resources
8612
+ */
8613
+ dispose() {
8614
+ this.encoder.free();
8615
+ }
8616
+ };
8617
+ function formatTokenCount(count) {
8618
+ if (count <= 999) {
8619
+ return count.toString();
8620
+ }
8621
+ if (count < 1e6) {
8622
+ const k = count / 1e3;
8623
+ return k % 1 === 0 ? `${k}k` : `${k.toFixed(1)}k`;
8624
+ }
8625
+ const m = count / 1e6;
8626
+ return m % 1 === 0 ? `${m}m` : `${m.toFixed(1)}m`;
8627
+ }
8628
+ function createTokenCounter(model) {
8629
+ return new TokenCounter(model);
8630
+ }
8631
+ function loadCustomInstructions(workingDirectory = process.cwd()) {
8632
+ try {
8633
+ const instructionsPath = path7.join(workingDirectory, ".grok", "GROK.md");
8634
+ if (!fs.existsSync(instructionsPath)) {
8635
+ return null;
8636
+ }
8637
+ const customInstructions = fs.readFileSync(instructionsPath, "utf-8");
8638
+ return customInstructions.trim();
8639
+ } catch (error) {
8640
+ console.warn("Failed to load custom instructions:", error);
8641
+ return null;
8642
+ }
8643
+ }
8644
+
8645
+ // src/agent/grok-agent.ts
8646
+ init_settings_manager();
8647
+ var DEFAULT_OPTIONS = {
8648
+ createPatches: true,
8649
+ createBackups: true,
8650
+ gitCommit: true,
8651
+ timeout: 3e5,
8652
+ // 5 minutes per step
8653
+ maxConcurrentSteps: 1
8654
+ };
8655
+ var ExecutionOrchestrator = class {
8656
+ constructor(agent, options = {}) {
8657
+ this.agent = agent;
8658
+ this.maxRecoveryAttempts = 3;
8659
+ this.recoveryAttempts = /* @__PURE__ */ new Map();
8660
+ this.options = { ...DEFAULT_OPTIONS, ...options };
8661
+ }
8662
+ /**
8663
+ * Execute a research plan's TODO items
8664
+ */
8665
+ async executePlan(plan) {
8666
+ console.log(`\u{1F680} Starting execution of ${plan.todo.length} tasks...`);
8667
+ console.log(`Summary: ${plan.summary}`);
8668
+ const executionPlan = {
8669
+ steps: plan.todo.map((todo, index) => ({
8670
+ id: index + 1,
8671
+ description: todo,
8672
+ status: "pending"
8673
+ })),
8674
+ totalSteps: plan.todo.length,
8675
+ completedSteps: 0,
8676
+ failedSteps: 0,
8677
+ startTime: /* @__PURE__ */ new Date(),
8678
+ summary: plan.summary
8679
+ };
8680
+ try {
8681
+ for (const step of executionPlan.steps) {
8682
+ await this.executeStep(step, executionPlan);
8683
+ if (step.status === "failed") {
8684
+ executionPlan.failedSteps++;
8685
+ } else {
8686
+ executionPlan.completedSteps++;
8687
+ }
8688
+ }
8689
+ executionPlan.endTime = /* @__PURE__ */ new Date();
8690
+ if (this.options.gitCommit && this.isGitRepository()) {
8691
+ try {
8692
+ executionPlan.gitCommitHash = await this.createGitCommit(executionPlan);
8693
+ } catch (error) {
8694
+ console.warn("[Execution] Failed to create git commit:", error);
8695
+ }
8696
+ }
8697
+ const success = executionPlan.failedSteps === 0;
8698
+ console.log(`\u2705 Execution ${success ? "completed" : "finished with errors"}: ${executionPlan.completedSteps}/${executionPlan.totalSteps} steps successful`);
8699
+ return {
8700
+ success,
8701
+ executionPlan
8702
+ };
8703
+ } catch (error) {
8704
+ executionPlan.endTime = /* @__PURE__ */ new Date();
8705
+ console.error("[Execution] Orchestration failed:", error);
8706
+ return {
8707
+ success: false,
8708
+ executionPlan,
8709
+ error: error instanceof Error ? error.message : "Unknown execution error"
8710
+ };
8711
+ }
8712
+ }
8713
+ /**
8714
+ * Execute a single step
8715
+ */
8716
+ async executeStep(step, _executionPlan) {
8717
+ step.status = "running";
8718
+ step.startTime = /* @__PURE__ */ new Date();
8719
+ console.log(`
8720
+ [x-cli] #${step.id} ${step.description} \u2026`);
8721
+ try {
8722
+ const beforeState = this.captureFileState();
8723
+ await this.agent.processUserMessage(step.description);
8724
+ await new Promise((resolve8) => setTimeout(resolve8, 1e3));
8725
+ const afterState = this.captureFileState();
8726
+ step.changes = this.calculateChanges(beforeState, afterState);
8727
+ await this.displayChanges(step);
8728
+ if (step.changes && step.changes.length > 0) {
8729
+ step.patchFile = await this.createPatchFile(step);
8730
+ await this.createBackups(step);
8731
+ }
8732
+ step.status = "completed";
8733
+ step.endTime = /* @__PURE__ */ new Date();
8734
+ console.log(`[x-cli] #${step.id} \u2713 Completed`);
8735
+ } catch (error) {
8736
+ step.status = "failed";
8737
+ step.endTime = /* @__PURE__ */ new Date();
8738
+ step.error = error instanceof Error ? error.message : "Unknown error";
8739
+ console.log(`[x-cli] #${step.id} \u2717 Failed: ${step.error}`);
8740
+ }
8741
+ }
8742
+ /**
8743
+ * Capture current file state (simplified - just track modification times)
8744
+ */
8745
+ captureFileState() {
8746
+ const state = /* @__PURE__ */ new Map();
8747
+ try {
8748
+ const walkDir = (dir) => {
8749
+ const files = fs.readdirSync(dir);
8750
+ for (const file of files) {
8751
+ const filePath = path7.join(dir, file);
8752
+ const stat = fs.statSync(filePath);
8753
+ if (stat.isDirectory() && !file.startsWith(".") && file !== "node_modules") {
8754
+ walkDir(filePath);
8755
+ } else if (stat.isFile()) {
8756
+ state.set(filePath, stat.mtime.getTime());
8757
+ }
8758
+ }
8759
+ };
8760
+ walkDir(".");
8761
+ } catch (error) {
8762
+ console.warn("[Execution] Failed to capture file state:", error);
8763
+ }
8764
+ return state;
8765
+ }
8766
+ /**
8767
+ * Calculate file changes between states
8768
+ */
8769
+ calculateChanges(before, after) {
8770
+ const changes = [];
8771
+ for (const [filePath, afterTime] of after) {
8772
+ const beforeTime = before.get(filePath);
8773
+ if (!beforeTime || beforeTime !== afterTime) {
8774
+ changes.push({
8775
+ filePath,
8776
+ changeType: beforeTime ? "modified" : "created"
8777
+ });
8778
+ }
8779
+ }
8780
+ for (const filePath of before.keys()) {
8781
+ if (!after.has(filePath)) {
8782
+ changes.push({
8783
+ filePath,
8784
+ changeType: "deleted"
8785
+ });
8786
+ }
8787
+ }
8788
+ return changes;
8789
+ }
8790
+ /**
8791
+ * Display changes with diffs
8792
+ */
8793
+ async displayChanges(step) {
8794
+ if (!step.changes || step.changes.length === 0) {
8795
+ return;
8796
+ }
8797
+ console.log(`[x-cli] #${step.id} Changes detected:`);
8798
+ for (const change of step.changes) {
8799
+ console.log(` ${change.changeType.toUpperCase()}: ${change.filePath}`);
8800
+ if (change.changeType === "modified" && fs.existsSync(change.filePath)) {
8801
+ try {
8802
+ if (this.isGitRepository()) {
8803
+ const diff = execSync(`git diff --no-index /dev/null ${change.filePath} 2>/dev/null || git diff ${change.filePath}`, {
8804
+ encoding: "utf-8",
8805
+ timeout: 5e3
8806
+ }).trim();
8807
+ if (diff) {
8808
+ console.log(" Diff:");
8809
+ console.log(diff.split("\n").map((line) => ` ${line}`).join("\n"));
8810
+ }
8811
+ }
8812
+ } catch (_error) {
8813
+ }
8814
+ }
8815
+ }
8816
+ }
8817
+ /**
8818
+ * Create patch file for changes
8819
+ */
8820
+ async createPatchFile(step) {
8821
+ if (!this.options.createPatches || !step.changes || step.changes.length === 0) {
8822
+ return void 0;
8823
+ }
8824
+ try {
8825
+ const patchesDir = path7.join(__require("os").homedir(), ".xcli", "patches");
8826
+ if (!fs.existsSync(patchesDir)) {
8827
+ fs.mkdirSync(patchesDir, { recursive: true });
8828
+ }
8829
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
8830
+ const patchFile = path7.join(patchesDir, `step-${step.id}-${timestamp}.patch`);
8831
+ let patchContent = `# Patch for step #${step.id}: ${step.description}
8832
+ `;
8833
+ patchContent += `# Generated: ${(/* @__PURE__ */ new Date()).toISOString()}
8834
+
8835
+ `;
8836
+ for (const change of step.changes) {
8837
+ if (change.changeType === "modified" && fs.existsSync(change.filePath)) {
8838
+ try {
8839
+ const diff = execSync(`git diff ${change.filePath}`, {
8840
+ encoding: "utf-8",
8841
+ timeout: 5e3
8842
+ });
8843
+ patchContent += `--- a/${change.filePath}
8844
+ +++ b/${change.filePath}
8845
+ ${diff}
8846
+ `;
8847
+ } catch {
8848
+ }
8849
+ }
8850
+ }
8851
+ fs.writeFileSync(patchFile, patchContent);
8852
+ console.log(`[x-cli] #${step.id} Patch saved: ${patchFile}`);
8853
+ return patchFile;
8854
+ } catch (error) {
8855
+ console.warn(`[Execution] Failed to create patch for step ${step.id}:`, error);
8856
+ return void 0;
8857
+ }
8858
+ }
8859
+ /**
8860
+ * Create backup files
8861
+ */
8862
+ async createBackups(step) {
8863
+ if (!this.options.createBackups || !step.changes) {
8864
+ return;
8865
+ }
8866
+ for (const change of step.changes) {
8867
+ if ((change.changeType === "modified" || change.changeType === "created") && fs.existsSync(change.filePath)) {
8868
+ try {
8869
+ const backupPath = `${change.filePath}.bak`;
8870
+ fs.copyFileSync(change.filePath, backupPath);
8871
+ change.backupPath = backupPath;
8872
+ console.log(`[x-cli] #${step.id} Backup created: ${backupPath}`);
8873
+ } catch (_error) {
8874
+ console.warn(`[Execution] Failed to create backup for ${change.filePath}:`, _error);
8875
+ }
8876
+ }
8877
+ }
8878
+ }
8879
+ /**
8880
+ * Check if current directory is a git repository
8881
+ */
8882
+ isGitRepository() {
8883
+ try {
8884
+ execSync("git rev-parse --git-dir", { stdio: "ignore" });
8885
+ return true;
8886
+ } catch {
8887
+ return false;
8888
+ }
8889
+ }
8890
+ /**
8891
+ * Create git commit for all changes
8892
+ */
8893
+ async createGitCommit(executionPlan) {
8894
+ try {
8895
+ execSync("git add .", { stdio: "ignore" });
8896
+ const commitMessage = `feat: ${executionPlan.summary}
8897
+
8898
+ Executed ${executionPlan.totalSteps} tasks:
8899
+ ${executionPlan.steps.map((step) => `- ${step.description}`).join("\n")}
8900
+
8901
+ Auto-generated by x-cli execution orchestrator`;
8902
+ execSync(`git commit -m "${commitMessage}"`, { stdio: "ignore" });
8903
+ const hash = execSync("git rev-parse HEAD", { encoding: "utf-8" }).trim();
8904
+ console.log(`\u2705 Git commit created: ${hash.substring(0, 8)}`);
8905
+ return hash;
8906
+ } catch (error) {
8907
+ throw new Error(`Git commit failed: ${error instanceof Error ? error.message : "Unknown error"}`);
8908
+ }
8909
+ }
8910
+ /**
8911
+ * Detect error patterns in step execution
8912
+ */
8913
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
8914
+ detectError(error) {
8915
+ const errorMessage = error instanceof Error ? error.message : String(error);
8916
+ if (errorMessage.includes("test") && (errorMessage.includes("fail") || errorMessage.includes("error"))) {
8917
+ return {
8918
+ stepId: -1,
8919
+ // Will be set by caller
8920
+ errorType: "test_failure",
8921
+ errorMessage,
8922
+ stackTrace: error instanceof Error ? error.stack : void 0,
8923
+ affectedFiles: this.findTestFiles(),
8924
+ contextData: { pattern: "test_failure" }
8925
+ };
8926
+ }
8927
+ if (errorMessage.includes("build") && (errorMessage.includes("fail") || errorMessage.includes("error"))) {
8928
+ return {
8929
+ stepId: -1,
8930
+ errorType: "build_failure",
8931
+ errorMessage,
8932
+ stackTrace: error instanceof Error ? error.stack : void 0,
8933
+ affectedFiles: this.findBuildFiles(),
8934
+ contextData: { pattern: "build_failure" }
8935
+ };
8936
+ }
8937
+ if (errorMessage.includes("lint") && (errorMessage.includes("fail") || errorMessage.includes("error"))) {
8938
+ return {
8939
+ stepId: -1,
8940
+ errorType: "lint_failure",
8941
+ errorMessage,
8942
+ stackTrace: error instanceof Error ? error.stack : void 0,
8943
+ affectedFiles: this.findSourceFiles(),
8944
+ contextData: { pattern: "lint_failure" }
8945
+ };
8946
+ }
8947
+ return {
8948
+ stepId: -1,
8949
+ errorType: "runtime_error",
8950
+ errorMessage,
8951
+ stackTrace: error instanceof Error ? error.stack : void 0,
8952
+ affectedFiles: [],
8953
+ contextData: { pattern: "runtime_error" }
8954
+ };
8955
+ }
8956
+ /**
8957
+ * Find test files in the project
8958
+ */
8959
+ findTestFiles() {
8960
+ try {
8961
+ const testFiles = [];
8962
+ const walkDir = (dir) => {
8963
+ const files = fs.readdirSync(dir);
8964
+ for (const file of files) {
8965
+ const filePath = path7.join(dir, file);
8966
+ const stat = fs.statSync(filePath);
8967
+ if (stat.isDirectory() && !file.startsWith(".") && file !== "node_modules") {
8968
+ walkDir(filePath);
8969
+ } else if (stat.isFile() && (file.includes("test") || file.includes("spec"))) {
8970
+ testFiles.push(filePath);
8971
+ }
8972
+ }
8973
+ };
8974
+ walkDir(".");
8975
+ return testFiles.slice(0, 10);
8976
+ } catch {
8977
+ return [];
8978
+ }
8979
+ }
8980
+ /**
8981
+ * Find build configuration files
8982
+ */
8983
+ findBuildFiles() {
8984
+ const buildFiles = ["package.json", "tsconfig.json", "webpack.config.js", "babel.config.js"];
8985
+ return buildFiles.filter((file) => fs.existsSync(file));
8986
+ }
8987
+ /**
8988
+ * Find source files
8989
+ */
8990
+ findSourceFiles() {
8991
+ try {
8992
+ const sourceFiles = [];
8993
+ const walkDir = (dir) => {
8994
+ const files = fs.readdirSync(dir);
8995
+ for (const file of files) {
8996
+ const filePath = path7.join(dir, file);
8997
+ const stat = fs.statSync(filePath);
8998
+ if (stat.isDirectory() && !file.startsWith(".") && file !== "node_modules") {
8999
+ walkDir(filePath);
9000
+ } else if (stat.isFile() && (file.endsWith(".ts") || file.endsWith(".js") || file.endsWith(".tsx") || file.endsWith(".jsx"))) {
9001
+ sourceFiles.push(filePath);
9002
+ }
9003
+ }
9004
+ };
9005
+ walkDir(".");
9006
+ return sourceFiles.slice(0, 20);
9007
+ } catch {
9008
+ return [];
9009
+ }
9010
+ }
9011
+ /**
9012
+ * Present error context to user
9013
+ */
9014
+ presentErrorContext(errorContext, _step) {
9015
+ console.log("\n" + "=".repeat(60));
9016
+ console.log("\u{1F6A8} ISSUE ENCOUNTERED");
9017
+ console.log("=".repeat(60));
9018
+ console.log(`[x-cli] Issue encountered: ${errorContext.errorMessage}`);
9019
+ if (errorContext.affectedFiles.length > 0) {
9020
+ console.log(`Affected files: ${errorContext.affectedFiles.slice(0, 5).join(", ")}`);
9021
+ if (errorContext.affectedFiles.length > 5) {
9022
+ console.log(`... and ${errorContext.affectedFiles.length - 5} more`);
9023
+ }
9024
+ }
9025
+ console.log("\n\u{1F504} Initiating adaptive recovery...");
9026
+ }
9027
+ /**
9028
+ * Handle recovery flow
9029
+ */
9030
+ async handleRecovery(originalRequest, errorContext, executionPlan, researchService) {
9031
+ const attempts = this.recoveryAttempts.get(errorContext.stepId) || 0;
9032
+ if (attempts >= this.maxRecoveryAttempts) {
9033
+ console.log(`\u274C Maximum recovery attempts (${this.maxRecoveryAttempts}) reached for step ${errorContext.stepId}`);
9034
+ return { approved: false, maxRetriesExceeded: true };
9035
+ }
9036
+ this.recoveryAttempts.set(errorContext.stepId, attempts + 1);
9037
+ const recoveryRequest = {
9038
+ userTask: `Recovery from execution error: ${errorContext.errorMessage}
9039
+
9040
+ Original task: ${originalRequest.userTask}
9041
+
9042
+ Error context:
9043
+ - Type: ${errorContext.errorType}
9044
+ - Message: ${errorContext.errorMessage}
9045
+ - Affected files: ${errorContext.affectedFiles.join(", ")}
9046
+
9047
+ Please provide a recovery plan to resolve this issue and continue execution.`,
9048
+ context: `This is a RECOVERY REQUEST for a failed execution step. The original task was part of a larger plan that encountered an error. Focus on fixing the specific issue and providing steps to resolve it.`,
9049
+ constraints: [
9050
+ "Focus on fixing the specific error encountered",
9051
+ "Provide actionable recovery steps",
9052
+ "Consider the broader execution context",
9053
+ "Ensure recovery steps are safe and reversible"
9054
+ ]
9055
+ };
9056
+ try {
9057
+ console.log("\u{1F50D} Analyzing error and generating recovery plan...");
9058
+ const { output, approval } = await researchService.researchAndGetApproval(recoveryRequest);
9059
+ if (approval.approved) {
9060
+ console.log("\u2705 Recovery plan approved. Resuming execution...");
9061
+ return { approved: true, recoveryPlan: output };
9062
+ } else {
9063
+ console.log("\u274C Recovery plan rejected by user.");
9064
+ return { approved: false };
9065
+ }
9066
+ } catch (error) {
9067
+ console.error("[Recovery] Failed to generate recovery plan:", error);
9068
+ return { approved: false };
9069
+ }
9070
+ }
9071
+ /**
9072
+ * Execute with adaptive recovery
9073
+ */
9074
+ async executeWithRecovery(plan, researchService, originalRequest) {
9075
+ console.log(`\u{1F680} Starting execution with adaptive recovery of ${plan.todo.length} tasks...`);
9076
+ console.log(`Summary: ${plan.summary}`);
9077
+ const executionPlan = {
9078
+ steps: plan.todo.map((todo, index) => ({
9079
+ id: index + 1,
9080
+ description: todo,
9081
+ status: "pending"
9082
+ })),
9083
+ totalSteps: plan.todo.length,
9084
+ completedSteps: 0,
9085
+ failedSteps: 0,
9086
+ startTime: /* @__PURE__ */ new Date(),
9087
+ summary: plan.summary
9088
+ };
9089
+ try {
9090
+ for (let i = 0; i < executionPlan.steps.length; i++) {
9091
+ const step = executionPlan.steps[i];
9092
+ try {
9093
+ await this.executeStep(step, executionPlan);
9094
+ if (step.status === "completed") {
9095
+ executionPlan.completedSteps++;
9096
+ } else {
9097
+ const errorContext = this.detectError(step.error);
9098
+ if (errorContext) {
9099
+ errorContext.stepId = step.id;
9100
+ this.presentErrorContext(errorContext, step);
9101
+ const recoveryResult = await this.handleRecovery(
9102
+ originalRequest,
9103
+ errorContext,
9104
+ executionPlan,
9105
+ researchService
9106
+ );
9107
+ if (recoveryResult.approved && recoveryResult.recoveryPlan) {
9108
+ const recoverySteps = recoveryResult.recoveryPlan.plan.todo.map((todo, idx) => ({
9109
+ id: executionPlan.steps.length + idx + 1,
9110
+ description: `[RECOVERY] ${todo}`,
9111
+ status: "pending"
9112
+ }));
9113
+ executionPlan.steps.splice(i + 1, 0, ...recoverySteps);
9114
+ executionPlan.totalSteps += recoverySteps.length;
9115
+ console.log(`\u{1F4CB} Added ${recoverySteps.length} recovery steps. Continuing execution...`);
9116
+ continue;
9117
+ }
9118
+ }
9119
+ executionPlan.failedSteps++;
9120
+ }
9121
+ } catch (error) {
9122
+ const errorContext = this.detectError(error);
9123
+ if (errorContext) {
9124
+ errorContext.stepId = step.id;
9125
+ step.status = "failed";
9126
+ step.error = errorContext.errorMessage;
9127
+ executionPlan.failedSteps++;
9128
+ console.log(`[x-cli] #${step.id} \u2717 Failed: ${errorContext.errorMessage}`);
9129
+ }
9130
+ }
9131
+ }
9132
+ executionPlan.endTime = /* @__PURE__ */ new Date();
9133
+ if (this.options.gitCommit && this.isGitRepository()) {
9134
+ try {
9135
+ executionPlan.gitCommitHash = await this.createGitCommit(executionPlan);
9136
+ } catch (error) {
9137
+ console.warn("[Execution] Failed to create git commit:", error);
9138
+ }
9139
+ }
9140
+ const success = executionPlan.failedSteps === 0;
9141
+ console.log(`\u2705 Execution ${success ? "completed" : "finished with errors"}: ${executionPlan.completedSteps}/${executionPlan.totalSteps} steps successful`);
9142
+ return {
9143
+ success,
9144
+ executionPlan
9145
+ };
9146
+ } catch (error) {
9147
+ executionPlan.endTime = /* @__PURE__ */ new Date();
9148
+ console.error("[Execution] Orchestration failed:", error);
9149
+ return {
9150
+ success: false,
9151
+ executionPlan,
9152
+ error: error instanceof Error ? error.message : "Unknown execution error"
9153
+ };
9154
+ }
9155
+ }
9156
+ };
9157
+ var DEFAULT_CONFIG = {
9158
+ maxOptions: 3,
9159
+ includeContext: true,
9160
+ timeout: 6e4
9161
+ // 60 seconds
9162
+ };
9163
+ var ResearchRecommendService = class {
9164
+ constructor(agent, config2 = DEFAULT_CONFIG) {
9165
+ this.agent = agent;
9166
+ this.config = config2;
9167
+ }
9168
+ /**
9169
+ * Perform research and generate recommendation
9170
+ */
9171
+ async researchAndRecommend(request, contextPack) {
9172
+ const prompt = this.buildResearchPrompt(request, contextPack);
9173
+ try {
9174
+ const response = await this.agent.processUserMessage(prompt);
9175
+ return this.parseResearchOutput(response);
9176
+ } catch (error) {
9177
+ console.error("[ResearchRecommend] Research failed:", error);
9178
+ throw new Error(`Research failed: ${error instanceof Error ? error.message : "Unknown error"}`);
9179
+ }
9180
+ }
9181
+ /**
9182
+ * Build the research prompt
9183
+ */
9184
+ buildResearchPrompt(request, contextPack) {
9185
+ let prompt = `Analyze the following task and provide a structured research output in JSON format.
9186
+
9187
+ TASK: ${request.userTask}
9188
+
9189
+ `;
9190
+ if (request.constraints && request.constraints.length > 0) {
9191
+ prompt += `CONSTRAINTS:
9192
+ ${request.constraints.map((c) => `- ${c}`).join("\n")}
9193
+
9194
+ `;
9195
+ }
9196
+ if (request.preferences && request.preferences.length > 0) {
9197
+ prompt += `PREFERENCES:
9198
+ ${request.preferences.map((p) => `- ${p}`).join("\n")}
9199
+
9200
+ `;
9201
+ }
9202
+ if (this.config.includeContext && contextPack) {
9203
+ prompt += `CONTEXT INFORMATION:
9204
+ System Documentation:
9205
+ ${contextPack.system}
9206
+
9207
+ SOP Documentation:
9208
+ ${contextPack.sop}
9209
+
9210
+ Recent Task Documentation:
9211
+ ${contextPack.tasks.slice(0, 5).map((t) => `${t.filename}:
9212
+ ${t.content}`).join("\n\n")}
9213
+
9214
+ `;
9215
+ }
9216
+ prompt += `Please provide your analysis in the following JSON structure:
9217
+ {
9218
+ "issues": [
9219
+ {
9220
+ "type": "fact|gap|risk",
9221
+ "description": "Description of the issue",
9222
+ "severity": "low|medium|high",
9223
+ "impact": "Impact description (optional)"
9224
+ }
9225
+ ],
9226
+ "options": [
9227
+ {
9228
+ "id": 1,
9229
+ "title": "Option title",
9230
+ "description": "Detailed description",
9231
+ "tradeoffs": {
9232
+ "pros": ["pro1", "pro2"],
9233
+ "cons": ["con1", "con2"]
9234
+ },
9235
+ "effort": "low|medium|high",
9236
+ "risk": "low|medium|high"
9237
+ }
9238
+ ],
9239
+ "recommendation": {
9240
+ "optionId": 1,
9241
+ "reasoning": "Why this option is recommended",
9242
+ "justification": "Detailed justification",
9243
+ "confidence": "low|medium|high"
9244
+ },
9245
+ "plan": {
9246
+ "summary": "Brief summary of the plan",
9247
+ "approach": ["step1", "step2", "step3"],
9248
+ "todo": ["TODO item 1", "TODO item 2"],
9249
+ "estimatedEffort": "Time estimate",
9250
+ "keyConsiderations": ["consideration1", "consideration2"]
9251
+ }
9252
+ }
9253
+
9254
+ Provide exactly ${this.config.maxOptions} options. Focus on actionable, practical solutions. Be thorough but concise. Respond with ONLY the JSON.`;
9255
+ return prompt;
9256
+ }
9257
+ /**
9258
+ * Parse the AI response into structured output
9259
+ */
9260
+ parseResearchOutput(response) {
9261
+ let jsonText = "";
9262
+ if (Array.isArray(response)) {
9263
+ for (const entry of response) {
9264
+ if (entry.type === "assistant" && entry.content) {
9265
+ jsonText = entry.content.trim();
9266
+ break;
9267
+ }
9268
+ }
9269
+ } else if (typeof response === "string") {
9270
+ jsonText = response;
9271
+ }
9272
+ const jsonMatch = jsonText.match(/\{[\s\S]*\}/);
9273
+ if (!jsonMatch) {
9274
+ throw new Error("No JSON found in response");
9275
+ }
9276
+ try {
9277
+ const parsed = JSON.parse(jsonMatch[0]);
9278
+ return {
9279
+ issues: this.validateIssues(parsed.issues || []),
9280
+ options: this.validateOptions(parsed.options || []),
9281
+ recommendation: this.validateRecommendation(parsed.recommendation),
9282
+ plan: this.validatePlan(parsed.plan)
9283
+ };
9284
+ } catch (error) {
9285
+ console.error("[ResearchRecommend] JSON parse error:", error);
9286
+ console.error("Raw response:", jsonText);
9287
+ throw new Error("Failed to parse research output JSON");
9288
+ }
9289
+ }
9290
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
9291
+ validateIssues(issues) {
9292
+ return issues.map((issue) => ({
9293
+ type: ["fact", "gap", "risk"].includes(issue.type) ? issue.type : "fact",
9294
+ description: issue.description || "No description provided",
9295
+ severity: ["low", "medium", "high"].includes(issue.severity) ? issue.severity : "medium",
9296
+ impact: issue.impact
9297
+ }));
9298
+ }
9299
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
9300
+ validateOptions(options) {
9301
+ return options.slice(0, this.config.maxOptions).map((option, index) => ({
9302
+ id: option.id || index + 1,
9303
+ title: option.title || `Option ${index + 1}`,
9304
+ description: option.description || "No description provided",
9305
+ tradeoffs: {
9306
+ pros: Array.isArray(option.tradeoffs?.pros) ? option.tradeoffs.pros : [],
9307
+ cons: Array.isArray(option.tradeoffs?.cons) ? option.tradeoffs.cons : []
9308
+ },
9309
+ effort: ["low", "medium", "high"].includes(option.effort) ? option.effort : "medium",
9310
+ risk: ["low", "medium", "high"].includes(option.risk) ? option.risk : "medium"
9311
+ }));
9312
+ }
9313
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
9314
+ validateRecommendation(rec) {
9315
+ return {
9316
+ optionId: rec?.optionId || 1,
9317
+ reasoning: rec?.reasoning || "No reasoning provided",
9318
+ justification: rec?.justification || "No justification provided",
9319
+ confidence: ["low", "medium", "high"].includes(rec?.confidence) ? rec.confidence : "medium"
8439
9320
  };
8440
9321
  }
8441
- };
8442
- var TokenCounter = class {
8443
- constructor(model = "gpt-4") {
8444
- try {
8445
- this.encoder = encoding_for_model(model);
8446
- } catch {
8447
- this.encoder = get_encoding("cl100k_base");
9322
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
9323
+ validatePlan(plan) {
9324
+ return {
9325
+ summary: plan?.summary || "No summary provided",
9326
+ approach: Array.isArray(plan?.approach) ? plan.approach : [],
9327
+ todo: Array.isArray(plan?.todo) ? plan.todo : [],
9328
+ estimatedEffort: plan?.estimatedEffort || "Unknown",
9329
+ keyConsiderations: Array.isArray(plan?.keyConsiderations) ? plan.keyConsiderations : []
9330
+ };
9331
+ }
9332
+ /**
9333
+ * Render research output to console
9334
+ */
9335
+ renderToConsole(output) {
9336
+ console.log("\n" + "=".repeat(50));
9337
+ console.log("\u{1F916} RESEARCH & RECOMMENDATION");
9338
+ console.log("=".repeat(50));
9339
+ this.renderIssues(output.issues);
9340
+ this.renderOptions(output.options);
9341
+ this.renderRecommendation(output.recommendation, output.options);
9342
+ this.renderPlan(output.plan);
9343
+ console.log("=".repeat(50));
9344
+ }
9345
+ renderIssues(issues) {
9346
+ console.log("\n\u{1F4CB} ISSUES");
9347
+ console.log("-".repeat(20));
9348
+ if (issues.length === 0) {
9349
+ console.log("No issues identified.");
9350
+ return;
9351
+ }
9352
+ for (const issue of issues) {
9353
+ const icon = issue.type === "fact" ? "\u{1F4CA}" : issue.type === "gap" ? "\u26A0\uFE0F" : "\u{1F6A8}";
9354
+ const severity = issue.severity ? ` (${issue.severity.toUpperCase()})` : "";
9355
+ console.log(`${icon} ${issue.type.toUpperCase()}${severity}: ${issue.description}`);
9356
+ if (issue.impact) {
9357
+ console.log(` Impact: ${issue.impact}`);
9358
+ }
9359
+ }
9360
+ }
9361
+ renderOptions(options) {
9362
+ console.log("\n\u{1F3AF} OPTIONS");
9363
+ console.log("-".repeat(20));
9364
+ for (const option of options) {
9365
+ console.log(`
9366
+ ${option.id}) ${option.title}`);
9367
+ console.log(` ${option.description}`);
9368
+ console.log(` Effort: ${option.effort.toUpperCase()} | Risk: ${option.risk.toUpperCase()}`);
9369
+ if (option.tradeoffs.pros.length > 0) {
9370
+ console.log(` \u2705 Pros: ${option.tradeoffs.pros.join(", ")}`);
9371
+ }
9372
+ if (option.tradeoffs.cons.length > 0) {
9373
+ console.log(` \u274C Cons: ${option.tradeoffs.cons.join(", ")}`);
9374
+ }
9375
+ }
9376
+ }
9377
+ renderRecommendation(recommendation, options) {
9378
+ console.log("\n\u{1F3AF} RECOMMENDATION");
9379
+ console.log("-".repeat(20));
9380
+ const recommendedOption = options.find((o) => o.id === recommendation.optionId);
9381
+ const optionTitle = recommendedOption ? recommendedOption.title : `Option ${recommendation.optionId}`;
9382
+ console.log(`\u2192 ${optionTitle} (Confidence: ${recommendation.confidence.toUpperCase()})`);
9383
+ console.log(`Reasoning: ${recommendation.reasoning}`);
9384
+ console.log(`Justification: ${recommendation.justification}`);
9385
+ }
9386
+ renderPlan(plan) {
9387
+ console.log("\n\u{1F4DD} PLAN SUMMARY");
9388
+ console.log("-".repeat(20));
9389
+ console.log(`Summary: ${plan.summary}`);
9390
+ console.log(`Estimated Effort: ${plan.estimatedEffort}`);
9391
+ if (plan.approach.length > 0) {
9392
+ console.log("\nApproach:");
9393
+ plan.approach.forEach((step, index) => {
9394
+ console.log(` ${index + 1}. ${step}`);
9395
+ });
9396
+ }
9397
+ if (plan.todo.length > 0) {
9398
+ console.log("\nTODO:");
9399
+ plan.todo.forEach((item) => {
9400
+ console.log(` [ ] ${item}`);
9401
+ });
9402
+ }
9403
+ if (plan.keyConsiderations.length > 0) {
9404
+ console.log("\nKey Considerations:");
9405
+ plan.keyConsiderations.forEach((consideration) => {
9406
+ console.log(` \u2022 ${consideration}`);
9407
+ });
8448
9408
  }
8449
9409
  }
8450
9410
  /**
8451
- * Count tokens in a string
9411
+ * Prompt user for approval with Y/n/R options
8452
9412
  */
8453
- countTokens(text) {
8454
- if (!text) return 0;
8455
- return this.encoder.encode(text).length;
9413
+ async promptForApproval(_output) {
9414
+ return new Promise((resolve8) => {
9415
+ const rl = readline.createInterface({
9416
+ input: process.stdin,
9417
+ output: process.stdout
9418
+ });
9419
+ const promptUser = () => {
9420
+ console.log("\nProceed with recommendation? (Y/n) [R=revise]");
9421
+ rl.question("> ", (answer) => {
9422
+ const cleanAnswer = answer.trim().toLowerCase();
9423
+ if (cleanAnswer === "y" || cleanAnswer === "yes" || cleanAnswer === "") {
9424
+ rl.close();
9425
+ resolve8({ approved: true, revised: false });
9426
+ } else if (cleanAnswer === "n" || cleanAnswer === "no") {
9427
+ rl.close();
9428
+ resolve8({ approved: false, revised: false });
9429
+ } else if (cleanAnswer === "r" || cleanAnswer === "revise") {
9430
+ rl.question("Revision note (brief description of changes needed): ", (revisionNote) => {
9431
+ rl.close();
9432
+ resolve8({
9433
+ approved: false,
9434
+ revised: true,
9435
+ revisionNote: revisionNote.trim() || "User requested revision"
9436
+ });
9437
+ });
9438
+ } else {
9439
+ console.log("\u274C Invalid input. Please enter Y (yes), N (no), or R (revise).");
9440
+ promptUser();
9441
+ }
9442
+ });
9443
+ };
9444
+ promptUser();
9445
+ });
8456
9446
  }
8457
9447
  /**
8458
- * Count tokens in messages array (for chat completions)
9448
+ * Handle revision flow with updated request
8459
9449
  */
8460
- countMessageTokens(messages) {
8461
- let totalTokens = 0;
8462
- for (const message of messages) {
8463
- totalTokens += 3;
8464
- if (message.content && typeof message.content === "string") {
8465
- totalTokens += this.countTokens(message.content);
8466
- }
8467
- if (message.role) {
8468
- totalTokens += this.countTokens(message.role);
8469
- }
8470
- if (message.tool_calls) {
8471
- totalTokens += this.countTokens(JSON.stringify(message.tool_calls));
8472
- }
8473
- }
8474
- totalTokens += 3;
8475
- return totalTokens;
9450
+ async handleRevision(originalRequest, revisionNote, contextPack) {
9451
+ console.log(`\u{1F504} Revising based on: "${revisionNote}"`);
9452
+ console.log("\u{1F50D} Re-researching with revision context...");
9453
+ const revisedRequest = {
9454
+ ...originalRequest,
9455
+ constraints: [
9456
+ ...originalRequest.constraints || [],
9457
+ `REVISION REQUEST: ${revisionNote}`
9458
+ ]
9459
+ };
9460
+ return await this.researchAndRecommend(revisedRequest, contextPack);
8476
9461
  }
8477
9462
  /**
8478
- * Estimate tokens for streaming content
8479
- * This is an approximation since we don't have the full response yet
9463
+ * Full research and approval workflow with revision support
8480
9464
  */
8481
- estimateStreamingTokens(accumulatedContent) {
8482
- return this.countTokens(accumulatedContent);
9465
+ async researchAndGetApproval(request, contextPack, maxRevisions = 3) {
9466
+ let currentRequest = request;
9467
+ let revisions = 0;
9468
+ while (revisions <= maxRevisions) {
9469
+ console.log("\u{1F50D} Researching and analyzing...");
9470
+ const output = await this.researchAndRecommend(currentRequest, contextPack);
9471
+ this.renderToConsole(output);
9472
+ const approval = await this.promptForApproval(output);
9473
+ if (approval.approved || !approval.revised) {
9474
+ return { output, approval, revisions };
9475
+ }
9476
+ revisions++;
9477
+ if (revisions > maxRevisions) {
9478
+ console.log(`\u274C Maximum revisions (${maxRevisions}) reached.`);
9479
+ return { output, approval, revisions };
9480
+ }
9481
+ console.log(`\u{1F504} Revision ${revisions}/${maxRevisions}`);
9482
+ currentRequest = {
9483
+ ...request,
9484
+ constraints: [
9485
+ ...request.constraints || [],
9486
+ `REVISION ${revisions}: ${approval.revisionNote}`
9487
+ ]
9488
+ };
9489
+ }
9490
+ throw new Error("Unexpected end of revision loop");
8483
9491
  }
8484
9492
  /**
8485
- * Clean up resources
9493
+ * Complete workflow: Research → Recommend → Execute with Adaptive Recovery
8486
9494
  */
8487
- dispose() {
8488
- this.encoder.free();
9495
+ async researchRecommendExecute(request, contextPack, maxRevisions = 3) {
9496
+ const { output, approval, revisions } = await this.researchAndGetApproval(request, contextPack, maxRevisions);
9497
+ if (!approval.approved) {
9498
+ return { output, approval, revisions };
9499
+ }
9500
+ console.log("\n\u{1F680} Proceeding with execution (with adaptive recovery)...");
9501
+ const orchestrator = new ExecutionOrchestrator(this.agent);
9502
+ const execution = await orchestrator.executeWithRecovery(output.plan, this, request);
9503
+ return {
9504
+ output,
9505
+ approval,
9506
+ revisions,
9507
+ execution
9508
+ };
8489
9509
  }
8490
9510
  };
8491
- function formatTokenCount(count) {
8492
- if (count <= 999) {
8493
- return count.toString();
8494
- }
8495
- if (count < 1e6) {
8496
- const k = count / 1e3;
8497
- return k % 1 === 0 ? `${k}k` : `${k.toFixed(1)}k`;
8498
- }
8499
- const m = count / 1e6;
8500
- return m % 1 === 0 ? `${m}m` : `${m.toFixed(1)}m`;
8501
- }
8502
- function createTokenCounter(model) {
8503
- return new TokenCounter(model);
8504
- }
8505
- function loadCustomInstructions(workingDirectory = process.cwd()) {
8506
- try {
8507
- const instructionsPath = path7.join(workingDirectory, ".grok", "GROK.md");
8508
- if (!fs.existsSync(instructionsPath)) {
8509
- return null;
8510
- }
8511
- const customInstructions = fs.readFileSync(instructionsPath, "utf-8");
8512
- return customInstructions.trim();
8513
- } catch (error) {
8514
- console.warn("Failed to load custom instructions:", error);
8515
- return null;
8516
- }
8517
- }
8518
9511
 
8519
9512
  // src/agent/grok-agent.ts
8520
- init_settings_manager();
8521
9513
  var GrokAgent = class extends EventEmitter {
8522
9514
  constructor(apiKey, baseURL, model, maxToolRounds, contextPack) {
8523
9515
  super();
@@ -8688,7 +9680,83 @@ Current working directory: ${process.cwd()}`
8688
9680
  if (/(20\d{2})/.test(q)) return true;
8689
9681
  return false;
8690
9682
  }
9683
+ // Detect if message should use the Research → Recommend → Execute workflow
9684
+ shouldUseWorkflow(message) {
9685
+ const q = message.toLowerCase();
9686
+ const complexityIndicators = [
9687
+ // Action verbs indicating multi-step tasks
9688
+ /\b(implement|build|create|refactor|optimize|add|update|modify|develop|design)\b/.test(q),
9689
+ /\b(system|feature|component|module|service|api|database)\b/.test(q),
9690
+ // Multi-step indicators
9691
+ /\b(and|then|after|finally|also|additionally)\b/.test(q),
9692
+ /\b(step|phase|stage|part|component)\b/.test(q),
9693
+ // Size/complexity indicators
9694
+ q.length > 150,
9695
+ // Long requests
9696
+ (q.match(/\b(and|or|but|however|therefore|consequently)\b/g) || []).length >= 2,
9697
+ // Complex logic
9698
+ // Technical complexity
9699
+ /\b(authentication|authorization|security|validation|testing|deployment|ci.cd|docker|kubernetes)\b/.test(q),
9700
+ /\b(multiple|several|various|different|complex|advanced)\b/.test(q)
9701
+ ];
9702
+ const indicatorCount = complexityIndicators.filter(Boolean).length;
9703
+ return indicatorCount >= 2;
9704
+ }
8691
9705
  async processUserMessage(message) {
9706
+ if (this.shouldUseWorkflow(message)) {
9707
+ return this.processWithWorkflow(message);
9708
+ }
9709
+ return this.processStandard(message);
9710
+ }
9711
+ /**
9712
+ * Process complex tasks using the Research → Recommend → Execute workflow
9713
+ */
9714
+ async processWithWorkflow(message) {
9715
+ const userEntry = {
9716
+ type: "user",
9717
+ content: message,
9718
+ timestamp: /* @__PURE__ */ new Date()
9719
+ };
9720
+ this.chatHistory.push(userEntry);
9721
+ this.logEntry(userEntry);
9722
+ this.messages.push({ role: "user", content: message });
9723
+ try {
9724
+ const contextPack = await this.loadContextPack();
9725
+ const workflowService = new ResearchRecommendService(this);
9726
+ const request = {
9727
+ userTask: message,
9728
+ context: contextPack ? "Project context loaded" : void 0
9729
+ };
9730
+ console.log("\u{1F50D} Researching and analyzing...");
9731
+ const { output, approval, revisions } = await workflowService.researchAndGetApproval(request, contextPack);
9732
+ if (!approval.approved) {
9733
+ const rejectionEntry = {
9734
+ type: "assistant",
9735
+ content: approval.revised ? `Plan revised ${revisions} time(s) but ultimately rejected by user.` : "Plan rejected by user.",
9736
+ timestamp: /* @__PURE__ */ new Date()
9737
+ };
9738
+ this.chatHistory.push(rejectionEntry);
9739
+ return [userEntry, rejectionEntry];
9740
+ }
9741
+ console.log("\u2705 Plan approved. Executing...");
9742
+ const orchestrator = new ExecutionOrchestrator(this);
9743
+ const executionResult = await orchestrator.executeWithRecovery(output.plan, workflowService, request);
9744
+ return this.workflowResultToChatEntries(userEntry, output, approval, executionResult);
9745
+ } catch (error) {
9746
+ console.error("[Workflow] Failed:", error);
9747
+ const errorEntry = {
9748
+ type: "assistant",
9749
+ content: `Workflow failed: ${error.message}`,
9750
+ timestamp: /* @__PURE__ */ new Date()
9751
+ };
9752
+ this.chatHistory.push(errorEntry);
9753
+ return [userEntry, errorEntry];
9754
+ }
9755
+ }
9756
+ /**
9757
+ * Standard processing for simple queries
9758
+ */
9759
+ async processStandard(message) {
8692
9760
  const userEntry = {
8693
9761
  type: "user",
8694
9762
  content: message,
@@ -9061,10 +10129,10 @@ Current working directory: ${process.cwd()}`
9061
10129
  return await this.textEditor.view(args.path, range);
9062
10130
  } catch (error) {
9063
10131
  console.warn(`view_file tool failed, falling back to bash: ${error.message}`);
9064
- const path31 = args.path;
9065
- let command = `cat "${path31}"`;
10132
+ const path32 = args.path;
10133
+ let command = `cat "${path32}"`;
9066
10134
  if (args.start_line && args.end_line) {
9067
- command = `sed -n '${args.start_line},${args.end_line}p' "${path31}"`;
10135
+ command = `sed -n '${args.start_line},${args.end_line}p' "${path32}"`;
9068
10136
  }
9069
10137
  return await this.bash.execute(command);
9070
10138
  }
@@ -9300,6 +10368,12 @@ EOF`;
9300
10368
  this.abortController.abort();
9301
10369
  }
9302
10370
  }
10371
+ getMessageCount() {
10372
+ return this.chatHistory.length;
10373
+ }
10374
+ getSessionTokenCount() {
10375
+ return this.tokenCounter.countMessageTokens(this.messages);
10376
+ }
9303
10377
  logEntry(entry) {
9304
10378
  try {
9305
10379
  const dir = path7__default.dirname(this.sessionLogPath);
@@ -9318,6 +10392,43 @@ EOF`;
9318
10392
  console.warn("Failed to log session entry:", error);
9319
10393
  }
9320
10394
  }
10395
+ /**
10396
+ * Load .agent context pack for enhanced recommendations
10397
+ */
10398
+ async loadContextPack() {
10399
+ try {
10400
+ const contextLoader = await Promise.resolve().then(() => (init_context_loader(), context_loader_exports));
10401
+ return await contextLoader.loadContext(".agent");
10402
+ } catch (error) {
10403
+ console.warn("[Workflow] Failed to load context pack:", error);
10404
+ return void 0;
10405
+ }
10406
+ }
10407
+ /**
10408
+ * Convert workflow results to chat entries for display
10409
+ */
10410
+ workflowResultToChatEntries(userEntry, output, approval, executionResult) {
10411
+ const entries = [userEntry];
10412
+ const summaryEntry = {
10413
+ type: "assistant",
10414
+ content: `Workflow completed: ${executionResult?.success ? "\u2705 Success" : "\u274C Failed"}
10415
+
10416
+ ${output?.plan?.summary || "Task completed"}`,
10417
+ timestamp: /* @__PURE__ */ new Date()
10418
+ };
10419
+ entries.push(summaryEntry);
10420
+ this.chatHistory.push(summaryEntry);
10421
+ if (executionResult?.executionPlan) {
10422
+ const detailsEntry = {
10423
+ type: "assistant",
10424
+ content: `Executed ${executionResult.executionPlan.completedSteps}/${executionResult.executionPlan.totalSteps} tasks successfully.`,
10425
+ timestamp: /* @__PURE__ */ new Date()
10426
+ };
10427
+ entries.push(detailsEntry);
10428
+ this.chatHistory.push(detailsEntry);
10429
+ }
10430
+ return entries;
10431
+ }
9321
10432
  };
9322
10433
 
9323
10434
  // src/utils/text-utils.ts
@@ -9924,7 +11035,7 @@ var CodebaseExplorer = class {
9924
11035
  return files;
9925
11036
  }
9926
11037
  try {
9927
- const entries = await fs4.readdir(dirPath, { withFileTypes: true });
11038
+ const entries = await fs6.readdir(dirPath, { withFileTypes: true });
9928
11039
  for (const entry of entries) {
9929
11040
  const fullPath = path7__default.join(dirPath, entry.name);
9930
11041
  const relativePath = path7__default.relative(options.rootPath, fullPath);
@@ -9945,7 +11056,7 @@ var CodebaseExplorer = class {
9945
11056
  files.push(...subFiles);
9946
11057
  } else {
9947
11058
  try {
9948
- const stats = await fs4.stat(fullPath);
11059
+ const stats = await fs6.stat(fullPath);
9949
11060
  fileInfo.size = stats.size;
9950
11061
  if (fileInfo.size > this.settings.maxFileSize) {
9951
11062
  continue;
@@ -10214,7 +11325,7 @@ var CodebaseExplorer = class {
10214
11325
  async detectProjectType(rootPath, files) {
10215
11326
  const packageJsonPath = path7__default.join(rootPath, "package.json");
10216
11327
  try {
10217
- const packageJson = await fs4.readFile(packageJsonPath, "utf-8");
11328
+ const packageJson = await fs6.readFile(packageJsonPath, "utf-8");
10218
11329
  const pkg = JSON.parse(packageJson);
10219
11330
  if (pkg.dependencies?.react || pkg.devDependencies?.react) return "react";
10220
11331
  if (pkg.dependencies?.vue || pkg.devDependencies?.vue) return "vue";
@@ -13454,7 +14565,7 @@ var ChangelogGenerator = class {
13454
14565
  }
13455
14566
  }
13456
14567
  async getGitCommits() {
13457
- const { execSync: execSync2 } = __require("child_process");
14568
+ const { execSync: execSync3 } = __require("child_process");
13458
14569
  try {
13459
14570
  let gitCommand2 = 'git log --pretty=format:"%H|%ad|%an|%s|%b" --date=short';
13460
14571
  if (this.config.sinceVersion) {
@@ -13464,7 +14575,7 @@ var ChangelogGenerator = class {
13464
14575
  } else {
13465
14576
  gitCommand2 += " -n 50";
13466
14577
  }
13467
- const output = execSync2(gitCommand2, {
14578
+ const output = execSync3(gitCommand2, {
13468
14579
  cwd: this.config.rootPath,
13469
14580
  encoding: "utf-8"
13470
14581
  });
@@ -13699,9 +14810,9 @@ var UpdateAgentDocs = class {
13699
14810
  hasNewFeatures: false
13700
14811
  };
13701
14812
  try {
13702
- const { execSync: execSync2 } = __require("child_process");
14813
+ const { execSync: execSync3 } = __require("child_process");
13703
14814
  try {
13704
- const commits = execSync2("git log --oneline -10", {
14815
+ const commits = execSync3("git log --oneline -10", {
13705
14816
  cwd: this.config.rootPath,
13706
14817
  encoding: "utf-8"
13707
14818
  });
@@ -13709,7 +14820,7 @@ var UpdateAgentDocs = class {
13709
14820
  } catch (error) {
13710
14821
  }
13711
14822
  try {
13712
- const changedFiles = execSync2("git diff --name-only HEAD~5..HEAD", {
14823
+ const changedFiles = execSync3("git diff --name-only HEAD~5..HEAD", {
13713
14824
  cwd: this.config.rootPath,
13714
14825
  encoding: "utf-8"
13715
14826
  });
@@ -14686,8 +15797,8 @@ ${guardrail.createdFrom ? `- Created from incident: ${guardrail.createdFrom}` :
14686
15797
  var package_default = {
14687
15798
  type: "module",
14688
15799
  name: "@xagent/x-cli",
14689
- version: "1.1.74",
14690
- description: "An open-source AI agent that brings the power of Grok directly into your terminal.",
15800
+ version: "1.1.75",
15801
+ description: "An open-source AI agent that brings advanced AI capabilities directly into your terminal.",
14691
15802
  main: "dist/index.js",
14692
15803
  module: "dist/index.js",
14693
15804
  types: "dist/index.d.ts",
@@ -14717,7 +15828,7 @@ var package_default = {
14717
15828
  local: "npm run build > /dev/null 2>&1 && npm link > /dev/null 2>&1 && node dist/index.js",
14718
15829
  "test:workflow": "node scripts/test-workflow.js",
14719
15830
  "start:bun": "bun run dist/index.js",
14720
- lint: "eslint . --ext .js,.jsx,.ts,.tsx",
15831
+ lint: "eslint . --ext .js,.jsx,.ts,.tsx --ignore-pattern 'dist/**'",
14721
15832
  typecheck: "tsc --noEmit",
14722
15833
  "install:bun": "bun install",
14723
15834
  preinstall: "echo '\u{1F916} Installing X CLI...'",
@@ -14730,10 +15841,10 @@ var package_default = {
14730
15841
  },
14731
15842
  "lint-staged": {
14732
15843
  "*.{ts,tsx}": [
14733
- "eslint --fix"
15844
+ "eslint --fix --ignore-pattern 'dist/**'"
14734
15845
  ],
14735
15846
  "*.{js,jsx,mjs}": [
14736
- "eslint --fix"
15847
+ "eslint --fix --ignore-pattern 'dist/**'"
14737
15848
  ],
14738
15849
  "*.{md,mdx}": [
14739
15850
  "prettier --write"
@@ -14746,10 +15857,10 @@ var package_default = {
14746
15857
  "cli",
14747
15858
  "agent",
14748
15859
  "text-editor",
14749
- "grok",
14750
- "ai"
15860
+ "ai",
15861
+ "x-ai"
14751
15862
  ],
14752
- author: "grok_cli",
15863
+ author: "x-cli-team",
14753
15864
  license: "MIT",
14754
15865
  dependencies: {
14755
15866
  "@modelcontextprotocol/sdk": "^1.17.0",
@@ -14804,7 +15915,7 @@ var package_default = {
14804
15915
  bugs: {
14805
15916
  url: "https://github.com/x-cli-team/x-cli/issues"
14806
15917
  },
14807
- homepage: "https://grokcli.dev",
15918
+ homepage: "https://x-cli.dev",
14808
15919
  icon: "docs/assets/logos/x-cli-logo.svg",
14809
15920
  publishConfig: {
14810
15921
  access: "public"
@@ -17216,11 +18327,11 @@ function useContextInfo(agent) {
17216
18327
  if (agent) {
17217
18328
  const modelName = agent.getCurrentModel?.() || "grok-code-fast-1";
17218
18329
  const maxTokens = getMaxTokensForModel(modelName);
17219
- const estimatedTokens = Math.floor(Math.random() * 1e3) + 500;
17220
- messagesCount = Math.floor(Math.random() * 10) + 1;
17221
- const tokenPercent = Math.round(estimatedTokens / maxTokens * 100);
18330
+ const sessionTokens = agent.getSessionTokenCount?.() || 0;
18331
+ messagesCount = agent.getMessageCount?.() || 0;
18332
+ const tokenPercent = Math.round(sessionTokens / maxTokens * 100);
17222
18333
  tokenUsage = {
17223
- current: estimatedTokens,
18334
+ current: sessionTokens,
17224
18335
  max: maxTokens,
17225
18336
  percent: tokenPercent
17226
18337
  };
@@ -19792,121 +20903,9 @@ function createToggleConfirmationsCommand() {
19792
20903
  });
19793
20904
  return toggleCommand;
19794
20905
  }
19795
- var CONTEXT_BUDGET_BYTES = 280 * 1024;
19796
- var MAX_SUMMARY_LENGTH = 2e3;
19797
- function loadMarkdownDirectory(dirPath) {
19798
- if (!fs__default.existsSync(dirPath)) {
19799
- return "";
19800
- }
19801
- const files = fs__default.readdirSync(dirPath).filter((file) => file.endsWith(".md")).sort();
19802
- let content = "";
19803
- for (const file of files) {
19804
- const filePath = path7__default.join(dirPath, file);
19805
- try {
19806
- const fileContent = fs__default.readFileSync(filePath, "utf-8");
19807
- content += `
19808
-
19809
- === ${file} ===
19810
-
19811
- ${fileContent}`;
19812
- } catch (error) {
19813
- console.warn(`Failed to read ${filePath}:`, error);
19814
- }
19815
- }
19816
- return content;
19817
- }
19818
- function extractDateFromFilename(filename) {
19819
- const match = filename.match(/^(\d{4}-\d{2}-\d{2})/);
19820
- if (match) {
19821
- return new Date(match[1]);
19822
- }
19823
- return /* @__PURE__ */ new Date(0);
19824
- }
19825
- function summarizeContent(content, maxLength = MAX_SUMMARY_LENGTH) {
19826
- if (content.length <= maxLength) {
19827
- return content;
19828
- }
19829
- const truncated = content.substring(0, maxLength);
19830
- const lastNewline = truncated.lastIndexOf("\n\n");
19831
- if (lastNewline > maxLength * 0.8) {
19832
- return truncated.substring(0, lastNewline);
19833
- }
19834
- return truncated + "\n\n[...content truncated for context budget...]";
19835
- }
19836
- function loadTaskFiles(tasksDir, maxBudget) {
19837
- if (!fs__default.existsSync(tasksDir)) {
19838
- return [];
19839
- }
19840
- const files = fs__default.readdirSync(tasksDir).filter((file) => file.endsWith(".md")).map((filename) => {
19841
- const filePath = path7__default.join(tasksDir, filename);
19842
- const content = fs__default.readFileSync(filePath, "utf-8");
19843
- return {
19844
- filename,
19845
- content,
19846
- size: Buffer.byteLength(content, "utf-8"),
19847
- date: extractDateFromFilename(filename),
19848
- isSummarized: false
19849
- };
19850
- }).sort((a, b) => b.date.getTime() - a.date.getTime());
19851
- const result = [];
19852
- let usedBudget = 0;
19853
- for (const file of files) {
19854
- let finalContent = file.content;
19855
- let isSummarized = false;
19856
- if (usedBudget + file.size > maxBudget) {
19857
- finalContent = summarizeContent(file.content);
19858
- const summarizedSize = Buffer.byteLength(finalContent, "utf-8");
19859
- if (usedBudget + summarizedSize > maxBudget) {
19860
- continue;
19861
- }
19862
- usedBudget += summarizedSize;
19863
- isSummarized = true;
19864
- } else {
19865
- usedBudget += file.size;
19866
- }
19867
- result.push({
19868
- ...file,
19869
- content: finalContent,
19870
- isSummarized
19871
- });
19872
- }
19873
- return result;
19874
- }
19875
- function loadContext(agentDir = ".agent") {
19876
- const systemContent = loadMarkdownDirectory(path7__default.join(agentDir, "system"));
19877
- const sopContent = loadMarkdownDirectory(path7__default.join(agentDir, "sop"));
19878
- const systemSize = Buffer.byteLength(systemContent, "utf-8");
19879
- const sopSize = Buffer.byteLength(sopContent, "utf-8");
19880
- const taskBudget = Math.max(0, CONTEXT_BUDGET_BYTES - systemSize - sopSize);
19881
- const tasks = loadTaskFiles(path7__default.join(agentDir, "tasks"), taskBudget);
19882
- const totalSize = systemSize + sopSize + tasks.reduce((sum, task) => sum + Buffer.byteLength(task.content, "utf-8"), 0);
19883
- const warnings = [];
19884
- if (totalSize > CONTEXT_BUDGET_BYTES) {
19885
- warnings.push(`Context size (${(totalSize / 1024).toFixed(1)}KB) exceeds budget (${CONTEXT_BUDGET_BYTES / 1024}KB)`);
19886
- }
19887
- return {
19888
- system: systemContent,
19889
- sop: sopContent,
19890
- tasks,
19891
- totalSize,
19892
- warnings
19893
- };
19894
- }
19895
- function formatContextStatus(pack) {
19896
- const taskCount = pack.tasks.length;
19897
- const summarizedCount = pack.tasks.filter((t) => t.isSummarized).length;
19898
- const sizeKB = (pack.totalSize / 1024).toFixed(1);
19899
- let status = `[x-cli] Context: loaded system docs, sop docs, ${taskCount} task docs (~${sizeKB} KB).`;
19900
- if (summarizedCount > 0) {
19901
- status += ` Summarized ${summarizedCount} older tasks for context budget.`;
19902
- }
19903
- if (pack.warnings.length > 0) {
19904
- status += ` Warnings: ${pack.warnings.join("; ")}`;
19905
- }
19906
- return status;
19907
- }
19908
20906
 
19909
20907
  // src/index.ts
20908
+ init_context_loader();
19910
20909
  dotenv.config();
19911
20910
  process.on("SIGTERM", () => {
19912
20911
  if (process.stdin.isTTY && process.stdin.setRawMode) {