vibecodingmachine-cli 2026.3.10-1807 → 2026.3.14-1528
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/vibecodingmachine.js +3 -0
- package/package.json +4 -3
- package/scripts/postinstall.js +161 -161
- package/src/commands/auth.js +100 -100
- package/src/commands/auto-direct.js +16 -5
- package/src/commands/auto-execution.js +25 -0
- package/src/commands/auto-requirement-management.js +8 -8
- package/src/commands/auto-status-helpers.js +5 -3
- 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/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-checker.js +25 -1
- package/src/utils/provider-checker/agent-runner.js +115 -37
- package/src/utils/provider-checker/agents-manager.js +210 -0
- package/src/utils/provider-checker/provider-validator.js +5 -49
- package/src/utils/provider-checker/requirements-manager.js +86 -65
- package/src/utils/provider-checker/test-requirements.js +25 -17
- package/src/utils/status-card.js +121 -121
- package/src/utils/stdout-interceptor.js +127 -127
- package/src/utils/user-tracking.js +299 -299
|
@@ -4,7 +4,7 @@ const fs = require('fs');
|
|
|
4
4
|
const path = require('path');
|
|
5
5
|
|
|
6
6
|
const { CLI_AUTO_INSTALL, isCLIAvailable, installCLI } = require('./cli-installer');
|
|
7
|
-
const { getResultFilePath
|
|
7
|
+
const { getResultFilePath } = require('./agents-manager');
|
|
8
8
|
const { runAgentCheck } = require('./agent-runner');
|
|
9
9
|
|
|
10
10
|
/**
|
|
@@ -28,21 +28,11 @@ async function checkProvider(providerId, config = {}, repoPath, onProgress = nul
|
|
|
28
28
|
return { status: 'error', message: `Unknown provider: ${providerId}`, checkedAt: new Date().toISOString(), requirementLeftPending: false };
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
// No platform restrictions needed anymore - all IDEs have cross-platform support
|
|
32
|
-
|
|
33
31
|
if (!repoPath) {
|
|
34
32
|
console.error(`[AGENT CHECK] No repository path available`);
|
|
35
33
|
return { status: 'error', message: 'No repository path available', checkedAt: new Date().toISOString(), requirementLeftPending: false };
|
|
36
34
|
}
|
|
37
35
|
|
|
38
|
-
const { getRequirementsPath } = require('vibecodingmachine-core');
|
|
39
|
-
let reqPath;
|
|
40
|
-
try {
|
|
41
|
-
reqPath = await getRequirementsPath(repoPath);
|
|
42
|
-
} catch {
|
|
43
|
-
reqPath = path.join(repoPath, '.vibecodingmachine', 'REQUIREMENTS.md');
|
|
44
|
-
}
|
|
45
|
-
|
|
46
36
|
// Ensure .vibecodingmachine directory exists
|
|
47
37
|
const vibeDir = path.join(repoPath, '.vibecodingmachine');
|
|
48
38
|
const tempDir = path.join(vibeDir, 'temp');
|
|
@@ -55,26 +45,6 @@ async function checkProvider(providerId, config = {}, repoPath, onProgress = nul
|
|
|
55
45
|
return { status: 'error', message: `Could not create .vibecodingmachine: ${err.message}`, checkedAt: new Date().toISOString(), requirementLeftPending: false };
|
|
56
46
|
}
|
|
57
47
|
|
|
58
|
-
// Ensure REQUIREMENTS.md exists with proper structure
|
|
59
|
-
try {
|
|
60
|
-
if (!fs.existsSync(reqPath)) {
|
|
61
|
-
const initialRequirements = `# Requirements for this project
|
|
62
|
-
|
|
63
|
-
## ⏳ Requirements not yet completed
|
|
64
|
-
|
|
65
|
-
## ✅ Verified by AI
|
|
66
|
-
|
|
67
|
-
`;
|
|
68
|
-
fs.writeFileSync(reqPath, initialRequirements, 'utf8');
|
|
69
|
-
console.log(`[AGENT CHECK] Created initial REQUIREMENTS.md`);
|
|
70
|
-
} else {
|
|
71
|
-
console.log(`[AGENT CHECK] REQUIREMENTS.md already exists`);
|
|
72
|
-
}
|
|
73
|
-
} catch (err) {
|
|
74
|
-
console.error(`[AGENT CHECK] Failed to ensure REQUIREMENTS.md: ${err.message}`);
|
|
75
|
-
return { status: 'error', message: `Could not create REQUIREMENTS.md: ${err.message}`, checkedAt: new Date().toISOString(), requirementLeftPending: false };
|
|
76
|
-
}
|
|
77
|
-
|
|
78
48
|
const resultFile = getResultFilePath(repoPath);
|
|
79
49
|
|
|
80
50
|
// Special handling: VS Code Copilot CLI is often installed but not authenticated.
|
|
@@ -235,11 +205,6 @@ async function checkProvider(providerId, config = {}, repoPath, onProgress = nul
|
|
|
235
205
|
}
|
|
236
206
|
}
|
|
237
207
|
|
|
238
|
-
// Add test requirement (idempotent — safe to call even if already present from a prior failed agent)
|
|
239
|
-
try { addTestRequirement(reqPath, resultFile); } catch (err) {
|
|
240
|
-
return { status: 'error', message: `Could not write to REQUIREMENTS.md: ${err.message}`, checkedAt: new Date().toISOString(), requirementLeftPending: false };
|
|
241
|
-
}
|
|
242
|
-
|
|
243
208
|
// IDE providers generally don't need CLI system configuration to work
|
|
244
209
|
// They can communicate directly with the IDE without requiring VCM CLI setup
|
|
245
210
|
|
|
@@ -248,20 +213,17 @@ async function checkProvider(providerId, config = {}, repoPath, onProgress = nul
|
|
|
248
213
|
const result = await runAgentCheck(providerId, def, repoPath, resultFile, timeoutMs, signal, onProgress);
|
|
249
214
|
|
|
250
215
|
if (result.status === 'success') {
|
|
251
|
-
// Agent completed —
|
|
252
|
-
try { removeTestRequirement(reqPath); } catch { }
|
|
216
|
+
// Agent completed — clean up result file
|
|
253
217
|
try { fs.unlinkSync(resultFile); } catch { }
|
|
254
218
|
return { ...result, requirementLeftPending: false };
|
|
255
219
|
}
|
|
256
220
|
|
|
257
|
-
// Agent failed
|
|
258
|
-
return { ...result, requirementLeftPending:
|
|
221
|
+
// Agent failed
|
|
222
|
+
return { ...result, requirementLeftPending: false };
|
|
259
223
|
}
|
|
260
224
|
|
|
261
225
|
/**
|
|
262
226
|
* Check all providers sequentially.
|
|
263
|
-
* The test requirement is shared: it stays pending between agents until one
|
|
264
|
-
* succeeds, then it's removed. If all fail, it's removed at the end.
|
|
265
227
|
*
|
|
266
228
|
* Returns { [providerId]: checkResult }
|
|
267
229
|
*/
|
|
@@ -275,14 +237,8 @@ async function checkAllProviders(providerIds, config = {}, repoPath, onProgress
|
|
|
275
237
|
if (onProgress) onProgress(id, 'done', results[id].status === 'error' ? results[id].message : null);
|
|
276
238
|
}
|
|
277
239
|
|
|
278
|
-
// Final cleanup — remove
|
|
240
|
+
// Final cleanup — remove result file if it exists
|
|
279
241
|
if (repoPath) {
|
|
280
|
-
const { getRequirementsPath } = require('vibecodingmachine-core');
|
|
281
|
-
let reqPath;
|
|
282
|
-
try { reqPath = await getRequirementsPath(repoPath); } catch {
|
|
283
|
-
reqPath = path.join(repoPath, '.vibecodingmachine', 'REQUIREMENTS.md');
|
|
284
|
-
}
|
|
285
|
-
try { removeTestRequirement(reqPath); } catch { }
|
|
286
242
|
try { fs.unlinkSync(getResultFilePath(repoPath)); } catch { }
|
|
287
243
|
}
|
|
288
244
|
|
|
@@ -3,10 +3,7 @@
|
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const path = require('path');
|
|
5
5
|
|
|
6
|
-
const TEST_REQ_TITLE = 'VCM
|
|
7
|
-
const PENDING_HEADER = '## ⏳ Requirements not yet completed';
|
|
8
|
-
const IN_PROGRESS_HEADER = '## 🔨 Current In Progress Requirement';
|
|
9
|
-
const VERIFIED_HEADER = '## ✅ Verified by AI';
|
|
6
|
+
const TEST_REQ_TITLE = '✅ ACTION REQUIRED - VCM Connectivity Test';
|
|
10
7
|
|
|
11
8
|
/**
|
|
12
9
|
* Return the absolute path to the result file used by the check.
|
|
@@ -16,94 +13,118 @@ function getResultFilePath(repoPath) {
|
|
|
16
13
|
}
|
|
17
14
|
|
|
18
15
|
/**
|
|
19
|
-
* Add
|
|
20
|
-
* The first item in the "Requirements not yet completed" section is the current in-progress item.
|
|
21
|
-
* Idempotent — if it's already there, do nothing.
|
|
16
|
+
* Add test requirement to requirements file
|
|
22
17
|
*/
|
|
23
|
-
function addTestRequirement(reqPath,
|
|
24
|
-
|
|
25
|
-
|
|
18
|
+
function addTestRequirement(reqPath, resultFilePath) {
|
|
19
|
+
if (!fs.existsSync(reqPath)) {
|
|
20
|
+
// Create the file with basic structure if it doesn't exist
|
|
21
|
+
const basicContent = `## Requirements
|
|
26
22
|
|
|
27
|
-
|
|
28
|
-
|
|
23
|
+
`;
|
|
24
|
+
fs.writeFileSync(reqPath, basicContent, 'utf8');
|
|
25
|
+
}
|
|
29
26
|
|
|
30
|
-
const
|
|
31
|
-
|
|
27
|
+
const content = fs.readFileSync(reqPath, 'utf8');
|
|
28
|
+
|
|
29
|
+
// Check if test requirement already exists
|
|
30
|
+
if (content.includes(TEST_REQ_TITLE)) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
32
33
|
|
|
33
|
-
|
|
34
|
+
const testRequirement = `- ${TEST_REQ_TITLE}
|
|
35
|
+
🚦 Status: [PREPARE]
|
|
34
36
|
|
|
35
|
-
|
|
37
|
+
**✅ ACTION REQUIRED - VCM Connectivity Test:**
|
|
36
38
|
|
|
37
|
-
|
|
38
|
-
- Change \`🚦 Current Status: [PREPARE]\` to \`🚦 Current Status: [DONE]\`
|
|
39
|
+
VCM is testing if it can communicate with your IDE. To complete this test:
|
|
39
40
|
|
|
40
|
-
|
|
41
|
+
**Change the status above from [PREPARE] to [DONE] and save the file.**
|
|
41
42
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
3. Save
|
|
43
|
+
Example:
|
|
44
|
+
Before: 🚦 Status: [PREPARE]
|
|
45
|
+
After: 🚦 Status: [DONE]
|
|
46
46
|
|
|
47
|
-
|
|
47
|
+
That's it! VCM will detect the status change within seconds.
|
|
48
48
|
|
|
49
49
|
`;
|
|
50
50
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
content = before + '\n\n' + testBlock + after;
|
|
51
|
+
// Find the Requirements section and add the test requirement
|
|
52
|
+
const requirementsMatch = content.match(/## Requirements\s*\n/);
|
|
53
|
+
if (requirementsMatch) {
|
|
54
|
+
const insertIndex = requirementsMatch.index + requirementsMatch[0].length;
|
|
55
|
+
const updatedContent = content.slice(0, insertIndex) + testRequirement + content.slice(insertIndex);
|
|
56
|
+
fs.writeFileSync(reqPath, updatedContent, 'utf8');
|
|
58
57
|
} else {
|
|
59
|
-
//
|
|
60
|
-
|
|
58
|
+
// If no Requirements section found, append to end
|
|
59
|
+
const updatedContent = content + testRequirement;
|
|
60
|
+
fs.writeFileSync(reqPath, updatedContent, 'utf8');
|
|
61
61
|
}
|
|
62
|
-
|
|
63
|
-
fs.writeFileSync(reqPath, content, 'utf8');
|
|
64
62
|
}
|
|
65
63
|
|
|
66
64
|
/**
|
|
67
|
-
* Remove
|
|
68
|
-
* Also cleans up triple-blank lines left behind.
|
|
65
|
+
* Remove test requirement from requirements file
|
|
69
66
|
*/
|
|
70
67
|
function removeTestRequirement(reqPath) {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
68
|
+
if (!fs.existsSync(reqPath)) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const content = fs.readFileSync(reqPath, 'utf8');
|
|
73
|
+
|
|
74
|
+
// Find the test requirement section
|
|
75
|
+
const startIndex = content.indexOf(`- ${TEST_REQ_TITLE}`);
|
|
76
|
+
if (startIndex === -1) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Find the end of the test requirement (next requirement or end of file)
|
|
81
|
+
let endIndex = content.indexOf('\n- ', startIndex + 1);
|
|
82
|
+
if (endIndex === -1) {
|
|
83
|
+
endIndex = content.length;
|
|
87
84
|
}
|
|
88
85
|
|
|
89
|
-
|
|
86
|
+
// Remove the test requirement and clean up extra blank lines
|
|
87
|
+
const beforeTest = content.slice(0, startIndex);
|
|
88
|
+
const afterTest = content.slice(endIndex);
|
|
89
|
+
|
|
90
|
+
// Clean up triple blank lines
|
|
91
|
+
const cleanedContent = (beforeTest + afterTest).replace(/\n{3,}/g, '\n\n');
|
|
92
|
+
|
|
93
|
+
fs.writeFileSync(reqPath, cleanedContent, 'utf8');
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Extract test requirement from requirements file
|
|
98
|
+
*/
|
|
99
|
+
function extractTestRequirement(reqPath) {
|
|
100
|
+
if (!fs.existsSync(reqPath)) {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const content = fs.readFileSync(reqPath, 'utf8');
|
|
105
|
+
|
|
106
|
+
// Find the test requirement section
|
|
107
|
+
const startIndex = content.indexOf(`- ${TEST_REQ_TITLE}`);
|
|
108
|
+
if (startIndex === -1) {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
90
111
|
|
|
91
|
-
// Find end of
|
|
92
|
-
let
|
|
93
|
-
|
|
94
|
-
|
|
112
|
+
// Find the end of the test requirement (next requirement or end of file)
|
|
113
|
+
let endIndex = content.indexOf('\n- ', startIndex + 1);
|
|
114
|
+
if (endIndex === -1) {
|
|
115
|
+
endIndex = content.length;
|
|
95
116
|
}
|
|
96
117
|
|
|
97
|
-
|
|
98
|
-
const
|
|
99
|
-
|
|
118
|
+
// Extract the test requirement content including the title line
|
|
119
|
+
const testContent = content.slice(startIndex, endIndex);
|
|
120
|
+
|
|
121
|
+
return testContent.trim();
|
|
100
122
|
}
|
|
101
123
|
|
|
102
124
|
module.exports = {
|
|
103
|
-
TEST_REQ_TITLE,
|
|
104
|
-
PENDING_HEADER,
|
|
105
|
-
VERIFIED_HEADER,
|
|
106
125
|
getResultFilePath,
|
|
107
126
|
addTestRequirement,
|
|
108
|
-
removeTestRequirement
|
|
127
|
+
removeTestRequirement,
|
|
128
|
+
extractTestRequirement,
|
|
129
|
+
TEST_REQ_TITLE
|
|
109
130
|
};
|
|
@@ -8,8 +8,7 @@ const fs = require('fs');
|
|
|
8
8
|
const path = require('path');
|
|
9
9
|
|
|
10
10
|
const TEST_REQ_TITLE = 'VCM agent connectivity check';
|
|
11
|
-
const
|
|
12
|
-
const VERIFIED_HEADER = '## ✅ Verified by AI';
|
|
11
|
+
const REQUIREMENTS_HEADER = '## Requirements';
|
|
13
12
|
|
|
14
13
|
/**
|
|
15
14
|
* Return the absolute path to the result file used by the check.
|
|
@@ -29,27 +28,36 @@ function addTestRequirement(reqPath, resultFile) {
|
|
|
29
28
|
// Check if test requirement already exists
|
|
30
29
|
if (content.includes(TEST_REQ_TITLE)) return;
|
|
31
30
|
|
|
32
|
-
//
|
|
33
|
-
|
|
34
|
-
if (pendingStart === -1) return;
|
|
31
|
+
// Clean up old header format if it exists
|
|
32
|
+
content = content.replace(/## ⏳ Requirements not yet completed\s*\n/g, '');
|
|
35
33
|
|
|
36
|
-
// Find the
|
|
37
|
-
const
|
|
38
|
-
|
|
34
|
+
// Find the Requirements section
|
|
35
|
+
const requirementsStart = content.indexOf(REQUIREMENTS_HEADER);
|
|
36
|
+
if (requirementsStart === -1) {
|
|
37
|
+
// If no Requirements section, create one at the beginning
|
|
38
|
+
content = REQUIREMENTS_HEADER + '\n\n' + content;
|
|
39
|
+
}
|
|
39
40
|
|
|
40
|
-
//
|
|
41
|
-
const
|
|
41
|
+
// Find where to insert (after the Requirements header)
|
|
42
|
+
const headerEnd = content.indexOf(REQUIREMENTS_HEADER) + REQUIREMENTS_HEADER.length;
|
|
43
|
+
const insertPos = headerEnd;
|
|
42
44
|
|
|
43
|
-
|
|
45
|
+
// Build the test requirement content with status tracking
|
|
46
|
+
const testReq = `
|
|
47
|
+
- ${TEST_REQ_TITLE}
|
|
48
|
+
🚦 Status: [PREPARE]
|
|
44
49
|
|
|
45
|
-
|
|
46
|
-
- Agent responds to test prompt
|
|
47
|
-
- No errors or timeouts
|
|
48
|
-
- Proper formatting and structure
|
|
50
|
+
**✅ ACTION REQUIRED - VCM Connectivity Test:**
|
|
49
51
|
|
|
50
|
-
|
|
52
|
+
VCM is testing if it can communicate with your IDE. To complete this test:
|
|
51
53
|
|
|
52
|
-
**
|
|
54
|
+
**Change the status above from [PREPARE] to [DONE] and save the file.**
|
|
55
|
+
|
|
56
|
+
Example:
|
|
57
|
+
Before: 🚦 Status: [PREPARE]
|
|
58
|
+
After: 🚦 Status: [DONE]
|
|
59
|
+
|
|
60
|
+
That's it! VCM will detect the status change within seconds.
|
|
53
61
|
|
|
54
62
|
`;
|
|
55
63
|
|
package/src/utils/status-card.js
CHANGED
|
@@ -1,121 +1,121 @@
|
|
|
1
|
-
const chalk = require('chalk');
|
|
2
|
-
const boxen = require('boxen');
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Render a status card showing current requirement progress
|
|
6
|
-
* Similar to the purple card in the GUI
|
|
7
|
-
* @param {object} status - Current status object
|
|
8
|
-
* @param {string} status.requirement - Current requirement being worked on
|
|
9
|
-
* @param {string} status.step - Current step (PREPARE, ACT, CLEAN UP, VERIFY, DONE)
|
|
10
|
-
* @param {number} status.chatCount - Current chat count
|
|
11
|
-
* @param {number|null} status.maxChats - Maximum chats or null for unlimited
|
|
12
|
-
* @param {number} status.progress - Progress percentage (0-100)
|
|
13
|
-
* @returns {string} Formatted status card
|
|
14
|
-
*/
|
|
15
|
-
function renderStatusCard(status) {
|
|
16
|
-
const {
|
|
17
|
-
requirement = 'No requirement loaded',
|
|
18
|
-
step = 'UNKNOWN',
|
|
19
|
-
chatCount = 0,
|
|
20
|
-
maxChats = null,
|
|
21
|
-
progress = 0
|
|
22
|
-
} = status;
|
|
23
|
-
|
|
24
|
-
// Step color mapping
|
|
25
|
-
const stepColors = {
|
|
26
|
-
'PREPARE': chalk.cyan,
|
|
27
|
-
'ACT': chalk.yellow,
|
|
28
|
-
'CLEAN UP': chalk.magenta,
|
|
29
|
-
'VERIFY': chalk.blue,
|
|
30
|
-
'DONE': chalk.green,
|
|
31
|
-
'UNKNOWN': chalk.gray
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
const stepColor = stepColors[step] || chalk.gray;
|
|
35
|
-
|
|
36
|
-
// Progress bar
|
|
37
|
-
const barWidth = 30;
|
|
38
|
-
const filledWidth = Math.round((progress / 100) * barWidth);
|
|
39
|
-
const emptyWidth = barWidth - filledWidth;
|
|
40
|
-
const progressBar = chalk.green('█'.repeat(filledWidth)) + chalk.gray('░'.repeat(emptyWidth));
|
|
41
|
-
|
|
42
|
-
// Chat counter
|
|
43
|
-
const chatDisplay = maxChats
|
|
44
|
-
? `Chat ${chatCount}/${maxChats}`
|
|
45
|
-
: `Chat ${chatCount} (unlimited)`;
|
|
46
|
-
|
|
47
|
-
// Build card content
|
|
48
|
-
const content = [
|
|
49
|
-
chalk.bold('📋 Current Requirement'),
|
|
50
|
-
'',
|
|
51
|
-
chalk.white(requirement.length > 60 ? requirement.substring(0, 57) + '...' : requirement),
|
|
52
|
-
'',
|
|
53
|
-
chalk.bold('🚦 Status: ') + stepColor.bold(step),
|
|
54
|
-
'',
|
|
55
|
-
`${progressBar} ${progress}%`,
|
|
56
|
-
'',
|
|
57
|
-
chalk.gray(chatDisplay)
|
|
58
|
-
].join('\n');
|
|
59
|
-
|
|
60
|
-
// Render with boxen (purple/magenta border like the GUI)
|
|
61
|
-
return boxen(content, {
|
|
62
|
-
padding: 1,
|
|
63
|
-
margin: { top: 0, right: 0, bottom: 1, left: 0 },
|
|
64
|
-
borderStyle: 'round',
|
|
65
|
-
borderColor: 'magenta',
|
|
66
|
-
title: 'Auto Mode Status',
|
|
67
|
-
titleAlignment: 'center',
|
|
68
|
-
width: 80
|
|
69
|
-
});
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* Clear the terminal and move cursor to top
|
|
74
|
-
*/
|
|
75
|
-
function clearAndMoveToTop() {
|
|
76
|
-
// ANSI escape codes
|
|
77
|
-
process.stdout.write('\x1B[2J'); // Clear entire screen
|
|
78
|
-
process.stdout.write('\x1B[H'); // Move cursor to home (top-left)
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Move cursor up N lines
|
|
83
|
-
* @param {number} lines - Number of lines to move up
|
|
84
|
-
*/
|
|
85
|
-
function moveCursorUp(lines) {
|
|
86
|
-
process.stdout.write(`\x1B[${lines}A`);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Save cursor position
|
|
91
|
-
*/
|
|
92
|
-
function saveCursor() {
|
|
93
|
-
process.stdout.write('\x1B[s');
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Restore cursor position
|
|
98
|
-
*/
|
|
99
|
-
function restoreCursor() {
|
|
100
|
-
process.stdout.write('\x1B[u');
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* Render the menu header and status card together
|
|
105
|
-
* @param {string} menuContent - The menu content to display
|
|
106
|
-
* @param {object} status - Status object for the status card
|
|
107
|
-
*/
|
|
108
|
-
function renderHeaderWithStatus(menuContent, status) {
|
|
109
|
-
clearAndMoveToTop();
|
|
110
|
-
console.log(menuContent);
|
|
111
|
-
console.log(renderStatusCard(status));
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
module.exports = {
|
|
115
|
-
renderStatusCard,
|
|
116
|
-
clearAndMoveToTop,
|
|
117
|
-
moveCursorUp,
|
|
118
|
-
saveCursor,
|
|
119
|
-
restoreCursor,
|
|
120
|
-
renderHeaderWithStatus
|
|
121
|
-
};
|
|
1
|
+
const chalk = require('chalk');
|
|
2
|
+
const boxen = require('boxen');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Render a status card showing current requirement progress
|
|
6
|
+
* Similar to the purple card in the GUI
|
|
7
|
+
* @param {object} status - Current status object
|
|
8
|
+
* @param {string} status.requirement - Current requirement being worked on
|
|
9
|
+
* @param {string} status.step - Current step (PREPARE, ACT, CLEAN UP, VERIFY, DONE)
|
|
10
|
+
* @param {number} status.chatCount - Current chat count
|
|
11
|
+
* @param {number|null} status.maxChats - Maximum chats or null for unlimited
|
|
12
|
+
* @param {number} status.progress - Progress percentage (0-100)
|
|
13
|
+
* @returns {string} Formatted status card
|
|
14
|
+
*/
|
|
15
|
+
function renderStatusCard(status) {
|
|
16
|
+
const {
|
|
17
|
+
requirement = 'No requirement loaded',
|
|
18
|
+
step = 'UNKNOWN',
|
|
19
|
+
chatCount = 0,
|
|
20
|
+
maxChats = null,
|
|
21
|
+
progress = 0
|
|
22
|
+
} = status;
|
|
23
|
+
|
|
24
|
+
// Step color mapping
|
|
25
|
+
const stepColors = {
|
|
26
|
+
'PREPARE': chalk.cyan,
|
|
27
|
+
'ACT': chalk.yellow,
|
|
28
|
+
'CLEAN UP': chalk.magenta,
|
|
29
|
+
'VERIFY': chalk.blue,
|
|
30
|
+
'DONE': chalk.green,
|
|
31
|
+
'UNKNOWN': chalk.gray
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const stepColor = stepColors[step] || chalk.gray;
|
|
35
|
+
|
|
36
|
+
// Progress bar
|
|
37
|
+
const barWidth = 30;
|
|
38
|
+
const filledWidth = Math.round((progress / 100) * barWidth);
|
|
39
|
+
const emptyWidth = barWidth - filledWidth;
|
|
40
|
+
const progressBar = chalk.green('█'.repeat(filledWidth)) + chalk.gray('░'.repeat(emptyWidth));
|
|
41
|
+
|
|
42
|
+
// Chat counter
|
|
43
|
+
const chatDisplay = maxChats
|
|
44
|
+
? `Chat ${chatCount}/${maxChats}`
|
|
45
|
+
: `Chat ${chatCount} (unlimited)`;
|
|
46
|
+
|
|
47
|
+
// Build card content
|
|
48
|
+
const content = [
|
|
49
|
+
chalk.bold('📋 Current Requirement'),
|
|
50
|
+
'',
|
|
51
|
+
chalk.white(requirement.length > 60 ? requirement.substring(0, 57) + '...' : requirement),
|
|
52
|
+
'',
|
|
53
|
+
chalk.bold('🚦 Status: ') + stepColor.bold(step),
|
|
54
|
+
'',
|
|
55
|
+
`${progressBar} ${progress}%`,
|
|
56
|
+
'',
|
|
57
|
+
chalk.gray(chatDisplay)
|
|
58
|
+
].join('\n');
|
|
59
|
+
|
|
60
|
+
// Render with boxen (purple/magenta border like the GUI)
|
|
61
|
+
return boxen(content, {
|
|
62
|
+
padding: 1,
|
|
63
|
+
margin: { top: 0, right: 0, bottom: 1, left: 0 },
|
|
64
|
+
borderStyle: 'round',
|
|
65
|
+
borderColor: 'magenta',
|
|
66
|
+
title: 'Auto Mode Status',
|
|
67
|
+
titleAlignment: 'center',
|
|
68
|
+
width: 80
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Clear the terminal and move cursor to top
|
|
74
|
+
*/
|
|
75
|
+
function clearAndMoveToTop() {
|
|
76
|
+
// ANSI escape codes
|
|
77
|
+
process.stdout.write('\x1B[2J'); // Clear entire screen
|
|
78
|
+
process.stdout.write('\x1B[H'); // Move cursor to home (top-left)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Move cursor up N lines
|
|
83
|
+
* @param {number} lines - Number of lines to move up
|
|
84
|
+
*/
|
|
85
|
+
function moveCursorUp(lines) {
|
|
86
|
+
process.stdout.write(`\x1B[${lines}A`);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Save cursor position
|
|
91
|
+
*/
|
|
92
|
+
function saveCursor() {
|
|
93
|
+
process.stdout.write('\x1B[s');
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Restore cursor position
|
|
98
|
+
*/
|
|
99
|
+
function restoreCursor() {
|
|
100
|
+
process.stdout.write('\x1B[u');
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Render the menu header and status card together
|
|
105
|
+
* @param {string} menuContent - The menu content to display
|
|
106
|
+
* @param {object} status - Status object for the status card
|
|
107
|
+
*/
|
|
108
|
+
function renderHeaderWithStatus(menuContent, status) {
|
|
109
|
+
clearAndMoveToTop();
|
|
110
|
+
console.log(menuContent);
|
|
111
|
+
console.log(renderStatusCard(status));
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
module.exports = {
|
|
115
|
+
renderStatusCard,
|
|
116
|
+
clearAndMoveToTop,
|
|
117
|
+
moveCursorUp,
|
|
118
|
+
saveCursor,
|
|
119
|
+
restoreCursor,
|
|
120
|
+
renderHeaderWithStatus
|
|
121
|
+
};
|