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/lib/logo.js CHANGED
@@ -1,144 +1,81 @@
1
- /**
2
- * V Code — Retro ASCII Logo with animated blinking eyes.
3
- */
4
-
5
1
  import chalk from 'chalk';
2
+ import { Byte, type ByteMood } from './byte/index.js';
6
3
 
7
- const FRAMES_OPEN = [
8
- `
9
- ╔═══════════════════════════════════════════════════════╗
10
- ║ ║
11
- ██╗ ██╗ ██████╗ ██████╗ ██████╗ ███████╗ ║
12
- ║ ██║ ██║ ██╔════╝██╔═══██╗██╔══██╗██╔════╝ ║
13
- ██║ ██║ ██║ ██║ ██║██║ ██║█████╗ ║
14
- ║ ╚██╗ ██╔╝ ██║ ██║ ██║██║ ██║██╔══╝ ║
15
- ║ ╚████╔╝ ╚██████╗╚██████╔╝██████╔╝███████╗ ║
16
- ║ ╚═══╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚══════╝ ║
17
- ║ ║
18
- ║ ┌──────────────────────┐ ║
19
- ║ │ ◉ ◉ │ ║
20
- ║ │ ─── │ ║
21
- ║ └──────────────────────┘ ║
22
- ║ ║
23
- ╚═══════════════════════════════════════════════════════╝`,
24
- ];
25
-
26
- const FRAMES_BLINK = [
27
- `
28
- ╔═══════════════════════════════════════════════════════╗
29
- ║ ║
30
- ║ ██╗ ██╗ ██████╗ ██████╗ ██████╗ ███████╗ ║
31
- ║ ██║ ██║ ██╔════╝██╔═══██╗██╔══██╗██╔════╝ ║
32
- ║ ██║ ██║ ██║ ██║ ██║██║ ██║█████╗ ║
33
- ║ ╚██╗ ██╔╝ ██║ ██║ ██║██║ ██║██╔══╝ ║
34
- ║ ╚████╔╝ ╚██████╗╚██████╔╝██████╔╝███████╗ ║
35
- ║ ╚═══╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚══════╝ ║
36
- ║ ║
37
- ║ ┌──────────────────────┐ ║
38
- ║ │ ━ ━ │ ║
39
- ║ │ ─── │ ║
40
- ║ └──────────────────────┘ ║
41
- ║ ║
42
- ╚═══════════════════════════════════════════════════════╝`,
43
- ];
4
+ const V_CODE_LOGO = `
5
+ ██████╗ ██████╗ ███████╗███╗ ███╗███████╗███╗ ██╗
6
+ ██╔════╝██╔═══██╗██╔════╝████╗ ████║██╔════╝████╗ ██║
7
+ ██║ ██║ ██║█████╗ ██╔████╔██║█████╗ ██╔██╗ ██║
8
+ ██║ ██║ ██║██╔══╝ ██║╚██╔╝██║██╔══╝ ██║╚██╗██║
9
+ ╚██████╗╚██████╔╝███████╗██║ ╚═╝ ██║███████╗██║ ╚████║
10
+ ╚═════╝ ╚═════╝ ╚══════╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═══╝
11
+ `;
44
12
 
