vcode-cli 1.0.4 → 2.0.0
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/LICENSE +24 -21
- package/bin/vcode.js +63 -41
- package/lib/agent/ai.js +131 -0
- package/lib/agent/context.js +111 -0
- package/lib/agent/loop.js +237 -0
- package/lib/auth/index.js +120 -0
- package/lib/byte/index.js +207 -0
- package/lib/commands/add.js +9 -0
- package/lib/commands/clear.js +8 -0
- package/lib/commands/config.js +37 -0
- package/lib/commands/help.js +27 -0
- package/lib/commands/history.js +29 -0
- package/lib/commands/start.js +17 -137
- package/lib/commands/undo.js +23 -0
- package/lib/logo.js +64 -127
- package/lib/tools/filesystem.js +315 -0
- package/lib/tools/permissions.js +87 -0
- package/lib/tools/shell.js +150 -0
- package/lib/ui/byte.js +178 -0
- package/lib/ui/logo.js +53 -0
- package/package.json +13 -16
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { spawn } from 'child_process';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
|
|
4
|
+
export interface CommandResult {
|
|
5
|
+
success: boolean;
|
|
6
|
+
stdout: string;
|
|
7
|
+
stderr: string;
|
|
8
|
+
exitCode: number;
|
|
9
|
+
command: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const DANGEROUS_COMMANDS = ['rm -rf', 'del /f /s /q', 'format', 'dd if=', 'mkfs', ':(){:|:&};:'];
|
|
13
|
+
|
|
14
|
+
export async function runCommand(command: string, cwd?: string, timeout = 60000): Promise<CommandResult> {
|
|
15
|
+
const isWindows = process.platform === 'win32';
|
|
16
|
+
const shell = isWindows ? 'cmd.exe' : '/bin/bash';
|
|
17
|
+
const shellArgs = isWindows ? ['/c', command] : ['-c', command];
|
|
18
|
+
|
|
19
|
+
// Security check
|
|
20
|
+
for (const dangerous of DANGEROUS_COMMANDS) {
|
|
21
|
+
if (command.toLowerCase().includes(dangerous.toLowerCase())) {
|
|
22
|
+
return {
|
|
23
|
+
success: false,
|
|
24
|
+
stdout: '',
|
|
25
|
+
stderr: `Command blocked: contains potentially dangerous operation "${dangerous}"`,
|
|
26
|
+
exitCode: 1,
|
|
27
|
+
command
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return new Promise((resolve) => {
|
|
33
|
+
let stdout = '';
|
|
34
|
+
let stderr = '';
|
|
35
|
+
|
|
36
|
+
const child = spawn(shell, shellArgs, {
|
|
37
|
+
cwd: cwd || process.cwd(),
|
|
38
|
+
env: { ...process.env, FORCE_COLOR: '1' }
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
const timeoutId = setTimeout(() => {
|
|
42
|
+
child.kill();
|
|
43
|
+
resolve({
|
|
44
|
+
success: false,
|
|
45
|
+
stdout,
|
|
46
|
+
stderr: 'Command timed out',
|
|
47
|
+
exitCode: 124,
|
|
48
|
+
command
|
|
49
|
+
});
|
|
50
|
+
}, timeout);
|
|
51
|
+
|
|
52
|
+
child.stdout.on('data', (data) => {
|
|
53
|
+
stdout += data.toString();
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
child.stderr.on('data', (data) => {
|
|
57
|
+
stderr += data.toString();
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
child.on('close', (code) => {
|
|
61
|
+
clearTimeout(timeoutId);
|
|
62
|
+
resolve({
|
|
63
|
+
success: code === 0,
|
|
64
|
+
stdout,
|
|
65
|
+
stderr,
|
|
66
|
+
exitCode: code || 0,
|
|
67
|
+
command
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
child.on('error', (err) => {
|
|
72
|
+
clearTimeout(timeoutId);
|
|
73
|
+
resolve({
|
|
74
|
+
success: false,
|
|
75
|
+
stdout,
|
|
76
|
+
stderr: err.message,
|
|
77
|
+
exitCode: 1,
|
|
78
|
+
command
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function formatCommandOutput(result: CommandResult): string {
|
|
85
|
+
let output = '';
|
|
86
|
+
|
|
87
|
+
if (result.stdout) {
|
|
88
|
+
output += chalk.white(result.stdout);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (result.stderr) {
|
|
92
|
+
output += chalk.red(result.stderr);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return output;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export async function runGitCommand(args: string[], cwd?: string): Promise<CommandResult> {
|
|
99
|
+
return runCommand(`git ${args.join(' ')}`, cwd);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export async function gitStatus(cwd?: string): Promise<{ modified: string[]; untracked: string[]; staged: string[] }> {
|
|
103
|
+
const result = await runGitCommand(['status', '--porcelain'], cwd);
|
|
104
|
+
|
|
105
|
+
if (!result.success) {
|
|
106
|
+
throw new Error(result.stderr);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const modified: string[] = [];
|
|
110
|
+
const untracked: string[] = [];
|
|
111
|
+
const staged: string[] = [];
|
|
112
|
+
|
|
113
|
+
for (const line of result.stdout.split('\n').filter(l => l.trim())) {
|
|
114
|
+
const status = line.substring(0, 2);
|
|
115
|
+
const file = line.substring(3);
|
|
116
|
+
|
|
117
|
+
if (status.includes('M') || status.includes('D')) {
|
|
118
|
+
modified.push(file);
|
|
119
|
+
}
|
|
120
|
+
if (status.includes('??')) {
|
|
121
|
+
untracked.push(file);
|
|
122
|
+
}
|
|
123
|
+
if (status.startsWith('M') || status.startsWith('A')) {
|
|
124
|
+
staged.push(file);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return { modified, untracked, staged };
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export async function gitDiff(file?: string, cwd?: string): Promise<string> {
|
|
132
|
+
const args = file ? ['diff', file] : ['diff'];
|
|
133
|
+
const result = await runGitCommand(args, cwd);
|
|
134
|
+
return result.stdout || result.stderr;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export async function gitCommit(message: string, cwd?: string): Promise<boolean> {
|
|
138
|
+
const result = await runGitCommand(['commit', '-m', message], cwd);
|
|
139
|
+
return result.success;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export async function gitPush(cwd?: string): Promise<boolean> {
|
|
143
|
+
const result = await runGitCommand(['push'], cwd);
|
|
144
|
+
return result.success;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export async function gitPull(cwd?: string): Promise<boolean> {
|
|
148
|
+
const result = await runGitCommand(['pull'], cwd);
|
|
149
|
+
return result.success;
|
|
150
|
+
}
|
package/lib/ui/byte.js
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
|
|
3
|
+
export type ByteMood = 'idle' | 'thinking' | 'working' | 'success' | 'error' | 'waiting' | 'celebrating';
|
|
4
|
+
|
|
5
|
+
const BYTE_ART = {
|
|
6
|
+
idle: `
|
|
7
|
+
/\\_____/\\
|
|
8
|
+
/ o o \\
|
|
9
|
+
( == ^ == )
|
|
10
|
+
) (
|
|
11
|
+
( )
|
|
12
|
+
( ( ) ( ) )
|
|
13
|
+
(__(__)___(__)__)
|
|
14
|
+
`,
|
|
15
|
+
|
|
16
|
+
thinking: `
|
|
17
|
+
/\\_____/\\
|
|
18
|
+
/ ? ? \\
|
|
19
|
+
( == O == )
|
|
20
|
+
) (
|
|
21
|
+
( ? )
|
|
22
|
+
( ( ) ( ) )
|
|
23
|
+
(__(__)___(__)__)
|
|
24
|
+
| |
|
|
25
|
+
_| |_
|
|
26
|
+
`,
|
|
27
|
+
|
|
28
|
+
working: `
|
|
29
|
+
/\\_____/\\
|
|
30
|
+
/ o o \\
|
|
31
|
+
( == ^ == )
|
|
32
|
+
) /+\\ (
|
|
33
|
+
( | )
|
|
34
|
+
( ( ) ( ) )
|
|
35
|
+
(__(__)___(__)__)
|
|
36
|
+
_||_ _||_
|
|
37
|
+
`,
|
|
38
|
+
|
|
39
|
+
success: `
|
|
40
|
+
/\\_____/\\
|
|
41
|
+
/ ^ ^ \\
|
|
42
|
+
( == ^ == )
|
|
43
|
+
) ^ (
|
|
44
|
+
( \\o/ )
|
|
45
|
+
( ( ) ( ) )
|
|
46
|
+
(__(__)___(__)__)
|
|
47
|
+
\\o/ \\o/
|
|
48
|
+
`,
|
|
49
|
+
|
|
50
|
+
error: `
|
|
51
|
+
/\\_____/\\
|
|
52
|
+
/ x x \\
|
|
53
|
+
( == ^ == )
|
|
54
|
+
) ___ (
|
|
55
|
+
( |_| )
|
|
56
|
+
( ( ) ( ) )
|
|
57
|
+
(__(__)___(__)__)
|
|
58
|
+
_____
|
|
59
|
+
| |
|
|
60
|
+
|_____|
|
|
61
|
+
`,
|
|
62
|
+
|
|
63
|
+
waiting: `
|
|
64
|
+
/\\_____/\\
|
|
65
|
+
/ o o \\
|
|
66
|
+
( == ^ == )
|
|
67
|
+
) ~ (
|
|
68
|
+
( | )
|
|
69
|
+
( ( ) ( ) )
|
|
70
|
+
(__(__)___(__)__)
|
|
71
|
+
--- --->>>
|
|
72
|
+
`,
|
|
73
|
+
|
|
74
|
+
celebrating: `
|
|
75
|
+
/\\_____/\\
|
|
76
|
+
/ ^ ^ \\
|
|
77
|
+
( == ^ == )
|
|
78
|
+
) \\o/ (
|
|
79
|
+
( ( ) )
|
|
80
|
+
( ( ) ( ) )
|
|
81
|
+
(__(__)___(__)__)
|
|
82
|
+
\\o/ \\o/
|
|
83
|
+
`
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const MESSAGES = {
|
|
87
|
+
idle: ['ready when you are~', 'waiting for your command~', 'what shall we build?'],
|
|
88
|
+
thinking: ['hmm let me think...', 'processing...', 'give me a moment~'],
|
|
89
|
+
working: ['on it~', 'working on it...', 'type type type~'],
|
|
90
|
+
success: ['done! purr~', 'all done!', '任务完成!'],
|
|
91
|
+
error: ['uh oh...', 'oopsie~', 'that didn\'t work~'],
|
|
92
|
+
waiting: ['you sure about this?', 'waiting for your approval~', 'need your okay~'],
|
|
93
|
+
celebrating: ['we did it!', 'purrfect!', 'amazing work!']
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
let currentMood: ByteMood = 'idle';
|
|
97
|
+
let companionEnabled = true;
|
|
98
|
+
let animationInterval: NodeJS.Timeout | null = null;
|
|
99
|
+
|
|
100
|
+
export function setCompanionEnabled(enabled: boolean): void {
|
|
101
|
+
companionEnabled = enabled;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function isCompanionEnabled(): boolean {
|
|
105
|
+
return companionEnabled;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export function showByte(mood: ByteMood, customMessage?: string): void {
|
|
109
|
+
if (!companionEnabled) return;
|
|
110
|
+
|
|
111
|
+
currentMood = mood;
|
|
112
|
+
const art = BYTE_ART[mood];
|
|
113
|
+
const messages = MESSAGES[mood];
|
|
114
|
+
const message = customMessage || messages[Math.floor(Math.random() * messages.length)];
|
|
115
|
+
|
|
116
|
+
// Clear previous animation
|
|
117
|
+
if (animationInterval) {
|
|
118
|
+
clearInterval(animationInterval);
|
|
119
|
+
animationInterval = null;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// For working mood, animate
|
|
123
|
+
if (mood === 'working') {
|
|
124
|
+
let frame = 0;
|
|
125
|
+
const frames = ['|', '/', '-', '\\'];
|
|
126
|
+
animationInterval = setInterval(() => {
|
|
127
|
+
const frameArt = BYTE_ART.working.replace(/\|/g, frames[frame % 4]);
|
|
128
|
+
console.clear();
|
|
129
|
+
console.log(chalk.hex('#a855f7')(frameArt));
|
|
130
|
+
console.log(chalk.gray(` ${message}`));
|
|
131
|
+
frame++;
|
|
132
|
+
}, 100);
|
|
133
|
+
} else {
|
|
134
|
+
console.clear();
|
|
135
|
+
console.log(chalk.hex('#a855f7')(art));
|
|
136
|
+
console.log(chalk.gray(` ${message}`));
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export function clearByte(): void {
|
|
141
|
+
if (animationInterval) {
|
|
142
|
+
clearInterval(animationInterval);
|
|
143
|
+
animationInterval = null;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export function getByteMessage(mood: ByteMood): string {
|
|
148
|
+
const messages = MESSAGES[mood];
|
|
149
|
+
return messages[Math.floor(Math.random() * messages.length)];
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export function showByteIdle(): void {
|
|
153
|
+
showByte('idle');
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export function showByteThinking(): void {
|
|
157
|
+
showByte('thinking');
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export function showByteWorking(): void {
|
|
161
|
+
showByte('working');
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export function showByteSuccess(): void {
|
|
165
|
+
showByte('success');
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export function showByteError(): void {
|
|
169
|
+
showByte('error');
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export function showByteWaiting(message?: string): void {
|
|
173
|
+
showByte('waiting', message);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export function showByteCelebrating(): void {
|
|
177
|
+
showByte('celebrating');
|
|
178
|
+
}
|
package/lib/ui/logo.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { showByteIdle } from './byte.js';
|
|
3
|
+
|
|
4
|
+
const LOGO = `
|
|
5
|
+
██████╗ ██╗██████╗ ███████╗██╗ ██╗███╗ ██╗███████╗
|
|
6
|
+
██╔═══██╗██║██╔══██╗██╔════╝██║ ██║████╗ ██║██╔════╝
|
|
7
|
+
██║ ██║██║██████╔╝█████╗ ██║ ██║██╔██╗ ██║█████╗
|
|
8
|
+
██║ ██║██║██╔══██╗██╔══╝ ██║ ██║██║╚██╗██║██╔══╝
|
|
9
|
+
╚██████╔╝██║██║ ██║███████╗███████╗██║██║ ╚████║███████╗
|
|
10
|
+
╚═════╝ ╚═╝╚═╝ ╚═╝╚══════╝╚══════╝╚═╝╚═╝ ╚═══╝╚══════╝
|
|
11
|
+
|
|
12
|
+
██████╗ ███████╗███████╗██╗ ██╗███╗ ██╗
|
|
13
|
+
██╔══██╗██╔════╝██╔════╝██║ ██║████╗ ██║
|
|
14
|
+
██║ ██║█████╗ █████╗ ██║ ██║██╔██╗ ██║
|
|
15
|
+
██║ ██║██╔══╝ ██╔══╝ ██║ ██║██║╚██╗██║
|
|
16
|
+
██████╔╝███████╗███████╗███████╗██║██║ ╚████║
|
|
17
|
+
╚═════╝ ╚══════╝╚══════╝╚══════╝╚═╝╚═╝ ╚═══╝
|
|
18
|
+
`;
|
|
19
|
+
|
|
20
|
+
let blinkInterval: NodeJS.Timeout | null = null;
|
|
21
|
+
|
|
22
|
+
export function showLogo(): void {
|
|
23
|
+
console.clear();
|
|
24
|
+
|
|
25
|
+
const blink = () => {
|
|
26
|
+
console.clear();
|
|
27
|
+
const eyeFrame = Math.floor(Date.now() / 500) % 2;
|
|
28
|
+
const logo = eyeFrame === 0 ? LOGO.replace(/O/g, '●').replace(/o/g, '●') : LOGO;
|
|
29
|
+
console.log(chalk.hex('#a855f7').bold(logo));
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
blinkInterval = setInterval(blink, 500);
|
|
33
|
+
|
|
34
|
+
// Stop after initial animation
|
|
35
|
+
setTimeout(() => {
|
|
36
|
+
if (blinkInterval) clearInterval(blinkInterval);
|
|
37
|
+
console.clear();
|
|
38
|
+
console.log(chalk.hex('#a855f7').bold(LOGO));
|
|
39
|
+
showByteIdle();
|
|
40
|
+
}, 3000);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function showLogoOnce(): void {
|
|
44
|
+
console.clear();
|
|
45
|
+
console.log(chalk.hex('#a855f7').bold(LOGO));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function stopAnimation(): void {
|
|
49
|
+
if (blinkInterval) {
|
|
50
|
+
clearInterval(blinkInterval);
|
|
51
|
+
blinkInterval = null;
|
|
52
|
+
}
|
|
53
|
+
}
|
package/package.json
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vcode-cli",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"description": "
|
|
5
|
+
"description": "V Code — Terminal AI Coding Agent powered by Vynthen. Autonomous file editing, terminal commands, and smart context management.",
|
|
6
6
|
"main": "bin/vcode.js",
|
|
7
7
|
"bin": {
|
|
8
8
|
"vcode": "bin/vcode.js"
|
|
9
9
|
},
|
|
10
10
|
"scripts": {
|
|
11
|
-
"start": "node bin/vcode.js
|
|
12
|
-
"
|
|
11
|
+
"start": "node bin/vcode.js",
|
|
12
|
+
"help": "node bin/vcode.js help",
|
|
13
13
|
"test": "echo \"No tests yet\" && exit 0"
|
|
14
14
|
},
|
|
15
15
|
"keywords": [
|
|
@@ -19,10 +19,11 @@
|
|
|
19
19
|
"code-agent",
|
|
20
20
|
"cli",
|
|
21
21
|
"terminal",
|
|
22
|
-
"developer-tools"
|
|
22
|
+
"developer-tools",
|
|
23
|
+
"coding-assistant"
|
|
23
24
|
],
|
|
24
|
-
"author": "
|
|
25
|
-
"license": "
|
|
25
|
+
"author": "Tibebe Solomon <hello@vynthen.com>",
|
|
26
|
+
"license": "PROPRIETARY",
|
|
26
27
|
"repository": {
|
|
27
28
|
"type": "git",
|
|
28
29
|
"url": "https://gitlab.com/TibebeSolomon/vcode-cli.git"
|
|
@@ -38,19 +39,15 @@
|
|
|
38
39
|
],
|
|
39
40
|
"dependencies": {
|
|
40
41
|
"chalk": "^5.3.0",
|
|
41
|
-
"clipboardy": "^4.0.0",
|
|
42
42
|
"commander": "^12.1.0",
|
|
43
|
+
"diff": "^5.2.0",
|
|
43
44
|
"inquirer": "^9.3.0",
|
|
44
|
-
"keytar": "^7.9.0"
|
|
45
|
-
"open": "^10.1.0",
|
|
46
|
-
"ora": "^8.0.1",
|
|
47
|
-
"screenshot-desktop": "^1.15.0",
|
|
48
|
-
"ws": "^8.18.0"
|
|
45
|
+
"keytar": "^7.9.0"
|
|
49
46
|
},
|
|
50
47
|
"files": [
|
|
51
48
|
"bin/",
|
|
52
49
|
"lib/",
|
|
53
|
-
"
|
|
54
|
-
"
|
|
50
|
+
"LICENSE",
|
|
51
|
+
"README.md"
|
|
55
52
|
]
|
|
56
|
-
}
|
|
53
|
+
}
|