vibecodingmachine-cli 2026.3.9-850 → 2026.3.10-1547

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.
Files changed (39) hide show
  1. package/README.md +85 -85
  2. package/bin/commands/agent-commands.js +295 -28
  3. package/bin/vibecodingmachine.js +0 -0
  4. package/package.json +2 -2
  5. package/scripts/postinstall.js +161 -161
  6. package/src/commands/auth.js +100 -100
  7. package/src/commands/auto-execution.js +120 -32
  8. package/src/commands/auto-requirement-management.js +9 -9
  9. package/src/commands/auto-status-helpers.js +6 -12
  10. package/src/commands/computers.js +318 -318
  11. package/src/commands/feature.js +123 -123
  12. package/src/commands/locale.js +72 -72
  13. package/src/commands/repo.js +163 -163
  14. package/src/commands/setup.js +93 -93
  15. package/src/commands/sync.js +287 -287
  16. package/src/index.js +5 -5
  17. package/src/utils/agent-selector.js +50 -50
  18. package/src/utils/asset-cleanup.js +60 -60
  19. package/src/utils/auth.js +6 -0
  20. package/src/utils/auto-mode-ansi-ui.js +237 -237
  21. package/src/utils/auto-mode-simple-ui.js +141 -141
  22. package/src/utils/copy-with-progress.js +167 -167
  23. package/src/utils/download-with-progress.js +84 -84
  24. package/src/utils/keyboard-handler.js +153 -153
  25. package/src/utils/kiro-installer.js +178 -178
  26. package/src/utils/logger.js +4 -4
  27. package/src/utils/persistent-header.js +114 -114
  28. package/src/utils/prompt-helper.js +63 -63
  29. package/src/utils/provider-checker/agent-runner.js +110 -31
  30. package/src/utils/provider-checker/ide-manager.js +37 -8
  31. package/src/utils/provider-checker/provider-validator.js +50 -0
  32. package/src/utils/provider-checker/requirements-manager.js +21 -6
  33. package/src/utils/status-card.js +121 -121
  34. package/src/utils/stdout-interceptor.js +127 -127
  35. package/src/utils/trui-main-handlers.js +41 -8
  36. package/src/utils/trui-main-menu.js +10 -3
  37. package/src/utils/trui-nav-agents.js +23 -33
  38. package/src/utils/trui-navigation.js +2 -2
  39. package/src/utils/user-tracking.js +299 -299
