vibecodingmachine-cli 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/.allnightai/REQUIREMENTS.md +11 -0
  2. package/.allnightai/temp/auto-status.json +6 -0
  3. package/.env +7 -0
  4. package/.eslintrc.js +16 -0
  5. package/README.md +85 -0
  6. package/bin/vibecodingmachine.js +274 -0
  7. package/jest.config.js +8 -0
  8. package/logs/audit/2025-11-07.jsonl +2 -0
  9. package/package.json +64 -0
  10. package/scripts/README.md +128 -0
  11. package/scripts/auto-start-wrapper.sh +92 -0
  12. package/scripts/postinstall.js +81 -0
  13. package/src/commands/auth.js +96 -0
  14. package/src/commands/auto-direct.js +1748 -0
  15. package/src/commands/auto.js +4692 -0
  16. package/src/commands/auto.js.bak +710 -0
  17. package/src/commands/ide.js +70 -0
  18. package/src/commands/repo.js +159 -0
  19. package/src/commands/requirements.js +161 -0
  20. package/src/commands/setup.js +91 -0
  21. package/src/commands/status.js +88 -0
  22. package/src/components/RequirementPage.js +0 -0
  23. package/src/file.js +0 -0
  24. package/src/index.js +5 -0
  25. package/src/main.js +0 -0
  26. package/src/ui/requirements-page.js +0 -0
  27. package/src/utils/auth.js +548 -0
  28. package/src/utils/auto-mode-ansi-ui.js +238 -0
  29. package/src/utils/auto-mode-simple-ui.js +161 -0
  30. package/src/utils/auto-mode-ui.js.bak.blessed +207 -0
  31. package/src/utils/auto-mode.js +65 -0
  32. package/src/utils/config.js +64 -0
  33. package/src/utils/interactive.js +3616 -0
  34. package/src/utils/keyboard-handler.js +152 -0
  35. package/src/utils/logger.js +4 -0
  36. package/src/utils/persistent-header.js +116 -0
  37. package/src/utils/provider-registry.js +128 -0
  38. package/src/utils/requirementUtils.js +0 -0
  39. package/src/utils/status-card.js +120 -0
  40. package/src/utils/status-manager.js +0 -0
  41. package/src/utils/status.js +0 -0
  42. package/src/utils/stdout-interceptor.js +127 -0
  43. package/tests/auto-mode.test.js +37 -0
  44. package/tests/config.test.js +34 -0
