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.
- package/README.md +85 -85
- package/bin/commands/agent-commands.js +295 -28
- package/bin/vibecodingmachine.js +0 -0
- package/package.json +2 -2
- package/scripts/postinstall.js +161 -161
- package/src/commands/auth.js +100 -100
- package/src/commands/auto-execution.js +120 -32
- package/src/commands/auto-requirement-management.js +9 -9
- package/src/commands/auto-status-helpers.js +6 -12
- package/src/commands/computers.js +318 -318
- package/src/commands/feature.js +123 -123
- package/src/commands/locale.js +72 -72
- package/src/commands/repo.js +163 -163
- package/src/commands/setup.js +93 -93
- package/src/commands/sync.js +287 -287
- package/src/index.js +5 -5
- package/src/utils/agent-selector.js +50 -50
- package/src/utils/asset-cleanup.js +60 -60
- package/src/utils/auth.js +6 -0
- package/src/utils/auto-mode-ansi-ui.js +237 -237
- package/src/utils/auto-mode-simple-ui.js +141 -141
- package/src/utils/copy-with-progress.js +167 -167
- package/src/utils/download-with-progress.js +84 -84
- package/src/utils/keyboard-handler.js +153 -153
- package/src/utils/kiro-installer.js +178 -178
- package/src/utils/logger.js +4 -4
- package/src/utils/persistent-header.js +114 -114
- package/src/utils/prompt-helper.js +63 -63
- package/src/utils/provider-checker/agent-runner.js +110 -31
- package/src/utils/provider-checker/ide-manager.js +37 -8
- package/src/utils/provider-checker/provider-validator.js +50 -0
- package/src/utils/provider-checker/requirements-manager.js +21 -6
- package/src/utils/status-card.js +121 -121
- package/src/utils/stdout-interceptor.js +127 -127
- package/src/utils/trui-main-handlers.js +41 -8
- package/src/utils/trui-main-menu.js +10 -3
- package/src/utils/trui-nav-agents.js +23 -33
- package/src/utils/trui-navigation.js +2 -2
- 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
|
+
};
|
package/src/utils/logger.js
CHANGED
|
@@ -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
|
+
};
|