codeep 1.2.13 → 1.2.15
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/renderer/App.d.ts +1 -0
- package/dist/renderer/App.js +9 -5
- package/dist/renderer/Input.js +9 -0
- package/dist/renderer/main.js +1 -0
- package/dist/utils/agent.d.ts +13 -0
- package/dist/utils/agent.js +45 -0
- package/dist/utils/agent.test.js +66 -1
- package/package.json +1 -1
package/dist/renderer/App.d.ts
CHANGED
package/dist/renderer/App.js
CHANGED
|
@@ -250,6 +250,7 @@ export class App {
|
|
|
250
250
|
// Paste detection state
|
|
251
251
|
pasteInfo = null;
|
|
252
252
|
pasteInfoOpen = false;
|
|
253
|
+
codeBlockCounter = 0; // Global code block counter for /copy numbering
|
|
253
254
|
// Inline help state
|
|
254
255
|
helpOpen = false;
|
|
255
256
|
helpScrollIndex = 0;
|
|
@@ -2402,6 +2403,7 @@ export class App {
|
|
|
2402
2403
|
*/
|
|
2403
2404
|
getVisibleMessages(height, width) {
|
|
2404
2405
|
const allLines = [];
|
|
2406
|
+
this.codeBlockCounter = 0; // Reset block counter for each render pass
|
|
2405
2407
|
// Logo at the top, scrolls with content
|
|
2406
2408
|
if (height >= 20) {
|
|
2407
2409
|
const logoWidth = LOGO_LINES[0].length;
|
|
@@ -2455,6 +2457,7 @@ export class App {
|
|
|
2455
2457
|
isFirstLine = false;
|
|
2456
2458
|
}
|
|
2457
2459
|
// Add code block with syntax highlighting
|
|
2460
|
+
this.codeBlockCounter++;
|
|
2458
2461
|
const rawLang = (match[1] || 'text').trim();
|
|
2459
2462
|
// Handle filepath:name.ext format - extract extension as language
|
|
2460
2463
|
let lang = rawLang;
|
|
@@ -2463,7 +2466,7 @@ export class App {
|
|
|
2463
2466
|
lang = ext;
|
|
2464
2467
|
}
|
|
2465
2468
|
const code = match[2];
|
|
2466
|
-
const codeLines = this.formatCodeBlock(code, lang, maxWidth);
|
|
2469
|
+
const codeLines = this.formatCodeBlock(code, lang, maxWidth, this.codeBlockCounter);
|
|
2467
2470
|
lines.push(...codeLines);
|
|
2468
2471
|
lastIndex = match.index + match[0].length;
|
|
2469
2472
|
isFirstLine = false;
|
|
@@ -2623,16 +2626,17 @@ export class App {
|
|
|
2623
2626
|
/**
|
|
2624
2627
|
* Format code block with syntax highlighting (no border)
|
|
2625
2628
|
*/
|
|
2626
|
-
formatCodeBlock(code, lang, maxWidth) {
|
|
2629
|
+
formatCodeBlock(code, lang, maxWidth, blockNum) {
|
|
2627
2630
|
const lines = [];
|
|
2628
2631
|
const codeLines = code.split('\n');
|
|
2629
2632
|
// Remove trailing empty line if exists
|
|
2630
2633
|
if (codeLines.length > 0 && codeLines[codeLines.length - 1] === '') {
|
|
2631
2634
|
codeLines.pop();
|
|
2632
2635
|
}
|
|
2633
|
-
// Language label
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
+
// Language label with block number for /copy
|
|
2637
|
+
const label = blockNum ? (lang ? ` ${lang} [${blockNum}]` : ` [${blockNum}]`) : (lang ? ' ' + lang : '');
|
|
2638
|
+
if (label) {
|
|
2639
|
+
lines.push({ text: label, style: SYNTAX.codeLang, raw: false });
|
|
2636
2640
|
}
|
|
2637
2641
|
// Code lines with highlighting and indent
|
|
2638
2642
|
for (const codeLine of codeLines) {
|
package/dist/renderer/Input.js
CHANGED
|
@@ -101,6 +101,15 @@ export class Input {
|
|
|
101
101
|
event.key = 'enter';
|
|
102
102
|
return event;
|
|
103
103
|
}
|
|
104
|
+
// Bracketed paste mode: terminal wraps Cmd+V paste in \x1b[200~ ... \x1b[201~
|
|
105
|
+
if (data.includes('\x1b[200~') || data.includes('\x1b[201~')) {
|
|
106
|
+
const pasteContent = data.replace(/\x1b\[200~/g, '').replace(/\x1b\[201~/g, '');
|
|
107
|
+
if (pasteContent.length > 0) {
|
|
108
|
+
event.key = pasteContent;
|
|
109
|
+
event.isPaste = true;
|
|
110
|
+
return event;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
104
113
|
// Detect paste: multiple printable characters at once (not escape sequences)
|
|
105
114
|
if (data.length > 1 && !data.startsWith('\x1b')) {
|
|
106
115
|
// Check if it's all printable characters (paste event)
|
package/dist/renderer/main.js
CHANGED
|
@@ -317,6 +317,7 @@ async function executeAgentTask(task, dryRun = false) {
|
|
|
317
317
|
const enrichedTask = fileContext ? fileContext + task : task;
|
|
318
318
|
const result = await runAgent(enrichedTask, context, {
|
|
319
319
|
dryRun,
|
|
320
|
+
chatHistory: app.getChatHistory(),
|
|
320
321
|
onIteration: (iteration) => {
|
|
321
322
|
app.updateAgentProgress(iteration);
|
|
322
323
|
},
|
package/dist/utils/agent.d.ts
CHANGED
|
@@ -21,6 +21,10 @@ export interface AgentOptions {
|
|
|
21
21
|
autoVerify?: boolean;
|
|
22
22
|
maxFixAttempts?: number;
|
|
23
23
|
usePlanning?: boolean;
|
|
24
|
+
chatHistory?: Array<{
|
|
25
|
+
role: 'user' | 'assistant';
|
|
26
|
+
content: string;
|
|
27
|
+
}>;
|
|
24
28
|
}
|
|
25
29
|
export interface AgentResult {
|
|
26
30
|
success: boolean;
|
|
@@ -35,6 +39,15 @@ export interface AgentResult {
|
|
|
35
39
|
* Returns the rules content formatted for system prompt, or empty string if no rules found
|
|
36
40
|
*/
|
|
37
41
|
export declare function loadProjectRules(projectRoot: string): string;
|
|
42
|
+
/**
|
|
43
|
+
* Format chat session history for inclusion in agent system prompt.
|
|
44
|
+
* Keeps the most recent messages within a character budget so the agent
|
|
45
|
+
* has conversational context without overwhelming the context window.
|
|
46
|
+
*/
|
|
47
|
+
export declare function formatChatHistoryForAgent(history?: Array<{
|
|
48
|
+
role: 'user' | 'assistant';
|
|
49
|
+
content: string;
|
|
50
|
+
}>, maxChars?: number): string;
|
|
38
51
|
/**
|
|
39
52
|
* Run the agent loop
|
|
40
53
|
*/
|
package/dist/utils/agent.js
CHANGED
|
@@ -75,6 +75,46 @@ export function loadProjectRules(projectRoot) {
|
|
|
75
75
|
}
|
|
76
76
|
return '';
|
|
77
77
|
}
|
|
78
|
+
/**
|
|
79
|
+
* Format chat session history for inclusion in agent system prompt.
|
|
80
|
+
* Keeps the most recent messages within a character budget so the agent
|
|
81
|
+
* has conversational context without overwhelming the context window.
|
|
82
|
+
*/
|
|
83
|
+
export function formatChatHistoryForAgent(history, maxChars = 16000) {
|
|
84
|
+
if (!history || history.length === 0)
|
|
85
|
+
return '';
|
|
86
|
+
// Filter out agent execution messages
|
|
87
|
+
const filtered = history.filter(m => {
|
|
88
|
+
const content = m.content.trimStart();
|
|
89
|
+
if (content.startsWith('[AGENT]') || content.startsWith('[DRY RUN]'))
|
|
90
|
+
return false;
|
|
91
|
+
if (content.startsWith('Agent completed') || content.startsWith('Agent failed') || content.startsWith('Agent stopped'))
|
|
92
|
+
return false;
|
|
93
|
+
return true;
|
|
94
|
+
});
|
|
95
|
+
if (filtered.length === 0)
|
|
96
|
+
return '';
|
|
97
|
+
// Walk backward (newest first) and accumulate within budget
|
|
98
|
+
const selected = [];
|
|
99
|
+
let totalChars = 0;
|
|
100
|
+
for (let i = filtered.length - 1; i >= 0; i--) {
|
|
101
|
+
const msg = filtered[i];
|
|
102
|
+
const entry = `${msg.role === 'user' ? 'User' : 'Assistant'}: ${msg.content}`;
|
|
103
|
+
if (totalChars + entry.length > maxChars && selected.length > 0)
|
|
104
|
+
break;
|
|
105
|
+
// If single message exceeds budget, truncate it
|
|
106
|
+
if (entry.length > maxChars) {
|
|
107
|
+
selected.unshift({ role: msg.role, content: msg.content.slice(0, maxChars - 100) + '\n[truncated]' });
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
selected.unshift(msg);
|
|
111
|
+
totalChars += entry.length;
|
|
112
|
+
}
|
|
113
|
+
if (selected.length === 0)
|
|
114
|
+
return '';
|
|
115
|
+
const lines = selected.map(m => `**${m.role === 'user' ? 'User' : 'Assistant'}:** ${m.content}`).join('\n\n');
|
|
116
|
+
return `\n\n## Prior Conversation Context\nThe following is the recent chat history from this session. Use it as background context to understand the user's intent, but focus on completing the current task.\n\n${lines}`;
|
|
117
|
+
}
|
|
78
118
|
/**
|
|
79
119
|
* Generate system prompt for agent mode (used with native tool calling)
|
|
80
120
|
*/
|
|
@@ -753,6 +793,11 @@ export async function runAgent(prompt, projectContext, options = {}) {
|
|
|
753
793
|
if (smartContextStr) {
|
|
754
794
|
systemPrompt += '\n\n' + smartContextStr;
|
|
755
795
|
}
|
|
796
|
+
// Inject prior chat session context
|
|
797
|
+
const chatHistoryStr = formatChatHistoryForAgent(opts.chatHistory);
|
|
798
|
+
if (chatHistoryStr) {
|
|
799
|
+
systemPrompt += chatHistoryStr;
|
|
800
|
+
}
|
|
756
801
|
// Initial user message with optional task plan
|
|
757
802
|
let initialPrompt = prompt;
|
|
758
803
|
if (taskPlan) {
|
package/dist/utils/agent.test.js
CHANGED
|
@@ -11,7 +11,7 @@ vi.mock('fs', async (importOriginal) => {
|
|
|
11
11
|
});
|
|
12
12
|
import { existsSync, readFileSync } from 'fs';
|
|
13
13
|
import { join } from 'path';
|
|
14
|
-
import { loadProjectRules, formatAgentResult } from './agent.js';
|
|
14
|
+
import { loadProjectRules, formatAgentResult, formatChatHistoryForAgent } from './agent.js';
|
|
15
15
|
// Cast mocked functions for convenience
|
|
16
16
|
const mockExistsSync = existsSync;
|
|
17
17
|
const mockReadFileSync = readFileSync;
|
|
@@ -248,3 +248,68 @@ describe('formatAgentResult', () => {
|
|
|
248
248
|
}
|
|
249
249
|
});
|
|
250
250
|
});
|
|
251
|
+
describe('formatChatHistoryForAgent', () => {
|
|
252
|
+
it('should return empty string for undefined input', () => {
|
|
253
|
+
expect(formatChatHistoryForAgent(undefined)).toBe('');
|
|
254
|
+
});
|
|
255
|
+
it('should return empty string for empty array', () => {
|
|
256
|
+
expect(formatChatHistoryForAgent([])).toBe('');
|
|
257
|
+
});
|
|
258
|
+
it('should format simple chat history', () => {
|
|
259
|
+
const history = [
|
|
260
|
+
{ role: 'user', content: 'How do I fix the login bug?' },
|
|
261
|
+
{ role: 'assistant', content: 'Check the auth middleware in src/auth.ts' },
|
|
262
|
+
];
|
|
263
|
+
const result = formatChatHistoryForAgent(history);
|
|
264
|
+
expect(result).toContain('## Prior Conversation Context');
|
|
265
|
+
expect(result).toContain('**User:** How do I fix the login bug?');
|
|
266
|
+
expect(result).toContain('**Assistant:** Check the auth middleware in src/auth.ts');
|
|
267
|
+
});
|
|
268
|
+
it('should filter out [AGENT] messages', () => {
|
|
269
|
+
const history = [
|
|
270
|
+
{ role: 'user', content: 'Hello' },
|
|
271
|
+
{ role: 'user', content: '[AGENT] fix the bug' },
|
|
272
|
+
{ role: 'assistant', content: 'Agent completed in 3 iteration(s)' },
|
|
273
|
+
{ role: 'user', content: 'Thanks' },
|
|
274
|
+
];
|
|
275
|
+
const result = formatChatHistoryForAgent(history);
|
|
276
|
+
expect(result).toContain('**User:** Hello');
|
|
277
|
+
expect(result).toContain('**User:** Thanks');
|
|
278
|
+
expect(result).not.toContain('[AGENT]');
|
|
279
|
+
expect(result).not.toContain('Agent completed');
|
|
280
|
+
});
|
|
281
|
+
it('should filter out [DRY RUN] messages', () => {
|
|
282
|
+
const history = [
|
|
283
|
+
{ role: 'user', content: '[DRY RUN] test task' },
|
|
284
|
+
];
|
|
285
|
+
const result = formatChatHistoryForAgent(history);
|
|
286
|
+
expect(result).toBe('');
|
|
287
|
+
});
|
|
288
|
+
it('should filter out Agent failed/stopped messages', () => {
|
|
289
|
+
const history = [
|
|
290
|
+
{ role: 'assistant', content: 'Agent failed: timeout' },
|
|
291
|
+
{ role: 'assistant', content: 'Agent stopped by user' },
|
|
292
|
+
];
|
|
293
|
+
const result = formatChatHistoryForAgent(history);
|
|
294
|
+
expect(result).toBe('');
|
|
295
|
+
});
|
|
296
|
+
it('should respect character budget and keep newest messages', () => {
|
|
297
|
+
const history = [
|
|
298
|
+
{ role: 'user', content: 'A'.repeat(5000) },
|
|
299
|
+
{ role: 'assistant', content: 'B'.repeat(5000) },
|
|
300
|
+
{ role: 'user', content: 'Most recent message' },
|
|
301
|
+
];
|
|
302
|
+
const result = formatChatHistoryForAgent(history, 6000);
|
|
303
|
+
expect(result).toContain('Most recent message');
|
|
304
|
+
// The first 5000-char message should be dropped due to budget
|
|
305
|
+
expect(result).not.toContain('AAAAA');
|
|
306
|
+
});
|
|
307
|
+
it('should truncate a single very long message', () => {
|
|
308
|
+
const history = [
|
|
309
|
+
{ role: 'user', content: 'X'.repeat(20000) },
|
|
310
|
+
];
|
|
311
|
+
const result = formatChatHistoryForAgent(history, 8000);
|
|
312
|
+
expect(result).toContain('[truncated]');
|
|
313
|
+
expect(result.length).toBeLessThan(9000);
|
|
314
|
+
});
|
|
315
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codeep",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.15",
|
|
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",
|