@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/README.md +1 -1
- package/dist/index.js +1318 -247
- package/dist/index.js.map +1 -1
- package/package.json +11 -11
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
|
|
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: "
|
|
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: "
|
|
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 (
|
|
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(
|
|
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,
|
|
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,
|
|
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 (
|
|
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 (
|
|
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,
|
|
7169
|
+
const dfs = (filePath, path32) => {
|
|
7044
7170
|
if (visiting.has(filePath)) {
|
|
7045
|
-
const cycleStart =
|
|
7046
|
-
const cycle =
|
|
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, [...
|
|
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
|
-
*
|
|
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
|
-
|
|
8482
|
-
return
|
|
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
|
-
*
|
|
9448
|
+
* Handle revision flow with updated request
|
|
8486
9449
|
*/
|
|
8487
|
-
|
|
8488
|
-
|
|
8489
|
-
|
|
8490
|
-
|
|
8491
|
-
|
|
8492
|
-
|
|
8493
|
-
|
|
8494
|
-
|
|
8495
|
-
|
|
8496
|
-
|
|
8497
|
-
return
|
|
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
|
-
|
|
8500
|
-
|
|
8501
|
-
|
|
8502
|
-
|
|
8503
|
-
|
|
8504
|
-
|
|
8505
|
-
|
|
8506
|
-
|
|
8507
|
-
|
|
8508
|
-
|
|
8509
|
-
|
|
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
|
-
|
|
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
|
|
9053
|
-
let command = `cat "${
|
|
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' "${
|
|
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
|
|
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
|
|
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
|
|
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:
|
|
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 =
|
|
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:
|
|
14813
|
+
const { execSync: execSync3 } = __require("child_process");
|
|
13691
14814
|
try {
|
|
13692
|
-
const commits =
|
|
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 =
|
|
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.
|
|
14678
|
-
description: "An open-source AI agent that brings
|
|
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
|
-
"
|
|
14738
|
-
"ai"
|
|
15860
|
+
"ai",
|
|
15861
|
+
"x-ai"
|
|
14739
15862
|
],
|
|
14740
|
-
author: "
|
|
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://
|
|
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.
|
|
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
|
|
16761
|
-
const
|
|
16762
|
-
|
|
16763
|
-
|
|
16764
|
-
const
|
|
16765
|
-
|
|
16766
|
-
|
|
16767
|
-
|
|
16768
|
-
|
|
16769
|
-
|
|
16770
|
-
|
|
16771
|
-
|
|
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
|
|
17907
|
+
const npmSkipEntry = {
|
|
16774
17908
|
type: "assistant",
|
|
16775
|
-
content: `\
|
|
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,
|
|
17912
|
+
setChatHistory((prev) => [...prev, npmSkipEntry]);
|
|
16779
17913
|
}
|
|
16780
17914
|
} else {
|
|
16781
|
-
const
|
|
17915
|
+
const npmSkipEntry = {
|
|
16782
17916
|
type: "assistant",
|
|
16783
|
-
content: `\
|
|
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,
|
|
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
|
|
17197
|
-
messagesCount =
|
|
17198
|
-
const tokenPercent = Math.round(
|
|
18330
|
+
const sessionTokens = agent.getSessionTokenCount?.() || 0;
|
|
18331
|
+
messagesCount = agent.getMessageCount?.() || 0;
|
|
18332
|
+
const tokenPercent = Math.round(sessionTokens / maxTokens * 100);
|
|
17199
18333
|
tokenUsage = {
|
|
17200
|
-
current:
|
|
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
|
-
|
|
17637
|
-
|
|
17638
|
-
|
|
17639
|
-
|
|
17640
|
-
|
|
17641
|
-
|
|
17642
|
-
|
|
17643
|
-
|
|
17644
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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();
|