vibecodingmachine-cli 2026.2.26-1739 → 2026.3.9-1621

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 (74) hide show
  1. package/bin/auth/auth-compliance.js +7 -1
  2. package/bin/commands/agent-commands.js +150 -228
  3. package/bin/commands/command-aliases.js +68 -0
  4. package/bin/vibecodingmachine.js +1 -2
  5. package/package.json +2 -2
  6. package/src/commands/agents/list.js +71 -115
  7. package/src/commands/agents-check.js +16 -4
  8. package/src/commands/analyze-file-sizes.js +1 -1
  9. package/src/commands/auto-direct/auto-provider-manager.js +290 -0
  10. package/src/commands/auto-direct/auto-status-display.js +331 -0
  11. package/src/commands/auto-direct/auto-utils.js +439 -0
  12. package/src/commands/auto-direct/file-operations.js +110 -0
  13. package/src/commands/auto-direct/provider-config.js +1 -1
  14. package/src/commands/auto-direct/provider-manager.js +1 -1
  15. package/src/commands/auto-direct/status-display.js +1 -1
  16. package/src/commands/auto-direct/utils.js +24 -18
  17. package/src/commands/auto-direct-refactored.js +413 -0
  18. package/src/commands/auto-direct.js +594 -188
  19. package/src/commands/requirements/commands.js +353 -0
  20. package/src/commands/requirements/default-handlers.js +272 -0
  21. package/src/commands/requirements/disable.js +97 -0
  22. package/src/commands/requirements/enable.js +97 -0
  23. package/src/commands/requirements/utils.js +194 -0
  24. package/src/commands/requirements-refactored.js +60 -0
  25. package/src/commands/requirements.js +38 -771
  26. package/src/commands/specs/disable.js +96 -0
  27. package/src/commands/specs/enable.js +96 -0
  28. package/src/trui/TruiInterface.js +5 -11
  29. package/src/trui/agents/AgentInterface.js +24 -396
  30. package/src/trui/agents/handlers/CommandHandler.js +93 -0
  31. package/src/trui/agents/handlers/ContextManager.js +117 -0
  32. package/src/trui/agents/handlers/DisplayHandler.js +243 -0
  33. package/src/trui/agents/handlers/HelpHandler.js +51 -0
  34. package/src/utils/auth.js +13 -111
  35. package/src/utils/config.js +5 -1
  36. package/src/utils/interactive/requirements-navigation.js +17 -15
  37. package/src/utils/interactive-broken.js +2 -2
  38. package/src/utils/provider-checker/agent-runner.js +15 -1
  39. package/src/utils/provider-checker/cli-installer.js +149 -7
  40. package/src/utils/provider-checker/opencode-checker.js +588 -0
  41. package/src/utils/provider-checker/provider-validator.js +88 -3
  42. package/src/utils/provider-checker/time-formatter.js +3 -2
  43. package/src/utils/provider-manager.js +28 -20
  44. package/src/utils/provider-registry.js +35 -3
  45. package/src/utils/requirements-navigator/index.js +94 -0
  46. package/src/utils/requirements-navigator/input-handler.js +217 -0
  47. package/src/utils/requirements-navigator/section-loader.js +188 -0
  48. package/src/utils/requirements-navigator/tree-builder.js +105 -0
  49. package/src/utils/requirements-navigator/tree-renderer.js +50 -0
  50. package/src/utils/requirements-navigator.js +2 -583
  51. package/src/utils/trui-clarifications.js +188 -0
  52. package/src/utils/trui-feedback.js +54 -1
  53. package/src/utils/trui-kiro-integration.js +398 -0
  54. package/src/utils/trui-main-handlers.js +194 -0
  55. package/src/utils/trui-main-menu.js +235 -0
  56. package/src/utils/trui-nav-agents.js +178 -25
  57. package/src/utils/trui-nav-requirements.js +203 -27
  58. package/src/utils/trui-nav-settings.js +114 -1
  59. package/src/utils/trui-nav-specifications.js +44 -3
  60. package/src/utils/trui-navigation-backup.js +603 -0
  61. package/src/utils/trui-navigation.js +70 -228
  62. package/src/utils/trui-provider-health.js +274 -0
  63. package/src/utils/trui-provider-manager.js +376 -0
  64. package/src/utils/trui-quick-menu.js +25 -1
  65. package/src/utils/trui-req-actions-backup.js +507 -0
  66. package/src/utils/trui-req-actions.js +148 -216
  67. package/src/utils/trui-req-editor.js +170 -0
  68. package/src/utils/trui-req-file-ops.js +278 -0
  69. package/src/utils/trui-req-tree-old.js +719 -0
  70. package/src/utils/trui-req-tree.js +348 -627
  71. package/src/utils/trui-specifications.js +25 -7
  72. package/src/utils/trui-windsurf.js +231 -10
  73. package/src/utils/welcome-screen-extracted.js +2 -2
  74. package/src/utils/welcome-screen.js +2 -2
