codeep 1.2.67 → 1.2.69

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.
@@ -44,6 +44,7 @@ export declare class App {
44
44
  private agentMaxIterations;
45
45
  private agentActions;
46
46
  private agentThinking;
47
+ private agentWaitingForAI;
47
48
  private pasteInfo;
48
49
  private pasteInfoOpen;
49
50
  private codeBlockCounter;
@@ -179,6 +180,7 @@ export declare class App {
179
180
  * Set agent thinking text
180
181
  */
181
182
  setAgentThinking(text: string): void;
183
+ setAgentWaitingForAI(waiting: boolean): void;
182
184
  /**
183
185
  * Paste from system clipboard (Ctrl+V)
184
186
  */
@@ -102,6 +102,7 @@ export class App {
102
102
  agentMaxIterations = 0;
103
103
  agentActions = [];
104
104
  agentThinking = '';
105
+ agentWaitingForAI = false;
105
106
  // Paste detection state
106
107
  pasteInfo = null;
107
108
  pasteInfoOpen = false;
@@ -365,6 +366,7 @@ export class App {
365
366
  this.agentMaxIterations = 0;
366
367
  this.agentActions = [];
367
368
  this.agentThinking = '';
369
+ this.agentWaitingForAI = true;
368
370
  this.isLoading = false; // Clear loading state when agent takes over
369
371
  this.startSpinner();
370
372
  }
@@ -394,6 +396,10 @@ export class App {
394
396
  this.agentThinking = text;
395
397
  this.render();
396
398
  }
399
+ setAgentWaitingForAI(waiting) {
400
+ this.agentWaitingForAI = waiting;
401
+ this.render();
402
+ }
397
403
  /**
398
404
  * Paste from system clipboard (Ctrl+V)
399
405
  */
@@ -1894,7 +1900,10 @@ export class App {
1894
1900
  y++;
1895
1901
  // Current action line (clear first to avoid stale text from longer previous paths)
1896
1902
  this.screen.writeLine(y, '');
1897
- if (this.agentActions.length > 0) {
1903
+ if (this.agentWaitingForAI) {
1904
+ this.screen.write(1, y, 'Thinking...', fg.gray);
1905
+ }
1906
+ else if (this.agentActions.length > 0) {
1898
1907
  const lastAction = this.agentActions[this.agentActions.length - 1];
1899
1908
  const actionLabel = this.getActionLabel(lastAction.type);
1900
1909
  const actionColor = this.getActionColor(lastAction.type);
@@ -119,6 +119,7 @@ export class Screen {
119
119
  this.buffer[y][x] = { char: ' ', style: '' };
120
120
  }
121
121
  // Parse text character by character, tracking ANSI escape sequences
122
+ // Use index-based loop to handle ANSI sequences, but read full Unicode code points
122
123
  let col = 0;
123
124
  let i = 0;
124
125
  let currentStyle = prefixStyle;
@@ -132,8 +133,8 @@ export class Screen {
132
133
  }
133
134
  if (escEnd < text.length) {
134
135
  const escSeq = text.slice(i, escEnd + 1);
135
- // Reset code clears style, otherwise accumulate
136
- if (escSeq === '\x1b[0m') {
136
+ // Reset code clears accumulated style back to prefix
137
+ if (escSeq === '\x1b[0m' || escSeq === '\x1b[m') {
137
138
  currentStyle = prefixStyle;
138
139
  }
139
140
  else {
@@ -146,17 +147,20 @@ export class Screen {
146
147
  }
147
148
  }
148
149
  else {
149
- // Regular character
150
- const w = charWidth(text[i]);
150
+ // Read full Unicode code point (handles surrogate pairs for emoji)
151
+ const codePoint = text.codePointAt(i) ?? text.charCodeAt(i);
152
+ const char = String.fromCodePoint(codePoint);
153
+ const charLen = char.length; // surrogate pair = 2, BMP char = 1
154
+ const w = charWidth(char);
151
155
  if (col < this.width) {
152
- this.buffer[y][col] = { char: text[i], style: currentStyle };
156
+ this.buffer[y][col] = { char, style: currentStyle };
153
157
  // Wide char: fill next cell with empty placeholder
154
158
  if (w === 2 && col + 1 < this.width) {
155
159
  this.buffer[y][col + 1] = { char: '', style: currentStyle };
156
160
  }
157
161
  }
158
162
  col += w;
159
- i++;
163
+ i += charLen;
160
164
  }
161
165
  }
162
166
  }
@@ -192,8 +196,14 @@ export class Screen {
192
196
  lineLength = wordLength;
193
197
  }
194
198
  else {
195
- line += (line ? ' ' : '') + word;
196
- lineLength += wordLength + (line.length > wordLength ? 1 : 0);
199
+ if (line) {
200
+ line += ' ' + word;
201
+ lineLength += 1 + wordLength;
202
+ }
203
+ else {
204
+ line = word;
205
+ lineLength = wordLength;
206
+ }
197
207
  }
198
208
  }
199
209
  if (line && currentY < this.height) {
@@ -236,8 +246,17 @@ export class Screen {
236
246
  if (cell.char === renderedCell.char && cell.style === renderedCell.style) {
237
247
  continue;
238
248
  }
239
- // Skip wide-char placeholder cells
249
+ // Wide-char placeholder cell (right half of emoji/CJK):
250
+ // If previously rendered something here, clear it with a space so there's no ghost.
251
+ // The wide char itself (left cell) already moved the terminal cursor past this cell.
240
252
  if (cell.char === '') {
253
+ if (renderedCell.char !== '') {
254
+ // Previous char here was something visible — overwrite with space to clear ghost
255
+ output += cursor.to(y + 1, x + 1);
256
+ output += style.reset;
257
+ lastStyle = '';
258
+ output += ' ';
259
+ }
241
260
  this.rendered[y][x] = { ...cell };
242
261
  continue;
243
262
  }
@@ -142,12 +142,14 @@ export async function executeAgentTask(task, dryRun, ctx) {
142
142
  chatHistory: app.getChatHistory(),
143
143
  onIteration: (iteration, message) => {
144
144
  app.updateAgentProgress(iteration);
145
+ app.setAgentWaitingForAI(true); // Waiting for AI response between tool calls
145
146
  // Show special status messages (timeout retries, verification) but not generic iteration messages
146
147
  if (message && !message.startsWith('Iteration ')) {
147
148
  app.addMessage({ role: 'system', content: `_${message}_` });
148
149
  }
149
150
  },
150
151
  onToolCall: (tool) => {
152
+ app.setAgentWaitingForAI(false); // AI responded, executing tool
151
153
  const toolName = tool.tool.toLowerCase();
152
154
  const target = tool.parameters.path ||
153
155
  tool.parameters.command ||
@@ -283,12 +283,16 @@ export async function runAgent(prompt, projectContext, options = {}) {
283
283
  await new Promise(resolve => setTimeout(resolve, 1000 * retryCount));
284
284
  continue;
285
285
  }
286
- // Handle 429 rate-limit / server overload with retry + backoff
287
- if (err.message.includes('429')) {
286
+ // Retry on transient errors: rate-limit, server errors, network failures
287
+ const isRateLimit = err.message.includes('429');
288
+ const isServerError = err.message.includes('500') || err.message.includes('502') || err.message.includes('503') || err.message.includes('529');
289
+ const isNetworkError = ['ECONNRESET', 'ECONNREFUSED', 'ETIMEDOUT', 'ENOTFOUND', 'EPIPE', 'EAI_AGAIN'].some(code => err.message.includes(code));
290
+ if (isRateLimit || isServerError || isNetworkError) {
288
291
  retryCount++;
289
292
  const waitSec = Math.min(5 * retryCount, 30); // 5s, 10s, 15s … max 30s
290
- debug(`429 rate limit (retry ${retryCount}/${maxTimeoutRetries}), waiting ${waitSec}s`);
291
- opts.onIteration?.(iteration, `Server busy (429), retrying in ${waitSec}s... (${retryCount}/${maxTimeoutRetries})`);
293
+ const code = isRateLimit ? '429' : isServerError ? '5xx' : 'network';
294
+ debug(`${code} error (retry ${retryCount}/${maxTimeoutRetries}), waiting ${waitSec}s`);
295
+ opts.onIteration?.(iteration, `Server error (${code}), retrying in ${waitSec}s... (${retryCount}/${maxTimeoutRetries})`);
292
296
  if (retryCount >= maxTimeoutRetries) {
293
297
  throw error; // Give up after max retries
294
298
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeep",
3
- "version": "1.2.67",
3
+ "version": "1.2.69",
4
4
  "description": "AI-powered coding assistant built for the terminal. Multiple LLM providers, project-aware context, and a seamless development workflow.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",