@xagent/x-cli 1.1.73 → 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
  }
@@ -8471,55 +8597,921 @@ var TokenCounter = class {
8471
8597
  totalTokens += this.countTokens(JSON.stringify(message.tool_calls));
8472
8598
  }
8473
8599
  }
8474
- totalTokens += 3;
8475
- return totalTokens;
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"
9320
+ };
9321
+ }
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
+ });
9408
+ }
8476
9409
  }
8477
9410
  /**
8478
- * Estimate tokens for streaming content
8479
- * This is an approximation since we don't have the full response yet
9411
+ * Prompt user for approval with Y/n/R options
8480
9412
  */
8481
- estimateStreamingTokens(accumulatedContent) {
8482
- return this.countTokens(accumulatedContent);
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
+ });
8483
9446
  }
8484
9447
  /**
8485
- * Clean up resources
9448
+ * Handle revision flow with updated request
8486
9449
  */
8487
- dispose() {
8488
- this.encoder.free();
8489
- }
8490
- };
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`;
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);
8498
9461
  }
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;
9462
+ /**
9463
+ * Full research and approval workflow with revision support
9464
+ */
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
+ };
8510
9489
  }
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;
9490
+ throw new Error("Unexpected end of revision loop");
8516
9491
  }
8517
- }
9492
+ /**
9493
+ * Complete workflow: Research → Recommend → Execute with Adaptive Recovery
9494
+ */
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
+ };
9509
+ }
9510
+ };
8518
9511
 
8519
9512
  // src/agent/grok-agent.ts
8520
- init_settings_manager();
8521
9513
  var GrokAgent = class extends EventEmitter {
8522
- constructor(apiKey, baseURL, model, maxToolRounds) {
9514
+ constructor(apiKey, baseURL, model, maxToolRounds, contextPack) {
8523
9515
  super();
8524
9516
  this.chatHistory = [];
8525
9517
  this.messages = [];
@@ -8562,9 +9554,21 @@ CUSTOM INSTRUCTIONS:
8562
9554
  ${customInstructions}
8563
9555
 
8564
9556
  The above custom instructions should be followed alongside the standard instructions below.` : "";
9557
+ const contextSection = contextPack ? `
9558
+
9559
+ PROJECT CONTEXT:
9560
+ ${contextPack.system}
9561
+
9562
+ SOP:
9563
+ ${contextPack.sop}
9564
+
9565
+ TASKS:
9566
+ ${contextPack.tasks.map((t) => `- ${t.filename}: ${t.content}`).join("\n")}
9567
+
9568
+ The above project context should inform your responses and decision making.` : "";
8565
9569
  this.messages.push({
8566
9570
  role: "system",
8567
- content: `You are X-CLI, an AI assistant that helps with file editing, coding tasks, and system operations.${customInstructionsSection}
9571
+ content: `You are X-CLI, an AI assistant that helps with file editing, coding tasks, and system operations.${customInstructionsSection}${contextSection}
8568
9572
 
8569
9573
  You have access to these tools:
8570
9574
 
@@ -8676,7 +9680,83 @@ Current working directory: ${process.cwd()}`
8676
9680
  if (/(20\d{2})/.test(q)) return true;
8677
9681
  return false;
8678
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
+ }
8679
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) {
8680
9760
  const userEntry = {
8681
9761
  type: "user",
8682
9762
  content: message,
@@ -9049,10 +10129,10 @@ Current working directory: ${process.cwd()}`
9049
10129
  return await this.textEditor.view(args.path, range);
9050
10130
  } catch (error) {
9051
10131
  console.warn(`view_file tool failed, falling back to bash: ${error.message}`);
9052
- const path31 = args.path;
9053
- let command = `cat "${path31}"`;
10132
+ const path32 = args.path;
10133
+ let command = `cat "${path32}"`;
9054
10134
  if (args.start_line && args.end_line) {
9055
- command = `sed -n '${args.start_line},${args.end_line}p' "${path31}"`;
10135
+ command = `sed -n '${args.start_line},${args.end_line}p' "${path32}"`;
9056
10136
  }
9057
10137
  return await this.bash.execute(command);
9058
10138
  }
@@ -9288,6 +10368,12 @@ EOF`;
9288
10368
  this.abortController.abort();
9289
10369
  }
9290
10370
  }
10371
+ getMessageCount() {
10372
+ return this.chatHistory.length;
10373
+ }
10374
+ getSessionTokenCount() {
10375
+ return this.tokenCounter.countMessageTokens(this.messages);
10376
+ }
9291
10377
  logEntry(entry) {
9292
10378
  try {
9293
10379
  const dir = path7__default.dirname(this.sessionLogPath);
@@ -9306,6 +10392,43 @@ EOF`;
9306
10392
  console.warn("Failed to log session entry:", error);
9307
10393
  }