@@ -0,0 +1,439 @@
1
+ /**
2
+ * Auto Mode Utilities
3
+ *
4
+ * Shared utility functions for auto mode operations
5
+ */
6
+
7
+ const chalk = require('chalk');
8
+ const stringWidth = require('string-width');
9
+ const { getRequirementsPath } = require('vibecodingmachine-core');
10
+ const fs = require('fs-extra');
11
+ const path = require('path');
12
+
13
+ /**
14
+ * Get timestamp for logging
15
+ */
16
+ function getTimestamp() {
17
+ const now = new Date();
18
+ return now.toLocaleTimeString('en-US', {
19
+ hour: '2-digit',
20
+ minute: '2-digit',
21
+ second: '2-digit'
22
+ });
23
+ }
24
+
25
+ /**
26
+ * Get a human-friendly timestamp for log prefixes that includes date, time, and timezone
27
+ * Example: "2025-01-02 3:45 PM MST"
28
+ */
29
+ function getLogTimestamp(date = new Date()) {
30
+ const datePart = date.toISOString().split('T')[0]; // YYYY-MM-DD
31
+ const timePart = date.toLocaleTimeString('en-US', {
32
+ hour: 'numeric',
33
+ minute: '2-digit',
34
+ timeZoneName: 'short'
35
+ });
36
+
37
+ return `${datePart} ${timePart}`;
38
+ }
39
+
40
+ /**
41
+ * Translate workflow stage names
42
+ */
43
+ function translateStage(stage) {
44
+ const stageMap = {
45
+ 'PREPARE': 'workflow.stage.prepare',
46
+ 'REPRODUCE': 'workflow.stage.reproduce',
47
+ 'ACT': 'workflow.stage.act',
48
+ 'CLEAN UP': 'workflow.stage.cleanup',
49
+ 'VERIFY': 'workflow.stage.verify',
50
+ 'DONE': 'workflow.stage.done'
51
+ };
52
+
53
+ return stageMap[stage] || stage;
54
+ }
55
+
56
+ /**
57
+ * Strip ANSI escape codes from a string
58
+ */
59
+ function stripAnsi(str) {
60
+ // eslint-disable-next-line no-control-regex
61
+ return str.replace(/\x1B\[[0-9;]*[mGKH]/g, '');
62
+ }
63
+
64
+ /**
65
+ * Get visual width of a string accounting for ANSI codes, emojis, and wide characters
66
+ * Uses string-width library for accurate Unicode width calculation
67
+ */
68
+ function getVisualWidth(str) {
69
+ return stringWidth(str);
70
+ }
71
+
72
+ /**
73
+ * Pad string to visual width accounting for emojis, ANSI codes, and wide characters
74
+ */
75
+ function padToVisualWidth(str, targetWidth) {
76
+ const visualWidth = getVisualWidth(str);
77
+ const paddingNeeded = targetWidth - visualWidth;
78
+ if (paddingNeeded <= 0) {
79
+ return str;
80
+ }
81
+ return str + ' '.repeat(paddingNeeded);
82
+ }
83
+
84
+ /**
85
+ * Check if message indicates rate limiting
86
+ */
87
+ function isRateLimitMessage(text) {
88
+ if (!text) return false;
89
+ const lower = text.toLowerCase();
90
+ return lower.includes('rate limit') ||
91
+ lower.includes('too many requests') ||
92
+ lower.includes('limit reached');
93
+ }
94
+
95
+ /**
96
+ * Sleep for specified milliseconds
97
+ */
98
+ function sleep(ms) {
99
+ return new Promise(resolve => setTimeout(resolve, ms));
100
+ }
101
+
102
+ /**
103
+ * Safely log to console with EPIPE protection
104
+ */
105
+ function safeLog(...args) {
106
+ try {
107
+ // Check if stdout is still writable before attempting to log
108
+ if (process.stdout && !process.stdout.destroyed) {
109
+ console.log(...args);
110
+ }
111
+ } catch (error) {
112
+ // Ignore EPIPE errors - common when piping output
113
+ if (error.code !== 'EPIPE') {
114
+ // For other errors, try writing to stderr
115
+ try {
116
+ if (process.stderr && !process.stderr.destroyed) {
117
+ console.error(...args);
118
+ }
119
+ } catch (stderrError) {
120
+ // If both fail, silently ignore
121
+ }
122
+ }
123
+ }
124
+ }
125
+
126
+ /**
127
+ * Update requirements status in file
128
+ */
129
+ async function updateRequirementsStatus(repoPath, status) {
130
+ try {
131
+ const reqPath = await getRequirementsPath(repoPath);
132
+ if (!reqPath || !await fs.pathExists(reqPath)) {
133
+ return;
134
+ }
135
+
136
+ let content = await fs.readFile(reqPath, 'utf8');
137
+
138
+ // Find and update status line
139
+ const statusLineRegex = /^## Current Status:.*$/m;
140
+ const newStatusLine = `## Current Status: ${status}`;
141
+
142
+ if (statusLineRegex.test(content)) {
143
+ content = content.replace(statusLineRegex, newStatusLine);
144
+ } else {
145
+ // Add status line after first line if not found
146
+ const lines = content.split('\n');
147
+ lines.splice(1, 0, newStatusLine);
148
+ content = lines.join('\n');
149
+ }
150
+
151
+ await fs.writeFile(reqPath, content, 'utf8');
152
+ } catch (error) {
153
+ console.error('Error updating requirements status:', error.message);
154
+ }
155
+ }
156
+
157
+ /**
158
+ * Get current requirement from REQUIREMENTS file
159
+ */
160
+ async function getCurrentRequirement(repoPath) {
161
+ try {
162
+ const reqPath = await getRequirementsPath(repoPath);
163
+ if (!reqPath || !await fs.pathExists(reqPath)) {
164
+ return null;
165
+ }
166
+
167
+ const content = await fs.readFile(reqPath, 'utf8');
168
+ const lines = content.split('\n');
169
+
170
+ // Look for current status section
171
+ let currentSection = null;
172
+ let currentRequirement = null;
173
+
174
+ for (let i = 0; i < lines.length; i++) {
175
+ const line = lines[i].trim();
176
+
177
+ if (line.startsWith('## Current Status:')) {
178
+ currentSection = 'current';
179
+ continue;
180
+ }
181
+
182
+ if (line.startsWith('##') && currentSection === 'current') {
183
+ break;
184
+ }
185
+
186
+ if (currentSection === 'current' && line.startsWith('###')) {
187
+ currentRequirement = line.replace(/^###\s*/, '').trim();
188
+ break;
189
+ }
190
+ }
191
+
192
+ return currentRequirement;
193
+ } catch (error) {
194
+ console.error('Error getting current requirement:', error.message);
195
+ return null;
196
+ }
197
+ }
198
+
199
+ /**
200
+ * Count total TODO requirements
201
+ */
202
+ async function countTodoRequirements(repoPath) {
203
+ try {
204
+ const reqPath = await getRequirementsPath(repoPath);
205
+ if (!reqPath || !await fs.pathExists(reqPath)) {
206
+ return 0;
207
+ }
208
+
209
+ const content = await fs.readFile(reqPath, 'utf8');
210
+ const lines = content.split('\n');
211
+
212
+ let inTodoSection = false;
213
+ let count = 0;
214
+
215
+ for (const line of lines) {
216
+ const trimmed = line.trim();
217
+
218
+ if (trimmed === '## TODO') {
219
+ inTodoSection = true;
220
+ continue;
221
+ }
222
+
223
+ if (trimmed.startsWith('##') && inTodoSection) {
224
+ break;
225
+ }
226
+
227
+ if (inTodoSection && trimmed.startsWith('###')) {
228
+ count++;
229
+ }
230
+ }
231
+
232
+ return count;
233
+ } catch (error) {
234
+ console.error('Error counting TODO requirements:', error.message);
235
+ return 0;
236
+ }
237
+ }
238
+
239
+ /**
240
+ * Move requirement from TODO to TO VERIFY BY HUMAN
241
+ */
242
+ async function moveRequirementToVerify(repoPath, requirementText) {
243
+ try {
244
+ const reqPath = await getRequirementsPath(repoPath);
245
+ if (!reqPath || !await fs.pathExists(reqPath)) {
246
+ return;
247
+ }
248
+
249
+ const content = await fs.readFile(reqPath, 'utf8');
250
+ const lines = content.split('\n');
251
+
252
+ // Find the requirement by its title
253
+ const normalizedRequirement = requirementText.trim();
254
+ const snippet = normalizedRequirement.substring(0, 80);
255
+ let requirementStartIndex = -1;
256
+ let requirementEndIndex = -1;
257
+ let inTodoSection = false;
258
+
259
+ for (let i = 0; i < lines.length; i++) {
260
+ const line = lines[i];
261
+
262
+ if (line.trim() === '## TODO') {
263
+ inTodoSection = true;
264
+ continue;
265
+ }
266
+
267
+ if (line.trim().startsWith('##') && inTodoSection) {
268
+ break;
269
+ }
270
+
271
+ if (inTodoSection && line.trim().startsWith('###')) {
272
+ const title = line.replace(/^###\s*/, '').trim();
273
+ if (title.includes(snippet) || title === normalizedRequirement) {
274
+ requirementStartIndex = i;
275
+
276
+ // Find end of requirement
277
+ for (let j = i + 1; j < lines.length; j++) {
278
+ if (lines[j].trim().startsWith('###') || lines[j].trim().startsWith('##')) {
279
+ requirementEndIndex = j;
280
+ break;
281
+ }
282
+ }
283
+
284
+ if (requirementEndIndex === -1) {
285
+ requirementEndIndex = lines.length;
286
+ }
287
+
288
+ break;
289
+ }
290
+ }
291
+ }
292
+
293
+ if (requirementStartIndex === -1) {
294
+ console.warn('Requirement not found in TODO section:', requirementText);
295
+ return;
296
+ }
297
+
298
+ // Extract the requirement block
299
+ const requirementBlock = lines.slice(requirementStartIndex, requirementEndIndex);
300
+
301
+ // Remove from TODO
302
+ lines.splice(requirementStartIndex, requirementEndIndex - requirementStartIndex);
303
+
304
+ // Find TO VERIFY BY HUMAN section and add requirement
305
+ let verifyIndex = -1;
306
+ for (let i = 0; i < lines.length; i++) {
307
+ if (lines[i].trim() === '## TO VERIFY BY HUMAN') {
308
+ verifyIndex = i + 1;
309
+ break;
310
+ }
311
+ }
312
+
313
+ if (verifyIndex === -1) {
314
+ // Create section if it doesn't exist
315
+ lines.push('## TO VERIFY BY HUMAN');
316
+ verifyIndex = lines.length;
317
+ }
318
+
319
+ // Insert requirement
320
+ lines.splice(verifyIndex, 0, ...requirementBlock);
321
+
322
+ // Write back to file
323
+ await fs.writeFile(reqPath, lines.join('\n'), 'utf8');
324
+
325
+ console.log(chalk.green(`Moved requirement to verify: ${requirementText}`));
326
+ } catch (error) {
327
+ console.error('Error moving requirement to verify:', error.message);
328
+ }
329
+ }
330
+
331
+ /**
332
+ * Move requirement to recycled section
333
+ */
334
+ async function moveRequirementToRecycle(repoPath, requirementText) {
335
+ try {
336
+ const reqPath = await getRequirementsPath(repoPath);
337
+ if (!reqPath || !await fs.pathExists(reqPath)) {
338
+ return;
339
+ }
340
+
341
+ const content = await fs.readFile(reqPath, 'utf8');
342
+ const lines = content.split('\n');
343
+
344
+ // Find and remove requirement from current section
345
+ const normalizedRequirement = requirementText.trim();
346
+ const snippet = normalizedRequirement.substring(0, 80);
347
+ let requirementStartIndex = -1;
348
+ let requirementEndIndex = -1;
349
+ let inCurrentSection = false;
350
+
351
+ for (let i = 0; i < lines.length; i++) {
352
+ const line = lines[i];
353
+
354
+ if (line.trim().startsWith('## Current Status:')) {
355
+ inCurrentSection = true;
356
+ continue;
357
+ }
358
+
359
+ if (line.trim().startsWith('##') && inCurrentSection) {
360
+ break;
361
+ }
362
+
363
+ if (inCurrentSection && line.trim().startsWith('###')) {
364
+ const title = line.replace(/^###\s*/, '').trim();
365
+ if (title.includes(snippet) || title === normalizedRequirement) {
366
+ requirementStartIndex = i;
367
+
368
+ // Find end of requirement
369
+ for (let j = i + 1; j < lines.length; j++) {
370
+ if (lines[j].trim().startsWith('###') || lines[j].trim().startsWith('##')) {
371
+ requirementEndIndex = j;
372
+ break;
373
+ }
374
+ }
375
+
376
+ if (requirementEndIndex === -1) {
377
+ requirementEndIndex = lines.length;
378
+ }
379
+
380
+ break;
381
+ }
382
+ }
383
+ }
384
+
385
+ if (requirementStartIndex === -1) {
386
+ console.warn('Requirement not found in current section:', requirementText);
387
+ return;
388
+ }
389
+
390
+ // Extract the requirement block
391
+ const requirementBlock = lines.slice(requirementStartIndex, requirementEndIndex);
392
+
393
+ // Remove from current section
394
+ lines.splice(requirementStartIndex, requirementEndIndex - requirementStartIndex);
395
+
396
+ // Find RECYCLED section and add requirement
397
+ let recycleIndex = -1;
398
+ for (let i = 0; i < lines.length; i++) {
399
+ if (lines[i].trim() === '## RECYCLED') {
400
+ recycleIndex = i + 1;
401
+ break;
402
+ }
403
+ }
404
+
405
+ if (recycleIndex === -1) {
406
+ // Create section if it doesn't exist
407
+ lines.push('## RECYCLED');
408
+ recycleIndex = lines.length;
409
+ }
410
+
411
+ // Add timestamp and requirement
412
+ const timestamp = new Date().toISOString();
413
+ lines.splice(recycleIndex, 0, `### ${requirementText}`, `*Recycled: ${timestamp}*`);
414
+
415
+ // Write back to file
416
+ await fs.writeFile(reqPath, lines.join('\n'), 'utf8');
417
+
418
+ console.log(chalk.yellow(`Recycled requirement: ${requirementText}`));
419
+ } catch (error) {
420
+ console.error('Error recycling requirement:', error.message);
421
+ }
422
+ }
423
+
424
+ module.exports = {
425
+ getTimestamp,
426
+ getLogTimestamp,
427
+ translateStage,
428
+ stripAnsi,
429
+ getVisualWidth,
430
+ padToVisualWidth,
431
+ isRateLimitMessage,
432
+ sleep,
433
+ safeLog,
434
+ updateRequirementsStatus,
435
+ getCurrentRequirement,
436
+ countTodoRequirements,
437
+ moveRequirementToVerify,
438
+ moveRequirementToRecycle
439
+ };
@@ -0,0 +1,110 @@
1
+ /**
2
+ * File operations for auto-direct mode
3
+ * Handles file searching, parsing LLM responses, and applying changes
4
+ */
5
+
6
+ const fs = require('fs-extra');
7
+ const path = require('path');
8
+ const chalk = require('chalk');
9
+ const { t } = require('vibecodingmachine-core');
10
+
11
+ /**
12
+ * Parse CREATE and FILE/SEARCH/REPLACE blocks from LLM response
13
+ */
14
+ function parseSearchReplaceBlocks(response) {
15
+ const changes = [];
16
+
17
+ // Match CREATE: path CONTENT: ``` content ``` format for new files
18
+ const createRegex = /CREATE:\s*(.+?)\nCONTENT:\s*```(?:[a-z]*)\n([\s\S]+?)```/g;
19
+
20
+ let match;
21
+ while ((match = createRegex.exec(response)) !== null) {
22
+ let filePath = match[1].trim();
23
+ const content = match[2];
24
+
25
+ // Clean up file path - remove "---" prefix if present
26
+ filePath = filePath.replace(/^---\s*/, '').trim();
27
+
28
+ changes.push({
29
+ type: 'create',
30
+ file: filePath,
31
+ content: content
32
+ });
33
+ }
34
+
35
+ // Match FILE: path SEARCH: ``` old ``` REPLACE: ``` new ``` format for modifications
36
+ const blockRegex = /FILE:\s*(.+?)\nSEARCH:\s*```(?:[a-z]*)\n([\s\S]+?)```\s*REPLACE:\s*```(?:[a-z]*)\n([\s\S]+?)```/g;
37
+
38
+ while ((match = blockRegex.exec(response)) !== null) {
39
+ let filePath = match[1].trim();
40
+ const searchText = match[2];
41
+ const replaceText = match[3];
42
+
43
+ // Clean up file path - remove "---" prefix if present
44
+ filePath = filePath.replace(/^---\s*/, '').trim();
45
+
46
+ changes.push({
47
+ type: 'modify',
48
+ file: filePath,
49
+ search: searchText,
50
+ replace: replaceText
51
+ });
52
+ }
53
+
54
+ return changes;
55
+ }
56
+
57
+ /**
58
+ * Normalize whitespace for comparison
59
+ */
60
+ function normalizeWhitespace(str) {
61
+ return str.replace(/\s+/g, ' ').trim();
62
+ }
63
+
64
+ /**
65
+ * Extract key identifiers from code (variable names, function names, strings)
66
+ */
67
+ function extractIdentifiers(code) {
68
+ const identifiers = new Set();
69
+
70
+ // Extract quoted strings
71
+ const stringMatches = code.match(/'([^']+)'|"([^"]+)"/g);
72
+ if (stringMatches) {
73
+ stringMatches.forEach(match => {
74
+ const str = match.slice(1, -1);
75
+ if (str.length > 3) {
76
+ identifiers.add(str);
77
+ }
78
+ });
79
+ }
80
+
81
+ // Extract variable/function names
82
+ const nameMatches = code.match(/\b([a-zA-Z_$][a-zA-Z0-9_$]*)\s*[:=]/g);
83
+ if (nameMatches) {
84
+ nameMatches.forEach(match => {
85
+ const name = match.replace(/[:=].*$/, '').trim();
86
+ if (name.length > 2) {
87
+ identifiers.add(name);
88
+ }
89
+ });
90
+ }
91
+
92
+ return Array.from(identifiers);
93
+ }
94
+
95
+ /**
96
+ * Extract structural pattern from code
97
+ */
98
+ function extractPattern(code) {
99
+ let pattern = code.replace(/'[^']+'|"[^"]+"/g, '"..."');
100
+ pattern = pattern.replace(/\b\d+\b/g, 'N');
101
+ pattern = normalizeWhitespace(pattern);
102
+ return pattern;
103
+ }
104
+
105
+ module.exports = {
106
+ parseSearchReplaceBlocks,
107
+ normalizeWhitespace,
108
+ extractIdentifiers,
109
+ extractPattern
110
+ };
@@ -3,7 +3,7 @@
3
3
  * Extracted from auto-direct.js to reduce file size
4
4
  */
5
5
 
6
- const { getAutoConfig } = require('../../utils/config');
6
+ const { getAutoConfig } = require('../../../utils/config');
7
7
  const { getProviderPreferences, getProviderDefinition, saveProviderPreferences } = require('../../utils/provider-registry');
8
8
  const ProviderManager = require('vibecodingmachine-core/src/ide-integration/provider-manager.cjs');
9
9
  const { checkAntigravityRateLimit, handleAntigravityRateLimit } = require('../../utils/antigravity-js-handler');
@@ -3,7 +3,7 @@
3
3
  * Extracted from auto-direct.js to reduce file size
4
4
  */
5
5
 
6
- const { getAutoConfig } = require('../../utils/config');
6
+ const { getAutoConfig } = require('../../../utils/config');
7
7
  const { getProviderPreferences, getProviderDefinition, saveProviderPreferences } = require('../../utils/provider-registry');
8
8
  const ProviderManager = require('vibecodingmachine-core/src/ide-integration/provider-manager.cjs');
9
9
  const { checkAntigravityRateLimit, handleAntigravityRateLimit } = require('../../utils/antigravity-js-handler');
@@ -4,7 +4,7 @@
4
4
  */
5
5
 
6
6
  const chalk = require('chalk');
7
- const { getAutoConfig, getStages, DEFAULT_STAGES } = require('../../utils/config');
7
+ const { getAutoConfig, getStages, DEFAULT_STAGES } = require('../../../utils/config');
8
8
 
9
9
  // Configured stages (will be loaded from config)
10
10
  let configuredStages = DEFAULT_STAGES;
@@ -1,32 +1,34 @@
1
1
  /**
2
- * Utility functions for auto-direct command
3
- * Extracted from auto-direct.js to reduce file size
2
+ * Utility functions for auto-direct mode
4
3
  */
5
4
 
6
5
  const chalk = require('chalk');
7
6
  const stringWidth = require('string-width');
7
+ const { t } = require('vibecodingmachine-core');
8
8
 
9
9
  /**
10
- * Get timestamp for logging
10
+ * Get timestamp in MST
11
11
  */
12
12
  function getTimestamp() {
13
13
  const now = new Date();
14
14
  return now.toLocaleTimeString('en-US', {
15
15
  hour: '2-digit',
16
16
  minute: '2-digit',
17
- second: '2-digit'
18
- });
17
+ hour12: false,
18
+ timeZone: 'America/Denver'
19
+ }) + ' MST';
19
20
  }
20
21
 
21
22
  /**
22
- * Get a human-friendly timestamp for log prefixes that includes date, time, and timezone
23
- * Example: "2025-01-02 3:45 PM MST"
23
+ * Get a human-friendly timestamp for log prefixes
24
24
  */
25
25
  function getLogTimestamp(date = new Date()) {
26
- const datePart = date.toISOString().split('T')[0]; // YYYY-MM-DD
26
+ const datePart = date.toISOString().split('T')[0];
27
27
  const timePart = date.toLocaleTimeString('en-US', {
28
28
  hour: 'numeric',
29
29
  minute: '2-digit',
30
+ hour12: true,
31
+ timeZone: 'America/Denver',
30
32
  timeZoneName: 'short'
31
33
  });
32
34
  return `${datePart} ${timePart}`;
@@ -39,32 +41,34 @@ function translateStage(stage) {
39
41
  const stageMap = {
40
42
  'PREPARE': 'workflow.stage.prepare',
41
43
  'REPRODUCE': 'workflow.stage.reproduce',
44
+ 'CREATE UNIT TEST': 'workflow.stage.create.unit.test',
42
45
  'ACT': 'workflow.stage.act',
43
- 'CLEAN UP': 'workflow.stage.cleanup',
46
+ 'CLEAN UP': 'workflow.stage.clean.up',
44
47
  'VERIFY': 'workflow.stage.verify',
48
+ 'RUN UNIT TESTS': 'workflow.stage.run.unit.tests',
45
49
  'DONE': 'workflow.stage.done'
46
50
  };
47
- return stageMap[stage] || stage;
51
+
52
+ const key = stageMap[stage];
53
+ return key ? t(key) : stage;
48
54
  }
49
55
 
50
56
  /**
51
57
  * Strip ANSI escape codes from a string
52
58
  */
53
59
  function stripAnsi(str) {
54
- // eslint-disable-next-line no-control-regex
55
60
  return str.replace(/\x1B\[[0-9;]*[mGKH]/g, '');
56
61
  }
57
62
 
58
63
  /**
59
- * Get visual width of a string accounting for ANSI codes, emojis, and wide characters
60
- * Uses string-width library for accurate Unicode width calculation
64
+ * Get visual width accounting for ANSI, emojis, wide characters
61
65
  */
62
66
  function getVisualWidth(str) {
63
67
  return stringWidth(str);
64
68
  }
65
69
 
66
70
  /**
67
- * Pad string to visual width accounting for emojis, ANSI codes, and wide characters
71
+ * Pad string to visual width
68
72
  */
69
73
  function padToVisualWidth(str, targetWidth) {
70
74
  const visualWidth = getVisualWidth(str);
@@ -82,13 +86,15 @@ function isRateLimitMessage(text) {
82
86
  if (!text) return false;
83
87
  const lower = text.toLowerCase();
84
88
  return lower.includes('rate limit') ||
85
- lower.includes('rate limited') ||
86
- lower.includes('too many requests') ||
87
- lower.includes('limit reached');
89
+ lower.includes('too many requests') ||
90
+ lower.includes('429') ||
91
+ lower.includes('weekly limit') ||
92
+ lower.includes('daily limit') ||
93
+ lower.includes('limit reached');
88
94
  }
89
95
 
90
96
  /**
91
- * Sleep for specified milliseconds
97
+ * Sleep helper
92
98
  */
93
99
  function sleep(ms) {
94
100
  return new Promise(resolve => setTimeout(resolve, ms));