codeep 1.3.41 → 2.0.0
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/README.md +208 -0
- package/dist/acp/commands.js +770 -7
- package/dist/acp/protocol.d.ts +11 -2
- package/dist/acp/server.js +179 -11
- package/dist/acp/session.d.ts +3 -0
- package/dist/acp/session.js +5 -0
- package/dist/api/index.js +39 -6
- package/dist/config/index.d.ts +13 -0
- package/dist/config/index.js +46 -1
- package/dist/config/providers.js +76 -1
- package/dist/renderer/App.d.ts +12 -0
- package/dist/renderer/App.js +96 -4
- package/dist/renderer/agentExecution.js +5 -0
- package/dist/renderer/commands.js +348 -2
- package/dist/renderer/components/Login.d.ts +1 -0
- package/dist/renderer/components/Login.js +24 -9
- package/dist/renderer/handlers.d.ts +11 -1
- package/dist/renderer/handlers.js +30 -0
- package/dist/renderer/main.js +73 -0
- package/dist/utils/agent.d.ts +17 -0
- package/dist/utils/agent.js +91 -7
- package/dist/utils/agentChat.d.ts +10 -2
- package/dist/utils/agentChat.js +48 -9
- package/dist/utils/agentStream.js +6 -2
- package/dist/utils/checkpoints.d.ts +93 -0
- package/dist/utils/checkpoints.js +205 -0
- package/dist/utils/context.d.ts +24 -0
- package/dist/utils/context.js +57 -0
- package/dist/utils/customCommands.d.ts +62 -0
- package/dist/utils/customCommands.js +201 -0
- package/dist/utils/hooks.d.ts +97 -0
- package/dist/utils/hooks.js +223 -0
- package/dist/utils/mcpClient.d.ts +229 -0
- package/dist/utils/mcpClient.js +497 -0
- package/dist/utils/mcpConfig.d.ts +55 -0
- package/dist/utils/mcpConfig.js +177 -0
- package/dist/utils/mcpMarketplace.d.ts +49 -0
- package/dist/utils/mcpMarketplace.js +175 -0
- package/dist/utils/mcpRegistry.d.ts +129 -0
- package/dist/utils/mcpRegistry.js +427 -0
- package/dist/utils/mcpSamplingBridge.d.ts +32 -0
- package/dist/utils/mcpSamplingBridge.js +88 -0
- package/dist/utils/mcpStreamableHttp.d.ts +65 -0
- package/dist/utils/mcpStreamableHttp.js +207 -0
- package/dist/utils/openrouterPrefs.d.ts +36 -0
- package/dist/utils/openrouterPrefs.js +83 -0
- package/dist/utils/skillBundles.d.ts +84 -0
- package/dist/utils/skillBundles.js +257 -0
- package/dist/utils/skillBundlesCloud.d.ts +66 -0
- package/dist/utils/skillBundlesCloud.js +196 -0
- package/dist/utils/tokenTracker.d.ts +14 -2
- package/dist/utils/tokenTracker.js +59 -45
- package/dist/utils/toolExecution.d.ts +17 -1
- package/dist/utils/toolExecution.js +184 -6
- package/dist/utils/tools.d.ts +22 -6
- package/dist/utils/tools.js +83 -8
- package/package.json +3 -2
- package/bin/codeep-macos-arm64 +0 -0
- package/bin/codeep-macos-x64 +0 -0
package/dist/config/providers.js
CHANGED
|
@@ -275,6 +275,42 @@ export const PROVIDERS = {
|
|
|
275
275
|
groupLabel: 'Google AI',
|
|
276
276
|
hint: 'Pay-per-use via Google AI API key (aistudio.google.com).',
|
|
277
277
|
},
|
|
278
|
+
'openrouter': {
|
|
279
|
+
name: 'OpenRouter',
|
|
280
|
+
description: 'Unified access to 100+ models via one API key',
|
|
281
|
+
protocols: {
|
|
282
|
+
openai: {
|
|
283
|
+
baseUrl: 'https://openrouter.ai/api/v1',
|
|
284
|
+
authHeader: 'Bearer',
|
|
285
|
+
supportsNativeTools: true,
|
|
286
|
+
},
|
|
287
|
+
},
|
|
288
|
+
// Top 12 — the full catalog (100+) is fetched lazily via
|
|
289
|
+
// fetchOpenRouterModels() because dynamicModels is true.
|
|
290
|
+
// We keep these hardcoded so first-time users without network
|
|
291
|
+
// get a working dropdown.
|
|
292
|
+
models: [
|
|
293
|
+
{ id: 'openrouter/auto', name: 'Auto-route', description: 'OpenRouter picks the best model for the task' },
|
|
294
|
+
{ id: 'anthropic/claude-opus-4', name: 'Claude Opus 4', description: 'Anthropic — most capable' },
|
|
295
|
+
{ id: 'anthropic/claude-sonnet-4', name: 'Claude Sonnet 4', description: 'Anthropic — balanced' },
|
|
296
|
+
{ id: 'openai/gpt-5.5', name: 'GPT-5.5', description: 'OpenAI — flagship' },
|
|
297
|
+
{ id: 'openai/gpt-5.4-mini', name: 'GPT-5.4 Mini', description: 'OpenAI — fast/cheap' },
|
|
298
|
+
{ id: 'google/gemini-3.1-pro', name: 'Gemini 3.1 Pro', description: 'Google — multimodal' },
|
|
299
|
+
{ id: 'meta-llama/llama-3.1-405b-instruct', name: 'Llama 3.1 405B', description: 'Meta — open weights, largest' },
|
|
300
|
+
{ id: 'meta-llama/llama-3.1-70b-instruct', name: 'Llama 3.1 70B', description: 'Meta — open weights, fast' },
|
|
301
|
+
{ id: 'deepseek/deepseek-v4', name: 'DeepSeek V4', description: 'DeepSeek via OpenRouter' },
|
|
302
|
+
{ id: 'mistralai/mistral-large', name: 'Mistral Large', description: 'Mistral — flagship' },
|
|
303
|
+
{ id: 'qwen/qwen-2.5-coder-32b-instruct', name: 'Qwen 2.5 Coder 32B', description: 'Alibaba — coding-tuned' },
|
|
304
|
+
{ id: 'x-ai/grok-2', name: 'Grok 2', description: 'xAI via OpenRouter' },
|
|
305
|
+
],
|
|
306
|
+
defaultModel: 'anthropic/claude-opus-4',
|
|
307
|
+
defaultProtocol: 'openai',
|
|
308
|
+
envKey: 'OPENROUTER_API_KEY',
|
|
309
|
+
subscribeUrl: 'https://openrouter.ai/keys',
|
|
310
|
+
dynamicModels: true,
|
|
311
|
+
groupLabel: 'OpenRouter — Aggregator',
|
|
312
|
+
hint: 'One key for 100+ models. Pay-per-use via openrouter.ai.',
|
|
313
|
+
},
|
|
278
314
|
'ollama': {
|
|
279
315
|
name: 'Ollama (local)',
|
|
280
316
|
description: 'Run models locally with Ollama',
|
|
@@ -299,8 +335,47 @@ export const PROVIDERS = {
|
|
|
299
335
|
export function getProvider(id) {
|
|
300
336
|
return PROVIDERS[id] || null;
|
|
301
337
|
}
|
|
338
|
+
/**
|
|
339
|
+
* Curated display order for the first-run login flow + `/provider` /
|
|
340
|
+
* `/login` pickers. Headline / popular providers float to the top so
|
|
341
|
+
* brand-new users see them first; regional + parameter-variant entries
|
|
342
|
+
* (Z.AI China, MiniMax variants) trail. Any provider id not in this
|
|
343
|
+
* list is appended afterward in object-declaration order, so adding a
|
|
344
|
+
* new provider to PROVIDERS without touching this list still shows up.
|
|
345
|
+
*/
|
|
346
|
+
const DISPLAY_ORDER = [
|
|
347
|
+
'anthropic',
|
|
348
|
+
'openai',
|
|
349
|
+
'openrouter', // 100+ models, one key — surfaced high on purpose for 2.0.0.
|
|
350
|
+
'z.ai',
|
|
351
|
+
'z.ai-api',
|
|
352
|
+
'deepseek',
|
|
353
|
+
'google',
|
|
354
|
+
'minimax',
|
|
355
|
+
'minimax-api',
|
|
356
|
+
'ollama',
|
|
357
|
+
'z.ai-cn',
|
|
358
|
+
'z.ai-cn-api',
|
|
359
|
+
'minimax-cn',
|
|
360
|
+
];
|
|
302
361
|
export function getProviderList() {
|
|
303
|
-
|
|
362
|
+
const all = Object.entries(PROVIDERS);
|
|
363
|
+
const byId = new Map(all);
|
|
364
|
+
const ordered = [];
|
|
365
|
+
// 1) Curated order first.
|
|
366
|
+
for (const id of DISPLAY_ORDER) {
|
|
367
|
+
const cfg = byId.get(id);
|
|
368
|
+
if (cfg) {
|
|
369
|
+
ordered.push([id, cfg]);
|
|
370
|
+
byId.delete(id);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
// 2) Anything else, in declaration order.
|
|
374
|
+
for (const entry of all) {
|
|
375
|
+
if (byId.has(entry[0]))
|
|
376
|
+
ordered.push(entry);
|
|
377
|
+
}
|
|
378
|
+
return ordered.map(([id, config]) => ({
|
|
304
379
|
id,
|
|
305
380
|
name: config.name,
|
|
306
381
|
description: config.description,
|
package/dist/renderer/App.d.ts
CHANGED
|
@@ -69,7 +69,11 @@ export declare class App {
|
|
|
69
69
|
private confirmSelection;
|
|
70
70
|
private menuOpen;
|
|
71
71
|
private menuTitle;
|
|
72
|
+
/** Filtered view shown to the user; derived from `menuItemsAll` + `menuFilter`. */
|
|
72
73
|
private menuItems;
|
|
74
|
+
/** Full unfiltered list captured on `showSelect`. */
|
|
75
|
+
private menuItemsAll;
|
|
76
|
+
private menuFilter;
|
|
73
77
|
private menuIndex;
|
|
74
78
|
private menuCurrentValue;
|
|
75
79
|
private menuCallback;
|
|
@@ -271,6 +275,7 @@ export declare class App {
|
|
|
271
275
|
showLogin(providers: Array<{
|
|
272
276
|
id: string;
|
|
273
277
|
name: string;
|
|
278
|
+
description?: string;
|
|
274
279
|
subscribeUrl?: string;
|
|
275
280
|
}>, callback: (result: {
|
|
276
281
|
providerId: string;
|
|
@@ -284,6 +289,13 @@ export declare class App {
|
|
|
284
289
|
* Show inline menu (renders below status bar)
|
|
285
290
|
*/
|
|
286
291
|
showSelect(title: string, items: SelectItem[], currentValue: string, callback: (item: SelectItem) => void): void;
|
|
292
|
+
/**
|
|
293
|
+
* Recompute the visible menu list from the current filter string.
|
|
294
|
+
* Matches across key/label/description, case-insensitive. Keeps the
|
|
295
|
+
* current value highlighted if it survives the filter; otherwise
|
|
296
|
+
* cursor returns to the top.
|
|
297
|
+
*/
|
|
298
|
+
private applyMenuFilter;
|
|
287
299
|
/**
|
|
288
300
|
* Handle keyboard input
|
|
289
301
|
*/
|
package/dist/renderer/App.js
CHANGED
|
@@ -142,7 +142,11 @@ export class App {
|
|
|
142
142
|
// Inline menu state (renders below input/status)
|
|
143
143
|
menuOpen = false;
|
|
144
144
|
menuTitle = '';
|
|
145
|
+
/** Filtered view shown to the user; derived from `menuItemsAll` + `menuFilter`. */
|
|
145
146
|
menuItems = [];
|
|
147
|
+
/** Full unfiltered list captured on `showSelect`. */
|
|
148
|
+
menuItemsAll = [];
|
|
149
|
+
menuFilter = '';
|
|
146
150
|
menuIndex = 0;
|
|
147
151
|
menuCurrentValue = '';
|
|
148
152
|
menuCallback = null;
|
|
@@ -723,7 +727,9 @@ export class App {
|
|
|
723
727
|
*/
|
|
724
728
|
showSelect(title, items, currentValue, callback) {
|
|
725
729
|
this.menuTitle = title;
|
|
730
|
+
this.menuItemsAll = items;
|
|
726
731
|
this.menuItems = items;
|
|
732
|
+
this.menuFilter = '';
|
|
727
733
|
this.menuCurrentValue = currentValue;
|
|
728
734
|
this.menuCallback = callback;
|
|
729
735
|
this.menuOpen = true;
|
|
@@ -732,6 +738,27 @@ export class App {
|
|
|
732
738
|
this.menuIndex = currentIndex >= 0 ? currentIndex : 0;
|
|
733
739
|
this.scheduleRender();
|
|
734
740
|
}
|
|
741
|
+
/**
|
|
742
|
+
* Recompute the visible menu list from the current filter string.
|
|
743
|
+
* Matches across key/label/description, case-insensitive. Keeps the
|
|
744
|
+
* current value highlighted if it survives the filter; otherwise
|
|
745
|
+
* cursor returns to the top.
|
|
746
|
+
*/
|
|
747
|
+
applyMenuFilter(next) {
|
|
748
|
+
this.menuFilter = next;
|
|
749
|
+
if (!next) {
|
|
750
|
+
this.menuItems = this.menuItemsAll;
|
|
751
|
+
}
|
|
752
|
+
else {
|
|
753
|
+
const f = next.toLowerCase();
|
|
754
|
+
this.menuItems = this.menuItemsAll.filter((item) => item.key.toLowerCase().includes(f) ||
|
|
755
|
+
item.label.toLowerCase().includes(f) ||
|
|
756
|
+
(item.description?.toLowerCase().includes(f) ?? false));
|
|
757
|
+
}
|
|
758
|
+
const surviving = this.menuItems.findIndex((item) => item.key === this.menuCurrentValue);
|
|
759
|
+
this.menuIndex = surviving >= 0 ? surviving : 0;
|
|
760
|
+
this.scheduleRender();
|
|
761
|
+
}
|
|
735
762
|
/**
|
|
736
763
|
* Handle keyboard input
|
|
737
764
|
*/
|
|
@@ -1157,10 +1184,14 @@ export class App {
|
|
|
1157
1184
|
const callback = this.menuCallback;
|
|
1158
1185
|
this.menuOpen = false;
|
|
1159
1186
|
this.menuCallback = null;
|
|
1187
|
+
this.menuFilter = '';
|
|
1188
|
+
this.menuItemsAll = [];
|
|
1160
1189
|
if (selected && callback)
|
|
1161
1190
|
callback(selected);
|
|
1162
1191
|
},
|
|
1163
1192
|
render: () => this.scheduleRender(),
|
|
1193
|
+
filter: this.menuFilter,
|
|
1194
|
+
setFilter: (v) => this.applyMenuFilter(v),
|
|
1164
1195
|
});
|
|
1165
1196
|
}
|
|
1166
1197
|
/**
|
|
@@ -1524,7 +1555,10 @@ export class App {
|
|
|
1524
1555
|
// Menu open (provider, model, lang, etc.)
|
|
1525
1556
|
if (this.menuOpen) {
|
|
1526
1557
|
this.screen.write(0, y, '> ', fg.gray);
|
|
1527
|
-
this.
|
|
1558
|
+
const hint = this.menuItemsAll.length > 10
|
|
1559
|
+
? 'Type to filter, ↑↓ to navigate, Enter to pick…'
|
|
1560
|
+
: 'Select an option below…';
|
|
1561
|
+
this.screen.write(2, y, hint, fg.yellow);
|
|
1528
1562
|
this.screen.showCursor(false);
|
|
1529
1563
|
return;
|
|
1530
1564
|
}
|
|
@@ -1669,7 +1703,7 @@ export class App {
|
|
|
1669
1703
|
*/
|
|
1670
1704
|
renderInlineMenu(startY, width) {
|
|
1671
1705
|
const items = this.menuItems;
|
|
1672
|
-
const maxVisible = Math.min(items.length, 10);
|
|
1706
|
+
const maxVisible = Math.min(Math.max(items.length, 1), 10);
|
|
1673
1707
|
// Calculate visible range with scroll
|
|
1674
1708
|
let visibleStart = 0;
|
|
1675
1709
|
if (items.length > maxVisible) {
|
|
@@ -1681,6 +1715,17 @@ export class App {
|
|
|
1681
1715
|
this.screen.horizontalLine(y++, '─', PRIMARY_COLOR);
|
|
1682
1716
|
// Title
|
|
1683
1717
|
this.screen.writeLine(y++, this.menuTitle, PRIMARY_COLOR + style.bold);
|
|
1718
|
+
// Filter line — only rendered while filtering, so the menu looks
|
|
1719
|
+
// identical to the pre-filter UI by default.
|
|
1720
|
+
if (this.menuFilter) {
|
|
1721
|
+
const matchInfo = items.length === 0
|
|
1722
|
+
? ' (no matches)'
|
|
1723
|
+
: ` (${items.length}/${this.menuItemsAll.length})`;
|
|
1724
|
+
this.screen.writeLine(y++, `Filter: ${this.menuFilter}█${matchInfo}`, fg.cyan);
|
|
1725
|
+
}
|
|
1726
|
+
if (items.length === 0) {
|
|
1727
|
+
this.screen.writeLine(y++, ' No items match. Backspace to edit, Esc to clear.', fg.gray);
|
|
1728
|
+
}
|
|
1684
1729
|
// Items
|
|
1685
1730
|
for (let i = 0; i < visibleItems.length; i++) {
|
|
1686
1731
|
const item = visibleItems[i];
|
|
@@ -1700,7 +1745,8 @@ export class App {
|
|
|
1700
1745
|
}
|
|
1701
1746
|
// Footer with navigation hints
|
|
1702
1747
|
const scrollInfo = items.length > maxVisible ? ` (${visibleStart + 1}-${visibleStart + visibleItems.length}/${items.length})` : '';
|
|
1703
|
-
this.
|
|
1748
|
+
const escHint = this.menuFilter ? 'Esc clear · Esc Esc cancel' : 'Esc cancel';
|
|
1749
|
+
this.screen.writeLine(y, `↑↓ navigate • Enter select • type to filter • ${escHint}${scrollInfo}`, fg.gray);
|
|
1704
1750
|
}
|
|
1705
1751
|
/**
|
|
1706
1752
|
* Render inline settings below status bar
|
|
@@ -2440,6 +2486,20 @@ export class App {
|
|
|
2440
2486
|
continue;
|
|
2441
2487
|
}
|
|
2442
2488
|
}
|
|
2489
|
+
// Strikethrough: ~~text~~ — using the SGR strikethrough escape (\x1b[9m).
|
|
2490
|
+
// Widely supported in modern terminals (iTerm2, Kitty, WezTerm, Alacritty,
|
|
2491
|
+
// gnome-terminal, Windows Terminal). Falls back gracefully to the dim
|
|
2492
|
+
// text colour on terminals that don't render the SGR.
|
|
2493
|
+
if (text.slice(i, i + 2) === '~~') {
|
|
2494
|
+
const end = text.indexOf('~~', i + 2);
|
|
2495
|
+
if (end !== -1) {
|
|
2496
|
+
const inner = text.slice(i + 2, end);
|
|
2497
|
+
result += '\x1b[9m' + fg.rgb(140, 140, 140) + inner + '\x1b[0m';
|
|
2498
|
+
hasFormatting = true;
|
|
2499
|
+
i = end + 2;
|
|
2500
|
+
continue;
|
|
2501
|
+
}
|
|
2502
|
+
}
|
|
2443
2503
|
result += text[i];
|
|
2444
2504
|
i++;
|
|
2445
2505
|
}
|
|
@@ -2479,6 +2539,25 @@ export class App {
|
|
|
2479
2539
|
});
|
|
2480
2540
|
continue;
|
|
2481
2541
|
}
|
|
2542
|
+
// Blockquote: `> text` — render with a left accent bar (PRIMARY_COLOR)
|
|
2543
|
+
// and the body in a dimmer grey so it visually sits behind regular text.
|
|
2544
|
+
// Strips one level of `> ` so nested quotes render with their own bar.
|
|
2545
|
+
const quoteMatch = line.match(/^(\s*)>\s?(.*)$/);
|
|
2546
|
+
if (quoteMatch) {
|
|
2547
|
+
const indent = quoteMatch[1];
|
|
2548
|
+
const quoteText = quoteMatch[2];
|
|
2549
|
+
const { formatted, hasFormatting } = this.applyInlineMarkdown(quoteText);
|
|
2550
|
+
const body = hasFormatting ? formatted : quoteText;
|
|
2551
|
+
// Vertical bar + space, then dim grey body. Skip if the body is
|
|
2552
|
+
// empty so an isolated `>` renders cleanly.
|
|
2553
|
+
const barred = PRIMARY_COLOR + '│' + '\x1b[0m' + (body ? ' ' + fg.rgb(160, 160, 160) + body + '\x1b[0m' : '');
|
|
2554
|
+
lines.push({
|
|
2555
|
+
text: prefix + indent + barred,
|
|
2556
|
+
style: prefixStyle,
|
|
2557
|
+
raw: true,
|
|
2558
|
+
});
|
|
2559
|
+
continue;
|
|
2560
|
+
}
|
|
2482
2561
|
// List items: - item or * item or numbered 1. item
|
|
2483
2562
|
const listMatch = line.match(/^(\s*)([-*]|\d+\.)\s+(.+)$/);
|
|
2484
2563
|
if (listMatch) {
|
|
@@ -2621,12 +2700,25 @@ export class App {
|
|
|
2621
2700
|
// Provider selection
|
|
2622
2701
|
this.screen.writeLine(y++, 'Select Provider', fg.cyan + style.bold);
|
|
2623
2702
|
y++;
|
|
2703
|
+
// Name column width — pad so descriptions align in a column.
|
|
2704
|
+
// Description is clipped to remaining terminal width with an ellipsis;
|
|
2705
|
+
// on narrow panes the user still sees the name and a hint of the
|
|
2706
|
+
// description rather than a silent overrun past the right edge.
|
|
2707
|
+
const longestName = this.loginProviders.reduce((m, p) => Math.max(m, p.name.length), 0);
|
|
2708
|
+
const descStartX = 2 + longestName + 2;
|
|
2709
|
+
const descBudget = Math.max(0, width - descStartX - 1);
|
|
2624
2710
|
for (let i = 0; i < this.loginProviders.length; i++) {
|
|
2625
2711
|
const provider = this.loginProviders[i];
|
|
2626
2712
|
const isSelected = i === this.loginProviderIndex;
|
|
2627
2713
|
const prefix = isSelected ? '→ ' : ' ';
|
|
2628
2714
|
this.screen.write(0, y, prefix, isSelected ? fg.green : '');
|
|
2629
|
-
this.screen.write(2, y, provider.name, isSelected ? fg.green + style.bold : fg.white);
|
|
2715
|
+
this.screen.write(2, y, provider.name.padEnd(longestName + 2), isSelected ? fg.green + style.bold : fg.white);
|
|
2716
|
+
if (provider.description && descBudget > 0) {
|
|
2717
|
+
const desc = provider.description.length > descBudget
|
|
2718
|
+
? provider.description.slice(0, Math.max(1, descBudget - 1)) + '…'
|
|
2719
|
+
: provider.description;
|
|
2720
|
+
this.screen.write(descStartX, y, desc, isSelected ? fg.white : fg.gray);
|
|
2721
|
+
}
|
|
2630
2722
|
y++;
|
|
2631
2723
|
}
|
|
2632
2724
|
y++;
|
|
@@ -179,6 +179,11 @@ export async function executeAgentTask(task, dryRun, ctx) {
|
|
|
179
179
|
dryRun,
|
|
180
180
|
onRequestPermission,
|
|
181
181
|
chatHistory: app.getChatHistory(),
|
|
182
|
+
// Route MCP-prefixed tool calls through the shared TUI session id.
|
|
183
|
+
// Servers were registered against this id at app startup (see
|
|
184
|
+
// renderer/main.ts) so the agent picks up any `.codeep/mcp_servers.json`
|
|
185
|
+
// entries plus global ones at runtime.
|
|
186
|
+
mcpSessionId: 'codeep-tui',
|
|
182
187
|
onIteration: (iteration, message) => {
|
|
183
188
|
app.updateAgentProgress(iteration);
|
|
184
189
|
app.setAgentWaitingForAI(true); // Waiting for AI response between tool calls
|