@xagent-ai/cli 1.2.0 → 1.2.2
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/README_CN.md +1 -1
- package/dist/agents.js +164 -164
- package/dist/agents.js.map +1 -1
- package/dist/ai-client.d.ts +4 -6
- package/dist/ai-client.d.ts.map +1 -1
- package/dist/ai-client.js +137 -115
- package/dist/ai-client.js.map +1 -1
- package/dist/auth.js +4 -4
- package/dist/auth.js.map +1 -1
- package/dist/cli.js +184 -1
- package/dist/cli.js.map +1 -1
- package/dist/config.js +3 -3
- package/dist/config.js.map +1 -1
- package/dist/context-compressor.d.ts.map +1 -1
- package/dist/context-compressor.js +65 -81
- package/dist/context-compressor.js.map +1 -1
- package/dist/conversation.d.ts +1 -1
- package/dist/conversation.d.ts.map +1 -1
- package/dist/conversation.js +5 -31
- package/dist/conversation.js.map +1 -1
- package/dist/memory.d.ts +5 -1
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +77 -37
- package/dist/memory.js.map +1 -1
- package/dist/remote-ai-client.d.ts +1 -8
- package/dist/remote-ai-client.d.ts.map +1 -1
- package/dist/remote-ai-client.js +55 -65
- package/dist/remote-ai-client.js.map +1 -1
- package/dist/retry.d.ts +35 -0
- package/dist/retry.d.ts.map +1 -0
- package/dist/retry.js +166 -0
- package/dist/retry.js.map +1 -0
- package/dist/session.d.ts +0 -5
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +243 -312
- package/dist/session.js.map +1 -1
- package/dist/slash-commands.d.ts +1 -0
- package/dist/slash-commands.d.ts.map +1 -1
- package/dist/slash-commands.js +91 -9
- package/dist/slash-commands.js.map +1 -1
- package/dist/smart-approval.d.ts.map +1 -1
- package/dist/smart-approval.js +18 -17
- package/dist/smart-approval.js.map +1 -1
- package/dist/system-prompt-generator.d.ts.map +1 -1
- package/dist/system-prompt-generator.js +149 -139
- package/dist/system-prompt-generator.js.map +1 -1
- package/dist/theme.d.ts +48 -0
- package/dist/theme.d.ts.map +1 -1
- package/dist/theme.js +254 -0
- package/dist/theme.js.map +1 -1
- package/dist/tools/edit-diff.d.ts +32 -0
- package/dist/tools/edit-diff.d.ts.map +1 -0
- package/dist/tools/edit-diff.js +185 -0
- package/dist/tools/edit-diff.js.map +1 -0
- package/dist/tools/edit.d.ts +11 -0
- package/dist/tools/edit.d.ts.map +1 -0
- package/dist/tools/edit.js +129 -0
- package/dist/tools/edit.js.map +1 -0
- package/dist/tools.d.ts +19 -5
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +979 -631
- package/dist/tools.js.map +1 -1
- package/dist/types.d.ts +6 -31
- package/dist/types.d.ts.map +1 -1
- package/package.json +3 -2
- package/src/agents.ts +504 -504
- package/src/ai-client.ts +1559 -1458
- package/src/auth.ts +4 -4
- package/src/cli.ts +195 -1
- package/src/config.ts +3 -3
- package/src/memory.ts +55 -14
- package/src/remote-ai-client.ts +663 -683
- package/src/retry.ts +217 -0
- package/src/session.ts +1736 -1840
- package/src/slash-commands.ts +98 -9
- package/src/smart-approval.ts +626 -625
- package/src/system-prompt-generator.ts +853 -843
- package/src/theme.ts +284 -0
- package/src/tools.ts +390 -70
package/src/theme.ts
CHANGED
|
@@ -1,5 +1,72 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Detect if terminal has dark or light background
|
|
5
|
+
* Returns 'dark' for dark backgrounds (default), 'light' for light backgrounds
|
|
6
|
+
*/
|
|
7
|
+
function getTerminalBackground(): 'dark' | 'light' {
|
|
8
|
+
// Check common environment variables
|
|
9
|
+
const colorfgbg = process.env.COLORFGBG; // e.g., "15;0" (light fg, dark bg)
|
|
10
|
+
const termProgram = process.env.TERM_PROGRAM;
|
|
11
|
+
const termProgramVersion = process.env.TERM_PROGRAM_VERSION;
|
|
12
|
+
|
|
13
|
+
// Try to parse COLORFGBG (format: "fg;bg" or "fg;color;bg")
|
|
14
|
+
if (colorfgbg) {
|
|
15
|
+
const parts = colorfgbg.split(';');
|
|
16
|
+
const bg = parts[parts.length - 1];
|
|
17
|
+
// 0-6 are typically dark colors, 7-15 are light
|
|
18
|
+
const bgNum = parseInt(bg, 10);
|
|
19
|
+
if (!isNaN(bgNum)) {
|
|
20
|
+
// Most terminal colors: 0=black, 1=red, 2=green, ... 7=white, 8-15=bright variants
|
|
21
|
+
// For background: 0-6 = dark, 7 = light
|
|
22
|
+
if (bgNum >= 0 && bgNum <= 6) return 'dark';
|
|
23
|
+
if (bgNum >= 7) return 'light';
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// iTerm2 on macOS can indicate background brightness
|
|
28
|
+
if (termProgram === 'Apple_Terminal' || termProgram === 'iTerm.app') {
|
|
29
|
+
// Apple Terminal and iTerm2 default to dark
|
|
30
|
+
return 'dark';
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Windows Terminal and VS Code Terminal typically dark
|
|
34
|
+
if (termProgram?.includes('WindowsTerminal') || termProgram?.includes('Code')) {
|
|
35
|
+
return 'dark';
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Default to dark background (most common for developers)
|
|
39
|
+
return 'dark';
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Mix a color with background to create a transparent effect
|
|
44
|
+
* @param hexColor - The base color in hex format
|
|
45
|
+
* @param opacity - Opacity from 0 (background) to 1 (full color)
|
|
46
|
+
* @param background - Terminal background color ('dark' or 'light')
|
|
47
|
+
*/
|
|
48
|
+
function mixWithBackground(hexColor: string, opacity: number, background: 'dark' | 'light' = 'dark'): string {
|
|
49
|
+
const hex = hexColor.replace('#', '');
|
|
50
|
+
const r = parseInt(hex.substring(0, 2), 16);
|
|
51
|
+
const g = parseInt(hex.substring(2, 4), 16);
|
|
52
|
+
const b = parseInt(hex.substring(4, 6), 16);
|
|
53
|
+
|
|
54
|
+
// Background colors
|
|
55
|
+
const bg = background === 'dark'
|
|
56
|
+
? { r: 0, g: 0, b: 0 } // Black for dark terminals
|
|
57
|
+
: { r: 255, g: 255, b: 255 }; // White for light terminals
|
|
58
|
+
|
|
59
|
+
const mr = Math.round(bg.r + (r - bg.r) * opacity);
|
|
60
|
+
const mg = Math.round(bg.g + (g - bg.g) * opacity);
|
|
61
|
+
const mb = Math.round(bg.b + (b - bg.b) * opacity);
|
|
62
|
+
|
|
63
|
+
return `#${mr.toString(16).padStart(2, '0')}${mg.toString(16).padStart(2, '0')}${mb.toString(16).padStart(2, '0')}`;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Get terminal background once at module load
|
|
67
|
+
const TERMINAL_BG = getTerminalBackground();
|
|
68
|
+
export { TERMINAL_BG, getTerminalBackground, mixWithBackground };
|
|
69
|
+
|
|
3
70
|
type ColorFunction = (text: string) => string;
|
|
4
71
|
|
|
5
72
|
interface BoxOptions {
|
|
@@ -66,6 +133,13 @@ export const colors = {
|
|
|
66
133
|
codeBackground: chalk.hex('#1f2937'), // Gray-800
|
|
67
134
|
codeText: chalk.hex('#e5e7eb'), // Gray-200
|
|
68
135
|
|
|
136
|
+
// Diff colors - git-style diff highlighting with transparent backgrounds
|
|
137
|
+
diffAdded: chalk.hex('#10b981'), // Green - added lines
|
|
138
|
+
diffRemoved: chalk.hex('#ef4444'), // Red - removed lines
|
|
139
|
+
diffContext: chalk.hex('#6b7280'), // Gray - context lines
|
|
140
|
+
diffAddedInverse: (text: string) => chalk.bgHex(mixWithBackground('#10b981', 0.25, TERMINAL_BG)).hex(TERMINAL_BG === 'dark' ? '#e5e7eb' : '#1f2937')(text), // Adaptive green bg
|
|
141
|
+
diffRemovedInverse: (text: string) => chalk.bgHex(mixWithBackground('#ef4444', 0.25, TERMINAL_BG)).hex(TERMINAL_BG === 'dark' ? '#e5e7eb' : '#1f2937')(text), // Adaptive red bg
|
|
142
|
+
|
|
69
143
|
// Gradient colors
|
|
70
144
|
gradient: (text: string) => {
|
|
71
145
|
const gradientColors = ['#06b6d4', '#8b5cf6', '#ec4899'];
|
|
@@ -452,4 +526,214 @@ export function renderMarkdown(text: string, maxWidth: number = 80): string {
|
|
|
452
526
|
return result.join('\n');
|
|
453
527
|
}
|
|
454
528
|
|
|
529
|
+
/**
|
|
530
|
+
* Parse a single diff line.
|
|
531
|
+
* Format: "+<num> content" or "-<num> content" or " <num> content"
|
|
532
|
+
* Also handles: "+<num>..." for skipped lines
|
|
533
|
+
*/
|
|
534
|
+
function parseDiffLine(line: string): { prefix: string; lineNum: string; content: string } | null {
|
|
535
|
+
// Skip empty lines
|
|
536
|
+
if (!line || line.trim() === '') return null;
|
|
537
|
+
|
|
538
|
+
const firstChar = line[0];
|
|
539
|
+
if (!['+', '-', ' '].includes(firstChar)) {
|
|
540
|
+
// Try to detect if this is a diff-like line with different format
|
|
541
|
+
// e.g., "+ 1 content" (with padding)
|
|
542
|
+
const match = line.match(/^[+\-\s]\s*/);
|
|
543
|
+
if (match) {
|
|
544
|
+
// This looks like a diff line but with unexpected format
|
|
545
|
+
// Extract the content after the prefix
|
|
546
|
+
const afterPrefix = line.slice(match[0].length).trim();
|
|
547
|
+
return { prefix: firstChar, lineNum: '', content: afterPrefix };
|
|
548
|
+
}
|
|
549
|
+
return null;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
// Extract content after the prefix char
|
|
553
|
+
const afterPrefix = line.slice(1).trim();
|
|
554
|
+
|
|
555
|
+
// Check for skipped lines marker
|
|
556
|
+
if (afterPrefix === '...') {
|
|
557
|
+
return { prefix: firstChar, lineNum: '', content: '...' };
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
// Return the rest as content
|
|
561
|
+
return { prefix: firstChar, lineNum: '', content: afterPrefix };
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
/**
|
|
565
|
+
* Replace tabs with spaces for consistent rendering.
|
|
566
|
+
*/
|
|
567
|
+
function replaceTabs(text: string): string {
|
|
568
|
+
return text.replace(/\t/g, ' ');
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
/**
|
|
572
|
+
* Compute word-level diff and render with inverse on changed parts.
|
|
573
|
+
* Uses diffWords which groups whitespace with adjacent words for cleaner highlighting.
|
|
574
|
+
*/
|
|
575
|
+
function renderIntraLineDiff(oldContent: string, newContent: string): { removedLine: string; addedLine: string } {
|
|
576
|
+
// Simple word diff without external dependency
|
|
577
|
+
const oldWords = oldContent.split(/(\s+)/);
|
|
578
|
+
const newWords = newContent.split(/(\s+)/);
|
|
579
|
+
|
|
580
|
+
let removedLine = '';
|
|
581
|
+
let addedLine = '';
|
|
582
|
+
let i = 0, j = 0;
|
|
583
|
+
|
|
584
|
+
while (i < oldWords.length || j < newWords.length) {
|
|
585
|
+
const oldWord = oldWords[i] || '';
|
|
586
|
+
const newWord = newWords[j] || '';
|
|
587
|
+
|
|
588
|
+
if (oldWord === newWord) {
|
|
589
|
+
removedLine += oldWord;
|
|
590
|
+
addedLine += newWord;
|
|
591
|
+
i++;
|
|
592
|
+
j++;
|
|
593
|
+
} else if (oldWord === '' || oldWord.match(/^\s+$/)) {
|
|
594
|
+
addedLine += newWord;
|
|
595
|
+
j++;
|
|
596
|
+
} else if (newWord === '' || newWord.match(/^\s+$/)) {
|
|
597
|
+
removedLine += oldWord;
|
|
598
|
+
i++;
|
|
599
|
+
} else {
|
|
600
|
+
// Simple heuristic: show removed with inverse, added with inverse
|
|
601
|
+
removedLine += colors.diffRemovedInverse(oldWord);
|
|
602
|
+
addedLine += colors.diffAddedInverse(newWord);
|
|
603
|
+
i++;
|
|
604
|
+
j++;
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
return { removedLine, addedLine };
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
/**
|
|
612
|
+
* Render a diff string with colored lines and intra-line change highlighting.
|
|
613
|
+
* - Context lines: gray
|
|
614
|
+
* - Removed lines: red
|
|
615
|
+
* - Added lines: green
|
|
616
|
+
*/
|
|
617
|
+
export function renderDiff(diffText: string): string {
|
|
618
|
+
if (!diffText) return '';
|
|
619
|
+
|
|
620
|
+
const lines = diffText.split('\n');
|
|
621
|
+
const result: string[] = [];
|
|
622
|
+
|
|
623
|
+
let i = 0;
|
|
624
|
+
while (i < lines.length) {
|
|
625
|
+
const line = lines[i];
|
|
626
|
+
const parsed = parseDiffLine(line);
|
|
627
|
+
|
|
628
|
+
if (!parsed) {
|
|
629
|
+
result.push(colors.diffContext(line));
|
|
630
|
+
i++;
|
|
631
|
+
continue;
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
if (parsed.prefix === '-') {
|
|
635
|
+
// Collect consecutive removed lines
|
|
636
|
+
const removedLines: { lineNum: string; content: string }[] = [];
|
|
637
|
+
while (i < lines.length) {
|
|
638
|
+
const p = parseDiffLine(lines[i]);
|
|
639
|
+
if (!p || p.prefix !== '-') break;
|
|
640
|
+
removedLines.push({ lineNum: p.lineNum, content: p.content });
|
|
641
|
+
i++;
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
// Collect consecutive added lines
|
|
645
|
+
const addedLines: { lineNum: string; content: string }[] = [];
|
|
646
|
+
while (i < lines.length) {
|
|
647
|
+
const p = parseDiffLine(lines[i]);
|
|
648
|
+
if (!p || p.prefix !== '+') break;
|
|
649
|
+
addedLines.push({ lineNum: p.lineNum, content: p.content });
|
|
650
|
+
i++;
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
// Intra-line diff for single line modification
|
|
654
|
+
if (removedLines.length === 1 && addedLines.length === 1) {
|
|
655
|
+
const removed = removedLines[0];
|
|
656
|
+
const added = addedLines[0];
|
|
657
|
+
const { removedLine, addedLine } = renderIntraLineDiff(replaceTabs(removed.content), replaceTabs(added.content));
|
|
658
|
+
const textColor = TERMINAL_BG === 'dark' ? '#e5e7eb' : '#1f2937';
|
|
659
|
+
result.push(chalk.bgHex(mixWithBackground('#ef4444', 0.25, TERMINAL_BG)).hex(textColor)(`-${removedLine}`));
|
|
660
|
+
result.push(chalk.bgHex(mixWithBackground('#10b981', 0.25, TERMINAL_BG)).hex(textColor)(`+${addedLine}`));
|
|
661
|
+
} else {
|
|
662
|
+
const textColor = TERMINAL_BG === 'dark' ? '#e5e7eb' : '#1f2937';
|
|
663
|
+
for (const removed of removedLines) {
|
|
664
|
+
result.push(chalk.bgHex(mixWithBackground('#ef4444', 0.25, TERMINAL_BG)).hex(textColor)(`-${replaceTabs(removed.content)}`));
|
|
665
|
+
}
|
|
666
|
+
for (const added of addedLines) {
|
|
667
|
+
result.push(chalk.bgHex(mixWithBackground('#10b981', 0.25, TERMINAL_BG)).hex(textColor)(`+${replaceTabs(added.content)}`));
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
} else if (parsed.prefix === '+') {
|
|
671
|
+
const textColor = TERMINAL_BG === 'dark' ? '#e5e7eb' : '#1f2937';
|
|
672
|
+
result.push(chalk.bgHex(mixWithBackground('#10b981', 0.25, TERMINAL_BG)).hex(textColor)(`+${replaceTabs(parsed.content)}`));
|
|
673
|
+
i++;
|
|
674
|
+
} else {
|
|
675
|
+
result.push(chalk.hex('#374151')(` ${replaceTabs(parsed.content)}`));
|
|
676
|
+
i++;
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
return result.join('\n');
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
/**
|
|
684
|
+
* Render a code preview with line numbers and syntax highlighting.
|
|
685
|
+
* Automatically adapts to dark/light terminal backgrounds.
|
|
686
|
+
*/
|
|
687
|
+
export function renderCodePreview(content: string, options: { filePath?: string; maxLines?: number; indent?: string } = {}): string {
|
|
688
|
+
const { filePath = '', maxLines = 10, indent = '' } = options;
|
|
689
|
+
const lines = content.split('\n');
|
|
690
|
+
const displayLines = lines.slice(0, maxLines);
|
|
691
|
+
const hasMore = lines.length > maxLines;
|
|
692
|
+
|
|
693
|
+
const textColor = TERMINAL_BG === 'dark' ? '#e5e7eb' : '#1f2931';
|
|
694
|
+
const bgColor = TERMINAL_BG === 'dark' ? '#1f2937' : '#f3f4f6';
|
|
695
|
+
const codeStyle = chalk.bgHex(bgColor).hex(textColor);
|
|
696
|
+
|
|
697
|
+
const lineNumWidth = String(displayLines.length).length;
|
|
698
|
+
const parts: string[] = [];
|
|
699
|
+
|
|
700
|
+
if (filePath) {
|
|
701
|
+
parts.push(`${indent}${chalk.hex(TERMINAL_BG === 'dark' ? '#22d3ee' : '#0891b2').bold(filePath)}`);
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
displayLines.forEach((line, idx) => {
|
|
705
|
+
const lineNum = String(idx + 1).padStart(lineNumWidth, ' ');
|
|
706
|
+
parts.push(`${indent} ${codeStyle(`${lineNum} │ ${line}`)}`);
|
|
707
|
+
});
|
|
708
|
+
|
|
709
|
+
if (hasMore) {
|
|
710
|
+
const dotsLine = `${indent} ${chalk.hex(TERMINAL_BG === 'dark' ? '#6b7280' : '#9ca3af').dim('...')}`;
|
|
711
|
+
parts.push(dotsLine);
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
return parts.join('\n');
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
/**
|
|
718
|
+
* Render new file content in diff-like style (without line numbers).
|
|
719
|
+
* Uses + prefix for added lines.
|
|
720
|
+
*/
|
|
721
|
+
export function renderLines(content: string, options: { maxLines?: number; indent?: string } = {}): string {
|
|
722
|
+
const { maxLines = 20, indent = '' } = options;
|
|
723
|
+
const lines = content.split('\n').slice(0, maxLines);
|
|
724
|
+
const hasMore = content.split('\n').length > maxLines;
|
|
725
|
+
|
|
726
|
+
const textColor = TERMINAL_BG === 'dark' ? '#e5e7eb' : '#1f2937';
|
|
727
|
+
const bgColor = mixWithBackground('#10b981', 0.15, TERMINAL_BG);
|
|
728
|
+
const lineStyle = chalk.bgHex(bgColor).hex(textColor);
|
|
729
|
+
|
|
730
|
+
const parts: string[] = lines.map(line => `${indent}${lineStyle(`+ ${line}`)}`);
|
|
731
|
+
|
|
732
|
+
if (hasMore) {
|
|
733
|
+
parts.push(`${indent}${chalk.hex(TERMINAL_BG === 'dark' ? '#6b7280' : '#9ca3af').dim('...')}`);
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
return parts.join('\n');
|
|
737
|
+
}
|
|
738
|
+
|
|
455
739
|
export default theme;
|