@@ -1,178 +1,178 @@
1
- const fs = require('fs-extra');
2
- const os = require('os');
3
- const ora = require('ora');
4
- const chalk = require('chalk');
5
-
6
- // Placeholder check - this should ideally check for the actual application path
7
- // On macOS, typically /Applications/AWS Kiro.app or similar
8
- // For now, we'll check a simulated path or just return false to test the flow
9
- function isKiroInstalled() {
10
- const platform = os.platform();
11
- if (platform === 'darwin') {
12
- return fs.existsSync('/Applications/AWS Kiro.app') || fs.existsSync('/Applications/Kiro.app');
13
- }
14
- // Add Windows/Linux checks if needed
15
- return false;
16
- }
17
-
18
- async function installKiro() {
19
- console.log(chalk.cyan('\nšŸš€ Initiating AWS Kiro AI IDE Installation...'));
20
-
21
- const spinner = ora('Checking system requirements...').start();
22
- await new Promise(resolve => setTimeout(resolve, 1000));
23
-
24
- // Detect architecture
25
- const arch = process.arch;
26
- const isArm = arch === 'arm64';
27
-
28
- // Verified working URL for ARM64 (v202507161958)
29
- // For x64, we don't have the exact timestamp/hash, so recent failures suggest we might need to fallback to manual
30
- const downloadUrl = isArm
31
- ? 'https://prod.download.desktop.kiro.dev/releases/202507161958-Kiro-dmg-darwin-arm64.dmg'
32
- : 'https://kiro.dev/downloads/'; // Fallback to downloads page for x64 until we find the direct link
33
-
34
- spinner.succeed(`System requirements met (${isArm ? 'Apple Silicon' : 'Intel'}).`);
35
-
36
- // for x64, we can't automate yet
37
- if (!isArm) {
38
- console.log(chalk.yellow('\nāš ļø Automated installation is currently only available for Apple Silicon (M1/M2/M3).'));
39
- console.log(chalk.cyan('Opening download page...'));
40
- // open v11+ is ESM only
41
- const open = (await import('open')).default;
42
- await open(downloadUrl);
43
- return false;
44
- }
45
-
46
- try {
47
- const fs = require('fs-extra');
48
- const path = require('path');
49
- const os = require('os');
50
- const { execSync } = require('child_process');
51
-
52
- console.log(chalk.cyan(`\nā¬‡ļø Downloading AWS Kiro for macOS (${arch})...`));
53
- const cacheDir = path.join(os.homedir(), '.vibecodingmachine', 'cache');
54
- await fs.ensureDir(cacheDir);
55
- const cachedPath = path.join(cacheDir, 'Kiro.dmg');
56
- const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'kiro-install-'));
57
- const dmgPath = path.join(tempDir, 'Kiro.dmg');
58
- const { downloadWithProgress } = require('./download-with-progress');
59
-
60
- // Try to reuse cached DMG to avoid re-downloading
61
- let usedCache = false;
62
- try {
63
- if (await fs.pathExists(cachedPath)) {
64
- try {
65
- const fetch = require('node-fetch');
66
- const head = await fetch(downloadUrl, { method: 'HEAD' });
67
- const total = Number(head.headers.get('content-length')) || 0;
68
- const stat = await fs.stat(cachedPath);
69
- if (total && stat.size === total) {
70
- await fs.copy(cachedPath, dmgPath);
71
- usedCache = true;
72
- console.log(chalk.gray('Using cached Kiro DMG'));
73
- }
74
- } catch (e) {
75
- await fs.copy(cachedPath, dmgPath);
76
- usedCache = true;
77
- console.log(chalk.gray('Using cached Kiro DMG (no HEAD)'));
78
- }
79
- }
80
- } catch (e) {
81
- usedCache = false;
82
- }
83
-
84
- if (!usedCache) {
85
- await downloadWithProgress(downloadUrl, dmgPath, { label: 'Downloading AWS Kiro...' });
86
- try { await fs.copy(dmgPath, cachedPath); } catch (e) { /* ignore */ }
87
- }
88
-
89
- const installSpinner = ora('Installing AWS Kiro...').start();
90
-
91
- try {
92
- // Mount DMG
93
- installSpinner.text = 'Mounting disk image...';
94
- // -nobrowse to prevent opening Finder window, -noverify to speed up
95
- execSync(`hdiutil attach "${dmgPath}" -nobrowse -noverify -mountpoint "${tempDir}/mount"`);
96
-
97
- // Copy App
98
- installSpinner.text = 'Copying to Applications...';
99
-
100
- // Look for .app file
101
- const files = await fs.readdir(path.join(tempDir, 'mount'));
102
- const appName = files.find(f => f.endsWith('.app'));
103
-
104
- if (!appName) {
105
- throw new Error('Could not find .app file in the downloaded DMG.');
106
- }
107
-
108
- const fullSourcePath = path.join(tempDir, 'mount', appName);
109
- const destinationPath = path.join('/Applications', appName);
110
-
111
- // Remove existing if any
112
- if (await fs.pathExists(destinationPath)) {
113
- installSpinner.text = 'Replacing existing installation...';
114
- await fs.remove(destinationPath);
115
- }
116
- // Use robust copy with progress (rsync -> ditto -> node stream)
117
- try {
118
- const { copyAppWithProgress } = require('./copy-with-progress');
119
- installSpinner.stop();
120
- const ok = await copyAppWithProgress(fullSourcePath, destinationPath, { spinner: installSpinner });
121
- if (!ok) {
122
- console.log('Falling back to cp -R for installation...');
123
- installSpinner.start();
124
- try {
125
- execSync(`cp -R "${fullSourcePath}" "/Applications/"`, { stdio: 'inherit' });
126
- } catch (e) {
127
- installSpinner.text = 'Copying (fs fallback) to /Applications...';
128
- await fs.copy(fullSourcePath, destinationPath);
129
- }
130
- } else {
131
- installSpinner.start();
132
- }
133
- } catch (e) {
134
- installSpinner.text = 'Copying (cp/fs fallback) to /Applications...';
135
- try {
136
- execSync(`cp -R "${fullSourcePath}" "/Applications/"`, { stdio: 'inherit' });
137
- } catch (err) {
138
- await fs.copy(fullSourcePath, destinationPath);
139
- }
140
- }
141
-
142
- // Unmount
143
- installSpinner.text = 'Cleaning up...';
144
- execSync(`hdiutil detach "${tempDir}/mount" -force`);
145
- await fs.remove(tempDir);
146
-
147
- installSpinner.succeed('AWS Kiro successfully installed!');
148
- console.log(chalk.green(`\nāœ… Installed to ${destinationPath}`));
149
-
150
- return true;
151
- } catch (installError) {
152
- installSpinner.fail('Installation failed during file operations');
153
- // Try to cleanup even if install failed
154
- try {
155
- if (fs.existsSync(path.join(tempDir, 'mount'))) {
156
- execSync(`hdiutil detach "${tempDir}/mount" -force`);
157
- }
158
- await fs.remove(tempDir);
159
- } catch (cleanupError) {
160
- // Ignore cleanup errors
161
- }
162
- throw installError;
163
- }
164
-
165
- } catch (error) {
166
- // If error was thrown, spinners should have been handled, but just in case
167
- // The main catch block for installKiro should not call ora().fail()
168
- // as specific spinners (downloadSpinner, installSpinner) would have already failed.
169
- // We log the manual download link
170
- console.log(chalk.red('\nYou may need to download manually from: https://kiro.dev'));
171
- return false;
172
- }
173
- }
174
-
175
- module.exports = {
176
- isKiroInstalled,
177
- installKiro
178
- };
1
+ const fs = require('fs-extra');
2
+ const os = require('os');
3
+ const ora = require('ora');
4
+ const chalk = require('chalk');
5
+
6
+ // Placeholder check - this should ideally check for the actual application path
7
+ // On macOS, typically /Applications/AWS Kiro.app or similar
8
+ // For now, we'll check a simulated path or just return false to test the flow
9
+ function isKiroInstalled() {
10
+ const platform = os.platform();
11
+ if (platform === 'darwin') {
12
+ return fs.existsSync('/Applications/AWS Kiro.app') || fs.existsSync('/Applications/Kiro.app');
13
+ }
14
+ // Add Windows/Linux checks if needed
15
+ return false;
16
+ }
17
+
18
+ async function installKiro() {
19
+ console.log(chalk.cyan('\nšŸš€ Initiating AWS Kiro AI IDE Installation...'));
20
+
21
+ const spinner = ora('Checking system requirements...').start();
22
+ await new Promise(resolve => setTimeout(resolve, 1000));
23
+
24
+ // Detect architecture
25
+ const arch = process.arch;
26
+ const isArm = arch === 'arm64';
27
+
28
+ // Verified working URL for ARM64 (v202507161958)
29
+ // For x64, we don't have the exact timestamp/hash, so recent failures suggest we might need to fallback to manual
30
+ const downloadUrl = isArm
31
+ ? 'https://prod.download.desktop.kiro.dev/releases/202507161958-Kiro-dmg-darwin-arm64.dmg'
32
+ : 'https://kiro.dev/downloads/'; // Fallback to downloads page for x64 until we find the direct link
33
+
34
+ spinner.succeed(`System requirements met (${isArm ? 'Apple Silicon' : 'Intel'}).`);
35
+
36
+ // for x64, we can't automate yet
37
+ if (!isArm) {
38
+ console.log(chalk.yellow('\nāš ļø Automated installation is currently only available for Apple Silicon (M1/M2/M3).'));
39
+ console.log(chalk.cyan('Opening download page...'));
40
+ // open v11+ is ESM only
41
+ const open = (await import('open')).default;
42
+ await open(downloadUrl);
43
+ return false;
44
+ }
45
+
46
+ try {
47
+ const fs = require('fs-extra');
48
+ const path = require('path');
49
+ const os = require('os');
50
+ const { execSync } = require('child_process');
51
+
52
+ console.log(chalk.cyan(`\nā¬‡ļø Downloading AWS Kiro for macOS (${arch})...`));
53
+ const cacheDir = path.join(os.homedir(), '.vibecodingmachine', 'cache');
54
+ await fs.ensureDir(cacheDir);
55
+ const cachedPath = path.join(cacheDir, 'Kiro.dmg');
56
+ const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'kiro-install-'));
57
+ const dmgPath = path.join(tempDir, 'Kiro.dmg');
58
+ const { downloadWithProgress } = require('./download-with-progress');
59
+
60
+ // Try to reuse cached DMG to avoid re-downloading
61
+ let usedCache = false;
62
+ try {
63
+ if (await fs.pathExists(cachedPath)) {
64
+ try {
65
+ const fetch = require('node-fetch');
66
+ const head = await fetch(downloadUrl, { method: 'HEAD' });
67
+ const total = Number(head.headers.get('content-length')) || 0;
68
+ const stat = await fs.stat(cachedPath);
69
+ if (total && stat.size === total) {
70
+ await fs.copy(cachedPath, dmgPath);
71
+ usedCache = true;
72
+ console.log(chalk.gray('Using cached Kiro DMG'));
73
+ }
74
+ } catch (e) {
75
+ await fs.copy(cachedPath, dmgPath);
76
+ usedCache = true;
77
+ console.log(chalk.gray('Using cached Kiro DMG (no HEAD)'));
78
+ }
79
+ }
80
+ } catch (e) {
81
+ usedCache = false;
82
+ }
83
+
84
+ if (!usedCache) {
85
+ await downloadWithProgress(downloadUrl, dmgPath, { label: 'Downloading AWS Kiro...' });
86
+ try { await fs.copy(dmgPath, cachedPath); } catch (e) { /* ignore */ }
87
+ }
88
+
89
+ const installSpinner = ora('Installing AWS Kiro...').start();
90
+
91
+ try {
92
+ // Mount DMG
93
+ installSpinner.text = 'Mounting disk image...';
94
+ // -nobrowse to prevent opening Finder window, -noverify to speed up
95
+ execSync(`hdiutil attach "${dmgPath}" -nobrowse -noverify -mountpoint "${tempDir}/mount"`);
96
+
97
+ // Copy App
98
+ installSpinner.text = 'Copying to Applications...';
99
+
100
+ // Look for .app file
101
+ const files = await fs.readdir(path.join(tempDir, 'mount'));
102
+ const appName = files.find(f => f.endsWith('.app'));
103
+
104
+ if (!appName) {
105
+ throw new Error('Could not find .app file in the downloaded DMG.');
106
+ }
107
+
108
+ const fullSourcePath = path.join(tempDir, 'mount', appName);
109
+ const destinationPath = path.join('/Applications', appName);
110
+
111
+ // Remove existing if any
112
+ if (await fs.pathExists(destinationPath)) {
113
+ installSpinner.text = 'Replacing existing installation...';
114
+ await fs.remove(destinationPath);
115
+ }
116
+ // Use robust copy with progress (rsync -> ditto -> node stream)
117
+ try {
118
+ const { copyAppWithProgress } = require('./copy-with-progress');
119
+ installSpinner.stop();
120
+ const ok = await copyAppWithProgress(fullSourcePath, destinationPath, { spinner: installSpinner });
121
+ if (!ok) {
122
+ console.log('Falling back to cp -R for installation...');
123
+ installSpinner.start();
124
+ try {
125
+ execSync(`cp -R "${fullSourcePath}" "/Applications/"`, { stdio: 'inherit' });
126
+ } catch (e) {
127
+ installSpinner.text = 'Copying (fs fallback) to /Applications...';
128
+ await fs.copy(fullSourcePath, destinationPath);
129
+ }
130
+ } else {
131
+ installSpinner.start();
132
+ }
133
+ } catch (e) {
134
+ installSpinner.text = 'Copying (cp/fs fallback) to /Applications...';
135
+ try {
136
+ execSync(`cp -R "${fullSourcePath}" "/Applications/"`, { stdio: 'inherit' });
137
+ } catch (err) {
138
+ await fs.copy(fullSourcePath, destinationPath);
139
+ }
140
+ }
141
+
142
+ // Unmount
143
+ installSpinner.text = 'Cleaning up...';
144
+ execSync(`hdiutil detach "${tempDir}/mount" -force`);
145
+ await fs.remove(tempDir);
146
+
147
+ installSpinner.succeed('AWS Kiro successfully installed!');
148
+ console.log(chalk.green(`\nāœ… Installed to ${destinationPath}`));
149
+
150
+ return true;
151
+ } catch (installError) {
152
+ installSpinner.fail('Installation failed during file operations');
153
+ // Try to cleanup even if install failed
154
+ try {
155
+ if (fs.existsSync(path.join(tempDir, 'mount'))) {
156
+ execSync(`hdiutil detach "${tempDir}/mount" -force`);
157
+ }
158
+ await fs.remove(tempDir);
159
+ } catch (cleanupError) {
160
+ // Ignore cleanup errors
161
+ }
162
+ throw installError;
163
+ }
164
+
165
+ } catch (error) {
166
+ // If error was thrown, spinners should have been handled, but just in case
167
+ // The main catch block for installKiro should not call ora().fail()
168
+ // as specific spinners (downloadSpinner, installSpinner) would have already failed.
169
+ // We log the manual download link
170
+ console.log(chalk.red('\nYou may need to download manually from: https://kiro.dev'));
171
+ return false;
172
+ }
173
+ }
174
+
175
+ module.exports = {
176
+ isKiroInstalled,
177
+ installKiro
178
+ };
@@ -1,4 +1,4 @@
1
- const { logger } = require('vibecodingmachine-core');
2
-
3
- // Re-export the logger from core for CLI use
4
- module.exports = logger;
1
+ const { logger } = require('vibecodingmachine-core');
2
+
3
+ // Re-export the logger from core for CLI use
4
+ module.exports = logger;
@@ -1,114 +1,114 @@
1
- const chalk = require('chalk');
2
-
3
- /**
4
- * Simple persistent header that stays at top while content scrolls below
5
- * Uses ANSI terminal control codes instead of blessed
6
- */
7
- class PersistentHeader {
8
- constructor(lines = 10) {
9
- this.headerLines = lines;
10
- this.headerContent = '';
11
- this.isActive = false;
12
- }
13
-
14
- /**
15
- * Initialize the header by saving cursor and creating space
16
- */
17
- start() {
18
- if (this.isActive) return;
19
- this.isActive = true;
20
-
21
- // Clear screen and move to top
22
- process.stdout.write('\x1B[2J\x1B[H');
23
-
24
- // Create space for header by printing empty lines
25
- for (let i = 0; i < this.headerLines; i++) {
26
- process.stdout.write('\n');
27
- }
28
- }
29
-
30
- /**
31
- * Update the header content
32
- * @param {string} content - New header content
33
- */
34
- update(content) {
35
- if (!this.isActive) return;
36
-
37
- this.headerContent = content;
38
-
39
- // Save current cursor position
40
- process.stdout.write('\x1B[s');
41
-
42
- // Move to top of screen
43
- process.stdout.write('\x1B[H');
44
-
45
- // Clear the header area
46
- for (let i = 0; i < this.headerLines; i++) {
47
- process.stdout.write('\x1B[2K'); // Clear line
48
- if (i < this.headerLines - 1) process.stdout.write('\n');
49
- }
50
-
51
- // Move back to top
52
- process.stdout.write('\x1B[H');
53
-
54
- // Write header content
55
- process.stdout.write(content);
56
-
57
- // Restore cursor position
58
- process.stdout.write('\x1B[u');
59
- }
60
-
61
- /**
62
- * Stop the persistent header
63
- */
64
- stop() {
65
- if (!this.isActive) return;
66
- this.isActive = false;
67
- }
68
- }
69
-
70
- /**
71
- * Format auto mode header
72
- */
73
- function formatAutoModeHeader(status) {
74
- const {
75
- requirement = 'No requirement',
76
- step = 'PREPARE',
77
- chatCount = 0,
78
- maxChats = null,
79
- progress = 0
80
- } = status;
81
-
82
- const stepColors = {
83
- 'PREPARE': chalk.cyan,
84
- 'ACT': chalk.yellow,
85
- 'CLEAN UP': chalk.magenta,
86
- 'VERIFY': chalk.blue,
87
- 'DONE': chalk.green,
88
- 'UNKNOWN': chalk.gray
89
- };
90
-
91
- const stepColor = stepColors[step] || chalk.gray;
92
-
93
- const chatDisplay = maxChats
94
- ? `Chat ${chatCount}/${maxChats}`
95
- : `Chat ${chatCount} (unlimited)`;
96
-
97
- const progressBar = chalk.green('ā–ˆ'.repeat(Math.floor(progress / 5))) +
98
- chalk.gray('ā–‘'.repeat(20 - Math.floor(progress / 5)));
99
-
100
- return `${chalk.cyan('━'.repeat(80))}
101
- ${chalk.bold.cyan(' VibeCodingMachine Auto Mode')} ${chalk.gray('- Press Ctrl+C to stop')}
102
- ${chalk.cyan('━'.repeat(80))}
103
- ${chalk.bold(' Requirement:')} ${requirement.substring(0, 60)}
104
- ${chalk.bold(' Status:')} ${stepColor(step)} ${chalk.bold('Progress:')} ${progressBar} ${progress}%
105
- ${chalk.bold(' Chats:')} ${chatDisplay}
106
- ${chalk.cyan('━'.repeat(80))}
107
-
108
- `;
109
- }
110
-
111
- module.exports = {
112
- PersistentHeader,
113
- formatAutoModeHeader
114
- };
1
+ const chalk = require('chalk');
2
+
3
+ /**
4
+ * Simple persistent header that stays at top while content scrolls below
5
+ * Uses ANSI terminal control codes instead of blessed
6
+ */
7
+ class PersistentHeader {
8
+ constructor(lines = 10) {
9
+ this.headerLines = lines;
10
+ this.headerContent = '';
11
+ this.isActive = false;
12
+ }
13
+
14
+ /**
15
+ * Initialize the header by saving cursor and creating space
16
+ */
17
+ start() {
18
+ if (this.isActive) return;
19
+ this.isActive = true;
20
+
21
+ // Clear screen and move to top
22
+ process.stdout.write('\x1B[2J\x1B[H');
23
+
24
+ // Create space for header by printing empty lines
25
+ for (let i = 0; i < this.headerLines; i++) {
26
+ process.stdout.write('\n');
27
+ }
28
+ }
29
+
30
+ /**
31
+ * Update the header content
32
+ * @param {string} content - New header content
33
+ */
34
+ update(content) {
35
+ if (!this.isActive) return;
36
+
37
+ this.headerContent = content;
38
+
39
+ // Save current cursor position
40
+ process.stdout.write('\x1B[s');
41
+
42
+ // Move to top of screen
43
+ process.stdout.write('\x1B[H');
44
+
45
+ // Clear the header area
46
+ for (let i = 0; i < this.headerLines; i++) {
47
+ process.stdout.write('\x1B[2K'); // Clear line
48
+ if (i < this.headerLines - 1) process.stdout.write('\n');
49
+ }
50
+
51
+ // Move back to top
52
+ process.stdout.write('\x1B[H');
53
+
54
+ // Write header content
55
+ process.stdout.write(content);
56
+
57
+ // Restore cursor position
58
+ process.stdout.write('\x1B[u');
59
+ }
60
+
61
+ /**
62
+ * Stop the persistent header
63
+ */
64
+ stop() {
65
+ if (!this.isActive) return;
66
+ this.isActive = false;
67
+ }
68
+ }
69
+
70
+ /**
71
+ * Format auto mode header
72
+ */
73
+ function formatAutoModeHeader(status) {
74
+ const {
75
+ requirement = 'No requirement',
76
+ step = 'PREPARE',
77
+ chatCount = 0,
78
+ maxChats = null,
79
+ progress = 0
80
+ } = status;
81
+
82
+ const stepColors = {
83
+ 'PREPARE': chalk.cyan,
84
+ 'ACT': chalk.yellow,
85
+ 'CLEAN UP': chalk.magenta,
86
+ 'VERIFY': chalk.blue,
87
+ 'DONE': chalk.green,
88
+ 'UNKNOWN': chalk.gray
89
+ };
90
+
91
+ const stepColor = stepColors[step] || chalk.gray;
92
+
93
+ const chatDisplay = maxChats
94
+ ? `Chat ${chatCount}/${maxChats}`
95
+ : `Chat ${chatCount} (unlimited)`;
96
+
97
+ const progressBar = chalk.green('ā–ˆ'.repeat(Math.floor(progress / 5))) +
98
+ chalk.gray('ā–‘'.repeat(20 - Math.floor(progress / 5)));
99
+
100
+ return `${chalk.cyan('━'.repeat(80))}
101
+ ${chalk.bold.cyan(' VibeCodingMachine Auto Mode')} ${chalk.gray('- Press Ctrl+C to stop')}
102
+ ${chalk.cyan('━'.repeat(80))}
103
+ ${chalk.bold(' Requirement:')} ${requirement.substring(0, 60)}
104
+ ${chalk.bold(' Status:')} ${stepColor(step)} ${chalk.bold('Progress:')} ${progressBar} ${progress}%
105
+ ${chalk.bold(' Chats:')} ${chatDisplay}
106
+ ${chalk.cyan('━'.repeat(80))}
107
+
108
+ `;
109
+ }
110
+
111
+ module.exports = {
112
+ PersistentHeader,
113
+ formatAutoModeHeader
114
+ };