45
- const FRAMES_LOOK_LEFT = [
46
- `
47
- ╔═══════════════════════════════════════════════════════╗
48
- ║ ║
49
- ║ ██╗ ██╗ ██████╗ ██████╗ ██████╗ ███████╗ ║
50
- ║ ██║ ██║ ██╔════╝██╔═══██╗██╔══██╗██╔════╝ ║
51
- ║ ██║ ██║ ██║ ██║ ██║██║ ██║█████╗
52
- ║ ╚██╗ ██╔╝ ██║ ██║ ██║██║ ██║██╔══╝ ║
53
- ║ ╚████╔╝ ╚██████╗╚██████╔╝██████╔╝███████╗ ║
54
- ║ ╚═══╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚══════╝ ║
55
- ║ ║
56
- ║ ┌──────────────────────┐ ║
57
- ║ │ ◉ ◉ │ ║
58
- ║ │ ─── │ ║
59
- ║ └──────────────────────┘ ║
60
- ║ ║
61
- ╚═══════════════════════════════════════════════════════╝`,
13
+ const EYE_FRAMES = [
14
+ ` ◉ ◉`,
15
+ ` ● ●`,
16
+ ` ● ●`,
17
+ ` ░ ░`,
18
+ ` ░ ░`,
19
+ ` ◉`,
62
20
  ];
63
21
 
64
- const FRAMES_LOOK_RIGHT = [
65
- `
66
- ╔═══════════════════════════════════════════════════════╗
67
- ║ ║
68
- ║ ██╗ ██╗ ██████╗ ██████╗ ██████╗ ███████╗ ║
69
- ║ ██║ ██║ ██╔════╝██╔═══██╗██╔══██╗██╔════╝ ║
70
- ║ ██║ ██║ ██║ ██║ ██║██║ ██║█████╗ ║
71
- ║ ╚██╗ ██╔╝ ██║ ██║ ██║██║ ██║██╔══╝ ║
72
- ║ ╚████╔╝ ╚██████╗╚██████╔╝██████╔╝███████╗ ║
73
- ║ ╚═══╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚══════╝ ║
74
- ║ ║
75
- ║ ┌──────────────────────┐ ║
76
- ║ │ ◉ ◉ │ ║
77
- ║ │ ─── │ ║
78
- ║ └──────────────────────┘ ║
79
- ║ ║
80
- ╚═══════════════════════════════════════════════════════╝`,
81
- ];
22
+ function clearScreen() {
23
+ process.stdout.write('\x1Bc');
24
+ }
82
25
 
83
- const allFrames = [FRAMES_OPEN, FRAMES_BLINK, FRAMES_LOOK_LEFT, FRAMES_LOOK_RIGHT];
26
+ async function animateEyes(duration: number = 3000) {
27
+ return new Promise<void>(resolve => {
28
+ let frameIndex = 0;
29
+ const interval = setInterval(() => {
30
+ frameIndex = (frameIndex + 1) % EYE_FRAMES.length;
31
+ }, 150);
84
32
 
85
- function colorize(frame) {
86
- return frame
87
- .replace(/[██╗╔╚╝║═╗╝╬╦╩╠╣█▀▄]/g, (m) => chalk.hex('#a855f7')(m))
88
- .replace(/[◉]/g, () => chalk.hex('#22d3ee')('◉'))
89
- .replace(/[━]/g, () => chalk.hex('#64748b')('━'))
90
- .replace(/[┌┐└┘│─┘┐]/g, (m) => chalk.hex('#334155')(m))
91
- .replace(/───/g, chalk.hex('#f97316')('───'));
33
+ setTimeout(() => {
34
+ clearInterval(interval);
35
+ resolve();
36
+ }, duration);
37
+ });
92
38
  }
93
39
 
94
- /**
95
- * Show static logo.
96
- */
97
- export async function showLogo(animate = false) {
98
- console.clear();
99
- const frame = colorize(FRAMES_OPEN[0]);
100
- console.log(frame);
101
- console.log(chalk.gray(' ─────────────────────────────────────────────────────'));
102
- console.log(chalk.hex('#a855f7').bold(' Vynthen V Code — Local Bridge'));
103
- console.log(chalk.gray(' ─────────────────────────────────────────────────────\n'));
40
+ export async function showLogo(mini: boolean = false, showAnimation: boolean = true) {
41
+ clearScreen();
42
+
43
+ if (showAnimation) {
44
+ console.log(chalk.hex('#a855f7').bold(mini ? '' : V_CODE_LOGO));
45
+ await animateEyes(2000);
46
+ } else {
47
+ console.log(chalk.hex('#a855f7').bold(mini ? '' : V_CODE_LOGO));
48
+ }
104
49
  }
105
50
 
106
- /**
107
- * Animate logo with blinking eyes. Returns a stop function.
108
- */
109
51
  export function startLogoAnimation() {
110
52
  let running = true;
111
- const sequence = [
112
- { frames: FRAMES_OPEN, duration: 2500 },
113
- { frames: FRAMES_BLINK, duration: 150 },
114
- { frames: FRAMES_OPEN, duration: 300 },
115
- { frames: FRAMES_BLINK, duration: 150 },
116
- { frames: FRAMES_OPEN, duration: 3000 },
117
- { frames: FRAMES_LOOK_LEFT, duration: 800 },
118
- { frames: FRAMES_OPEN, duration: 1500 },
119
- { frames: FRAMES_LOOK_RIGHT, duration: 800 },
120
- { frames: FRAMES_OPEN, duration: 2000 },
121
- ];
122
-
123
- let idx = 0;
124
-
125
- const render = () => {
126
- if (!running) return;
127
- const step = sequence[idx % sequence.length];
128
- // Move cursor to top and re-draw logo portion
129
- process.stdout.write('\x1b[1;1H');
130
- process.stdout.write(colorize(step.frames[0]));
131
- idx++;
132
- setTimeout(render, step.duration);
53
+
54
+ const render = async () => {
55
+ clearScreen();
56
+ console.log(chalk.hex('#a855f7').bold(V_CODE_LOGO));
57
+ await animateEyes(1500);
58
+ if (running) render();
133
59
  };
134
-
135
- console.clear();
60
+
136
61
  render();
137
- // Print static footer below the logo area
138
- process.stdout.write('\n');
139
- process.stdout.write(chalk.gray(' ─────────────────────────────────────────────────────\n'));
140
- process.stdout.write(chalk.hex('#a855f7').bold(' Vynthen V Code — Local Bridge\n'));
141
- process.stdout.write(chalk.gray(' ─────────────────────────────────────────────────────\n'));
142
-
62
+
143
63
  return () => { running = false; };
144
64
  }
65
+
66
+ export function showVersion() {
67
+ const version = 'v2.0.0';
68
+ const columns = process.stdout.columns || 80;
69
+ const padding = Math.max(0, columns - version.length - 1);
70
+ console.log(chalk.gray(' '.repeat(padding) + version));
71
+ }
72
+
73
+ export function showStartup(user: string, plan: string, project: string) {
74
+ console.log('');
75
+ console.log(chalk.gray(' User: ') + chalk.cyan(user));
76
+ console.log(chalk.gray(' Plan: ') + (plan === 'pro' ? chalk.green(plan) : chalk.yellow(plan)));
77
+ console.log(chalk.gray(' Project: ') + chalk.white(project));
78
+ console.log('');
79
+ }
80
+
81
+ export { Byte, type ByteMood };
@@ -0,0 +1,315 @@
1
+ import * as fs from 'fs';
2
+ import * as path from 'path';
3
+ import chalk from 'chalk';
4
+ import { diffLines } from 'diff';
5
+
6
+ export interface FileOperation {
7
+ type: 'read' | 'write' | 'delete' | 'create_dir' | 'rename' | 'move';
8
+ path: string;
9
+ content?: string;
10
+ newPath?: string;
11
+ timestamp: number;
12
+ }
13
+
14
+ const operationHistory: FileOperation[] = [];
15
+ const MAX_HISTORY = 50;
16
+
17
+ export function addOperation(op: FileOperation): void {
18
+ operationHistory.unshift(op);
19
+ if (operationHistory.length > MAX_HISTORY) {
20
+ operationHistory.pop();
21
+ }
22
+ logOperation(op);
23
+ }
24
+
25
+ export function getOperationHistory(): FileOperation[] {
26
+ return [...operationHistory];
27
+ }
28
+
29
+ export function getLastOperation(): FileOperation | null {
30
+ return operationHistory[0] || null;
31
+ }
32
+
33
+ export function clearHistory(): void {
34
+ operationHistory.length = 0;
35
+ }
36
+
37
+ function logOperation(op: FileOperation): void {
38
+ const homeDir = process.env.HOME || process.env.USERPROFILE || '';
39
+ const logDir = path.join(homeDir, '.vcode');
40
+ const logFile = path.join(logDir, 'audit.log');
41
+
42
+ // Ensure directory exists
43
+ if (!fs.existsSync(logDir)) {
44
+ fs.mkdirSync(logDir, { recursive: true });
45
+ }
46
+
47
+ const logEntry = `[${new Date().toISOString()}] ${op.type}: ${op.path}${op.newPath ? ` -> ${op.newPath}` : ''}\n`;
48
+
49
+ try {
50
+ fs.appendFileSync(logFile, logEntry);
51
+ } catch {
52
+ // Silently fail if we can't write to log
53
+ }
54
+ }
55
+
56
+ // File operations
57
+ export async function readFile(filePath: string): Promise<string> {
58
+ const fullPath = path.resolve(filePath);
59
+
60
+ if (!fs.existsSync(fullPath)) {
61
+ throw new Error(`File not found: ${filePath}`);
62
+ }
63
+
64
+ const stats = fs.statSync(fullPath);
65
+ if (stats.isDirectory()) {
66
+ throw new Error(`Path is a directory: ${filePath}`);
67
+ }
68
+
69
+ const content = fs.readFileSync(fullPath, 'utf8');
70
+
71
+ addOperation({
72
+ type: 'read',
73
+ path: fullPath,
74
+ timestamp: Date.now()
75
+ });
76
+
77
+ return content;
78
+ }
79
+
80
+ export async function writeFile(filePath: string, content: string, createDiff = true): Promise<{ success: boolean; diff?: string }> {
81
+ const fullPath = path.resolve(filePath);
82
+ const dir = path.dirname(fullPath);
83
+
84
+ // Ensure directory exists
85
+ if (!fs.existsSync(dir)) {
86
+ fs.mkdirSync(dir, { recursive: true });
87
+ }
88
+
89
+ let diffOutput = '';
90
+ if (createDiff && fs.existsSync(fullPath)) {
91
+ const oldContent = fs.readFileSync(fullPath, 'utf8');
92
+ diffOutput = generateDiff(fullPath, oldContent, content);
93
+ }
94
+
95
+ fs.writeFileSync(fullPath, content, 'utf8');
96
+
97
+ addOperation({
98
+ type: 'write',
99
+ path: fullPath,
100
+ content,
101
+ timestamp: Date.now()
102
+ });
103
+
104
+ return { success: true, diff: diffOutput };
105
+ }
106
+
107
+ export async function deleteFile(filePath: string): Promise<boolean> {
108
+ const fullPath = path.resolve(filePath);
109
+
110
+ if (!fs.existsSync(fullPath)) {
111
+ throw new Error(`Path does not exist: ${filePath}`);
112
+ }
113
+
114
+ const stats = fs.statSync(fullPath);
115
+
116
+ if (stats.isDirectory()) {
117
+ fs.rmSync(fullPath, { recursive: true });
118
+ } else {
119
+ fs.unlinkSync(fullPath);
120
+ }
121
+
122
+ addOperation({
123
+ type: 'delete',
124
+ path: fullPath,
125
+ timestamp: Date.now()
126
+ });
127
+
128
+ return true;
129
+ }
130
+
131
+ export async function createDirectory(dirPath: string): Promise<boolean> {
132
+ const fullPath = path.resolve(dirPath);
133
+
134
+ if (fs.existsSync(fullPath)) {
135
+ throw new Error(`Directory already exists: ${dirPath}`);
136
+ }
137
+
138
+ fs.mkdirSync(fullPath, { recursive: true });
139
+
140
+ addOperation({
141
+ type: 'create_dir',
142
+ path: fullPath,
143
+ timestamp: Date.now()
144
+ });
145
+
146
+ return true;
147
+ }
148
+
149
+ export async function renameFile(oldPath: string, newPath: string): Promise<boolean> {
150
+ const fullOldPath = path.resolve(oldPath);
151
+ const fullNewPath = path.resolve(newPath);
152
+
153
+ if (!fs.existsSync(fullOldPath)) {
154
+ throw new Error(`Path does not exist: ${oldPath}`);
155
+ }
156
+
157
+ fs.renameSync(fullOldPath, fullNewPath);
158
+
159
+ addOperation({
160
+ type: 'rename',
161
+ path: fullOldPath,
162
+ newPath: fullNewPath,
163
+ timestamp: Date.now()
164
+ });
165
+
166
+ return true;
167
+ }
168
+
169
+ export async function listDirectory(dirPath: string): Promise<Array<{ name: string; isDirectory: boolean; path: string }>> {
170
+ const fullPath = path.resolve(dirPath);
171
+
172
+ if (!fs.existsSync(fullPath)) {
173
+ throw new Error(`Directory not found: ${dirPath}`);
174
+ }
175
+
176
+ const stats = fs.statSync(fullPath);
177
+ if (!stats.isDirectory()) {
178
+ throw new Error(`Path is not a directory: ${dirPath}`);
179
+ }
180
+
181
+ const items = fs.readdirSync(fullPath, { withFileTypes: true });
182
+
183
+ return items.map(item => ({
184
+ name: item.name,
185
+ isDirectory: item.isDirectory(),
186
+ path: path.join(fullPath, item.name)
187
+ }));
188
+ }
189
+
190
+ export async function getFileTree(dirPath: string, maxDepth = 3, currentDepth = 0): Promise<Array<{ name: string; type: 'file' | 'directory'; path: string; children?: any[] }>> {
191
+ if (currentDepth >= maxDepth) return [];
192
+
193
+ const fullPath = path.resolve(dirPath);
194
+
195
+ if (!fs.existsSync(fullPath)) {
196
+ throw new Error(`Directory not found: ${dirPath}`);
197
+ }
198
+
199
+ const items = fs.readdirSync(fullPath, { withFileTypes: true });
200
+ const result = [];
201
+
202
+ for (const item of items) {
203
+ // Skip hidden files and common ignored directories
204
+ if (item.name.startsWith('.') || ['node_modules', 'dist', 'build', '__pycache__', '.git'].includes(item.name)) {
205
+ continue;
206
+ }
207
+
208
+ const itemPath = path.join(fullPath, item.name);
209
+
210
+ if (item.isDirectory()) {
211
+ const children = await getFileTree(itemPath, maxDepth, currentDepth + 1);
212
+ result.push({
213
+ name: item.name,
214
+ type: 'directory',
215
+ path: itemPath,
216
+ children
217
+ });
218
+ } else {
219
+ result.push({
220
+ name: item.name,
221
+ type: 'file',
222
+ path: itemPath
223
+ });
224
+ }
225
+ }
226
+
227
+ return result.sort((a, b) => {
228
+ if (a.type === b.type) return a.name.localeCompare(b.name);
229
+ return a.type === 'directory' ? -1 : 1;
230
+ });
231
+ }
232
+
233
+ export function generateDiff(filePath: string, oldContent: string, newContent: string): string {
234
+ const changes = diffLines(oldContent, newContent);
235
+ let output = chalk.bold.cyan(`\n--- ${filePath}\n`);
236
+ output += chalk.bold.cyan(`+++ ${filePath}\n`);
237
+
238
+ let lineNum = 1;
239
+ for (const change of changes) {
240
+ const lines = change.value.split('\n').filter(l => l !== '');
241
+ for (const line of lines) {
242
+ if (change.added) {
243
+ output += chalk.green(`+${lineNum}: ${line}\n`);
244
+ } else if (change.removed) {
245
+ output += chalk.red(`-${lineNum}: ${line}\n`);
246
+ } else {
247
+ output += chalk.gray(` ${lineNum}: ${line}\n`);
248
+ }
249
+ lineNum++;
250
+ }
251
+ }
252
+
253
+ return output;
254
+ }
255
+
256
+ export function formatFileTree(tree: any[], prefix = '', isLast = true): string {
257
+ let output = '';
258
+
259
+ for (let i = 0; i < tree.length; i++) {
260
+ const item = tree[i];
261
+ const isLastItem = i === tree.length - 1;
262
+ const connector = isLastItem ? '└── ' : '├── ';
263
+ const typeIcon = item.type === 'directory' ? '📁 ' : '📄 ';
264
+
265
+ output += `${prefix}${connector}${typeIcon}${item.name}\n`;
266
+
267
+ if (item.children) {
268
+ const newPrefix = prefix + (isLastItem ? ' ' : '│ ');
269
+ output += formatFileTree(item.children, newPrefix, isLastItem);
270
+ }
271
+ }
272
+
273
+ return output;
274
+ }
275
+
276
+ export async function searchFiles(dirPath: string, pattern: string, isRegex = false): Promise<Array<{ file: string; line: number; content: string }>> {
277
+ const results: Array<{ file: string; line: number; content: string }> = [];
278
+ const regex = isRegex ? new RegExp(pattern) : new RegExp(pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'i');
279
+
280
+ async function searchInDir(dir: string) {
281
+ const items = fs.readdirSync(dir, { withFileTypes: true });
282
+
283
+ for (const item of items) {
284
+ const itemPath = path.join(dir, item.name);
285
+
286
+ if (item.name.startsWith('.') || ['node_modules', 'dist', 'build', '__pycache__', '.git'].includes(item.name)) {
287
+ continue;
288
+ }
289
+
290
+ if (item.isDirectory()) {
291
+ await searchInDir(itemPath);
292
+ } else {
293
+ try {
294
+ const content = fs.readFileSync(itemPath, 'utf8');
295
+ const lines = content.split('\n');
296
+
297
+ for (let i = 0; i < lines.length; i++) {
298
+ if (regex.test(lines[i])) {
299
+ results.push({
300
+ file: itemPath,
301
+ line: i + 1,
302
+ content: lines[i].trim()
303
+ });
304
+ }
305
+ }
306
+ } catch {
307
+ // Skip files that can't be read
308
+ }
309
+ }
310
+ }
311
+ }
312
+
313
+ await searchInDir(path.resolve(dirPath));
314
+ return results;
315
+ }
@@ -0,0 +1,87 @@
1
+ import inquirer from 'inquirer';
2
+ import chalk from 'chalk';
3
+
4
+ export type PermissionDecision = 'approve' | 'deny' | 'approve_all' | 'deny_all' | 'skip' | 'skip_all';
5
+
6
+ let sessionApproveAll = false;
7
+ let sessionDenyAll = false;
8
+
9
+ export function setSessionApproveAll(enabled: boolean): void {
10
+ sessionApproveAll = enabled;
11
+ }
12
+
13
+ export function setSessionDenyAll(enabled: boolean): void {
14
+ sessionDenyAll = enabled;
15
+ }
16
+
17
+ export function resetSessionPermissions(): void {
18
+ sessionApproveAll = false;
19
+ sessionDenyAll = false;
20
+ }
21
+
22
+ export async function askPermission(message: string, type: 'write' | 'delete' | 'command'): Promise<PermissionDecision> {
23
+ if (sessionApproveAll) return 'approve';
24
+ if (sessionDenyAll) return 'deny';
25
+
26
+ const choices = [
27
+ { name: 'y', value: 'approve', short: 'y - yes' },
28
+ { name: 'n', value: 'deny', short: 'n - no' },
29
+ { name: 'a', value: 'approve_all', short: 'a - approve all' },
30
+ { name: 's', value: 'skip_all', short: 's - skip all' }
31
+ ];
32
+
33
+ const typeIndicator = {
34
+ write: chalk.green('📝'),
35
+ delete: chalk.red('🗑️'),
36
+ command: chalk.yellow('⚡')
37
+ }[type];
38
+
39
+ const { decision } = await inquirer.prompt([
40
+ {
41
+ type: 'list',
42
+ name: 'decision',
43
+ message: `${typeIndicator} ${message}`,
44
+ choices,
45
+ default: 'deny'
46
+ }
47
+ ]);
48
+
49
+ if (decision === 'approve_all') {
50
+ sessionApproveAll = true;
51
+ } else if (decision === 'skip_all') {
52
+ sessionDenyAll = true;
53
+ }
54
+
55
+ return decision;
56
+ }
57
+
58
+ export async function confirmDelete(path: string): Promise<boolean> {
59
+ console.log(chalk.red(`\n ⚠️ WARNING: You are about to DELETE:`));
60
+ console.log(chalk.red(` ${path}`));
61
+ console.log(chalk.red(` This cannot be undone!\n`));
62
+
63
+ const { confirm } = await inquirer.prompt([
64
+ {
65
+ type: 'confirm',
66
+ name: 'confirm',
67
+ message: chalk.red(' Are you absolutely sure? Type "yes" to confirm:'),
68
+ default: false
69
+ }
70
+ ]);
71
+
72
+ return confirm === true;
73
+ }
74
+
75
+ export async function showDiffAndConfirm(path: string, diff: string): Promise<PermissionDecision> {
76
+ console.log(chalk.bold.cyan(`\n 📝 File changes for: ${path}`));
77
+ console.log(diff);
78
+
79
+ return askPermission(`Apply these changes?`, 'write');
80
+ }
81
+
82
+ export async function showCommandAndConfirm(command: string): Promise<PermissionDecision> {
83
+ console.log(chalk.bold.yellow(`\n ⚡ Command to execute:`));
84
+ console.log(chalk.bgYellow.black(` ${command} `));
85
+
86
+ return askPermission(`Run this command?`, 'command');
87
+ }