codeep 2.9.0 → 2.11.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 +3 -3
- package/dist/acp/commands.js +29 -1
- package/dist/acp/protocol.d.ts +4 -0
- package/dist/acp/server.js +7 -0
- package/dist/config/index.d.ts +3 -0
- package/dist/config/index.js +31 -21
- package/dist/config/providers.js +20 -12
- package/dist/renderer/App.d.ts +7 -1
- package/dist/renderer/App.js +78 -50
- package/dist/renderer/commands.js +57 -9
- package/dist/renderer/components/Help.d.ts +0 -5
- package/dist/renderer/components/Help.js +1 -68
- package/dist/renderer/components/Settings.d.ts +0 -5
- package/dist/renderer/components/Settings.js +1 -80
- package/dist/renderer/highlight.js +16 -0
- package/dist/renderer/index.d.ts +1 -2
- package/dist/renderer/index.js +1 -2
- package/dist/utils/tokenTracker.js +8 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +2 -4
- package/dist/renderer/ChatUI.d.ts +0 -71
- package/dist/renderer/ChatUI.js +0 -286
- package/dist/renderer/demo-app.d.ts +0 -6
- package/dist/renderer/demo-app.js +0 -85
- package/dist/renderer/demo.d.ts +0 -6
- package/dist/renderer/demo.js +0 -52
package/README.md
CHANGED
|
@@ -346,7 +346,7 @@ Run `/agents` to list them. The agent invokes them itself via the `delegate` too
|
|
|
346
346
|
name: migrator
|
|
347
347
|
description: Writes and runs database migrations
|
|
348
348
|
tools: [read_file, write_file, edit_file, execute_command] # allowlist; omit = all
|
|
349
|
-
model: glm-5.
|
|
349
|
+
model: glm-5.2[1m] # optional override
|
|
350
350
|
personality: senior-reviewer # optional preset
|
|
351
351
|
maxIterations: 12 # optional budget
|
|
352
352
|
---
|
|
@@ -1007,14 +1007,14 @@ After installation, `codeep` is available globally in your terminal. Simply run
|
|
|
1007
1007
|
**Model favorites** — save provider+model combos and switch instantly:
|
|
1008
1008
|
```
|
|
1009
1009
|
> /provider # switch to z.ai
|
|
1010
|
-
> /model glm-5.
|
|
1010
|
+
> /model glm-5.2[1m]
|
|
1011
1011
|
> /profile save fast
|
|
1012
1012
|
|
|
1013
1013
|
> /provider # switch to openai
|
|
1014
1014
|
> /model gpt-4.1
|
|
1015
1015
|
> /profile save work
|
|
1016
1016
|
|
|
1017
|
-
> /model fast # instantly switch to z.ai / glm-5.
|
|
1017
|
+
> /model fast # instantly switch to z.ai / glm-5.2
|
|
1018
1018
|
> /model work # instantly switch to openai / gpt-4.1
|
|
1019
1019
|
```
|
|
1020
1020
|
|
package/dist/acp/commands.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Slash command handler for ACP sessions.
|
|
3
3
|
// Mirrors CLI commands from renderer/commands.ts but returns plain text
|
|
4
4
|
// responses (no TUI) suitable for streaming back via session/update.
|
|
5
|
-
import { config, getCurrentProvider, getModelsForCurrentProvider, setProvider, setApiKey, isConfigured, listSessionsWithInfo, startNewSession, loadSession, saveSession, initializeAsProject, isManuallyInitializedProject, setProjectPermission, hasWritePermission, hasReadPermission, isTelemetryEnabled, telemetryForcedOffByEnv, } from '../config/index.js';
|
|
5
|
+
import { config, getCurrentProvider, getModelsForCurrentProvider, setProvider, setApiKey, isConfigured, listSessionsWithInfo, startNewSession, loadSession, saveSession, initializeAsProject, isManuallyInitializedProject, setProjectPermission, hasWritePermission, hasReadPermission, isTelemetryEnabled, telemetryForcedOffByEnv, isKeySyncEnabled, keySyncForcedOffByEnv, } from '../config/index.js';
|
|
6
6
|
import { getProviderList, getProvider } from '../config/providers.js';
|
|
7
7
|
import { getProjectContext } from '../utils/project.js';
|
|
8
8
|
import { loadCustomCommands } from '../utils/customCommands.js';
|
|
@@ -237,6 +237,34 @@ export async function handleCommand(input, session, onChunk, abortSignal) {
|
|
|
237
237
|
lines.push('', 'Toggle with `/telemetry on` | `/telemetry off`. Controls automatic uploads of usage stats, session transcripts, progress, and memory notes.');
|
|
238
238
|
return { handled: true, response: lines.join('\n') };
|
|
239
239
|
}
|
|
240
|
+
case 'keysync': {
|
|
241
|
+
const sub = args[0]?.toLowerCase();
|
|
242
|
+
const envOff = keySyncForcedOffByEnv();
|
|
243
|
+
if (sub === 'on' || sub === 'off') {
|
|
244
|
+
if (envOff) {
|
|
245
|
+
return { handled: true, response: 'Cloud key sync is forced **off** by the `CODEEP_NO_KEY_SYNC` env var — unset it to change this. The config flag can\'t override an env var.' };
|
|
246
|
+
}
|
|
247
|
+
config.set('syncKeysToCloud', sub === 'on');
|
|
248
|
+
return {
|
|
249
|
+
handled: true,
|
|
250
|
+
response: sub === 'on'
|
|
251
|
+
? 'Cloud key sync **on** — `codeep account push`/`sync` will now upload/download API keys. Note: synced keys are stored server-readable on codeep.dev.'
|
|
252
|
+
: 'Cloud key sync **off** — API keys stay in your OS keychain only. (`codeep account purge-keys` wipes any keys already on the server.)',
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
if (sub && sub !== 'status') {
|
|
256
|
+
return { handled: true, response: 'Usage: `/keysync` · `/keysync on` · `/keysync off`' };
|
|
257
|
+
}
|
|
258
|
+
const flag = config.get('syncKeysToCloud') === true;
|
|
259
|
+
const lines = [
|
|
260
|
+
`**Cloud key sync:** ${isKeySyncEnabled() ? 'on' : 'off'}`,
|
|
261
|
+
`- Config flag \`syncKeysToCloud\`: ${flag}`,
|
|
262
|
+
];
|
|
263
|
+
if (envOff)
|
|
264
|
+
lines.push('- Forced **off** by `CODEEP_NO_KEY_SYNC` (env overrides the flag).');
|
|
265
|
+
lines.push('', 'OFF by default — API keys live only in your OS keychain unless enabled. When on, `codeep account push`/`sync` move keys, stored **server-readable** on codeep.dev.');
|
|
266
|
+
return { handled: true, response: lines.join('\n') };
|
|
267
|
+
}
|
|
240
268
|
case 'login': {
|
|
241
269
|
const [providerId, apiKey] = args;
|
|
242
270
|
if (!providerId || !apiKey) {
|
package/dist/acp/protocol.d.ts
CHANGED
|
@@ -102,6 +102,10 @@ export interface SessionNewParams {
|
|
|
102
102
|
}
|
|
103
103
|
export interface SessionNewResult {
|
|
104
104
|
sessionId: string;
|
|
105
|
+
history?: {
|
|
106
|
+
role: string;
|
|
107
|
+
content: string;
|
|
108
|
+
}[];
|
|
105
109
|
modes?: SessionModeState | null;
|
|
106
110
|
configOptions?: SessionConfigOption[] | null;
|
|
107
111
|
}
|
package/dist/acp/server.js
CHANGED
|
@@ -67,6 +67,9 @@ const AVAILABLE_COMMANDS = [
|
|
|
67
67
|
{ name: 'learn', description: 'Learn coding preferences from project files' },
|
|
68
68
|
{ name: 'memory', description: 'Project memory notes — add / list / remove / clear', input: { hint: '<note> | list | remove <n> | clear' } },
|
|
69
69
|
{ name: 'profile', description: 'Save / load / delete provider+model presets', input: { hint: 'save | load | delete | list | <name>' } },
|
|
70
|
+
// Privacy toggles
|
|
71
|
+
{ name: 'telemetry', description: 'Show or toggle automatic cloud telemetry', input: { hint: '[on|off]' } },
|
|
72
|
+
{ name: 'keysync', description: 'Show or toggle syncing API keys to codeep.dev (off by default)', input: { hint: '[on|off]' } },
|
|
70
73
|
// Skills + custom commands
|
|
71
74
|
{ name: 'skills', description: 'List/create/share skill bundles. Subcommands: bundles, create-bundle, show, publish, install, browse, unpublish', input: { hint: '[query] | bundles | create-bundle <name> | show <name> | publish <slug> [--public] | install <owner>/<slug> | browse [q] | unpublish <owner>/<slug>' } },
|
|
72
75
|
{ name: 'commands', description: 'List user-authored commands from .codeep/commands/*.md' },
|
|
@@ -549,6 +552,10 @@ export function startAcpServer() {
|
|
|
549
552
|
});
|
|
550
553
|
const result = {
|
|
551
554
|
sessionId: acpSessionId,
|
|
555
|
+
// On a resume (fresh=false) `history` holds the prior transcript;
|
|
556
|
+
// return it (user/assistant only) so a reconnected client can repaint
|
|
557
|
+
// the chat. Empty on a fresh session, so this is harmless there.
|
|
558
|
+
history: history.filter(m => m.role === 'user' || m.role === 'assistant'),
|
|
552
559
|
modes: AGENT_MODES,
|
|
553
560
|
configOptions: buildConfigOptions(),
|
|
554
561
|
};
|
package/dist/config/index.d.ts
CHANGED
|
@@ -47,6 +47,9 @@ interface ConfigSchema {
|
|
|
47
47
|
* can't execute shell on first tool call). Granted via `/hooks trust`. */
|
|
48
48
|
trustedHookProjects: string[];
|
|
49
49
|
currentSessionId: string;
|
|
50
|
+
/** Highest one-shot config migration applied (see the migration block
|
|
51
|
+
* after config creation). Bump MIGRATION_VERSION when adding one. */
|
|
52
|
+
migrationVersion: number;
|
|
50
53
|
temperature: number;
|
|
51
54
|
maxTokens: number;
|
|
52
55
|
apiTimeout: number;
|
package/dist/config/index.js
CHANGED
|
@@ -141,8 +141,9 @@ function isWritable(dir) {
|
|
|
141
141
|
function createConfig() {
|
|
142
142
|
const defaults = {
|
|
143
143
|
apiKey: '',
|
|
144
|
+
migrationVersion: 0,
|
|
144
145
|
provider: 'z.ai',
|
|
145
|
-
model: 'glm-5.
|
|
146
|
+
model: 'glm-5.2[1m]',
|
|
146
147
|
agentMode: 'on',
|
|
147
148
|
ollamaUrl: 'http://localhost:11434',
|
|
148
149
|
ollamaNativeApi: false,
|
|
@@ -247,26 +248,35 @@ export const config = createConfig();
|
|
|
247
248
|
if (config.get('agentMode') === 'auto') {
|
|
248
249
|
config.set('agentMode', 'on');
|
|
249
250
|
}
|
|
250
|
-
//
|
|
251
|
-
//
|
|
252
|
-
//
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
config.
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
config.
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
config.
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
config.
|
|
251
|
+
// One-shot migrations of old defaults. These used to run unconditionally on
|
|
252
|
+
// EVERY startup, which silently clobbered values the user later chose in
|
|
253
|
+
// /settings (e.g. maxTokens 8192 was forced back to 32768 each launch — the
|
|
254
|
+
// affected sliders were effectively lies). Each migration now runs exactly
|
|
255
|
+
// once per config, recorded via `migrationVersion`; after that, whatever the
|
|
256
|
+
// user sets sticks. Bump MIGRATION_VERSION when adding a new one.
|
|
257
|
+
const MIGRATION_VERSION = 1;
|
|
258
|
+
if ((config.get('migrationVersion') ?? 0) < 1) {
|
|
259
|
+
// Migrate the old runaway default (10000 iterations) down to a sane
|
|
260
|
+
// ceiling, and old conservative defaults up to the current ones.
|
|
261
|
+
if (config.get('agentMaxIterations') >= 10000) {
|
|
262
|
+
config.set('agentMaxIterations', 50);
|
|
263
|
+
}
|
|
264
|
+
if (config.get('agentMaxDuration') < 480) {
|
|
265
|
+
config.set('agentMaxDuration', 480);
|
|
266
|
+
}
|
|
267
|
+
if (config.get('maxTokens') < 32768) {
|
|
268
|
+
config.set('maxTokens', 32768);
|
|
269
|
+
}
|
|
270
|
+
if (config.get('agentApiTimeout') <= 180000) {
|
|
271
|
+
config.set('agentApiTimeout', 600000);
|
|
272
|
+
}
|
|
273
|
+
if (config.get('rateLimitApi') <= 30) {
|
|
274
|
+
config.set('rateLimitApi', 10000);
|
|
275
|
+
}
|
|
276
|
+
if (config.get('rateLimitCommands') <= 100) {
|
|
277
|
+
config.set('rateLimitCommands', 10000);
|
|
278
|
+
}
|
|
279
|
+
config.set('migrationVersion', MIGRATION_VERSION);
|
|
270
280
|
}
|
|
271
281
|
// Global sessions directory - use same directory as conf package for cross-platform consistency
|
|
272
282
|
// config.path gives us something like ~/.config/codeep-nodejs/config.json, we use its parent
|
package/dist/config/providers.js
CHANGED
|
@@ -18,11 +18,13 @@ export const PROVIDERS = {
|
|
|
18
18
|
},
|
|
19
19
|
},
|
|
20
20
|
models: [
|
|
21
|
-
{ id: 'glm-5.
|
|
21
|
+
{ id: 'glm-5.2[1m]', name: 'GLM-5.2 (1M)', description: 'Latest GLM model with 1M context, available to all users' },
|
|
22
|
+
{ id: 'glm-5.2', name: 'GLM-5.2', description: 'Latest GLM model, available to all users' },
|
|
23
|
+
{ id: 'glm-5.1', name: 'GLM-5.1', description: 'Previous GLM model, available to all users' },
|
|
22
24
|
{ id: 'glm-5-turbo', name: 'GLM-5 Turbo', description: 'Fast GLM-5 variant, available to all users' },
|
|
23
|
-
{ id: 'glm-5', name: 'GLM-5', description: 'Most capable GLM model (Pro/Max plan only)' },
|
|
25
|
+
{ id: 'glm-5', name: 'GLM-5', description: 'Most capable GLM-5 model (Pro/Max plan only)' },
|
|
24
26
|
],
|
|
25
|
-
defaultModel: 'glm-5.
|
|
27
|
+
defaultModel: 'glm-5.2[1m]',
|
|
26
28
|
defaultProtocol: 'openai',
|
|
27
29
|
envKey: 'ZAI_API_KEY',
|
|
28
30
|
subscribeUrl: 'https://z.ai/subscribe?ic=NXYNXZOV14',
|
|
@@ -45,11 +47,13 @@ export const PROVIDERS = {
|
|
|
45
47
|
},
|
|
46
48
|
},
|
|
47
49
|
models: [
|
|
48
|
-
{ id: 'glm-5.
|
|
50
|
+
{ id: 'glm-5.2[1m]', name: 'GLM-5.2 (1M)', description: 'Latest GLM model with 1M context' },
|
|
51
|
+
{ id: 'glm-5.2', name: 'GLM-5.2', description: 'Latest GLM model' },
|
|
52
|
+
{ id: 'glm-5.1', name: 'GLM-5.1', description: 'Previous GLM model' },
|
|
49
53
|
{ id: 'glm-5-turbo', name: 'GLM-5 Turbo', description: 'Fast GLM-5 variant' },
|
|
50
|
-
{ id: 'glm-5', name: 'GLM-5', description: 'Most capable GLM model' },
|
|
54
|
+
{ id: 'glm-5', name: 'GLM-5', description: 'Most capable GLM-5 model' },
|
|
51
55
|
],
|
|
52
|
-
defaultModel: 'glm-5.
|
|
56
|
+
defaultModel: 'glm-5.2[1m]',
|
|
53
57
|
defaultProtocol: 'openai',
|
|
54
58
|
envKey: 'ZAI_API_KEY',
|
|
55
59
|
subscribeUrl: 'https://api.z.ai',
|
|
@@ -72,11 +76,13 @@ export const PROVIDERS = {
|
|
|
72
76
|
},
|
|
73
77
|
},
|
|
74
78
|
models: [
|
|
75
|
-
{ id: 'glm-5.
|
|
79
|
+
{ id: 'glm-5.2[1m]', name: 'GLM-5.2 (1M)', description: 'Latest GLM model with 1M context, available to all users' },
|
|
80
|
+
{ id: 'glm-5.2', name: 'GLM-5.2', description: 'Latest GLM model, available to all users' },
|
|
81
|
+
{ id: 'glm-5.1', name: 'GLM-5.1', description: 'Previous GLM model, available to all users' },
|
|
76
82
|
{ id: 'glm-5-turbo', name: 'GLM-5 Turbo', description: 'Fast GLM-5 variant, available to all users' },
|
|
77
|
-
{ id: 'glm-5', name: 'GLM-5', description: 'Most capable GLM model (Pro/Max plan only)' },
|
|
83
|
+
{ id: 'glm-5', name: 'GLM-5', description: 'Most capable GLM-5 model (Pro/Max plan only)' },
|
|
78
84
|
],
|
|
79
|
-
defaultModel: 'glm-5.
|
|
85
|
+
defaultModel: 'glm-5.2[1m]',
|
|
80
86
|
defaultProtocol: 'openai',
|
|
81
87
|
envKey: 'ZAI_CN_API_KEY',
|
|
82
88
|
subscribeUrl: 'https://open.bigmodel.cn/glm-coding',
|
|
@@ -99,11 +105,13 @@ export const PROVIDERS = {
|
|
|
99
105
|
},
|
|
100
106
|
},
|
|
101
107
|
models: [
|
|
102
|
-
{ id: 'glm-5.
|
|
108
|
+
{ id: 'glm-5.2[1m]', name: 'GLM-5.2 (1M)', description: 'Latest GLM model with 1M context' },
|
|
109
|
+
{ id: 'glm-5.2', name: 'GLM-5.2', description: 'Latest GLM model' },
|
|
110
|
+
{ id: 'glm-5.1', name: 'GLM-5.1', description: 'Previous GLM model' },
|
|
103
111
|
{ id: 'glm-5-turbo', name: 'GLM-5 Turbo', description: 'Fast GLM-5 variant' },
|
|
104
|
-
{ id: 'glm-5', name: 'GLM-5', description: 'Most capable GLM model' },
|
|
112
|
+
{ id: 'glm-5', name: 'GLM-5', description: 'Most capable GLM-5 model' },
|
|
105
113
|
],
|
|
106
|
-
defaultModel: 'glm-5.
|
|
114
|
+
defaultModel: 'glm-5.2[1m]',
|
|
107
115
|
defaultProtocol: 'openai',
|
|
108
116
|
envKey: 'ZAI_CN_API_KEY',
|
|
109
117
|
subscribeUrl: 'https://open.bigmodel.cn',
|
package/dist/renderer/App.d.ts
CHANGED
|
@@ -40,6 +40,9 @@ export declare class App {
|
|
|
40
40
|
private isLoading;
|
|
41
41
|
private options;
|
|
42
42
|
private scrollOffset;
|
|
43
|
+
/** Messages that arrived while the user was scrolled up — drives the
|
|
44
|
+
* status bar's "↓ N new" badge; 0 whenever the view is at the bottom. */
|
|
45
|
+
private unseenWhileScrolled;
|
|
43
46
|
private notification;
|
|
44
47
|
private notificationIsWarn;
|
|
45
48
|
private notificationTimeout;
|
|
@@ -126,7 +129,10 @@ export declare class App {
|
|
|
126
129
|
*/
|
|
127
130
|
stop(): void;
|
|
128
131
|
/**
|
|
129
|
-
* Add a message
|
|
132
|
+
* Add a message. Autoscrolls only when the user is already at the
|
|
133
|
+
* bottom — if they scrolled up to read something, new messages must
|
|
134
|
+
* not yank the view away; the status bar shows a "↓ N new" badge
|
|
135
|
+
* instead (cleared when they return to the bottom).
|
|
130
136
|
*/
|
|
131
137
|
addMessage(message: Message): void;
|
|
132
138
|
setMessages(messages: Message[]): void;
|
package/dist/renderer/App.js
CHANGED
|
@@ -49,6 +49,12 @@ const COMMAND_DESCRIPTIONS = {
|
|
|
49
49
|
'git-commit': 'Commit with message',
|
|
50
50
|
'push': 'Git push',
|
|
51
51
|
'pull': 'Git pull',
|
|
52
|
+
'amend': 'Amend the last commit',
|
|
53
|
+
'pr': 'Create a pull request description',
|
|
54
|
+
'changelog': 'Generate changelog from recent commits',
|
|
55
|
+
'branch': 'Create a new branch with smart naming',
|
|
56
|
+
'stash': 'Stash changes with a meaningful message',
|
|
57
|
+
'unstash': 'Apply and drop the most recent stash',
|
|
52
58
|
'init': 'Initialize project (.codeep/)',
|
|
53
59
|
'scan': 'Scan project',
|
|
54
60
|
'memory': 'Add/list/remove project memory notes',
|
|
@@ -66,6 +72,38 @@ const COMMAND_DESCRIPTIONS = {
|
|
|
66
72
|
'explain': 'Explain code',
|
|
67
73
|
'optimize': 'Optimize performance',
|
|
68
74
|
'debug': 'Debug problems',
|
|
75
|
+
'test-fix': 'Fix failing tests',
|
|
76
|
+
'coverage': 'Analyze test coverage and suggest improvements',
|
|
77
|
+
'e2e': 'Generate end-to-end tests',
|
|
78
|
+
'mock': 'Generate mock data for testing',
|
|
79
|
+
'readme': 'Generate or update README',
|
|
80
|
+
'api-docs': 'Generate API documentation',
|
|
81
|
+
'translate': 'Translate code comments to English',
|
|
82
|
+
'types': 'Add or improve TypeScript types',
|
|
83
|
+
'cleanup': 'Clean up code (remove unused, format)',
|
|
84
|
+
'modernize': 'Update code to use modern syntax',
|
|
85
|
+
'migrate': 'Migrate code to newer version',
|
|
86
|
+
'split': 'Split a large file into smaller modules',
|
|
87
|
+
'security': 'Security audit',
|
|
88
|
+
'log': 'Add logging to code',
|
|
89
|
+
'build': 'Build the project',
|
|
90
|
+
'deploy': 'Build and deploy',
|
|
91
|
+
'release': 'Create a new release',
|
|
92
|
+
'publish': 'Publish package to npm',
|
|
93
|
+
'component': 'Generate a React/Vue component',
|
|
94
|
+
'api': 'Generate an API endpoint',
|
|
95
|
+
'hook': 'Generate a React hook',
|
|
96
|
+
'service': 'Generate a service/utility module',
|
|
97
|
+
'page': 'Generate a new page/route',
|
|
98
|
+
'form': 'Generate a form with validation',
|
|
99
|
+
'crud': 'Generate full CRUD for an entity',
|
|
100
|
+
'docker': 'Generate Dockerfile and docker-compose',
|
|
101
|
+
'ci': 'Generate CI/CD configuration',
|
|
102
|
+
'env': 'Setup environment configuration',
|
|
103
|
+
'k8s': 'Generate Kubernetes manifests',
|
|
104
|
+
'terraform': 'Generate Terraform configuration',
|
|
105
|
+
'nginx': 'Generate Nginx configuration',
|
|
106
|
+
'monitor': 'Add monitoring and observability',
|
|
69
107
|
'skills': 'List all skills',
|
|
70
108
|
'provider': 'Switch provider',
|
|
71
109
|
'model': 'Switch model',
|
|
@@ -74,13 +112,14 @@ const COMMAND_DESCRIPTIONS = {
|
|
|
74
112
|
'grant': 'Grant write permission',
|
|
75
113
|
'login': 'Change API key',
|
|
76
114
|
'logout': 'Logout',
|
|
115
|
+
'account': 'Link this machine to your codeep.dev account',
|
|
77
116
|
'context-save': 'Save conversation',
|
|
78
117
|
'context-load': 'Load conversation',
|
|
79
118
|
'context-clear': 'Clear saved context',
|
|
80
119
|
'learn': 'Learn code preferences',
|
|
81
120
|
'cost': 'Show session cost and token usage',
|
|
82
121
|
'profile': 'Save/load settings profiles',
|
|
83
|
-
'tasks': '
|
|
122
|
+
'tasks': 'List/add/done/delete codeep.dev tasks — add <title> [--bug|--feature]',
|
|
84
123
|
'sync': 'Sync learning preferences and profiles to codeep.dev',
|
|
85
124
|
'telemetry': 'Show or toggle automatic cloud telemetry (on/off)',
|
|
86
125
|
'keysync': 'Show or toggle syncing API keys to codeep.dev (on/off)',
|
|
@@ -116,6 +155,9 @@ export class App {
|
|
|
116
155
|
isLoading = false;
|
|
117
156
|
options;
|
|
118
157
|
scrollOffset = 0;
|
|
158
|
+
/** Messages that arrived while the user was scrolled up — drives the
|
|
159
|
+
* status bar's "↓ N new" badge; 0 whenever the view is at the bottom. */
|
|
160
|
+
unseenWhileScrolled = 0;
|
|
119
161
|
notification = '';
|
|
120
162
|
notificationIsWarn = false;
|
|
121
163
|
notificationTimeout = null;
|
|
@@ -216,41 +258,13 @@ export class App {
|
|
|
216
258
|
loginCallback = null;
|
|
217
259
|
// Glitch characters for intro animation
|
|
218
260
|
static GLITCH_CHARS = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ@#$%&*<>?/;:[]=';
|
|
219
|
-
//
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
'test', 'docs', 'refactor', 'fix', 'explain', 'optimize', 'debug', 'skills',
|
|
227
|
-
'amend', 'pr', 'changelog', 'branch', 'stash', 'unstash',
|
|
228
|
-
'build', 'deploy', 'release', 'publish',
|
|
229
|
-
'component', 'api', 'hook', 'service', 'page', 'form', 'crud',
|
|
230
|
-
'security', 'profile', 'log', 'types', 'cleanup', 'modernize', 'migrate',
|
|
231
|
-
'split', 'rename', 'coverage', 'e2e', 'mock', 'readme', 'translate',
|
|
232
|
-
'docker', 'ci', 'env', 'k8s', 'terraform', 'nginx', 'monitor',
|
|
233
|
-
'test-fix', 'api-docs',
|
|
234
|
-
'multiline', 'memory', 'init',
|
|
235
|
-
'provider', 'model', 'protocol', 'lang', 'grant', 'login', 'logout',
|
|
236
|
-
'context-save', 'context-load', 'context-clear', 'learn',
|
|
237
|
-
'cost', 'tasks', 'account', 'sync', 'telemetry',
|
|
238
|
-
// 2.0 — extensions, checkpoints, MCP, custom commands, OpenRouter prefs.
|
|
239
|
-
// Keep in lockstep with COMMAND_DESCRIPTIONS below and helpCategories.
|
|
240
|
-
'compact', 'commands', 'checkpoint', 'checkpoints', 'rewind',
|
|
241
|
-
'hooks', 'mcp', 'openrouter',
|
|
242
|
-
// 2.0.2 — plan mode.
|
|
243
|
-
'plan', 'go',
|
|
244
|
-
// 2.0.3 — personalities + insights.
|
|
245
|
-
'personality', 'insights',
|
|
246
|
-
// 2.1.0 — cross-session recall.
|
|
247
|
-
'recall',
|
|
248
|
-
// 2.2.0 — user profile.
|
|
249
|
-
'me',
|
|
250
|
-
// 2.3.0 — sub-agents / delegation.
|
|
251
|
-
'agents',
|
|
252
|
-
'c', 't', 'd', 'r', 'f', 'e', 'o', 'b', 'p',
|
|
253
|
-
];
|
|
261
|
+
// The `/` autocomplete list, derived from COMMAND_DESCRIPTIONS so it IS
|
|
262
|
+
// the registry — a command can no longer ship without a description (a
|
|
263
|
+
// hand-maintained parallel array drifted to 48 blank rows over time).
|
|
264
|
+
// Single-letter shortcuts (c, t, d, r, f, e, o, b, p) stay routable in
|
|
265
|
+
// commands.ts but are deliberately not listed: they alias commands that
|
|
266
|
+
// already appear, and bare one-letter rows just cluttered the dropdown.
|
|
267
|
+
static COMMANDS = Object.keys(COMMAND_DESCRIPTIONS);
|
|
254
268
|
constructor(options) {
|
|
255
269
|
this.screen = new Screen();
|
|
256
270
|
this.input = new Input();
|
|
@@ -283,24 +297,34 @@ export class App {
|
|
|
283
297
|
this.screen.cleanup();
|
|
284
298
|
}
|
|
285
299
|
/**
|
|
286
|
-
* Add a message
|
|
300
|
+
* Add a message. Autoscrolls only when the user is already at the
|
|
301
|
+
* bottom — if they scrolled up to read something, new messages must
|
|
302
|
+
* not yank the view away; the status bar shows a "↓ N new" badge
|
|
303
|
+
* instead (cleared when they return to the bottom).
|
|
287
304
|
*/
|
|
288
305
|
addMessage(message) {
|
|
289
306
|
this.messages.push(message);
|
|
290
307
|
this.messageCache.push(null); // slot za novu poruku
|
|
291
|
-
this.scrollOffset
|
|
308
|
+
if (this.scrollOffset === 0) {
|
|
309
|
+
this.unseenWhileScrolled = 0;
|
|
310
|
+
}
|
|
311
|
+
else {
|
|
312
|
+
this.unseenWhileScrolled++;
|
|
313
|
+
}
|
|
292
314
|
this.scheduleRender();
|
|
293
315
|
}
|
|
294
316
|
setMessages(messages) {
|
|
295
317
|
this.messages = messages;
|
|
296
318
|
this.messageCache = new Array(messages.length).fill(null);
|
|
297
319
|
this.scrollOffset = 0;
|
|
320
|
+
this.unseenWhileScrolled = 0;
|
|
298
321
|
this.scheduleRender();
|
|
299
322
|
}
|
|
300
323
|
clearMessages() {
|
|
301
324
|
this.messages = [];
|
|
302
325
|
this.messageCache = [];
|
|
303
326
|
this.scrollOffset = 0;
|
|
327
|
+
this.unseenWhileScrolled = 0;
|
|
304
328
|
this.scheduleRender();
|
|
305
329
|
}
|
|
306
330
|
/**
|
|
@@ -972,20 +996,15 @@ export class App {
|
|
|
972
996
|
if (event.key === 'pagedown') {
|
|
973
997
|
// Scroll down (show newer messages)
|
|
974
998
|
this.scrollOffset = Math.max(0, this.scrollOffset - 10);
|
|
999
|
+
if (this.scrollOffset === 0)
|
|
1000
|
+
this.unseenWhileScrolled = 0;
|
|
975
1001
|
this.scheduleRender();
|
|
976
1002
|
return;
|
|
977
1003
|
}
|
|
978
|
-
//
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
return;
|
|
983
|
-
}
|
|
984
|
-
if (event.key === 'down' && !this.editor.getValue() && !this.showAutocomplete && this.scrollOffset > 0) {
|
|
985
|
-
this.scrollOffset = Math.max(0, this.scrollOffset - 3);
|
|
986
|
-
this.scheduleRender();
|
|
987
|
-
return;
|
|
988
|
-
}
|
|
1004
|
+
// NOTE: ↑/↓ on an empty input deliberately fall through to the editor —
|
|
1005
|
+
// that's prompt-history recall (Input.ts), which the status bar has
|
|
1006
|
+
// always advertised but this handler used to shadow with a 3-line
|
|
1007
|
+
// scroll. Scrolling lives on PgUp/PgDn and the mouse wheel.
|
|
989
1008
|
// Mouse scroll
|
|
990
1009
|
if (event.key === 'scrollup') {
|
|
991
1010
|
this.scrollOffset += 3;
|
|
@@ -994,6 +1013,8 @@ export class App {
|
|
|
994
1013
|
}
|
|
995
1014
|
if (event.key === 'scrolldown') {
|
|
996
1015
|
this.scrollOffset = Math.max(0, this.scrollOffset - 3);
|
|
1016
|
+
if (this.scrollOffset === 0)
|
|
1017
|
+
this.unseenWhileScrolled = 0;
|
|
997
1018
|
this.scheduleRender();
|
|
998
1019
|
return;
|
|
999
1020
|
}
|
|
@@ -2297,7 +2318,14 @@ export class App {
|
|
|
2297
2318
|
this.screen.write(leftX, y, ' · ', fg.gray);
|
|
2298
2319
|
this.screen.write(leftX + 3, y, tokenStr, fg.gray);
|
|
2299
2320
|
}
|
|
2300
|
-
// Right: context-sensitive hints
|
|
2321
|
+
// Right: context-sensitive hints. While scrolled up, the "new
|
|
2322
|
+
// messages below" badge takes priority — it's the only signal that
|
|
2323
|
+
// the conversation moved on (addMessage no longer yanks the view).
|
|
2324
|
+
if (this.scrollOffset > 0 && this.unseenWhileScrolled > 0) {
|
|
2325
|
+
const badge = `↓ ${this.unseenWhileScrolled} new · PgDn `;
|
|
2326
|
+
this.screen.write(width - badge.length, y, badge, PRIMARY_COLOR);
|
|
2327
|
+
return;
|
|
2328
|
+
}
|
|
2301
2329
|
let rightText;
|
|
2302
2330
|
if (this.isStreaming || this.isLoading) {
|
|
2303
2331
|
rightText = 'Esc to stop ';
|
|
@@ -1833,11 +1833,40 @@ Describe what this skill does. The agent reads this body verbatim when it invoke
|
|
|
1833
1833
|
}
|
|
1834
1834
|
break;
|
|
1835
1835
|
}
|
|
1836
|
-
// /tasks add <title> — create a
|
|
1836
|
+
// /tasks add <title> [--bug | --feature] [--desc <text>] — create a task
|
|
1837
|
+
// on the dashboard. Type matches the dashboard picker (task | bug |
|
|
1838
|
+
// feature); a --bug/--feature/--task flag anywhere sets it (default task).
|
|
1839
|
+
// --desc/--description captures the following words (until the next flag)
|
|
1840
|
+
// as the description — the same field the dashboard + macOS app set, and
|
|
1841
|
+
// which the list view and the agent task-context prompt already render.
|
|
1837
1842
|
if (subCmd === 'add') {
|
|
1838
|
-
const
|
|
1843
|
+
const TASK_TYPES = ['task', 'bug', 'feature'];
|
|
1844
|
+
let type = 'task';
|
|
1845
|
+
const titleWords = [];
|
|
1846
|
+
const descWords = [];
|
|
1847
|
+
let capturingDesc = false;
|
|
1848
|
+
for (const w of args.slice(1)) {
|
|
1849
|
+
const flag = /^--([\w-]+)$/.exec(w);
|
|
1850
|
+
if (flag) {
|
|
1851
|
+
const name = flag[1].toLowerCase();
|
|
1852
|
+
if (name === 'desc' || name === 'description') {
|
|
1853
|
+
capturingDesc = true;
|
|
1854
|
+
continue;
|
|
1855
|
+
}
|
|
1856
|
+
if (TASK_TYPES.includes(name))
|
|
1857
|
+
type = name;
|
|
1858
|
+
capturingDesc = false; // any non-desc flag ends description capture
|
|
1859
|
+
continue;
|
|
1860
|
+
}
|
|
1861
|
+
if (capturingDesc)
|
|
1862
|
+
descWords.push(w);
|
|
1863
|
+
else
|
|
1864
|
+
titleWords.push(w);
|
|
1865
|
+
}
|
|
1866
|
+
const title = titleWords.join(' ').trim();
|
|
1867
|
+
const description = descWords.join(' ').trim();
|
|
1839
1868
|
if (!title) {
|
|
1840
|
-
ctx.app.notify('Usage: /tasks add <title>');
|
|
1869
|
+
ctx.app.notify('Usage: /tasks add <title> [--bug | --feature] [--desc <text>]');
|
|
1841
1870
|
break;
|
|
1842
1871
|
}
|
|
1843
1872
|
const projectName = ctx.projectContext?.name;
|
|
@@ -1852,10 +1881,10 @@ Describe what this skill does. The agent reads this body verbatim when it invoke
|
|
|
1852
1881
|
const res = await fetch('https://codeep.dev/api/tasks', {
|
|
1853
1882
|
method: 'POST',
|
|
1854
1883
|
headers: { 'Content-Type': 'application/json', 'x-sync-token': syncToken },
|
|
1855
|
-
body: JSON.stringify({ projectName: projectName || '', projectId: projectId ?? null, title, type:
|
|
1884
|
+
body: JSON.stringify({ projectName: projectName || '', projectId: projectId ?? null, title, type, ...(description ? { description } : {}) }),
|
|
1856
1885
|
});
|
|
1857
1886
|
if (res.ok) {
|
|
1858
|
-
ctx.app.notify(`+
|
|
1887
|
+
ctx.app.notify(`+ ${type[0].toUpperCase()}${type.slice(1)} added: ${title}`);
|
|
1859
1888
|
}
|
|
1860
1889
|
else {
|
|
1861
1890
|
ctx.app.notify('Failed to add task');
|
|
@@ -1884,7 +1913,10 @@ Describe what this skill does. The agent reads this body verbatim when it invoke
|
|
|
1884
1913
|
const lines = [`## Tasks${projectName ? ` — ${projectName}` : ''}`, ''];
|
|
1885
1914
|
tasks.forEach((t, i) => {
|
|
1886
1915
|
const icon = TYPE_ICON[t.type] ?? '[task]';
|
|
1887
|
-
|
|
1916
|
+
// In a global listing (not scoped to one project) tag each row with its
|
|
1917
|
+
// project so a mixed list is legible — matches the macOS/web task rows.
|
|
1918
|
+
const proj = !projectName && t.project_name ? ` _(${t.project_name})_` : '';
|
|
1919
|
+
lines.push(`${i + 1}. ${icon} ${t.title}${proj}${t.description ? `\n ${t.description}` : ''}`);
|
|
1888
1920
|
});
|
|
1889
1921
|
lines.push('', `*${tasks.length} pending task${tasks.length > 1 ? 's' : ''}. Use /tasks done <n> to mark complete.*`);
|
|
1890
1922
|
lines.push('*Tasks loaded into agent context — agent will see them in the next message.*');
|
|
@@ -2034,9 +2066,12 @@ Describe what this skill does. The agent reads this body verbatim when it invoke
|
|
|
2034
2066
|
});
|
|
2035
2067
|
break;
|
|
2036
2068
|
}
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2069
|
+
// /stats — detailed session view: per-model breakdown, total, prompt-cache
|
|
2070
|
+
// summary, and the per-1M pricing reference. `/cost` is the concise sibling
|
|
2071
|
+
// (formatCostReport, above); the two are intentionally distinct, so this
|
|
2072
|
+
// case no longer also claims 'cost' (which always hit the handler above).
|
|
2073
|
+
case 'stats': {
|
|
2074
|
+
const { getCostBreakdown, getSessionStats, formatTokenCount, getPricingTable, getCacheStats } = await import('../utils/tokenTracker.js');
|
|
2040
2075
|
const stats = getSessionStats();
|
|
2041
2076
|
const lines = ['## Session Cost', ''];
|
|
2042
2077
|
if (stats.requestCount === 0) {
|
|
@@ -2064,6 +2099,19 @@ Describe what this skill does. The agent reads this body verbatim when it invoke
|
|
|
2064
2099
|
lines.push(`**Total: ~$${stats.estimatedCost.toFixed(4)}**`);
|
|
2065
2100
|
}
|
|
2066
2101
|
}
|
|
2102
|
+
// Prompt caching — parity with /cost (the 2.0.2 caching section was
|
|
2103
|
+
// only wired into formatCostReport). Shown only when caching landed.
|
|
2104
|
+
const cache = getCacheStats();
|
|
2105
|
+
if (cache.cacheReadTokens > 0 || cache.cacheCreationTokens > 0) {
|
|
2106
|
+
lines.push('', '### Prompt caching');
|
|
2107
|
+
lines.push(`Cache reads: ${formatTokenCount(cache.cacheReadTokens)} tokens (billed at 0.1× input rate)`);
|
|
2108
|
+
if (cache.cacheCreationTokens > 0) {
|
|
2109
|
+
lines.push(`Cache writes: ${formatTokenCount(cache.cacheCreationTokens)} tokens (billed at 1.25× input rate)`);
|
|
2110
|
+
}
|
|
2111
|
+
if (cache.estimatedSavingsUsd > 0) {
|
|
2112
|
+
lines.push(`Estimated savings vs no caching: $${cache.estimatedSavingsUsd.toFixed(4)}`);
|
|
2113
|
+
}
|
|
2114
|
+
}
|
|
2067
2115
|
lines.push('');
|
|
2068
2116
|
}
|
|
2069
2117
|
lines.push('### Pricing (per 1M tokens)');
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Help screen component
|
|
3
3
|
*/
|
|
4
|
-
import { Screen } from '../Screen';
|
|
5
4
|
export interface HelpCategory {
|
|
6
5
|
title: string;
|
|
7
6
|
items: Array<{
|
|
@@ -24,7 +23,3 @@ export declare const keyboardShortcuts: {
|
|
|
24
23
|
* Get total number of help pages
|
|
25
24
|
*/
|
|
26
25
|
export declare function getHelpTotalPages(screenHeight: number): number;
|
|
27
|
-
/**
|
|
28
|
-
* Render full help screen
|
|
29
|
-
*/
|
|
30
|
-
export declare function renderHelpScreen(screen: Screen, page?: number): void;
|