@selesai/code 0.1.14 → 0.1.16
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.
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
* Interactive mode for the coding agent.
|
|
3
3
|
* Handles TUI rendering and user interaction, delegating business logic to AgentSession.
|
|
4
4
|
*/
|
|
5
|
-
import * as crypto from "node:crypto";
|
|
6
5
|
import * as fs from "node:fs";
|
|
7
6
|
import * as os from "node:os";
|
|
8
7
|
import * as path from "node:path";
|
|
@@ -29,6 +28,7 @@ import { getChangelogPath, getNewEntries, normalizeChangelogLinks, parseChangelo
|
|
|
29
28
|
import { copyToClipboard } from "../../utils/clipboard.js";
|
|
30
29
|
import { extensionForImageMimeType, readClipboardImage } from "../../utils/clipboard-image.js";
|
|
31
30
|
import { parseGitUrl } from "../../utils/git.js";
|
|
31
|
+
import { formatDimensionNote, resizeImage } from "../../utils/image-resize.js";
|
|
32
32
|
import { getCwdRelativePath } from "../../utils/paths.js";
|
|
33
33
|
import { getPiUserAgent } from "../../utils/pi-user-agent.js";
|
|
34
34
|
import { killTrackedDetachedChildren } from "../../utils/shell.js";
|
|
@@ -152,6 +152,7 @@ export class InteractiveMode {
|
|
|
152
152
|
isInitialized = false;
|
|
153
153
|
onInputCallback;
|
|
154
154
|
pendingUserInputs = [];
|
|
155
|
+
pendingInputImages = [];
|
|
155
156
|
loadingAnimation = undefined;
|
|
156
157
|
workingMessage = undefined;
|
|
157
158
|
workingVisible = true;
|
|
@@ -595,7 +596,7 @@ export class InteractiveMode {
|
|
|
595
596
|
while (true) {
|
|
596
597
|
const userInput = await this.getUserInput();
|
|
597
598
|
try {
|
|
598
|
-
await this.session.prompt(userInput);
|
|
599
|
+
await this.session.prompt(userInput.text, { images: userInput.images });
|
|
599
600
|
}
|
|
600
601
|
catch (error) {
|
|
601
602
|
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
@@ -1994,27 +1995,66 @@ export class InteractiveMode {
|
|
|
1994
1995
|
try {
|
|
1995
1996
|
const image = await readClipboardImage();
|
|
1996
1997
|
if (!image) {
|
|
1998
|
+
this.showWarning("No image found in clipboard");
|
|
1997
1999
|
return;
|
|
1998
2000
|
}
|
|
1999
|
-
// Write to temp file
|
|
2000
2001
|
const tmpDir = os.tmpdir();
|
|
2001
2002
|
const ext = extensionForImageMimeType(image.mimeType) ?? "png";
|
|
2002
|
-
const fileName = `pi-clipboard-${
|
|
2003
|
+
const fileName = `pi-clipboard-${Date.now()}-${this.pendingInputImages.length + 1}.${ext}`;
|
|
2003
2004
|
const filePath = path.join(tmpDir, fileName);
|
|
2004
2005
|
fs.writeFileSync(filePath, Buffer.from(image.bytes));
|
|
2005
|
-
|
|
2006
|
-
|
|
2006
|
+
let attachment;
|
|
2007
|
+
let dimensionNote;
|
|
2008
|
+
const autoResizeImages = this.settingsManager.getImageAutoResize();
|
|
2009
|
+
if (autoResizeImages) {
|
|
2010
|
+
const resized = await resizeImage(image.bytes, image.mimeType).catch(() => null);
|
|
2011
|
+
if (!resized) {
|
|
2012
|
+
this.showWarning("Clipboard image attached without resize");
|
|
2013
|
+
attachment = {
|
|
2014
|
+
type: "image",
|
|
2015
|
+
mimeType: image.mimeType,
|
|
2016
|
+
data: Buffer.from(image.bytes).toString("base64"),
|
|
2017
|
+
};
|
|
2018
|
+
}
|
|
2019
|
+
else {
|
|
2020
|
+
dimensionNote = formatDimensionNote(resized);
|
|
2021
|
+
attachment = {
|
|
2022
|
+
type: "image",
|
|
2023
|
+
mimeType: resized.mimeType,
|
|
2024
|
+
data: resized.data,
|
|
2025
|
+
};
|
|
2026
|
+
}
|
|
2027
|
+
}
|
|
2028
|
+
else {
|
|
2029
|
+
attachment = {
|
|
2030
|
+
type: "image",
|
|
2031
|
+
mimeType: image.mimeType,
|
|
2032
|
+
data: Buffer.from(image.bytes).toString("base64"),
|
|
2033
|
+
};
|
|
2034
|
+
}
|
|
2035
|
+
this.pendingInputImages.push(attachment);
|
|
2036
|
+
const label = dimensionNote ? `${filePath} ${dimensionNote}` : filePath;
|
|
2037
|
+
this.editor.insertTextAtCursor?.(label);
|
|
2007
2038
|
this.ui.requestRender();
|
|
2008
2039
|
}
|
|
2009
|
-
catch {
|
|
2010
|
-
|
|
2040
|
+
catch (error) {
|
|
2041
|
+
this.showWarning(`Failed to paste image: ${error instanceof Error ? error.message : String(error)}`);
|
|
2042
|
+
}
|
|
2043
|
+
}
|
|
2044
|
+
consumePendingInputImages() {
|
|
2045
|
+
if (this.pendingInputImages.length === 0) {
|
|
2046
|
+
return undefined;
|
|
2011
2047
|
}
|
|
2048
|
+
const images = this.pendingInputImages;
|
|
2049
|
+
this.pendingInputImages = [];
|
|
2050
|
+
return images;
|
|
2012
2051
|
}
|
|
2013
2052
|
setupEditorSubmitHandler() {
|
|
2014
2053
|
this.defaultEditor.onSubmit = async (text) => {
|
|
2015
2054
|
text = text.trim();
|
|
2016
2055
|
if (!text)
|
|
2017
2056
|
return;
|
|
2057
|
+
const images = this.consumePendingInputImages();
|
|
2018
2058
|
// Handle commands
|
|
2019
2059
|
if (text === "/settings") {
|
|
2020
2060
|
this.showSettingsSelector();
|
|
@@ -2165,10 +2205,10 @@ export class InteractiveMode {
|
|
|
2165
2205
|
if (this.isExtensionCommand(text)) {
|
|
2166
2206
|
this.editor.addToHistory?.(text);
|
|
2167
2207
|
this.editor.setText("");
|
|
2168
|
-
await this.session.prompt(text);
|
|
2208
|
+
await this.session.prompt(text, { images });
|
|
2169
2209
|
}
|
|
2170
2210
|
else {
|
|
2171
|
-
this.queueCompactionMessage(text, "steer");
|
|
2211
|
+
this.queueCompactionMessage(text, "steer", images);
|
|
2172
2212
|
}
|
|
2173
2213
|
return;
|
|
2174
2214
|
}
|
|
@@ -2177,7 +2217,7 @@ export class InteractiveMode {
|
|
|
2177
2217
|
if (this.session.isStreaming) {
|
|
2178
2218
|
this.editor.addToHistory?.(text);
|
|
2179
2219
|
this.editor.setText("");
|
|
2180
|
-
await this.session.prompt(text, { streamingBehavior: "steer" });
|
|
2220
|
+
await this.session.prompt(text, { streamingBehavior: "steer", images });
|
|
2181
2221
|
this.updatePendingMessagesDisplay();
|
|
2182
2222
|
this.ui.requestRender();
|
|
2183
2223
|
return;
|
|
@@ -2185,11 +2225,12 @@ export class InteractiveMode {
|
|
|
2185
2225
|
// Normal message submission
|
|
2186
2226
|
// First, move any pending bash components to chat
|
|
2187
2227
|
this.flushPendingBashComponents();
|
|
2228
|
+
const input = { text, images };
|
|
2188
2229
|
if (this.onInputCallback) {
|
|
2189
|
-
this.onInputCallback(
|
|
2230
|
+
this.onInputCallback(input);
|
|
2190
2231
|
}
|
|
2191
2232
|
else {
|
|
2192
|
-
this.pendingUserInputs.push(
|
|
2233
|
+
this.pendingUserInputs.push(input);
|
|
2193
2234
|
}
|
|
2194
2235
|
this.editor.addToHistory?.(text);
|
|
2195
2236
|
};
|
|
@@ -2685,9 +2726,9 @@ export class InteractiveMode {
|
|
|
2685
2726
|
return queuedInput;
|
|
2686
2727
|
}
|
|
2687
2728
|
return new Promise((resolve) => {
|
|
2688
|
-
this.onInputCallback = (
|
|
2729
|
+
this.onInputCallback = (input) => {
|
|
2689
2730
|
this.onInputCallback = undefined;
|
|
2690
|
-
resolve(
|
|
2731
|
+
resolve(input);
|
|
2691
2732
|
};
|
|
2692
2733
|
});
|
|
2693
2734
|
}
|
|
@@ -2882,22 +2923,24 @@ export class InteractiveMode {
|
|
|
2882
2923
|
return;
|
|
2883
2924
|
// Queue input during compaction (extension commands execute immediately)
|
|
2884
2925
|
if (this.session.isCompacting) {
|
|
2926
|
+
const images = this.consumePendingInputImages();
|
|
2885
2927
|
if (this.isExtensionCommand(text)) {
|
|
2886
2928
|
this.editor.addToHistory?.(text);
|
|
2887
2929
|
this.editor.setText("");
|
|
2888
|
-
await this.session.prompt(text);
|
|
2930
|
+
await this.session.prompt(text, { images });
|
|
2889
2931
|
}
|
|
2890
2932
|
else {
|
|
2891
|
-
this.queueCompactionMessage(text, "followUp");
|
|
2933
|
+
this.queueCompactionMessage(text, "followUp", images);
|
|
2892
2934
|
}
|
|
2893
2935
|
return;
|
|
2894
2936
|
}
|
|
2895
2937
|
// Alt+Enter queues a follow-up message (waits until agent finishes)
|
|
2896
2938
|
// This handles extension commands (execute immediately), prompt template expansion, and queueing
|
|
2897
2939
|
if (this.session.isStreaming) {
|
|
2940
|
+
const images = this.consumePendingInputImages();
|
|
2898
2941
|
this.editor.addToHistory?.(text);
|
|
2899
2942
|
this.editor.setText("");
|
|
2900
|
-
await this.session.prompt(text, { streamingBehavior: "followUp" });
|
|
2943
|
+
await this.session.prompt(text, { streamingBehavior: "followUp", images });
|
|
2901
2944
|
this.updatePendingMessagesDisplay();
|
|
2902
2945
|
this.ui.requestRender();
|
|
2903
2946
|
}
|
|
@@ -3040,6 +3083,7 @@ export class InteractiveMode {
|
|
|
3040
3083
|
// =========================================================================
|
|
3041
3084
|
clearEditor() {
|
|
3042
3085
|
this.editor.setText("");
|
|
3086
|
+
this.pendingInputImages = [];
|
|
3043
3087
|
this.ui.requestRender();
|
|
3044
3088
|
}
|
|
3045
3089
|
showError(errorMessage) {
|
|
@@ -3157,8 +3201,8 @@ export class InteractiveMode {
|
|
|
3157
3201
|
}
|
|
3158
3202
|
return allQueued.length;
|
|
3159
3203
|
}
|
|
3160
|
-
queueCompactionMessage(text, mode) {
|
|
3161
|
-
this.compactionQueuedMessages.push({ text, mode });
|
|
3204
|
+
queueCompactionMessage(text, mode, images) {
|
|
3205
|
+
this.compactionQueuedMessages.push({ text, images, mode });
|
|
3162
3206
|
this.editor.addToHistory?.(text);
|
|
3163
3207
|
this.editor.setText("");
|
|
3164
3208
|
this.updatePendingMessagesDisplay();
|
|
@@ -3190,13 +3234,13 @@ export class InteractiveMode {
|
|
|
3190
3234
|
// When retry is pending, queue messages for the retry turn
|
|
3191
3235
|
for (const message of queuedMessages) {
|
|
3192
3236
|
if (this.isExtensionCommand(message.text)) {
|
|
3193
|
-
await this.session.prompt(message.text);
|
|
3237
|
+
await this.session.prompt(message.text, { images: message.images });
|
|
3194
3238
|
}
|
|
3195
3239
|
else if (message.mode === "followUp") {
|
|
3196
|
-
await this.session.followUp(message.text);
|
|
3240
|
+
await this.session.followUp(message.text, message.images);
|
|
3197
3241
|
}
|
|
3198
3242
|
else {
|
|
3199
|
-
await this.session.steer(message.text);
|
|
3243
|
+
await this.session.steer(message.text, message.images);
|
|
3200
3244
|
}
|
|
3201
3245
|
}
|
|
3202
3246
|
this.updatePendingMessagesDisplay();
|
|
@@ -3207,7 +3251,7 @@ export class InteractiveMode {
|
|
|
3207
3251
|
if (firstPromptIndex === -1) {
|
|
3208
3252
|
// All extension commands - execute them all
|
|
3209
3253
|
for (const message of queuedMessages) {
|
|
3210
|
-
await this.session.prompt(message.text);
|
|
3254
|
+
await this.session.prompt(message.text, { images: message.images });
|
|
3211
3255
|
}
|
|
3212
3256
|
return;
|
|
3213
3257
|
}
|
|
@@ -3216,22 +3260,22 @@ export class InteractiveMode {
|
|
|
3216
3260
|
const firstPrompt = queuedMessages[firstPromptIndex];
|
|
3217
3261
|
const rest = queuedMessages.slice(firstPromptIndex + 1);
|
|
3218
3262
|
for (const message of preCommands) {
|
|
3219
|
-
await this.session.prompt(message.text);
|
|
3263
|
+
await this.session.prompt(message.text, { images: message.images });
|
|
3220
3264
|
}
|
|
3221
3265
|
// Send first prompt (starts streaming)
|
|
3222
|
-
const promptPromise = this.session.prompt(firstPrompt.text).catch((error) => {
|
|
3266
|
+
const promptPromise = this.session.prompt(firstPrompt.text, { images: firstPrompt.images }).catch((error) => {
|
|
3223
3267
|
restoreQueue(error);
|
|
3224
3268
|
});
|
|
3225
3269
|
// Queue remaining messages
|
|
3226
3270
|
for (const message of rest) {
|
|
3227
3271
|
if (this.isExtensionCommand(message.text)) {
|
|
3228
|
-
await this.session.prompt(message.text);
|
|
3272
|
+
await this.session.prompt(message.text, { images: message.images });
|
|
3229
3273
|
}
|
|
3230
3274
|
else if (message.mode === "followUp") {
|
|
3231
|
-
await this.session.followUp(message.text);
|
|
3275
|
+
await this.session.followUp(message.text, message.images);
|
|
3232
3276
|
}
|
|
3233
3277
|
else {
|
|
3234
|
-
await this.session.steer(message.text);
|
|
3278
|
+
await this.session.steer(message.text, message.images);
|
|
3235
3279
|
}
|
|
3236
3280
|
}
|
|
3237
3281
|
this.updatePendingMessagesDisplay();
|