coder-agent 2.8.0 → 2.8.2

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.
package/dist/agent.js CHANGED
@@ -563,6 +563,12 @@ export class Agent {
563
563
  setModel(model) {
564
564
  this.model = model;
565
565
  }
566
+ getApiKey() {
567
+ return this.apiKey;
568
+ }
569
+ setApiKey(key) {
570
+ this.apiKey = key;
571
+ }
566
572
  async chat(userMessage, signal) {
567
573
  try {
568
574
  if (signal?.aborted) {
@@ -831,9 +837,10 @@ ${JSON.stringify(allMessages.slice(1).map(m => ({ role: m.role, content: m.conte
831
837
 
832
838
  Instructions:
833
839
  1. Review the conversation history and extract any important new project setup details, style preferences, build/test commands, package quirks, or persistent instructions that should be remembered for future sessions.
834
- 2. Integrate these new learnings into the existing memory structure. Keep existing useful learnings/preferences, but clean up duplicates or obsolete info.
835
- 3. Keep the content concise, clean, and formatted in Markdown.
836
- 4. If there are no new learnings, setup details, or instructions in the conversation, output EXACTLY the existing memory content. Do not add conversational text, just output the updated/existing markdown content.`;
840
+ 2. Maintain and update the "## File-to-Data Map" section. If any files were read, modified, created, or discussed, verify if they are listed in the map. If not, add them with their relative path and a brief 1-sentence description of the data, logic, or functions they contain. If they are already listed but their content/purpose changed, update their description.
841
+ 3. Integrate these new learnings and file catalog updates into the existing memory structure. Keep existing useful learnings/preferences, but clean up duplicates or obsolete info.
842
+ 4. Keep the content concise, clean, and formatted in Markdown.
843
+ 5. If there are no new learnings, setup details, file updates, or instructions in the conversation, output EXACTLY the existing memory content. Do not add conversational text, just output the updated/existing markdown content.`;
837
844
  const responseObj = await callGeminiAPIWithRotation(this.apiKey, {
838
845
  model: this.model,
839
846
  messages: [{ role: "user", content: prompt }],
package/dist/index.js CHANGED
@@ -180,9 +180,15 @@ async function main() {
180
180
  console.log(chalk.dim(`✓ Default model set to: ${tempModel}`));
181
181
  process.exit(0);
182
182
  }
183
- // Bootstrap API Key if missing
184
- if (!apiKey) {
185
- apiKey = await promptApiKey();
183
+ // Load API Key
184
+ apiKey = apiKey || "";
185
+ // Single-Shot Mode key check
186
+ if (queryArgs.length > 0 && !apiKey) {
187
+ console.log(chalk.hex('#ff453a')('✕ error'));
188
+ console.log(chalk.dim(" Gemini API Key is required but missing."));
189
+ console.log(chalk.dim(" Please set it using the GEMINI_API_KEY environment variable or run:"));
190
+ console.log(` ${chalk.cyan("coder-agent --set-key <key>")}`);
191
+ process.exit(1);
186
192
  }
187
193
  let currentAbortController = null;
188
194
  let originalListeners = [];
@@ -279,7 +285,12 @@ async function main() {
279
285
  // Save/update last used info immediately
280
286
  await saveLastUsedInfo(CURRENT_VERSION, Date.now());
281
287
  printBanner(modelToUse);
282
- if (showWelcomeCommands) {
288
+ if (!agent.getApiKey()) {
289
+ console.log(chalk.hex('#ff9f0a')(' 🔑 Gemini API Key is missing!'));
290
+ console.log(` Please type ${chalk.cyan('/key <your_api_key>')} to set and save it globally.`);
291
+ console.log(chalk.dim(" Get a free key at https://aistudio.google.com\n"));
292
+ }
293
+ else if (showWelcomeCommands) {
283
294
  console.log(chalk.white.bold(" 💡 Get started with interactive slash commands:"));
284
295
  console.log(` ${chalk.hex('#0a84ff')('/model')} ${chalk.gray('[name]')} — View active model or switch to [name]`);
285
296
  console.log(` ${chalk.hex('#0a84ff')('/clear')} — Wipe conversation memory`);
@@ -288,11 +299,15 @@ async function main() {
288
299
  console.log(` ${chalk.hex('#0a84ff')('/exit')} — Exit Coder`);
289
300
  console.log();
290
301
  }
302
+ let isRlClosed = false;
291
303
  rl = readline.createInterface({
292
304
  input: process.stdin,
293
305
  output: process.stdout,
294
306
  terminal: true,
295
307
  });
308
+ rl.on("close", () => {
309
+ isRlClosed = true;
310
+ });
296
311
  process.stdin.on("keypress", (char, key) => {
297
312
  if (isHijacked || !rl)
298
313
  return;
@@ -301,6 +316,7 @@ async function main() {
301
316
  console.log();
302
317
  console.log(chalk.dim(" Available Commands:"));
303
318
  console.log(` ${chalk.hex('#0a84ff')('/model')} ${chalk.gray('[name]')} ${chalk.dim('— View active model or switch to [name]')}`);
319
+ console.log(` ${chalk.hex('#0a84ff')('/key')} ${chalk.gray('[api_key]')} ${chalk.dim('— Set/save your Gemini API Key globally')}`);
304
320
  console.log(` ${chalk.hex('#0a84ff')('/clear')} ${chalk.dim('— Wipe conversation memory')}`);
305
321
  console.log(` ${chalk.hex('#0a84ff')('/status')} ${chalk.dim('— Show active model and memory usage')}`);
306
322
  console.log(` ${chalk.hex('#0a84ff')('/help')} ${chalk.dim('— Show help screen')}`);
@@ -314,24 +330,42 @@ async function main() {
314
330
  let pasteTimeout = null;
315
331
  let lineCountInBurst = 0;
316
332
  async function executeAgentChat(trimmed) {
333
+ if (isRlClosed)
334
+ return;
317
335
  // Pause standard input processing during agent thinking & updates
318
- rl.pause();
336
+ try {
337
+ rl.pause();
338
+ }
339
+ catch { }
319
340
  // Built-in slash commands
320
341
  if (trimmed === "/exit" || trimmed === "/quit") {
321
- rl.close();
342
+ try {
343
+ rl.close();
344
+ }
345
+ catch { }
322
346
  process.exit(0);
323
347
  }
324
348
  if (trimmed === "/clear") {
325
349
  agent.clearMemory();
326
350
  console.log(chalk.hex('#30d158')('✓') + ' ' + chalk.gray('Memory cleared'));
327
- rl.resume();
328
- rl.prompt();
351
+ if (!isRlClosed) {
352
+ try {
353
+ rl.resume();
354
+ rl.prompt();
355
+ }
356
+ catch { }
357
+ }
329
358
  return;
330
359
  }
331
360
  if (trimmed === "/status") {
332
361
  console.log(chalk.dim(`session · ${agent.memoryStatus()}`));
333
- rl.resume();
334
- rl.prompt();
362
+ if (!isRlClosed) {
363
+ try {
364
+ rl.resume();
365
+ rl.prompt();
366
+ }
367
+ catch { }
368
+ }
335
369
  return;
336
370
  }
337
371
  if (trimmed.startsWith("/model")) {
@@ -351,22 +385,77 @@ async function main() {
351
385
  console.log(chalk.dim(` Model must be one of: ${VALID_MODELS.join(" · ")}`));
352
386
  }
353
387
  }
354
- rl.resume();
355
- rl.prompt();
388
+ if (!isRlClosed) {
389
+ try {
390
+ rl.resume();
391
+ rl.prompt();
392
+ }
393
+ catch { }
394
+ }
395
+ return;
396
+ }
397
+ if (trimmed.startsWith("/key")) {
398
+ const parts = trimmed.split(/\s+/);
399
+ if (parts.length === 1) {
400
+ const keyToCheck = agent.getApiKey();
401
+ if (keyToCheck) {
402
+ const masked = keyToCheck.slice(0, 7) + "..." + keyToCheck.slice(-4);
403
+ console.log(chalk.dim(` Gemini API Key is set: `) + chalk.gray(masked));
404
+ }
405
+ else {
406
+ console.log(chalk.hex('#ff453a')('✕ error') + chalk.dim(' — Gemini API Key is missing. Set it using: /key <your_api_key>'));
407
+ }
408
+ }
409
+ else {
410
+ const newKey = parts[1].trim();
411
+ if (!newKey) {
412
+ console.log(chalk.hex('#ff453a')('✕ error') + chalk.dim(' — API Key cannot be empty.'));
413
+ }
414
+ else {
415
+ await saveApiKey(newKey);
416
+ agent.setApiKey(newKey);
417
+ console.log(chalk.hex('#30d158')('✓') + ' ' + chalk.gray('Gemini API Key saved and set successfully.'));
418
+ }
419
+ }
420
+ if (!isRlClosed) {
421
+ try {
422
+ rl.resume();
423
+ rl.prompt();
424
+ }
425
+ catch { }
426
+ }
356
427
  return;
357
428
  }
358
429
  if (trimmed === "/help") {
359
430
  console.log(chalk.white.bold("\n Interactive Commands:"));
360
431
  console.log(chalk.gray(` /model [name] — View active model or switch to [name]
432
+ /key [api_key]— Set/save your Gemini API Key globally (hides the key from the LLM)
361
433
  /clear — Wipe conversation memory
362
434
  /status — Show active model and memory usage
363
435
  /exit — Exit Coder`));
364
436
  console.log(chalk.white.bold("\n API Keys & Configuration:"));
365
437
  console.log(chalk.gray(` • Stored at: ~/.coder-config.json
366
- • To change key: Exit and run 'coder-agent --set-key <key>'
438
+ • To change key: Type '/key <api_key>' or run 'coder-agent --set-key <key>'
367
439
  • Env variable option: GEMINI_API_KEY`));
368
- rl.resume();
369
- rl.prompt();
440
+ if (!isRlClosed) {
441
+ try {
442
+ rl.resume();
443
+ rl.prompt();
444
+ }
445
+ catch { }
446
+ }
447
+ return;
448
+ }
449
+ // Block logic execution if API Key is missing
450
+ if (!agent.getApiKey()) {
451
+ console.log(chalk.hex('#ff453a')('✕ error') + chalk.dim(' — Gemini API Key is missing. Please set it using: /key <your_api_key>'));
452
+ if (!isRlClosed) {
453
+ try {
454
+ rl.resume();
455
+ rl.prompt();
456
+ }
457
+ catch { }
458
+ }
370
459
  return;
371
460
  }
372
461
  currentAbortController = new AbortController();
@@ -395,9 +484,14 @@ async function main() {
395
484
  stopHijack();
396
485
  currentAbortController = null;
397
486
  }
398
- rl.resume();
399
- console.log(chalk.dim('────────────────────────────────────────────────'));
400
- rl.prompt();
487
+ if (!isRlClosed) {
488
+ try {
489
+ rl.resume();
490
+ console.log(chalk.dim('────────────────────────────────────────────────'));
491
+ rl.prompt();
492
+ }
493
+ catch { }
494
+ }
401
495
  }
402
496
  console.log(chalk.dim('────────────────────────────────────────────────'));
403
497
  rl.setPrompt(chalk.hex('#0a84ff')('›') + ' ');
package/dist/memory.js CHANGED
@@ -315,7 +315,7 @@ export async function loadAgentMemoryPrompt(agentType, scope) {
315
315
  }
316
316
  catch {
317
317
  // Create default template
318
- const defaultTemplate = `# Persistent Agent Memory\n\nUse this section to write general rules, project setup details, style preferences, or learnings. Update this file using your write_file/patch_file tools to persist memories.\n\n## Learnings & Guidelines\n- (No memories stored yet. Add your learnings here.)\n`;
318
+ const defaultTemplate = `# Persistent Agent Memory\n\nUse this section to write general rules, project setup details, style preferences, or learnings. Update this file using your write_file/patch_file tools to persist memories.\n\n## Learnings & Guidelines\n- (No memories stored yet. Add your learnings here.)\n\n## File-to-Data Map\n- (List of files and what data/logic they contain. This helps the agent know what file has what data.)\n`;
319
319
  try {
320
320
  await fs.writeFile(memoryFile, defaultTemplate, "utf-8");
321
321
  memoryContent = defaultTemplate;
@@ -333,13 +333,13 @@ export async function loadAgentMemoryPrompt(agentType, scope) {
333
333
  let scopeNote = "";
334
334
  switch (scope) {
335
335
  case "user":
336
- scopeNote = "- Since this memory has 'user' scope, keep learnings general since they apply across all of the user's projects.";
336
+ scopeNote = "- Since this memory has 'user' scope, keep learnings and cataloging general since they apply across all of the user's projects.";
337
337
  break;
338
338
  case "project":
339
- scopeNote = "- Since this memory has 'project' scope and is shared with the team via version control, tailor memories specifically to this project.";
339
+ scopeNote = "- Since this memory has 'project' scope and is shared with the team via version control, tailor memories and file cataloging specifically to this project.";
340
340
  break;
341
341
  case "local":
342
- scopeNote = "- Since this memory has 'local' scope (not checked into version control), tailor memories specifically to this machine and project.";
342
+ scopeNote = "- Since this memory has 'local' scope (not checked into version control), tailor memories and file cataloging specifically to this machine and project.";
343
343
  break;
344
344
  }
345
345
  return `
@@ -349,8 +349,9 @@ This memory is loaded at the start of every session and injected into your syste
349
349
 
350
350
  ### Guidelines for Memory Usage:
351
351
  1. Read the memory contents below to see existing learnings, project instructions, or user preferences.
352
- 2. If you learn something new that would be useful for future sessions (e.g., build commands, package quirks, style guidelines, API details, tool workarounds), update this file directly using your \`write_file\` or \`patch_file\` tools targeting the file path: \`${memoryFile}\`.
353
- 3. Keep the content concise, clean, and organized.
352
+ 2. Refer to the '## File-to-Data Map' section to quickly find which files contain what data, features, logic, or configurations.
353
+ 3. If you learn something new that would be useful for future sessions (e.g., build commands, package quirks, style guidelines, API details, tool workarounds), or if you create/modify files, update this file directly using your \`write_file\` or \`patch_file\` tools targeting the file path: \`${memoryFile}\`.
354
+ 4. Keep the content concise, clean, and organized.
354
355
  ${scopeNote}
355
356
 
356
357
  ### Current Memory Contents:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "coder-agent",
3
- "version": "2.8.0",
3
+ "version": "2.8.2",
4
4
  "description": "CLI coding agent powered by Google Gemini",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",