minovative-mind-cli 2.9.0 → 2.9.1
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.
|
@@ -31,6 +31,8 @@ export declare class AsyncInputHandler {
|
|
|
31
31
|
private stopped;
|
|
32
32
|
/** Reference to the AbortController controlling the active AI request to trigger cancellations */
|
|
33
33
|
private ac;
|
|
34
|
+
/** Timeout reference for reattaching listener after prompting to avoid race conditions */
|
|
35
|
+
private reattachTimeout;
|
|
34
36
|
/**
|
|
35
37
|
* Registers the active AbortController for the current AI request.
|
|
36
38
|
* This is triggered when the user commands a process cancel (e.g., typing "stop").
|
|
@@ -45,6 +47,14 @@ export declare class AsyncInputHandler {
|
|
|
45
47
|
* @returns True if a text prompt is currently displayed, false otherwise.
|
|
46
48
|
*/
|
|
47
49
|
isCurrentlyPrompting(): boolean;
|
|
50
|
+
/**
|
|
51
|
+
* Checks if there are any queued user messages pending.
|
|
52
|
+
*/
|
|
53
|
+
hasQueuedMessages(): boolean;
|
|
54
|
+
/**
|
|
55
|
+
* Returns a copy of currently queued messages without clearing them.
|
|
56
|
+
*/
|
|
57
|
+
peek(): string[];
|
|
48
58
|
/**
|
|
49
59
|
* Blocks and waits until any active prompting action is completed.
|
|
50
60
|
* Guarantees terminal stdout is clean before resuming logs.
|
|
@@ -63,16 +73,21 @@ export declare class AsyncInputHandler {
|
|
|
63
73
|
private onData;
|
|
64
74
|
/**
|
|
65
75
|
* Starts intercepting keystrokes and enables raw terminal processing.
|
|
66
|
-
*
|
|
76
|
+
* Clears any stale queued messages and saves the original raw mode configuration.
|
|
67
77
|
*
|
|
68
78
|
* @param spinner - The current active Clack spinner UI reference, if any.
|
|
69
79
|
*/
|
|
70
80
|
start(spinner?: ReturnType<typeof p.spinner>): void;
|
|
71
81
|
/**
|
|
72
|
-
* Disables raw mode, stops intercepting keystrokes,
|
|
73
|
-
* the terminal stdin stream to its original raw/cooked state
|
|
82
|
+
* Disables raw mode, stops intercepting keystrokes, restores
|
|
83
|
+
* the terminal stdin stream to its original raw/cooked state, and
|
|
84
|
+
* completely clears the queued messages.
|
|
74
85
|
*/
|
|
75
86
|
stop(): void;
|
|
87
|
+
/**
|
|
88
|
+
* Clears all pending queued messages without modifying stream state.
|
|
89
|
+
*/
|
|
90
|
+
clear(): void;
|
|
76
91
|
/**
|
|
77
92
|
* Clears the spinner reference without stopping the input handler.
|
|
78
93
|
*/
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import * as p from '@clack/prompts';
|
|
2
2
|
import pc from 'picocolors';
|
|
3
3
|
import { truncateForTerminal } from '../../utils/terminal.js';
|
|
4
|
+
if (process.stdin.setMaxListeners) {
|
|
5
|
+
process.stdin.setMaxListeners(50);
|
|
6
|
+
}
|
|
7
|
+
if (process.stdout.setMaxListeners) {
|
|
8
|
+
process.stdout.setMaxListeners(50);
|
|
9
|
+
}
|
|
4
10
|
/**
|
|
5
11
|
* Asynchronous Input Handler (AsyncInputHandler)
|
|
6
12
|
*
|
|
@@ -33,6 +39,8 @@ export class AsyncInputHandler {
|
|
|
33
39
|
stopped = true;
|
|
34
40
|
/** Reference to the AbortController controlling the active AI request to trigger cancellations */
|
|
35
41
|
ac = null;
|
|
42
|
+
/** Timeout reference for reattaching listener after prompting to avoid race conditions */
|
|
43
|
+
reattachTimeout = null;
|
|
36
44
|
/**
|
|
37
45
|
* Registers the active AbortController for the current AI request.
|
|
38
46
|
* This is triggered when the user commands a process cancel (e.g., typing "stop").
|
|
@@ -51,6 +59,18 @@ export class AsyncInputHandler {
|
|
|
51
59
|
isCurrentlyPrompting() {
|
|
52
60
|
return this.isPrompting;
|
|
53
61
|
}
|
|
62
|
+
/**
|
|
63
|
+
* Checks if there are any queued user messages pending.
|
|
64
|
+
*/
|
|
65
|
+
hasQueuedMessages() {
|
|
66
|
+
return this.queue.length > 0;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Returns a copy of currently queued messages without clearing them.
|
|
70
|
+
*/
|
|
71
|
+
peek() {
|
|
72
|
+
return [...this.queue];
|
|
73
|
+
}
|
|
54
74
|
/**
|
|
55
75
|
* Blocks and waits until any active prompting action is completed.
|
|
56
76
|
* Guarantees terminal stdout is clean before resuming logs.
|
|
@@ -72,7 +92,7 @@ export class AsyncInputHandler {
|
|
|
72
92
|
*/
|
|
73
93
|
onData = async (chunk) => {
|
|
74
94
|
try {
|
|
75
|
-
if (this.isPrompting)
|
|
95
|
+
if (this.stopped || this.isPrompting)
|
|
76
96
|
return;
|
|
77
97
|
const char = chunk.toString();
|
|
78
98
|
// Handle Ctrl+C (End of Text ASCII 0x03) immediately
|
|
@@ -99,6 +119,9 @@ export class AsyncInputHandler {
|
|
|
99
119
|
placeholder: '(Leave blank and press Enter to cancel)',
|
|
100
120
|
initialValue: char,
|
|
101
121
|
});
|
|
122
|
+
if (this.stopped) {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
102
125
|
let wasAborted = false;
|
|
103
126
|
if (!p.isCancel(userInput) && userInput.trim()) {
|
|
104
127
|
const text = userInput.trim();
|
|
@@ -114,7 +137,7 @@ export class AsyncInputHandler {
|
|
|
114
137
|
p.log.info(pc.cyan(`📥 Queued message: "${text}"`));
|
|
115
138
|
}
|
|
116
139
|
}
|
|
117
|
-
if (this.spinner && !wasAborted) {
|
|
140
|
+
if (this.spinner && !wasAborted && !this.stopped) {
|
|
118
141
|
const resumeMsg = this.spinner._lastMessage || 'Resuming execution...';
|
|
119
142
|
this.spinner.start(resumeMsg);
|
|
120
143
|
}
|
|
@@ -128,9 +151,14 @@ export class AsyncInputHandler {
|
|
|
128
151
|
process.stdin.setRawMode(true);
|
|
129
152
|
}
|
|
130
153
|
process.stdin.resume();
|
|
154
|
+
if (this.reattachTimeout) {
|
|
155
|
+
clearTimeout(this.reattachTimeout);
|
|
156
|
+
}
|
|
131
157
|
// Small delay before reattaching listener to avoid capturing duplicate keypress frames
|
|
132
|
-
setTimeout(() => {
|
|
158
|
+
this.reattachTimeout = setTimeout(() => {
|
|
159
|
+
this.reattachTimeout = null;
|
|
133
160
|
if (!this.stopped) {
|
|
161
|
+
process.stdin.removeListener('data', this.onData);
|
|
134
162
|
process.stdin.on('data', this.onData);
|
|
135
163
|
}
|
|
136
164
|
this.isPrompting = false;
|
|
@@ -143,56 +171,53 @@ export class AsyncInputHandler {
|
|
|
143
171
|
};
|
|
144
172
|
/**
|
|
145
173
|
* Starts intercepting keystrokes and enables raw terminal processing.
|
|
146
|
-
*
|
|
174
|
+
* Clears any stale queued messages and saves the original raw mode configuration.
|
|
147
175
|
*
|
|
148
176
|
* @param spinner - The current active Clack spinner UI reference, if any.
|
|
149
177
|
*/
|
|
150
178
|
start(spinner) {
|
|
179
|
+
if (this.reattachTimeout) {
|
|
180
|
+
clearTimeout(this.reattachTimeout);
|
|
181
|
+
this.reattachTimeout = null;
|
|
182
|
+
}
|
|
151
183
|
this.stopped = false;
|
|
184
|
+
this.queue = [];
|
|
152
185
|
if (spinner) {
|
|
153
|
-
this.spinner
|
|
154
|
-
// Monkey-patch to track the latest message for un-pausing
|
|
155
|
-
if (!spinner._isPatched) {
|
|
156
|
-
;
|
|
157
|
-
spinner._isPatched = true;
|
|
158
|
-
spinner._lastMessage = 'Executing...';
|
|
159
|
-
const originalMessage = spinner.message.bind(spinner);
|
|
160
|
-
spinner.message = (msg) => {
|
|
161
|
-
if (msg) {
|
|
162
|
-
;
|
|
163
|
-
spinner._lastMessage = msg;
|
|
164
|
-
}
|
|
165
|
-
originalMessage(msg ? truncateForTerminal(msg) : msg);
|
|
166
|
-
};
|
|
167
|
-
const originalStart = spinner.start.bind(spinner);
|
|
168
|
-
spinner.start = (msg) => {
|
|
169
|
-
if (msg) {
|
|
170
|
-
;
|
|
171
|
-
spinner._lastMessage = msg;
|
|
172
|
-
}
|
|
173
|
-
originalStart(msg ? truncateForTerminal(msg) : msg);
|
|
174
|
-
};
|
|
175
|
-
}
|
|
186
|
+
this.setSpinner(spinner);
|
|
176
187
|
}
|
|
177
188
|
if (process.stdin.isTTY) {
|
|
178
189
|
this.originalRawMode = process.stdin.isRaw;
|
|
179
190
|
process.stdin.setRawMode(true);
|
|
180
191
|
}
|
|
181
192
|
process.stdin.resume();
|
|
193
|
+
process.stdin.removeListener('data', this.onData);
|
|
182
194
|
process.stdin.on('data', this.onData);
|
|
183
195
|
}
|
|
184
196
|
/**
|
|
185
|
-
* Disables raw mode, stops intercepting keystrokes,
|
|
186
|
-
* the terminal stdin stream to its original raw/cooked state
|
|
197
|
+
* Disables raw mode, stops intercepting keystrokes, restores
|
|
198
|
+
* the terminal stdin stream to its original raw/cooked state, and
|
|
199
|
+
* completely clears the queued messages.
|
|
187
200
|
*/
|
|
188
201
|
stop() {
|
|
189
202
|
this.stopped = true;
|
|
203
|
+
this.isPrompting = false;
|
|
204
|
+
if (this.reattachTimeout) {
|
|
205
|
+
clearTimeout(this.reattachTimeout);
|
|
206
|
+
this.reattachTimeout = null;
|
|
207
|
+
}
|
|
190
208
|
process.stdin.removeListener('data', this.onData);
|
|
191
209
|
if (process.stdin.isTTY) {
|
|
192
210
|
process.stdin.setRawMode(this.originalRawMode);
|
|
193
211
|
}
|
|
194
212
|
process.stdin.resume();
|
|
195
213
|
this.spinner = null;
|
|
214
|
+
this.queue = [];
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Clears all pending queued messages without modifying stream state.
|
|
218
|
+
*/
|
|
219
|
+
clear() {
|
|
220
|
+
this.queue = [];
|
|
196
221
|
}
|
|
197
222
|
/**
|
|
198
223
|
* Clears the spinner reference without stopping the input handler.
|
package/dist/services/agent.js
CHANGED
|
@@ -504,6 +504,7 @@ export async function executeSingleTurn(workspaceRoot, userInput, chat, inputHan
|
|
|
504
504
|
const orchestrator = new Orchestrator(workspaceRoot, chatSessionState.id, inputHandler);
|
|
505
505
|
const handledByOrchestrator = await orchestrator.runOrchestration(finalInput, dynamicSystemInstruction, ac.signal);
|
|
506
506
|
if (typeof handledByOrchestrator === 'string') {
|
|
507
|
+
inputHandler.stop();
|
|
507
508
|
const isStopped = handledByOrchestrator.includes('Generation stopped') ||
|
|
508
509
|
handledByOrchestrator.includes('[Generation stopped');
|
|
509
510
|
if (isStopped) {
|
|
@@ -654,6 +655,7 @@ export async function executeSingleTurn(workspaceRoot, userInput, chat, inputHan
|
|
|
654
655
|
// Stage 2: Recursive Tool Loops and Automated Self-Correction (delegated to a helper function to avoid nested loop warning)
|
|
655
656
|
const correctionRes = await executeSelfCorrectionLoop(chat, result, workspaceRoot, inputHandler, effectiveTargetAgent, ac.signal, spinner, userInput, isPlanMode);
|
|
656
657
|
const finalText = correctionRes.finalText;
|
|
658
|
+
inputHandler.stop();
|
|
657
659
|
// Update latest usage metadata to reflect all completed turns
|
|
658
660
|
const usage = getAndResetTurnUsage();
|
|
659
661
|
const postExecutionChanges = changeLogger.getCurrentChangeSet()?.changes || [];
|
|
@@ -890,6 +892,7 @@ export async function executeSingleTurn(workspaceRoot, userInput, chat, inputHan
|
|
|
890
892
|
p.log.error(`${pc.red('Error:')} ${message}`);
|
|
891
893
|
}
|
|
892
894
|
finally {
|
|
895
|
+
inputHandler.stop();
|
|
893
896
|
spinner.stop();
|
|
894
897
|
process.stdout.write('\x1b[2K\r');
|
|
895
898
|
// Persist any verified or partial changes into our session change ledger.
|
package/oclif.manifest.json
CHANGED
package/package.json
CHANGED