@@ -0,0 +1,238 @@
1
+ const chalk = require('chalk');
2
+ const ansiEscapes = require('ansi-escapes');
3
+ const stripAnsi = require('strip-ansi');
4
+
5
+ /**
6
+ * ANSI-based UI for Auto Mode
7
+ * Simple, reliable, no module system issues
8
+ */
9
+ class AutoModeANSIUI {
10
+ constructor(options = {}) {
11
+ this.menuContent = options.menuContent || '';
12
+ this.onExit = options.onExit;
13
+ this.requirement = 'Loading...';
14
+ this.step = 'PREPARE';
15
+ this.chatCount = 0;
16
+ this.maxChats = null;
17
+ this.progress = 0;
18
+ this.outputLines = [];
19
+ this.maxOutputLines = 15;
20
+
21
+ // Hide cursor for cleaner output
22
+ process.stdout.write(ansiEscapes.cursorHide);
23
+
24
+ // Handle exit - just use SIGINT, don't try to capture keys
25
+ // (stdin might be used by child process like Aider)
26
+ this.handleExit = () => {
27
+ this.destroy();
28
+ if (this.onExit) {
29
+ this.onExit();
30
+ }
31
+ process.exit(0);
32
+ };
33
+
34
+ process.on('SIGINT', this.handleExit);
35
+ process.on('SIGTERM', this.handleExit);
36
+
37
+ // Initial render
38
+ this.render();
39
+ }
40
+
41
+ updateStatus(status) {
42
+ if (status.requirement !== undefined) this.requirement = status.requirement;
43
+ if (status.step !== undefined) this.step = status.step;
44
+ if (status.chatCount !== undefined) this.chatCount = status.chatCount;
45
+ if (status.maxChats !== undefined) this.maxChats = status.maxChats;
46
+ if (status.progress !== undefined) this.progress = status.progress;
47
+ this.render();
48
+ }
49
+
50
+ appendOutput(line) {
51
+ // Safety: truncate very long lines to prevent memory issues
52
+ const maxLineLength = 500;
53
+ const safeLine = line.length > maxLineLength ? line.substring(0, maxLineLength) + '...' : line;
54
+
55
+ this.outputLines.push(safeLine);
56
+ // Keep only last N lines
57
+ if (this.outputLines.length > this.maxOutputLines) {
58
+ this.outputLines = this.outputLines.slice(-this.maxOutputLines);
59
+ }
60
+ this.render();
61
+ }
62
+
63
+ clearOutput() {
64
+ this.outputLines = [];
65
+ this.render();
66
+ }
67
+
68
+ render() {
69
+ // Clear screen and move cursor to top
70
+ process.stdout.write(ansiEscapes.clearScreen);
71
+ process.stdout.write(ansiEscapes.cursorTo(0, 0));
72
+
73
+ let output = '';
74
+
75
+ // Header box
76
+ output += this.drawBox(this.menuContent, { color: 'cyan', style: 'round' });
77
+ output += '\n';
78
+
79
+ // Status card
80
+ const statusContent = this.buildStatusContent();
81
+ output += this.drawBox(statusContent, { color: 'magenta', style: 'round', title: ' Auto Mode Status ' });
82
+ output += '\n';
83
+
84
+ // Output log
85
+ const outputContent = this.buildOutputContent();
86
+ output += this.drawBox(outputContent, { color: 'green', style: 'single', title: ' Aider Output ' });
87
+
88
+ process.stdout.write(output);
89
+ }
90
+
91
+ buildStatusContent() {
92
+ const stepColors = {
93
+ 'PREPARE': chalk.cyan,
94
+ 'ACT': chalk.yellow,
95
+ 'CLEAN UP': chalk.magenta,
96
+ 'VERIFY': chalk.blue,
97
+ 'DONE': chalk.green,
98
+ 'UNKNOWN': chalk.gray
99
+ };
100
+
101
+ const stepColor = stepColors[this.step] || chalk.gray;
102
+
103
+ // Progress bar
104
+ const barWidth = 40;
105
+ const filledWidth = Math.round((this.progress / 100) * barWidth);
106
+ const emptyWidth = barWidth - filledWidth;
107
+ const progressBar = chalk.green('█'.repeat(filledWidth)) + chalk.gray('░'.repeat(emptyWidth));
108
+
109
+ // Chat counter
110
+ const chatDisplay = this.maxChats
111
+ ? `Chat ${this.chatCount}/${this.maxChats}`
112
+ : `Chat ${this.chatCount} (unlimited)`;
113
+
114
+ // Truncate requirement if too long
115
+ const displayReq = this.requirement.length > 70
116
+ ? this.requirement.substring(0, 67) + '...'
117
+ : this.requirement;
118
+
119
+ return `
120
+ ${chalk.bold('📋 Current Requirement')}
121
+
122
+ ${displayReq}
123
+
124
+ ${chalk.bold('🚦 Status:')} ${stepColor.bold(this.step)}
125
+
126
+ ${progressBar} ${this.progress}%
127
+
128
+ ${chalk.gray(chatDisplay)}
129
+ `.trim();
130
+ }
131
+
132
+ buildOutputContent() {
133
+ if (this.outputLines.length === 0) {
134
+ return chalk.gray('No output yet...');
135
+ }
136
+ return this.outputLines.join('\n');
137
+ }
138
+
139
+ drawBox(content, options = {}) {
140
+ const { color = 'white', style = 'single', title = '' } = options;
141
+ const colorFn = chalk[color] || chalk.white;
142
+
143
+ // Box drawing characters
144
+ const chars = {
145
+ single: {
146
+ topLeft: '┌',
147
+ topRight: '┐',
148
+ bottomLeft: '└',
149
+ bottomRight: '┘',
150
+ horizontal: '─',
151
+ vertical: '│'
152
+ },
153
+ round: {
154
+ topLeft: '╭',
155
+ topRight: '╮',
156
+ bottomLeft: '╰',
157
+ bottomRight: '╯',
158
+ horizontal: '─',
159
+ vertical: '│'
160
+ },
161
+ double: {
162
+ topLeft: '╔',
163
+ topRight: '╗',
164
+ bottomLeft: '╚',
165
+ bottomRight: '╝',
166
+ horizontal: '═',
167
+ vertical: '║'
168
+ }
169
+ };
170
+
171
+ const boxChars = chars[style] || chars.single;
172
+ const lines = content.split('\n');
173
+
174
+ // Use fixed width to avoid any width calculation issues
175
+ const terminalWidth = process.stdout.columns || 80;
176
+ const width = Math.min(terminalWidth - 4, 60);
177
+
178
+ // Top border
179
+ let box = colorFn(boxChars.topLeft);
180
+ if (title) {
181
+ const titleLen = title.length;
182
+ const leftPad = Math.floor((width - titleLen - 2) / 2);
183
+ box += colorFn(boxChars.horizontal.repeat(leftPad));
184
+ box += colorFn(title);
185
+ box += colorFn(boxChars.horizontal.repeat(width - leftPad - titleLen));
186
+ } else {
187
+ box += colorFn(boxChars.horizontal.repeat(width));
188
+ }
189
+ box += colorFn(boxChars.topRight) + '\n';
190
+
191
+ // Content lines - use fixed padding to avoid width calculations
192
+ lines.forEach(line => {
193
+ // Truncate long lines
194
+ const maxLen = width - 4;
195
+ const displayLine = line.length > maxLen ? line.substring(0, maxLen - 3) + '...' : line;
196
+ const padding = Math.max(0, width - displayLine.length - 2);
197
+ box += colorFn(boxChars.vertical) + ' ' + displayLine + ' '.repeat(padding) + ' ' + colorFn(boxChars.vertical) + '\n';
198
+ });
199
+
200
+ // Bottom border
201
+ box += colorFn(boxChars.bottomLeft) + colorFn(boxChars.horizontal.repeat(width)) + colorFn(boxChars.bottomRight);
202
+
203
+ return box;
204
+ }
205
+
206
+ getStringWidth(str) {
207
+ // Simple character count - don't try to strip ANSI
208
+ // This avoids any regex issues entirely
209
+ return str.length;
210
+ }
211
+
212
+ destroy() {
213
+ // Clean up listeners
214
+ if (this.handleExit) {
215
+ process.removeListener('SIGINT', this.handleExit);
216
+ process.removeListener('SIGTERM', this.handleExit);
217
+ }
218
+
219
+ // Show cursor again
220
+ process.stdout.write(ansiEscapes.cursorShow);
221
+ }
222
+ }
223
+
224
+ /**
225
+ * Create and display an ANSI-based UI for Auto Mode
226
+ * @param {object} options - UI configuration
227
+ * @param {string} options.menuContent - Menu content to display in header
228
+ * @param {function} options.onExit - Callback when user presses Ctrl+C
229
+ * @returns {AutoModeANSIUI} UI instance
230
+ */
231
+ function createAutoModeUI(options = {}) {
232
+ return new AutoModeANSIUI(options);
233
+ }
234
+
235
+ module.exports = {
236
+ createAutoModeUI,
237
+ AutoModeANSIUI
238
+ };
@@ -0,0 +1,161 @@
1
+ const chalk = require('chalk');
2
+ const boxen = require('boxen');
3
+
4
+ /**
5
+ * Simple status-line UI for Auto Mode with persistent workflow card
6
+ * Shows a purple card similar to Electron app that stays visible
7
+ */
8
+ class AutoModeSimpleUI {
9
+ constructor(options = {}) {
10
+ this.menuContent = options.menuContent || '';
11
+ this.requirement = 'Loading...';
12
+ this.step = 'PREPARE';
13
+ this.chatCount = 0;
14
+ this.maxChats = null;
15
+ this.progress = 0;
16
+ this.outputLineCount = 0; // Track output lines for periodic re-render
17
+ this.lastCardPrintTime = Date.now(); // Track when we last printed the card
18
+ this.lastCardPrintLine = 0; // Track line count when we last printed the card
19
+
20
+ // Print header once
21
+ console.log('\n' + chalk.bold.cyan('═══════════════════════════════════════════════════════════'));
22
+ console.log(this.menuContent);
23
+ console.log(chalk.bold.cyan('═══════════════════════════════════════════════════════════') + '\n');
24
+
25
+ // Initial workflow card
26
+ this.printWorkflowCard();
27
+ }
28
+
29
+ updateStatus(status) {
30
+ if (status.requirement !== undefined) this.requirement = status.requirement;
31
+ if (status.step !== undefined) this.step = status.step;
32
+ if (status.chatCount !== undefined) this.chatCount = status.chatCount;
33
+ if (status.maxChats !== undefined) this.maxChats = status.maxChats;
34
+ if (status.progress !== undefined) this.progress = status.progress;
35
+
36
+ this.printWorkflowCard();
37
+ }
38
+
39
+ /**
40
+ * Get icon for each workflow stage based on current step
41
+ */
42
+ getStepIcon(stage, currentStage) {
43
+ const stageOrder = ['PREPARE', 'ACT', 'CLEAN UP', 'VERIFY', 'DONE'];
44
+ const currentIndex = stageOrder.indexOf(currentStage);
45
+ const stepIndex = stageOrder.indexOf(stage);
46
+
47
+ if (stepIndex < currentIndex) {
48
+ return '✅'; // Completed steps
49
+ } else if (stepIndex === currentIndex) {
50
+ if (currentStage === 'DONE') {
51
+ return '✅'; // Done is always checked
52
+ } else {
53
+ return '🔨'; // Current step
54
+ }
55
+ } else {
56
+ return '⏳'; // Pending steps
57
+ }
58
+ }
59
+
60
+ /**
61
+ * Render the persistent workflow card (similar to Electron app's purple card)
62
+ */
63
+ printWorkflowCard() {
64
+ // Step color mapping
65
+ const stepColors = {
66
+ 'PREPARE': chalk.cyan,
67
+ 'ACT': chalk.yellow,
68
+ 'CLEAN UP': chalk.magenta,
69
+ 'VERIFY': chalk.blue,
70
+ 'DONE': chalk.green,
71
+ 'UNKNOWN': chalk.gray
72
+ };
73
+
74
+ const currentStepColor = stepColors[this.step] || chalk.gray;
75
+
76
+ // Build workflow states line: PREPARE ⏳ ACT ⏳ CLEAN UP ⏳ VERIFY ⏳ DONE
77
+ const stages = [
78
+ { name: 'PREPARE', color: chalk.cyan },
79
+ { name: 'ACT', color: chalk.yellow },
80
+ { name: 'CLEAN UP', color: chalk.magenta },
81
+ { name: 'VERIFY', color: chalk.blue },
82
+ { name: 'DONE', color: chalk.green }
83
+ ];
84
+
85
+ const workflowLine = stages.map((stage, index) => {
86
+ const icon = this.getStepIcon(stage.name, this.step);
87
+ const isCurrent = stage.name === this.step;
88
+ const stageColor = isCurrent ? currentStepColor.bold : stage.color;
89
+ return `${icon} ${stageColor(stage.name)}`;
90
+ }).join(` ${chalk.gray('⏳')} `);
91
+
92
+ // Truncate requirement if too long
93
+ const displayReq = this.requirement.length > 70
94
+ ? this.requirement.substring(0, 67) + '...'
95
+ : this.requirement;
96
+
97
+ // Build card content
98
+ const cardContent = [
99
+ workflowLine,
100
+ '',
101
+ chalk.bold.white(`🎯 Working on: ${displayReq}`)
102
+ ].join('\n');
103
+
104
+ // Render card with purple/magenta border (matching Electron app)
105
+ const card = boxen(cardContent, {
106
+ padding: { left: 1, right: 1, top: 1, bottom: 1 },
107
+ margin: { top: 0, right: 0, bottom: 1, left: 0 },
108
+ borderStyle: 'round',
109
+ borderColor: 'magenta',
110
+ backgroundColor: 'black'
111
+ });
112
+
113
+ // Print the card with a separator line before it for visibility
114
+ if (this.outputLineCount > 0) {
115
+ // Add separator when re-printing after output
116
+ console.log(chalk.gray('─'.repeat(80)));
117
+ }
118
+ console.log(card);
119
+ this.lastCardPrintTime = Date.now();
120
+ this.lastCardPrintLine = this.outputLineCount;
121
+ }
122
+
123
+ appendOutput(line) {
124
+ // Periodically re-print the workflow card to keep it visible
125
+ // Re-print every 20 lines of output, or if more than 5 seconds have passed
126
+ const linesSinceLastCard = this.outputLineCount - this.lastCardPrintLine;
127
+ const timeSinceLastCard = Date.now() - this.lastCardPrintTime;
128
+
129
+ if (linesSinceLastCard >= 20 || timeSinceLastCard > 5000) {
130
+ this.printWorkflowCard();
131
+ }
132
+
133
+ // Increment output line counter
134
+ if (line && line.trim()) {
135
+ this.outputLineCount++;
136
+ console.log(line);
137
+ }
138
+ }
139
+
140
+ clearOutput() {
141
+ // No-op for simple UI
142
+ }
143
+
144
+ destroy() {
145
+ console.log('\n' + chalk.bold.green('Auto mode exited.') + '\n');
146
+ }
147
+ }
148
+
149
+ /**
150
+ * Create and display a simple status UI for Auto Mode
151
+ * @param {object} options - UI configuration
152
+ * @returns {AutoModeSimpleUI} UI instance
153
+ */
154
+ function createAutoModeUI(options = {}) {
155
+ return new AutoModeSimpleUI(options);
156
+ }
157
+
158
+ module.exports = {
159
+ createAutoModeUI,
160
+ AutoModeSimpleUI
161
+ };
@@ -0,0 +1,207 @@
1
+ const blessed = require('blessed');
2
+ const chalk = require('chalk');
3
+
4
+ /**
5
+ * Create and display a blessed-based UI for Auto Mode
6
+ * Shows: persistent menu header, status card, and scrolling output
7
+ * @param {object} options - UI configuration
8
+ * @param {string} options.menuContent - Menu content to display in header
9
+ * @param {function} options.onExit - Callback when user presses Ctrl+C or q
10
+ * @returns {object} UI interface with methods to update status and append output
11
+ */
12
+ function createAutoModeUI(options = {}) {
13
+ const { menuContent = '', onExit } = options;
14
+
15
+ // Create screen
16
+ const screen = blessed.screen({
17
+ smartCSR: true,
18
+ title: 'AllNightAI Auto Mode',
19
+ fullUnicode: true,
20
+ forceUnicode: true
21
+ });
22
+
23
+ // Clear screen and render immediately
24
+ screen.clearRegion(0, screen.width, 0, screen.height);
25
+
26
+ // Header box (menu) - use fixed height instead of shrink
27
+ const headerHeight = 11; // Fixed height for header
28
+ const header = blessed.box({
29
+ top: 0,
30
+ left: 0,
31
+ width: '100%',
32
+ height: headerHeight,
33
+ content: menuContent,
34
+ tags: true,
35
+ border: {
36
+ type: 'line'
37
+ },
38
+ style: {
39
+ border: {
40
+ fg: 'cyan'
41
+ }
42
+ }
43
+ });
44
+
45
+ // Status card (purple/magenta border like GUI)
46
+ const statusCardHeight = 12;
47
+ const statusCard = blessed.box({
48
+ top: headerHeight,
49
+ left: 0,
50
+ width: '100%',
51
+ height: statusCardHeight,
52
+ tags: true,
53
+ border: {
54
+ type: 'round'
55
+ },
56
+ style: {
57
+ border: {
58
+ fg: 'magenta'
59
+ }
60
+ },
61
+ label: ' Auto Mode Status '
62
+ });
63
+
64
+ // Output log (scrollable)
65
+ const totalHeaderHeight = headerHeight + statusCardHeight;
66
+ const outputLog = blessed.log({
67
+ top: totalHeaderHeight,
68
+ left: 0,
69
+ width: '100%',
70
+ height: `100%-${totalHeaderHeight}`,
71
+ tags: true,
72
+ border: {
73
+ type: 'line'
74
+ },
75
+ style: {
76
+ border: {
77
+ fg: 'green'
78
+ }
79
+ },
80
+ label: ' Aider Output ',
81
+ scrollable: true,
82
+ alwaysScroll: true,
83
+ mouse: true,
84
+ keys: true,
85
+ vi: true,
86
+ scrollbar: {
87
+ ch: ' ',
88
+ track: {
89
+ bg: 'cyan'
90
+ },
91
+ style: {
92
+ inverse: true
93
+ }
94
+ }
95
+ });
96
+
97
+ // Append all elements to screen
98
+ screen.append(header);
99
+ screen.append(statusCard);
100
+ screen.append(outputLog);
101
+
102
+ // Focus the output log for scrolling
103
+ outputLog.focus();
104
+
105
+ // Key bindings
106
+ screen.key(['escape', 'q', 'C-c'], () => {
107
+ if (onExit) {
108
+ onExit();
109
+ }
110
+ screen.destroy();
111
+ process.exit(0);
112
+ });
113
+
114
+ // Initial render
115
+ screen.render();
116
+
117
+ /**
118
+ * Update the status card with new status information
119
+ * @param {object} status - Status object
120
+ * @param {string} status.requirement - Current requirement
121
+ * @param {string} status.step - Current step (PREPARE, ACT, CLEAN UP, VERIFY, DONE)
122
+ * @param {number} status.chatCount - Current chat count
123
+ * @param {number|null} status.maxChats - Max chats or null for unlimited
124
+ * @param {number} status.progress - Progress percentage (0-100)
125
+ */
126
+ function updateStatus(status) {
127
+ const {
128
+ requirement = 'No requirement loaded',
129
+ step = 'UNKNOWN',
130
+ chatCount = 0,
131
+ maxChats = null,
132
+ progress = 0
133
+ } = status;
134
+
135
+ // Step color mapping
136
+ const stepColors = {
137
+ 'PREPARE': '{cyan-fg}',
138
+ 'ACT': '{yellow-fg}',
139
+ 'CLEAN UP': '{magenta-fg}',
140
+ 'VERIFY': '{blue-fg}',
141
+ 'DONE': '{green-fg}',
142
+ 'UNKNOWN': '{gray-fg}'
143
+ };
144
+
145
+ const stepColor = stepColors[step] || '{gray-fg}';
146
+
147
+ // Progress bar
148
+ const barWidth = 40;
149
+ const filledWidth = Math.round((progress / 100) * barWidth);
150
+ const emptyWidth = barWidth - filledWidth;
151
+ const progressBar = '{green-fg}' + '█'.repeat(filledWidth) + '{/green-fg}' +
152
+ '{gray-fg}' + '░'.repeat(emptyWidth) + '{/gray-fg}';
153
+
154
+ // Chat counter
155
+ const chatDisplay = maxChats
156
+ ? `Chat ${chatCount}/${maxChats}`
157
+ : `Chat ${chatCount} (unlimited)`;
158
+
159
+ // Build card content
160
+ const content = `
161
+ {bold}📋 Current Requirement{/bold}
162
+
163
+ ${requirement.length > 70 ? requirement.substring(0, 67) + '...' : requirement}
164
+
165
+ {bold}🚦 Status:{/bold} ${stepColor}{bold}${step}{/bold}{/}
166
+
167
+ ${progressBar} ${progress}%
168
+
169
+ {gray-fg}${chatDisplay}{/gray-fg}
170
+ `;
171
+
172
+ statusCard.setContent(content);
173
+ screen.render();
174
+ }
175
+
176
+ /**
177
+ * Append a line to the output log
178
+ * @param {string} line - Line to append
179
+ */
180
+ function appendOutput(line) {
181
+ outputLog.log(line);
182
+ screen.render();
183
+ }
184
+
185
+ /**
186
+ * Clear the output log
187
+ */
188
+ function clearOutput() {
189
+ outputLog.setContent('');
190
+ screen.render();
191
+ }
192
+
193
+ return {
194
+ screen,
195
+ header,
196
+ statusCard,
197
+ outputLog,
198
+ updateStatus,
199
+ appendOutput,
200
+ clearOutput,
201
+ destroy: () => screen.destroy()
202
+ };
203
+ }
204
+
205
+ module.exports = {
206
+ createAutoModeUI
207
+ };
@@ -0,0 +1,65 @@
1
+ const path = require('path');
2
+ const fs = require('fs-extra');
3
+ const { getRepoPath } = require('./config');
4
+ const { logAutoModeStart, logAutoModeStop } = require('@vibecodingmachine/core');
5
+
6
+ function getStatusPath(repoPath) {
7
+ return path.join(repoPath, '.vibecodingmachine', 'temp', 'auto-status.json');
8
+ }
9
+
10
+ async function checkAutoModeStatus() {
11
+ const repoPath = await getRepoPath();
12
+ if (!repoPath) return { running: false };
13
+ const statusPath = getStatusPath(repoPath);
14
+ if (!await fs.pathExists(statusPath)) return { running: false };
15
+ try {
16
+ const status = await fs.readJson(statusPath);
17
+ return status;
18
+ } catch {
19
+ return { running: false };
20
+ }
21
+ }
22
+
23
+ async function startAutoMode(repoPath, config) {
24
+ const statusPath = getStatusPath(repoPath);
25
+ await fs.ensureDir(path.dirname(statusPath));
26
+ const status = {
27
+ running: true,
28
+ startedAt: new Date().toISOString(),
29
+ chatCount: 0,
30
+ ide: config.ide || 'cline'
31
+ };
32
+ await fs.writeJson(statusPath, status, { spaces: 2 });
33
+
34
+ // Log to audit log
35
+ logAutoModeStart(config.ide || 'cline', config.maxChats || 0);
36
+ }
37
+
38
+ async function stopAutoMode(reason = 'manual') {
39
+ const repoPath = await getRepoPath();
40
+ if (!repoPath) return;
41
+ const statusPath = getStatusPath(repoPath);
42
+ if (!await fs.pathExists(statusPath)) return;
43
+ const current = await fs.readJson(statusPath).catch(() => ({}));
44
+ await fs.writeJson(statusPath, { ...current, running: false }, { spaces: 2 });
45
+
46
+ // Log to audit log
47
+ logAutoModeStop(reason);
48
+ }
49
+
50
+ async function updateAutoModeStatus(repoPath, updates) {
51
+ const statusPath = getStatusPath(repoPath);
52
+ if (!await fs.pathExists(statusPath)) return;
53
+ const current = await fs.readJson(statusPath).catch(() => ({ running: true }));
54
+ await fs.writeJson(statusPath, { ...current, ...updates }, { spaces: 2 });
55
+ }
56
+
57
+ module.exports = {
58
+ checkAutoModeStatus,
59
+ startAutoMode,
60
+ stopAutoMode,
61
+ updateAutoModeStatus
62
+ };
63
+
64
+
65
+