coder-agent 2.3.4 → 2.3.5

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 (2) hide show
  1. package/dist/index.js +105 -81
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -209,111 +209,135 @@ async function main() {
209
209
  });
210
210
  let inputBuffer = "";
211
211
  let pasteTimeout = null;
212
+ let lineCountInBurst = 0;
212
213
  let currentAbortController = null;
213
- rl.setPrompt(chalk.hex('#0a84ff')('›') + ' ');
214
- rl.prompt();
215
- rl.on("line", (line) => {
216
- inputBuffer += (inputBuffer ? "\n" : "") + line;
217
- if (pasteTimeout) {
218
- clearTimeout(pasteTimeout);
214
+ async function executeAgentChat(trimmed) {
215
+ // Pause standard input processing during agent thinking & updates
216
+ rl.pause();
217
+ // Built-in slash commands
218
+ if (trimmed === "/exit" || trimmed === "/quit") {
219
+ rl.close();
220
+ process.exit(0);
219
221
  }
220
- pasteTimeout = setTimeout(async () => {
221
- pasteTimeout = null;
222
- const accumulatedInput = inputBuffer;
223
- inputBuffer = "";
224
- const trimmed = accumulatedInput.trim();
225
- if (!trimmed) {
226
- rl.prompt();
227
- return;
228
- }
229
- // Pause standard input processing during agent thinking & updates
230
- rl.pause();
231
- // Built-in slash commands
232
- if (trimmed === "/exit" || trimmed === "/quit") {
233
- rl.close();
234
- process.exit(0);
235
- }
236
- if (trimmed === "/clear") {
237
- agent.clearMemory();
238
- console.log(chalk.hex('#30d158')('✓') + ' ' + chalk.gray('Memory cleared'));
239
- rl.resume();
240
- rl.prompt();
241
- return;
242
- }
243
- if (trimmed === "/status") {
244
- console.log(chalk.dim(`session · ${agent.memoryStatus()}`));
245
- rl.resume();
246
- rl.prompt();
247
- return;
222
+ if (trimmed === "/clear") {
223
+ agent.clearMemory();
224
+ console.log(chalk.hex('#30d158')('✓') + ' ' + chalk.gray('Memory cleared'));
225
+ rl.resume();
226
+ rl.prompt();
227
+ return;
228
+ }
229
+ if (trimmed === "/status") {
230
+ console.log(chalk.dim(`session · ${agent.memoryStatus()}`));
231
+ rl.resume();
232
+ rl.prompt();
233
+ return;
234
+ }
235
+ if (trimmed.startsWith("/model")) {
236
+ const parts = trimmed.split(/\s+/);
237
+ if (parts.length === 1) {
238
+ console.log(chalk.dim(`model `) + chalk.gray(agent.getModel()));
239
+ console.log(chalk.gray(`options `) + chalk.gray(VALID_MODELS.join(" · ")));
248
240
  }
249
- if (trimmed.startsWith("/model")) {
250
- const parts = trimmed.split(/\s+/);
251
- if (parts.length === 1) {
252
- console.log(chalk.dim(`model `) + chalk.gray(agent.getModel()));
253
- console.log(chalk.gray(`options `) + chalk.gray(VALID_MODELS.join(" · ")));
241
+ else {
242
+ const newModel = parts[1];
243
+ if (VALID_MODELS.includes(newModel)) {
244
+ agent.setModel(newModel);
245
+ console.log(chalk.hex('#30d158')('✓') + ' ' + chalk.gray(`Switched model to: ${newModel}`));
254
246
  }
255
247
  else {
256
- const newModel = parts[1];
257
- if (VALID_MODELS.includes(newModel)) {
258
- agent.setModel(newModel);
259
- console.log(chalk.hex('#30d158')('✓') + ' ' + chalk.gray(`Switched model to: ${newModel}`));
260
- }
261
- else {
262
- console.log(chalk.hex('#ff453a')('✕ error'));
263
- console.log(chalk.dim(` Model must be one of: ${VALID_MODELS.join(" · ")}`));
264
- }
248
+ console.log(chalk.hex('#ff453a')('✕ error'));
249
+ console.log(chalk.dim(` Model must be one of: ${VALID_MODELS.join(" · ")}`));
265
250
  }
266
- rl.resume();
267
- rl.prompt();
268
- return;
269
251
  }
270
- if (trimmed === "/help") {
271
- console.log(chalk.white.bold("\n Interactive Commands:"));
272
- console.log(chalk.gray(` /model [name] — View active model or switch to [name]
252
+ rl.resume();
253
+ rl.prompt();
254
+ return;
255
+ }
256
+ if (trimmed === "/help") {
257
+ console.log(chalk.white.bold("\n Interactive Commands:"));
258
+ console.log(chalk.gray(` /model [name] — View active model or switch to [name]
273
259
  /clear — Wipe conversation memory
274
260
  /status — Show active model and memory usage
275
261
  /exit — Exit Coder`));
276
- console.log(chalk.white.bold("\n API Keys & Configuration:"));
277
- console.log(chalk.gray(` • Stored at: ~/.coder-config.json
262
+ console.log(chalk.white.bold("\n API Keys & Configuration:"));
263
+ console.log(chalk.gray(` • Stored at: ~/.coder-config.json
278
264
  • To change key: Exit and run 'coder-agent --set-key <key>'
279
265
  • Env variable option: GEMINI_API_KEY`));
280
- rl.resume();
281
- rl.prompt();
282
- return;
283
- }
284
- currentAbortController = new AbortController();
285
- try {
286
- await agent.chat(trimmed, currentAbortController.signal);
266
+ rl.resume();
267
+ rl.prompt();
268
+ return;
269
+ }
270
+ currentAbortController = new AbortController();
271
+ try {
272
+ await agent.chat(trimmed, currentAbortController.signal);
273
+ }
274
+ catch (err) {
275
+ if (err?.name === "AbortError" || currentAbortController.signal.aborted) {
276
+ console.log(chalk.hex('#ff453a')('\n✕ cancelled'));
287
277
  }
288
- catch (err) {
289
- if (err?.name === "AbortError" || currentAbortController.signal.aborted) {
290
- console.log(chalk.hex('#ff453a')('\n✕ cancelled'));
278
+ else {
279
+ console.log(chalk.hex('#ff453a')('✕ error'));
280
+ if (err?.status === 401) {
281
+ console.log(chalk.dim(" Invalid API key. Check your configuration."));
282
+ }
283
+ else if (err?.status === 429) {
284
+ console.log(chalk.dim(" Rate limit exceeded on Gemini API."));
291
285
  }
292
286
  else {
293
- console.log(chalk.hex('#ff453a')('✕ error'));
294
- if (err?.status === 401) {
295
- console.log(chalk.dim(" Invalid API key. Check your configuration."));
296
- }
297
- else if (err?.status === 429) {
298
- console.log(chalk.dim(" Rate limit exceeded on Gemini API."));
299
- }
300
- else {
301
- console.log(chalk.dim(` ${err.message}`));
302
- }
287
+ console.log(chalk.dim(` ${err.message}`));
303
288
  }
304
289
  }
305
- finally {
306
- currentAbortController = null;
290
+ }
291
+ finally {
292
+ currentAbortController = null;
293
+ }
294
+ rl.resume();
295
+ rl.prompt();
296
+ }
297
+ rl.setPrompt(chalk.hex('#0a84ff')('›') + ' ');
298
+ rl.prompt();
299
+ rl.on("line", (line) => {
300
+ inputBuffer += (inputBuffer ? "\n" : "") + line;
301
+ lineCountInBurst++;
302
+ if (pasteTimeout) {
303
+ clearTimeout(pasteTimeout);
304
+ }
305
+ pasteTimeout = setTimeout(async () => {
306
+ pasteTimeout = null;
307
+ const burstCount = lineCountInBurst;
308
+ lineCountInBurst = 0;
309
+ // Check if there is an unfinished line in the readline buffer (e.g. pasted text without trailing newline)
310
+ const unfinishedLine = rl.line;
311
+ // If we got multiple lines in this burst, or there is an unfinished line,
312
+ // we consider it a multi-line paste. We don't submit yet; we let the user
313
+ // edit the unfinished line or type more.
314
+ if (burstCount > 1 || unfinishedLine.trim() !== "") {
315
+ return;
307
316
  }
308
- rl.resume();
309
- rl.prompt();
310
- }, 80);
317
+ const accumulatedInput = inputBuffer;
318
+ inputBuffer = "";
319
+ const trimmed = accumulatedInput.trim();
320
+ if (!trimmed) {
321
+ rl.prompt();
322
+ return;
323
+ }
324
+ await executeAgentChat(trimmed);
325
+ }, 50);
311
326
  });
312
327
  // Handle Ctrl+C gracefully
313
328
  const sigintHandler = () => {
314
329
  if (currentAbortController) {
315
330
  currentAbortController.abort();
316
331
  }
332
+ else if (inputBuffer !== "" || rl.line !== "") {
333
+ inputBuffer = "";
334
+ lineCountInBurst = 0;
335
+ // Clear the current input buffer in readline
336
+ rl.write(null, { ctrl: true, name: 'u' });
337
+ console.log();
338
+ rl.setPrompt(chalk.hex('#0a84ff')('›') + ' ');
339
+ rl.prompt();
340
+ }
317
341
  else {
318
342
  rl.close();
319
343
  process.exit(0);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "coder-agent",
3
- "version": "2.3.4",
3
+ "version": "2.3.5",
4
4
  "description": "CLI coding agent powered by Google Gemini",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",