9308
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
+ }
9309
10432
  };
9310
10433
 
9311
10434
  // src/utils/text-utils.ts
@@ -9912,7 +11035,7 @@ var CodebaseExplorer = class {
9912
11035
  return files;
9913
11036
  }
9914
11037
  try {
9915
- const entries = await fs4.readdir(dirPath, { withFileTypes: true });
11038
+ const entries = await fs6.readdir(dirPath, { withFileTypes: true });
9916
11039
  for (const entry of entries) {
9917
11040
  const fullPath = path7__default.join(dirPath, entry.name);
9918
11041
  const relativePath = path7__default.relative(options.rootPath, fullPath);
@@ -9933,7 +11056,7 @@ var CodebaseExplorer = class {
9933
11056
  files.push(...subFiles);
9934
11057
  } else {
9935
11058
  try {
9936
- const stats = await fs4.stat(fullPath);
11059
+ const stats = await fs6.stat(fullPath);
9937
11060
  fileInfo.size = stats.size;
9938
11061
  if (fileInfo.size > this.settings.maxFileSize) {
9939
11062
  continue;
@@ -10202,7 +11325,7 @@ var CodebaseExplorer = class {
10202
11325
  async detectProjectType(rootPath, files) {
10203
11326
  const packageJsonPath = path7__default.join(rootPath, "package.json");
10204
11327
  try {
10205
- const packageJson = await fs4.readFile(packageJsonPath, "utf-8");
11328
+ const packageJson = await fs6.readFile(packageJsonPath, "utf-8");
10206
11329
  const pkg = JSON.parse(packageJson);
10207
11330
  if (pkg.dependencies?.react || pkg.devDependencies?.react) return "react";
10208
11331
  if (pkg.dependencies?.vue || pkg.devDependencies?.vue) return "vue";
@@ -13442,7 +14565,7 @@ var ChangelogGenerator = class {
13442
14565
  }
13443
14566
  }
13444
14567
  async getGitCommits() {
13445
- const { execSync: execSync2 } = __require("child_process");
14568
+ const { execSync: execSync3 } = __require("child_process");
13446
14569
  try {
13447
14570
  let gitCommand2 = 'git log --pretty=format:"%H|%ad|%an|%s|%b" --date=short';
13448
14571
  if (this.config.sinceVersion) {
@@ -13452,7 +14575,7 @@ var ChangelogGenerator = class {
13452
14575
  } else {
13453
14576
  gitCommand2 += " -n 50";
13454
14577
  }
13455
- const output = execSync2(gitCommand2, {
14578
+ const output = execSync3(gitCommand2, {
13456
14579
  cwd: this.config.rootPath,
13457
14580
  encoding: "utf-8"
13458
14581
  });
@@ -13687,9 +14810,9 @@ var UpdateAgentDocs = class {
13687
14810
  hasNewFeatures: false
13688
14811
  };
13689
14812
  try {
13690
- const { execSync: execSync2 } = __require("child_process");
14813
+ const { execSync: execSync3 } = __require("child_process");
13691
14814
  try {
13692
- const commits = execSync2("git log --oneline -10", {
14815
+ const commits = execSync3("git log --oneline -10", {
13693
14816
  cwd: this.config.rootPath,
13694
14817
  encoding: "utf-8"
13695
14818
  });
@@ -13697,7 +14820,7 @@ var UpdateAgentDocs = class {
13697
14820
  } catch (error) {
13698
14821
  }
13699
14822
  try {
13700
- const changedFiles = execSync2("git diff --name-only HEAD~5..HEAD", {
14823
+ const changedFiles = execSync3("git diff --name-only HEAD~5..HEAD", {
13701
14824
  cwd: this.config.rootPath,
13702
14825
  encoding: "utf-8"
13703
14826
  });
@@ -14674,8 +15797,8 @@ ${guardrail.createdFrom ? `- Created from incident: ${guardrail.createdFrom}` :
14674
15797
  var package_default = {
14675
15798
  type: "module",
14676
15799
  name: "@xagent/x-cli",
14677
- version: "1.1.73",
14678
- 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.",
14679
15802
  main: "dist/index.js",
14680
15803
  module: "dist/index.js",
14681
15804
  types: "dist/index.d.ts",
@@ -14702,10 +15825,10 @@ var package_default = {
14702
15825
  "dev:node": "tsx src/index.ts",
14703
15826
  "dev:watch": "npm run build && node --watch dist/index.js",
14704
15827
  start: "node dist/index.js",
14705
- local: "npm run build && npm link && node dist/index.js",
15828
+ local: "npm run build > /dev/null 2>&1 && npm link > /dev/null 2>&1 && node dist/index.js",
14706
15829
  "test:workflow": "node scripts/test-workflow.js",
14707
15830
  "start:bun": "bun run dist/index.js",
14708
- lint: "eslint . --ext .js,.jsx,.ts,.tsx",
15831
+ lint: "eslint . --ext .js,.jsx,.ts,.tsx --ignore-pattern 'dist/**'",
14709
15832
  typecheck: "tsc --noEmit",
14710
15833
  "install:bun": "bun install",
14711
15834
  preinstall: "echo '\u{1F916} Installing X CLI...'",
@@ -14718,10 +15841,10 @@ var package_default = {
14718
15841
  },
14719
15842
  "lint-staged": {
14720
15843
  "*.{ts,tsx}": [
14721
- "eslint --fix"
15844
+ "eslint --fix --ignore-pattern 'dist/**'"
14722
15845
  ],
14723
15846
  "*.{js,jsx,mjs}": [
14724
- "eslint --fix"
15847
+ "eslint --fix --ignore-pattern 'dist/**'"
14725
15848
  ],
14726
15849
  "*.{md,mdx}": [
14727
15850
  "prettier --write"
@@ -14734,10 +15857,10 @@ var package_default = {
14734
15857
  "cli",
14735
15858
  "agent",
14736
15859
  "text-editor",
14737
- "grok",
14738
- "ai"
15860
+ "ai",
15861
+ "x-ai"
14739
15862
  ],
14740
- author: "grok_cli",
15863
+ author: "x-cli-team",
14741
15864
  license: "MIT",
14742
15865
  dependencies: {
14743
15866
  "@modelcontextprotocol/sdk": "^1.17.0",
@@ -14792,7 +15915,7 @@ var package_default = {
14792
15915
  bugs: {
14793
15916
  url: "https://github.com/x-cli-team/x-cli/issues"
14794
15917
  },
14795
- homepage: "https://grokcli.dev",
15918
+ homepage: "https://x-cli.dev",
14796
15919
  icon: "docs/assets/logos/x-cli-logo.svg",
14797
15920
  publishConfig: {
14798
15921
  access: "public"
@@ -14802,7 +15925,7 @@ var package_default = {
14802
15925
  },
14803
15926
  optionalDependencies: {
14804
15927
  "tree-sitter": "^0.21.1",
14805
- "tree-sitter-javascript": "^0.21.2",
15928
+ "tree-sitter-javascript": "^0.21.4",
14806
15929
  "tree-sitter-python": "^0.21.0",
14807
15930
  "tree-sitter-typescript": "^0.21.2"
14808
15931
  },
@@ -16757,33 +17880,44 @@ ${statusCheckResult.output || "Unknown status"}`,
16757
17880
  };
16758
17881
  setChatHistory((prev) => [...prev, waitEntry]);
16759
17882
  await new Promise((resolve8) => setTimeout(resolve8, 1e4));
16760
- const npmCheckResult = await agent.executeBashCommand("npm view @xagent/x-cli version");
16761
- const localVersionResult = await agent.executeBashCommand(`node -p "require('./package.json').version"`);
16762
- const localVersion = localVersionResult.success ? localVersionResult.output?.trim() : "unknown";
16763
- if (npmCheckResult.success && npmCheckResult.output?.trim()) {
16764
- const npmVersion = npmCheckResult.output.trim();
16765
- if (npmVersion === localVersion) {
16766
- const npmConfirmEntry = {
16767
- type: "tool_result",
16768
- content: `\u2705 **NPM Package Confirmed**: Version ${npmVersion} published successfully`,
16769
- timestamp: /* @__PURE__ */ new Date()
16770
- };
16771
- setChatHistory((prev) => [...prev, npmConfirmEntry]);
17883
+ const localPackageResult = await agent.executeBashCommand(`node -p "require('./package.json').name" 2>/dev/null || echo 'no-package'`);
17884
+ const localName = localPackageResult.success && localPackageResult.output?.trim() !== "no-package" ? localPackageResult.output?.trim() : null;
17885
+ if (localName) {
17886
+ const localVersionResult = await agent.executeBashCommand(`node -p "require('./package.json').version"`);
17887
+ const localVersion = localVersionResult.success ? localVersionResult.output?.trim() : "unknown";
17888
+ const npmCheckResult = await agent.executeBashCommand(`npm view ${localName} version 2>/dev/null || echo 'not-found'`);
17889
+ if (npmCheckResult.success && npmCheckResult.output?.trim() && npmCheckResult.output?.trim() !== "not-found") {
17890
+ const npmVersion = npmCheckResult.output.trim();
17891
+ if (npmVersion === localVersion) {
17892
+ const npmConfirmEntry = {
17893
+ type: "tool_result",
17894
+ content: `\u2705 **NPM Package Confirmed**: ${localName} v${npmVersion} published successfully`,
17895
+ timestamp: /* @__PURE__ */ new Date()
17896
+ };
17897
+ setChatHistory((prev) => [...prev, npmConfirmEntry]);
17898
+ } else {
17899
+ const npmPendingEntry = {
17900
+ type: "assistant",
17901
+ content: `\u23F3 **NPM Status**: Local ${localName} v${localVersion}, NPM v${npmVersion}. Publishing may still be in progress.`,
17902
+ timestamp: /* @__PURE__ */ new Date()
17903
+ };
17904
+ setChatHistory((prev) => [...prev, npmPendingEntry]);
17905
+ }
16772
17906
  } else {
16773
- const npmPendingEntry = {
17907
+ const npmSkipEntry = {
16774
17908
  type: "assistant",
16775
- content: `\u23F3 **NPM Status**: Local version ${localVersion}, NPM version ${npmVersion}. Publishing may still be in progress.`,
17909
+ content: `\u2139\uFE0F **NPM Check Skipped**: Package ${localName} not found on NPM (may not be published yet)`,
16776
17910
  timestamp: /* @__PURE__ */ new Date()
16777
17911
  };
16778
- setChatHistory((prev) => [...prev, npmPendingEntry]);
17912
+ setChatHistory((prev) => [...prev, npmSkipEntry]);
16779
17913
  }
16780
17914
  } else {
16781
- const npmErrorEntry = {
17915
+ const npmSkipEntry = {
16782
17916
  type: "assistant",
16783
- content: `\u274C **NPM Check Failed**: ${npmCheckResult.error || "Unable to check NPM version"}`,
17917
+ content: `\u2139\uFE0F **NPM Check Skipped**: No package.json found or not an NPM package`,
16784
17918
  timestamp: /* @__PURE__ */ new Date()
16785
17919
  };
16786
- setChatHistory((prev) => [...prev, npmErrorEntry]);
17920
+ setChatHistory((prev) => [...prev, npmSkipEntry]);
16787
17921
  }
16788
17922
  const finalSuccessEntry = {
16789
17923
  type: "assistant",
@@ -17193,11 +18327,11 @@ function useContextInfo(agent) {
17193
18327
  if (agent) {
17194
18328
  const modelName = agent.getCurrentModel?.() || "grok-code-fast-1";
17195
18329
  const maxTokens = getMaxTokensForModel(modelName);
17196
- const estimatedTokens = Math.floor(Math.random() * 1e3) + 500;
17197
- messagesCount = Math.floor(Math.random() * 10) + 1;
17198
- 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);
17199
18333
  tokenUsage = {
17200
- current: estimatedTokens,
18334
+ current: sessionTokens,
17201
18335
  max: maxTokens,
17202
18336
  percent: tokenPercent
17203
18337
  };
@@ -17633,15 +18767,63 @@ function useConfirmations(confirmationService, state) {
17633
18767
  handleRejection
17634
18768
  };
17635
18769
  }
17636
- function useConsoleSetup(quiet = false) {
17637
- useEffect(() => {
17638
- if (quiet) return;
17639
- const isWindows = process.platform === "win32";
17640
- const isPowerShell = process.env.ComSpec?.toLowerCase().includes("powershell") || process.env.PSModulePath !== void 0;
17641
- if (!isWindows || !isPowerShell) {
17642
- console.clear();
17643
- }
17644
- }, [quiet]);
18770
+
18771
+ // src/hooks/use-console-setup.ts
18772
+ function printWelcomeBanner(_quiet = false) {
18773
+ if (_quiet) return;
18774
+ const isTTY = !!process.stdout.isTTY;
18775
+ if (isTTY) {
18776
+ process.stdout.write("\x1B[?25l");
18777
+ process.stdout.write("\x1B[H");
18778
+ process.stdout.write("\x1B[2J");
18779
+ process.stdout.write("\x1B[3J");
18780
+ process.stdout.write("\x1B[H");
18781
+ }
18782
+ const isFancy = process.env.X_CLI_ASCII !== "block";
18783
+ const fancyAscii = String.raw`__/\\\_______/\\\______________________/\\\\\\\\\__/\\\______________/\\\\\\\\\\\_
18784
+ _\///\\\___/\\\/____________________/\\\////////__\/\\\_____________\/////\\\///__
18785
+ ___\///\\\\\\/____________________/\\\/___________\/\\\_________________\/\\\_____
18786
+ _____\//\\\\_______/\\\\\\\\\\\__/\\\_____________\/\\\_________________\/\\\_____
18787
+ ______\/\\\\______\///////////__\/\\\_____________\/\\\_________________\/\\\_____
18788
+ ______/\\\\\\___________________\//\\\____________\/\\\_________________\/\\\_____
18789
+ ____/\\\////\\\__________________\///\\\__________\/\\\_________________\/\\\_____
18790
+ __/\\\/___\///\\\__________________\////\\\\\\\\\_\/\\\\\\\\\\\\\\\__/\\\\\\\\\\\_
18791
+ _\///_______\///______________________\/////////__\///////////////__\///////////__`;
18792
+ const blockAscii = String.raw`\x1b[34m ████ ████████ ████ ████
18793
+ ████████ ██████████████ ████████
18794
+ ██████████ ██████████████ ████████
18795
+ ██████████ ██████████████ ████████
18796
+ ████████ ██████████████ ████████
18797
+ ████ ████████ ████ ████\x1b[0m`;
18798
+ const asciiArt = (isFancy ? fancyAscii : blockAscii).normalize("NFC");
18799
+ process.stdout.write(asciiArt + "\n");
18800
+ const welcomeBanner = [
18801
+ "",
18802
+ `\x1B[32m Welcome to X-CLI v${package_default.version} \u26A1\x1B[0m`,
18803
+ "",
18804
+ `\x1B[36m \u{1F680} Claude Code-level intelligence in your terminal!\x1B[0m`,
18805
+ "",
18806
+ `\x1B[33m \u2714 Ready. Type your first command or paste code to begin.\x1B[0m`,
18807
+ "",
18808
+ `\x1B[35m \u{1F4A1} Quick Start Tips:\x1B[0m`,
18809
+ "",
18810
+ ` \u2022 Ask anything: "Create a React component" or "Debug this Python script"`,
18811
+ ` \u2022 Edit files: "Add error handling to app.js"`,
18812
+ ` \u2022 Run commands: "Set up a new Node.js project"`,
18813
+ ` \u2022 Get help: Type "/help" for all commands`,
18814
+ "",
18815
+ `\x1B[35m \u{1F6E0}\uFE0F Power Features:\x1B[0m`,
18816
+ "",
18817
+ ` \u2022 Auto-edit mode: Press Shift+Tab to toggle hands-free editing`,
18818
+ ` \u2022 Project memory: Create .grok/GROK.md to customize behavior`,
18819
+ ` \u2022 Documentation: Run "/init-agent" for .agent docs system`,
18820
+ ` \u2022 Error recovery: Run "/heal" after errors to add guardrails`,
18821
+ "",
18822
+ `\x1B[37m Type your request in natural language. Ctrl+C to clear, 'exit' to quit.\x1B[0m`,
18823
+ ""
18824
+ ].join("\n");
18825
+ process.stdout.write(welcomeBanner);
18826
+ if (isTTY) process.stdout.write("\x1B[?25h");
17645
18827
  }
17646
18828
  function useSessionLogging(chatHistory) {
17647
18829
  const lastChatHistoryLength = useRef(0);
@@ -19354,7 +20536,9 @@ function ChatInterfaceRenderer({
19354
20536
  function ChatInterfaceWithAgent({
19355
20537
  agent,
19356
20538
  initialMessage,
19357
- quiet = false
20539
+ quiet = false,
20540
+ contextPack: _contextPack,
20541
+ contextStatus
19358
20542
  }) {
19359
20543
  const [chatHistory, setChatHistory] = useState([]);
19360
20544
  const [isProcessing, setIsProcessing] = useState(false);
@@ -19364,11 +20548,18 @@ function ChatInterfaceWithAgent({
19364
20548
  const [confirmationOptions, setConfirmationOptions] = useState(null);
19365
20549
  const [showContextTooltip, setShowContextTooltip] = useState(false);
19366
20550
  const processingStartTime = useRef(0);
19367
- useConsoleSetup(quiet);
19368
20551
  useAutoRead(setChatHistory);
19369
20552
  useEffect(() => {
19370
- setChatHistory([]);
19371
- }, []);
20553
+ const initialHistory = [];
20554
+ if (contextStatus) {
20555
+ initialHistory.push({
20556
+ type: "assistant",
20557
+ content: `\u{1F527} ${contextStatus}`,
20558
+ timestamp: /* @__PURE__ */ new Date()
20559
+ });
20560
+ }
20561
+ setChatHistory(initialHistory);
20562
+ }, [contextStatus]);
19372
20563
  useSessionLogging(chatHistory);
19373
20564
  const { contextInfo } = useContextInfo(agent);
19374
20565
  const handleGlobalShortcuts = (str, key) => {
@@ -19458,7 +20649,9 @@ function ChatInterfaceWithAgent({
19458
20649
  function ChatInterface({
19459
20650
  agent,
19460
20651
  initialMessage,
19461
- quiet = false
20652
+ quiet = false,
20653
+ contextPack,
20654
+ contextStatus
19462
20655
  }) {
19463
20656
  const [currentAgent, setCurrentAgent] = useState(
19464
20657
  agent || null
@@ -19474,7 +20667,9 @@ function ChatInterface({
19474
20667
  {
19475
20668
  agent: currentAgent,
19476
20669
  initialMessage,
19477
- quiet
20670
+ quiet,
20671
+ contextPack,
20672
+ contextStatus
19478
20673
  }
19479
20674
  );
19480
20675
  }
@@ -19708,121 +20903,9 @@ function createToggleConfirmationsCommand() {
19708
20903
  });
19709
20904
  return toggleCommand;
19710
20905
  }
19711
- var CONTEXT_BUDGET_BYTES = 280 * 1024;
19712
- var MAX_SUMMARY_LENGTH = 2e3;
19713
- function loadMarkdownDirectory(dirPath) {
19714
- if (!fs__default.existsSync(dirPath)) {
19715
- return "";
19716
- }
19717
- const files = fs__default.readdirSync(dirPath).filter((file) => file.endsWith(".md")).sort();
19718
- let content = "";
19719
- for (const file of files) {
19720
- const filePath = path7__default.join(dirPath, file);
19721
- try {
19722
- const fileContent = fs__default.readFileSync(filePath, "utf-8");
19723
- content += `
19724
-
19725
- === ${file} ===
19726
-
19727
- ${fileContent}`;
19728
- } catch (error) {
19729
- console.warn(`Failed to read ${filePath}:`, error);
19730
- }
19731
- }
19732
- return content;
19733
- }
19734
- function extractDateFromFilename(filename) {
19735
- const match = filename.match(/^(\d{4}-\d{2}-\d{2})/);
19736
- if (match) {
19737
- return new Date(match[1]);
19738
- }
19739
- return /* @__PURE__ */ new Date(0);
19740
- }
19741
- function summarizeContent(content, maxLength = MAX_SUMMARY_LENGTH) {
19742
- if (content.length <= maxLength) {
19743
- return content;
19744
- }
19745
- const truncated = content.substring(0, maxLength);
19746
- const lastNewline = truncated.lastIndexOf("\n\n");
19747
- if (lastNewline > maxLength * 0.8) {
19748
- return truncated.substring(0, lastNewline);
19749
- }
19750
- return truncated + "\n\n[...content truncated for context budget...]";
19751
- }
19752
- function loadTaskFiles(tasksDir, maxBudget) {
19753
- if (!fs__default.existsSync(tasksDir)) {
19754
- return [];
19755
- }
19756
- const files = fs__default.readdirSync(tasksDir).filter((file) => file.endsWith(".md")).map((filename) => {
19757
- const filePath = path7__default.join(tasksDir, filename);
19758
- const content = fs__default.readFileSync(filePath, "utf-8");
19759
- return {
19760
- filename,
19761
- content,
19762
- size: Buffer.byteLength(content, "utf-8"),
19763
- date: extractDateFromFilename(filename),
19764
- isSummarized: false
19765
- };
19766
- }).sort((a, b) => b.date.getTime() - a.date.getTime());
19767
- const result = [];
19768
- let usedBudget = 0;
19769
- for (const file of files) {
19770
- let finalContent = file.content;
19771
- let isSummarized = false;
19772
- if (usedBudget + file.size > maxBudget) {
19773
- finalContent = summarizeContent(file.content);
19774
- const summarizedSize = Buffer.byteLength(finalContent, "utf-8");
19775
- if (usedBudget + summarizedSize > maxBudget) {
19776
- continue;
19777
- }
19778
- usedBudget += summarizedSize;
19779
- isSummarized = true;
19780
- } else {
19781
- usedBudget += file.size;
19782
- }
19783
- result.push({
19784
- ...file,
19785
- content: finalContent,
19786
- isSummarized
19787
- });
19788
- }
19789
- return result;
19790
- }
19791
- function loadContext(agentDir = ".agent") {
19792
- const systemContent = loadMarkdownDirectory(path7__default.join(agentDir, "system"));
19793
- const sopContent = loadMarkdownDirectory(path7__default.join(agentDir, "sop"));
19794
- const systemSize = Buffer.byteLength(systemContent, "utf-8");
19795
- const sopSize = Buffer.byteLength(sopContent, "utf-8");
19796
- const taskBudget = Math.max(0, CONTEXT_BUDGET_BYTES - systemSize - sopSize);
19797
- const tasks = loadTaskFiles(path7__default.join(agentDir, "tasks"), taskBudget);
19798
- const totalSize = systemSize + sopSize + tasks.reduce((sum, task) => sum + Buffer.byteLength(task.content, "utf-8"), 0);
19799
- const warnings = [];
19800
- if (totalSize > CONTEXT_BUDGET_BYTES) {
19801
- warnings.push(`Context size (${(totalSize / 1024).toFixed(1)}KB) exceeds budget (${CONTEXT_BUDGET_BYTES / 1024}KB)`);
19802
- }
19803
- return {
19804
- system: systemContent,
19805
- sop: sopContent,
19806
- tasks,
19807
- totalSize,
19808
- warnings
19809
- };
19810
- }
19811
- function formatContextStatus(pack) {
19812
- const taskCount = pack.tasks.length;
19813
- const summarizedCount = pack.tasks.filter((t) => t.isSummarized).length;
19814
- const sizeKB = (pack.totalSize / 1024).toFixed(1);
19815
- let status = `[x-cli] Context: loaded system docs, sop docs, ${taskCount} task docs (~${sizeKB} KB).`;
19816
- if (summarizedCount > 0) {
19817
- status += ` Summarized ${summarizedCount} older tasks for context budget.`;
19818
- }
19819
- if (pack.warnings.length > 0) {
19820
- status += ` Warnings: ${pack.warnings.join("; ")}`;
19821
- }
19822
- return status;
19823
- }
19824
20906
 
19825
20907
  // src/index.ts
20908
+ init_context_loader();
19826
20909
  dotenv.config();
19827
20910
  process.on("SIGTERM", () => {
19828
20911
  if (process.stdin.isTTY && process.stdin.setRawMode) {
@@ -19869,18 +20952,6 @@ function checkAutoCompact() {
19869
20952
  } catch {
19870
20953
  }
19871
20954
  }
19872
- async function checkStartupUpdates() {
19873
- try {
19874
- const versionInfo = await checkForUpdates();
19875
- if (versionInfo.isUpdateAvailable) {
19876
- console.log(`
19877
- \u{1F504} Update available: v${versionInfo.latest} (current: v${versionInfo.current})`);
19878
- console.log(` Use '/upgrade' command or run: ${versionInfo.updateCommand}
19879
- `);
19880
- }
19881
- } catch {
19882
- }
19883
- }
19884
20955
  function loadApiKey() {
19885
20956
  const manager = getSettingsManager();
19886
20957
  return manager.getApiKey();
@@ -20109,27 +21180,27 @@ program.name("grok").description(
20109
21180
  console.error("\u274C Error: X CLI requires an interactive terminal. Please run in a TTY environment.");
20110
21181
  process.exit(1);
20111
21182
  }
20112
- const agent = new GrokAgent(apiKey, baseURL, model, maxToolRounds);
21183
+ let contextPack;
21184
+ let statusMessage;
21185
+ try {
21186
+ contextPack = loadContext();
21187
+ statusMessage = formatContextStatus(contextPack);
21188
+ console.log(statusMessage);
21189
+ } catch (error) {
21190
+ console.warn("\u26A0\uFE0F Failed to load .agent/ context:", error instanceof Error ? error.message : String(error));
21191
+ }
21192
+ const agent = new GrokAgent(apiKey, baseURL, model, maxToolRounds, contextPack);
20113
21193
  const settingsManager = getSettingsManager();
20114
21194
  const assistantName = settingsManager.getUserSetting("assistantName") || "X CLI";
20115
21195
  if (!options.quiet) {
20116
21196
  console.log(`\u{1F916} Starting ${assistantName} Conversational Assistant...
20117
21197
  `);
20118
21198
  }
20119
- if (!options.quiet) {
20120
- try {
20121
- const contextPack = loadContext();
20122
- const statusMessage = formatContextStatus(contextPack);
20123
- console.log(statusMessage);
20124
- } catch (error) {
20125
- console.warn("\u26A0\uFE0F Failed to load .agent/ context:", error instanceof Error ? error.message : String(error));
20126
- }
20127
- }
20128
21199
  ensureUserSettingsDirectory();
20129
21200
  checkAutoCompact();
20130
- checkStartupUpdates();
20131
21201
  const initialMessage = Array.isArray(message) ? message.join(" ") : message;
20132
- const app = render(React4.createElement(ChatInterface, { agent, initialMessage, quiet: options.quiet }));
21202
+ printWelcomeBanner(options.quiet);
21203
+ const app = render(React4.createElement(ChatInterface, { agent, initialMessage, quiet: options.quiet, contextStatus: statusMessage }));
20133
21204
  const cleanup = () => {
20134
21205
  app.unmount();
20135
21206
  agent.abortCurrentOperation();