codeep 2.14.0 → 2.16.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.
Files changed (70) hide show
  1. package/README.md +47 -27
  2. package/dist/acp/commands.js +22 -1
  3. package/dist/acp/server.js +13 -2
  4. package/dist/acp/session.js +22 -1
  5. package/dist/config/index.d.ts +10 -0
  6. package/dist/config/index.js +2 -2
  7. package/dist/config/providers.js +35 -24
  8. package/dist/renderer/App.d.ts +77 -30
  9. package/dist/renderer/App.js +429 -659
  10. package/dist/renderer/agentExecution.d.ts +1 -0
  11. package/dist/renderer/agentExecution.js +3 -2
  12. package/dist/renderer/commands/helpers.d.ts +251 -0
  13. package/dist/renderer/commands/helpers.js +450 -0
  14. package/dist/renderer/commands/registry.js +7 -1
  15. package/dist/renderer/commands.d.ts +4 -0
  16. package/dist/renderer/commands.js +363 -318
  17. package/dist/renderer/components/ActionFormatting.d.ts +17 -0
  18. package/dist/renderer/components/ActionFormatting.js +67 -0
  19. package/dist/renderer/components/Autocomplete.d.ts +58 -0
  20. package/dist/renderer/components/Autocomplete.js +75 -0
  21. package/dist/renderer/components/Intro.d.ts +9 -0
  22. package/dist/renderer/components/Intro.js +5 -15
  23. package/dist/renderer/components/MessageFormatter.d.ts +96 -0
  24. package/dist/renderer/components/MessageFormatter.js +375 -0
  25. package/dist/renderer/components/Permission.d.ts +4 -0
  26. package/dist/renderer/components/Permission.js +1 -1
  27. package/dist/renderer/components/Status.d.ts +4 -0
  28. package/dist/renderer/components/Status.js +2 -3
  29. package/dist/renderer/components/WelcomeFormatter.d.ts +19 -0
  30. package/dist/renderer/components/WelcomeFormatter.js +79 -0
  31. package/dist/renderer/components/uiConstants.d.ts +8 -0
  32. package/dist/renderer/components/uiConstants.js +24 -0
  33. package/dist/renderer/inputParsing.d.ts +22 -0
  34. package/dist/renderer/inputParsing.js +28 -0
  35. package/dist/renderer/layout.d.ts +219 -0
  36. package/dist/renderer/layout.js +338 -0
  37. package/dist/renderer/main.d.ts +2 -1
  38. package/dist/renderer/main.js +79 -11
  39. package/dist/renderer/ollamaHint.d.ts +12 -0
  40. package/dist/renderer/ollamaHint.js +29 -0
  41. package/dist/utils/agentChat.js +23 -1
  42. package/dist/utils/codeepCloud.d.ts +54 -0
  43. package/dist/utils/codeepCloud.js +95 -0
  44. package/dist/utils/diffPreview.d.ts +31 -0
  45. package/dist/utils/diffPreview.js +102 -0
  46. package/dist/utils/export.d.ts +12 -0
  47. package/dist/utils/export.js +3 -3
  48. package/dist/utils/git.d.ts +28 -0
  49. package/dist/utils/git.js +111 -1
  50. package/dist/utils/hooks.d.ts +26 -0
  51. package/dist/utils/hooks.js +69 -1
  52. package/dist/utils/keychain.js +45 -29
  53. package/dist/utils/logger.d.ts +12 -0
  54. package/dist/utils/logger.js +1 -1
  55. package/dist/utils/mcpConfig.d.ts +26 -0
  56. package/dist/utils/mcpConfig.js +109 -4
  57. package/dist/utils/mentions.d.ts +195 -0
  58. package/dist/utils/mentions.js +672 -0
  59. package/dist/utils/skillBundles.d.ts +14 -0
  60. package/dist/utils/skillBundles.js +3 -3
  61. package/dist/utils/skillBundlesCloud.d.ts +7 -0
  62. package/dist/utils/skillBundlesCloud.js +1 -1
  63. package/dist/utils/tokenTracker.js +21 -5
  64. package/dist/utils/toolParsing.d.ts +11 -0
  65. package/dist/utils/toolParsing.js +6 -0
  66. package/dist/utils/webFetch.d.ts +101 -0
  67. package/dist/utils/webFetch.js +375 -0
  68. package/dist/version.d.ts +1 -1
  69. package/dist/version.js +1 -1
  70. package/package.json +2 -2
package/README.md CHANGED
@@ -23,27 +23,14 @@
23
23
  <a href="https://github.com/VladoIvankovic/Codeep"><img src="https://img.shields.io/github/stars/VladoIvankovic/Codeep?style=social" alt="GitHub stars"></a>
24
24
  </p>
25
25
 
26
- ## Upgrading from 1.x to 2.0
26
+ ## Upgrading
27
27
 
28
- Most users **do not need to do anything** — `npm install -g codeep@latest`
29
- picks up the new version and everything carries over (config, saved
30
- profiles, project memory, MCP servers, skill bundles).
31
-
32
- Two breaking changes only affect a small audience:
33
-
34
- 1. **MCP `clientInfo` version bumped from `1.4.0` → `2.0.0`.** If you
35
- maintain a custom MCP server that allowlists Codeep by version
36
- string, update your check.
37
- 2. **`McpServer` protocol shape** in ACP now has `command?`, `args?`,
38
- `url?`, `headers?` (transport-agnostic for stdio + Streamable HTTP).
39
- The old required-`command` shape is still accepted — parser handles
40
- both — but new clients should emit the optional shape.
41
-
42
- See [CHANGELOG.md](CHANGELOG.md#200--2026-05-18) for the full list of
43
- additions (full MCP support, OpenRouter provider with authoritative
44
- per-call cost, Claude-Code-compatible skill bundles + web marketplace,
45
- custom slash commands, lifecycle hooks, checkpoints, `/cost`,
46
- `/compact`, and more).
28
+ Running `npm install -g codeep@latest` picks up the new version — config,
29
+ saved profiles, project memory, MCP servers, and skill bundles all carry
30
+ over. Breaking changes (if any) are listed at the top of each release in
31
+ [CHANGELOG.md](CHANGELOG.md); the last breaking change was
32
+ [2.0.0](CHANGELOG.md#200--2026-05-18) (MCP `clientInfo` version bump,
33
+ transport-agnostic `McpServer` shape).
47
34
 
48
35
  ## Features
49
36
 
@@ -77,6 +64,7 @@ When started in a project directory, Codeep automatically:
77
64
  - **AI session titles** - Sessions auto-title themselves with an LLM one-liner ("OAuth2 migration for auth module") instead of a truncated first message. This makes one small background API call per session; turn it off with the `autoSessionTitle` setting (`/settings`) if you prefer zero unsolicited calls
78
65
  - **Search current session** - Find text in the open conversation with `/search`
79
66
  - **Cross-session recall** - `/recall <query>` searches across **all** saved sessions, ranked by relevance + recency. Add `--resume` to load the top match, or `--summarize` for an LLM recap of what you accomplished across matches
67
+ - **Cross-device resume (`/cloud`)** - Sessions you run on one device (CLI, Mac app, VS Code) sync to `codeep.dev`. `/cloud` lists them and pulls the selected one back locally, so you can start a session on your desktop and continue on your laptop. Run `codeep account` once to link your devices; scoped to the current project when one is open.
80
68
  - **Export** - Save to Markdown, JSON, or plain text
81
69
  - `/cost` - Per-session token usage and estimated cost (per provider/model)
82
70
  - `/compact [keepN]` - AI-summarize older messages to free up context (keeps last N, default 4)
@@ -101,15 +89,19 @@ When started in a project directory, Codeep automatically:
101
89
  - Press Enter to send, Escape to cancel
102
90
  - Works reliably in all terminals (no Ctrl+V issues)
103
91
 
104
- ### File Context (`/add`, `/drop`)
92
+ ### File Context (`/add`, `/drop`, `@mentions`)
105
93
  Explicitly add files to the conversation context:
106
94
 
107
95
  - **`/add <path>`** - Add one or more files to context
108
96
  - **`/add`** (no args) - Show currently added files
109
97
  - **`/drop <path>`** - Remove a specific file from context
110
98
  - **`/drop`** (no args) - Remove all files from context
99
+ - **`@path/to/file`** - Inline mention: type `@` followed by a file path anywhere in your message and its contents are attached for that message only. Supports relative paths (`@src/index.ts`), absolute (`@/etc/hosts`), home (`@~/.codeep/profile.md`), and quoted paths with spaces (`@"my file.ts"`). In the CLI, typing `@` opens an autocomplete file picker — ↑↓ to navigate, Tab to insert.
100
+ - **`@folder <path>`** (alias `@dir`) - Attach an entire directory: type `@folder src/components` and every source file under it is loaded recursively. Skips `node_modules`, `.git`, `dist`, binary extensions (images, archives, lockfiles), and files over the per-file size cap. Stops at a 200 KB total cap per mention (with a notification when it hits the limit). Quoted paths supported: `@folder "my components"`.
101
+ - **`@web <url>`** - Inline web fetch: type `@web https://example.com/docs` (or `@web example.com/docs`) and the page is fetched, converted to readable text, and attached to the message. HTML is stripped; plain text and JSON pass through as-is. Capped at 32 KB, 12 s timeout. Successful fetches are cached for 30 min (per session, up to 50 entries) so repeated `@web` for the same URL is instant and free; use `/web-cache clear` to reset.
102
+ - **`@git <ref>`** - Inline git context: type `@git diff`, `@git diff --staged`, `@git HEAD`, `@git <sha>`, `@git main:src/x.ts`, or `@git diff a..b` to inject that diff / commit / file-at-ref into the message as a `[Git ref]` block. Capped at 64 KB per mention (truncated with a marker). Unknown refs surface a friendly failure note.
111
103
 
112
- Added files are automatically attached to every message (both chat and agent mode) until dropped. Useful for giving the AI specific files to work with.
104
+ Added files (via `/add`) are automatically attached to every message (both chat and agent mode) until dropped. `@mentions` are per-message — handy for one-off references without polluting the persistent context.
113
105
 
114
106
  ```
115
107
  > /add src/utils/api.ts src/types/index.ts
@@ -118,6 +110,10 @@ Added 2 file(s) to context (2 total)
118
110
  > refactor the API client to use async/await
119
111
  # AI sees both files attached to your message
120
112
 
113
+ > review @src/auth/login.ts for security issues
114
+ Loaded 1 file(s) from @mentions
115
+ # AI sees login.ts attached for this message only
116
+
121
117
  > /drop
122
118
  Dropped all 2 file(s) from context
123
119
  ```
@@ -358,10 +354,11 @@ You write safe, reversible DB migrations…
358
354
  **Guaranteed review:** enable **Agent Auto-Review** in `/settings` (`agentAutoReview`) and after any run that changes files, Codeep automatically delegates to the `reviewer` and appends its findings — a review stage that always happens. Off by default.
359
355
 
360
356
  ### Project Rules
361
- Define project-specific instructions that the AI always follows. Create a rules file in your project root:
357
+ Define project-specific instructions that the AI always follows. Create a rules file in your project root (first non-empty file wins):
362
358
 
363
359
  - **`.codeep/rules.md`** - Primary location (inside `.codeep/` folder)
364
360
  - **`CODEEP.md`** - Alternative location (project root, similar to `CLAUDE.md` or `.cursorrules`)
361
+ - **`AGENTS.md`** - Cross-tool standard (works with Claude Code, Cursor, Kilo Code — no duplication needed)
365
362
 
366
363
  The rules file content is automatically included in every system prompt — both chat and agent mode. Use it to define:
367
364
 
@@ -469,11 +466,12 @@ Full spec coverage in 2.0.0 — stdio and Streamable HTTP transports,
469
466
  tools / resources / prompts / sampling, capability negotiation with
470
467
  `roots`, auto-restart on crash, mid-run catalog refresh.
471
468
 
472
- **Three ways to wire MCP servers** (all merge; project > global > ACP wins last):
469
+ **Four ways to wire MCP servers** (all merge; project > `.mcp.json` > global > ACP wins last):
473
470
 
474
471
  1. **Project config** — `.codeep/mcp_servers.json` (committed with the repo)
475
- 2. **Global config** — `~/.codeep/mcp_servers.json` (per machine)
476
- 3. **ACP client config** — Zed / Claude Desktop pass `mcpServers` on `session/new`
472
+ 2. **`.mcp.json`** — cross-tool standard at the workspace root (works with Claude Code, Cursor, Kilo Code — point everyone at the same file)
473
+ 3. **Global config** — `~/.codeep/mcp_servers.json` (per machine)
474
+ 4. **ACP client config** — Zed / Claude Desktop pass `mcpServers` on `session/new`
477
475
 
478
476
  Two transports per entry, mutually exclusive:
479
477
 
@@ -573,6 +571,26 @@ cloned repo's hooks are **not** run until you approve the workspace. Run
573
571
  own projects just need a one-time `/hooks trust`. Global `~/.codeep/hooks/` are
574
572
  never run for the same reason.
575
573
 
574
+ #### Windows notes
575
+
576
+ Lifecycle hooks are POSIX shell (`.sh`) scripts:
577
+
578
+ - **macOS / Linux** — run directly (via the script's shebang). No setup.
579
+ - **Windows** — a `.sh` needs a POSIX `sh`. Install [Git for Windows](https://git-scm.com/download/win)
580
+ (it ships `sh.exe`) and Codeep runs `.sh` hooks through it automatically — no
581
+ PATH tweaking needed if Git is installed to its default location. **Without a
582
+ POSIX shell**, `/hooks` and the welcome banner report an explicit
583
+ *"cannot run on this system"* state and hooks are skipped — they never block a
584
+ tool call or commit, and the rest of Codeep is unaffected. (`.ps1`/`.cmd` hooks
585
+ are not executed; keep hook logic in `.sh`.)
586
+
587
+ Everything else is **Node-native and shell-independent on Windows**: project
588
+ rules (`AGENTS.md` / `CODEEP.md` / `.codeep/rules.md`), custom slash commands
589
+ (`.codeep/commands/*.md`), and skill bundles (`.codeep/skills/`) are Markdown;
590
+ MCP config (`.mcp.json` / `.codeep/mcp_servers.json`) is JSON. MCP servers spawn
591
+ via their configured `command` — `npx` resolves through `npx.cmd`, and a custom
592
+ `command` like `python` just needs to be on your PATH.
593
+
576
594
  ### Skill Bundles (new in 2.0)
577
595
  Beyond the built-in skills and custom slash commands, Codeep now supports
578
596
  **structured skill bundles** — directory-based capability packs the agent
@@ -1024,6 +1042,7 @@ After installation, `codeep` is available globally in your terminal. Simply run
1024
1042
  |---------|-------------|
1025
1043
  | `/sessions` | List and load saved sessions |
1026
1044
  | `/sessions delete <name>` | Delete a specific session |
1045
+ | `/cloud` | List and resume sessions synced from other devices |
1027
1046
  | `/rename <name>` | Rename current session |
1028
1047
  | `/search <term>` | Search through chat history |
1029
1048
  | `/export` | Export chat to MD/JSON/TXT format |
@@ -1032,7 +1051,8 @@ After installation, `codeep` is available globally in your terminal. Simply run
1032
1051
 
1033
1052
  | Command | Description |
1034
1053
  |---------|-------------|
1035
- | `/apply` | Apply file changes from AI response |
1054
+ | `/apply` | Apply file changes from AI response (shows hunk counts; `--only file.ts:0,1` for selective apply, `--interactive`/`-i` for per-hunk review) |
1055
+ | `/web-cache` | Show `@web` fetch cache stats (alias `/web-cache clear` to reset) |
1036
1056
  | `/copy [n]` | Copy code block to clipboard (n = block number, -1 = last) |
1037
1057
  | `/paste` | Paste content from clipboard into chat |
1038
1058
  | `/add <path>` | Add file(s) to conversation context |
@@ -1023,8 +1023,29 @@ Anything else the agent should know — edge cases, gotchas, things to double-ch
1023
1023
  }
1024
1024
  case 'mcp': {
1025
1025
  const sub = args[0]?.toLowerCase();
1026
- const { addProjectMcpServer, removeProjectMcpServer, loadMcpServerConfig } = await import('../utils/mcpConfig.js');
1026
+ const { addProjectMcpServer, removeProjectMcpServer, loadMcpServerConfig, loadMcpServerConfigSplit, isWorkspaceMcpTrusted, trustWorkspaceMcp, untrustWorkspaceMcp } = await import('../utils/mcpConfig.js');
1027
1027
  const { registerSessionServers } = await import('../utils/mcpRegistry.js');
1028
+ if (sub === 'trust') {
1029
+ if (isWorkspaceMcpTrusted(session.workspaceRoot)) {
1030
+ return { handled: true, response: '_Workspace MCP servers are already trusted here._' };
1031
+ }
1032
+ trustWorkspaceMcp(session.workspaceRoot);
1033
+ const { workspace } = loadMcpServerConfigSplit(session.workspaceRoot);
1034
+ if (workspace.length === 0) {
1035
+ return { handled: true, response: '_Workspace trusted — no workspace MCP servers defined yet._' };
1036
+ }
1037
+ onChunk(`_Workspace trusted. Spawning ${workspace.length} MCP server(s)…_\n\n`);
1038
+ const merged = loadMcpServerConfig(session.workspaceRoot);
1039
+ const { registered, errors } = await registerSessionServers(session.sessionId, merged, { workspaceRoot: session.workspaceRoot });
1040
+ const lines = [`Trusted workspace MCP servers (${registered.length} tool(s) available).`];
1041
+ for (const e of errors)
1042
+ lines.push(`- \`${e.server}\` failed: ${e.error}`);
1043
+ return { handled: true, response: lines.join('\n') };
1044
+ }
1045
+ if (sub === 'untrust') {
1046
+ untrustWorkspaceMcp(session.workspaceRoot);
1047
+ return { handled: true, response: '_Workspace MCP trust revoked — workspace servers won\'t spawn for new sessions._' };
1048
+ }
1028
1049
  if (sub === 'add') {
1029
1050
  // /mcp add <name> <command> [args...]
1030
1051
  const name = args[1];
@@ -8,7 +8,7 @@ import { StdioTransport } from './transport.js';
8
8
  import { runAgentSession } from './session.js';
9
9
  import { loadCustomCommands } from '../utils/customCommands.js';
10
10
  import { registerSessionServers, disposeAllSessions as disposeAllMcpSessions } from '../utils/mcpRegistry.js';
11
- import { loadMcpServerConfig, mergeMcpServers } from '../utils/mcpConfig.js';
11
+ import { loadMcpServerConfigSplit, isWorkspaceMcpTrusted, mergeMcpServers } from '../utils/mcpConfig.js';
12
12
  import { handleMcpSamplingRequest } from '../utils/mcpSamplingBridge.js';
13
13
  import { executeCommandAsync } from '../utils/shell.js';
14
14
  import { initWorkspace, loadWorkspace, handleCommand } from './commands.js';
@@ -539,7 +539,18 @@ export function startAcpServer() {
539
539
  * in-flight registration so tool calls don't race the startup.
540
540
  */
541
541
  function spawnMcpServersForSession(acpSessionId, cwd, acpServers, label) {
542
- const fromConfig = loadMcpServerConfig(cwd);
542
+ // Workspace-sourced servers (.codeep/mcp_servers.json, .mcp.json) travel
543
+ // with the repo, so they spawn only for workspaces the user has trusted
544
+ // (same gate the TUI prompts for at startup). ACP-provided servers are
545
+ // the editor's own config and global ~/.codeep entries are the user's —
546
+ // both spawn unconditionally.
547
+ const { global: globalServers, workspace: workspaceServers } = loadMcpServerConfigSplit(cwd);
548
+ const workspaceTrusted = isWorkspaceMcpTrusted(cwd);
549
+ if (workspaceServers.length > 0 && !workspaceTrusted) {
550
+ process.stderr.write(`[codeep-acp] MCP (${label}): skipped ${workspaceServers.length} workspace server(s) — untrusted workspace. ` +
551
+ `Run /mcp trust (or \`codeep\` in the repo once) to enable.\n`);
552
+ }
553
+ const fromConfig = workspaceTrusted ? [...globalServers, ...workspaceServers] : globalServers;
543
554
  const merged = mergeMcpServers(fromConfig, acpServers);
544
555
  if (merged.length === 0)
545
556
  return;
@@ -87,11 +87,32 @@ export function buildRawOutput(toolName, params, toolResult) {
87
87
  }
88
88
  export async function runAgentSession(opts) {
89
89
  const projectContext = buildProjectContext(opts.workspaceRoot);
90
+ // Expand `@folder`/`@file`/`@git` mentions in the user prompt, the
91
+ // same way the TUI does (see src/renderer/main.ts). ACP clients
92
+ // (VS Code / Zed) get the same inline-context UX. `@web` is async
93
+ // and fetched here too.
94
+ let enrichedPrompt = opts.prompt;
95
+ try {
96
+ const { expandFileAndFolderMentions, expandGitMentions } = await import('../utils/mentions.js');
97
+ const { expandWebMentions } = await import('../utils/webFetch.js');
98
+ const fileResult = expandFileAndFolderMentions(enrichedPrompt, { root: opts.workspaceRoot });
99
+ const gitResult = await expandGitMentions(fileResult.enrichedPrompt, { root: opts.workspaceRoot });
100
+ const webResult = await expandWebMentions(gitResult.enrichedPrompt);
101
+ enrichedPrompt = webResult.enrichedPrompt;
102
+ // Surface failures as thoughts so the editor shows them.
103
+ const allFailures = [...fileResult.failures, ...gitResult.failures, ...webResult.failures];
104
+ if (allFailures.length > 0 && opts.onThought) {
105
+ opts.onThought(allFailures.map((f) => `${f.mention}: ${f.reason}`).join(' · '));
106
+ }
107
+ }
108
+ catch {
109
+ // Mention expansion is best-effort — never block the agent run.
110
+ }
90
111
  let toolCallCounter = 0;
91
112
  // Maps tool call key → ACP toolCallId so onToolResult can emit finished/error status
92
113
  const toolCallIdMap = new Map();
93
114
  let chunksEmitted = 0;
94
- const result = await runAgent(opts.prompt, projectContext, {
115
+ const result = await runAgent(enrichedPrompt, projectContext, {
95
116
  abortSignal: opts.abortSignal,
96
117
  onChunk: (text) => { chunksEmitted++; opts.onChunk(text); },
97
118
  onIteration: (_iteration, message) => {
@@ -140,6 +140,16 @@ export interface ConfigSchema {
140
140
  }
141
141
  export type { AgentMode };
142
142
  export type { LanguageCode };
143
+ /**
144
+ * Get sessions directory - local .codeep/sessions/ if in project, otherwise global
145
+ */
146
+ export declare function getSessionsDir(projectPath?: string): string;
147
+ /**
148
+ * Check if directory is a project
149
+ * Looks for common project indicators: package.json, pyproject.toml, Cargo.toml, go.mod, composer.json, etc.
150
+ * Also checks if user has manually initialized this folder as a project (.codeep/project.json)
151
+ */
152
+ export declare function isProjectDirectory(path: string): boolean;
143
153
  /**
144
154
  * Check if directory has standard project markers (not manually initialized)
145
155
  */
@@ -9,7 +9,7 @@ import { createSecureStorage } from '../utils/keychain.js';
9
9
  /**
10
10
  * Get sessions directory - local .codeep/sessions/ if in project, otherwise global
11
11
  */
12
- function getSessionsDir(projectPath) {
12
+ export function getSessionsDir(projectPath) {
13
13
  if (projectPath && isProjectDirectory(projectPath)) {
14
14
  const localDir = join(projectPath, '.codeep', 'sessions');
15
15
  if (!existsSync(localDir)) {
@@ -36,7 +36,7 @@ function getLocalConfigPath(projectPath) {
36
36
  * Looks for common project indicators: package.json, pyproject.toml, Cargo.toml, go.mod, composer.json, etc.
37
37
  * Also checks if user has manually initialized this folder as a project (.codeep/project.json)
38
38
  */
39
- function isProjectDirectory(path) {
39
+ export function isProjectDirectory(path) {
40
40
  // Check if user has manually initialized this as a project
41
41
  const manualProjectMarker = join(path, '.codeep', 'project.json');
42
42
  if (existsSync(manualProjectMarker)) {
@@ -232,18 +232,21 @@ export const PROVIDERS = {
232
232
  openai: { baseUrl: 'https://api.moonshot.ai/v1', authHeader: 'Bearer', supportsNativeTools: true },
233
233
  },
234
234
  models: [
235
- { id: 'kimi-k2.7-code', name: 'Kimi K2.7 Code', description: 'Flagship agentic coding model (256K context)' },
235
+ { id: 'kimi-k3-code', name: 'Kimi K3 Code', description: 'Newest flagship agentic coding model (1M context, deep reasoning)' },
236
+ { id: 'kimi-k3-code-highspeed', name: 'Kimi K3 Code (High-Speed)', description: 'Throughput-tuned K3 Code for latency-sensitive loops' },
237
+ { id: 'kimi-k3-thinking', name: 'Kimi K3 Thinking', description: 'K3 with explicit reasoning traces (highest quality, slower)' },
238
+ { id: 'kimi-k2.7-code', name: 'Kimi K2.7 Code', description: 'Previous-gen flagship agentic coding model (256K context)' },
236
239
  { id: 'kimi-k2.7-code-highspeed', name: 'Kimi K2.7 Code (High-Speed)', description: 'Throughput-tuned K2.7 Code for latency-sensitive loops' },
237
240
  { id: 'kimi-k2.6', name: 'Kimi K2.6', description: 'Previous-gen multimodal reasoning model' },
238
241
  { id: 'kimi-k2.5', name: 'Kimi K2.5', description: 'Older general-purpose model (cheaper)' },
239
242
  ],
240
- defaultModel: 'kimi-k2.7-code',
243
+ defaultModel: 'kimi-k3-code',
241
244
  defaultProtocol: 'openai',
242
- maxOutputTokens: 32_768,
245
+ maxOutputTokens: 65_536,
243
246
  envKey: 'MOONSHOT_API_KEY',
244
247
  subscribeUrl: 'https://platform.kimi.ai/console/api-keys',
245
248
  groupLabel: 'Kimi — API (pay-per-use)',
246
- hint: 'Pay-per-use via Moonshot API key (platform.kimi.ai).',
249
+ hint: 'Pay-per-use via Moonshot API key (platform.kimi.ai). K3 models support 1M context and explicit reasoning.',
247
250
  },
248
251
  'kimi-cn': {
249
252
  name: 'Kimi China (Moonshot)',
@@ -252,18 +255,21 @@ export const PROVIDERS = {
252
255
  openai: { baseUrl: 'https://api.moonshot.cn/v1', authHeader: 'Bearer', supportsNativeTools: true },
253
256
  },
254
257
  models: [
255
- { id: 'kimi-k2.7-code', name: 'Kimi K2.7 Code', description: 'Flagship agentic coding model (256K context)' },
258
+ { id: 'kimi-k3-code', name: 'Kimi K3 Code', description: 'Newest flagship agentic coding model (1M context, deep reasoning)' },
259
+ { id: 'kimi-k3-code-highspeed', name: 'Kimi K3 Code (High-Speed)', description: 'Throughput-tuned K3 Code' },
260
+ { id: 'kimi-k3-thinking', name: 'Kimi K3 Thinking', description: 'K3 with explicit reasoning traces' },
261
+ { id: 'kimi-k2.7-code', name: 'Kimi K2.7 Code', description: 'Previous-gen flagship agentic coding model (256K context)' },
256
262
  { id: 'kimi-k2.7-code-highspeed', name: 'Kimi K2.7 Code (High-Speed)', description: 'Throughput-tuned K2.7 Code' },
257
263
  { id: 'kimi-k2.6', name: 'Kimi K2.6', description: 'Previous-gen multimodal reasoning model' },
258
264
  { id: 'kimi-k2.5', name: 'Kimi K2.5', description: 'Older general-purpose model' },
259
265
  ],
260
- defaultModel: 'kimi-k2.7-code',
266
+ defaultModel: 'kimi-k3-code',
261
267
  defaultProtocol: 'openai',
262
- maxOutputTokens: 32_768,
268
+ maxOutputTokens: 65_536,
263
269
  envKey: 'MOONSHOT_CN_API_KEY',
264
270
  subscribeUrl: 'https://platform.moonshot.cn/console/api-keys',
265
271
  groupLabel: 'Kimi China — API (pay-per-use)',
266
- hint: 'Pay-per-use via Moonshot China API key (platform.moonshot.cn).',
272
+ hint: 'Pay-per-use via Moonshot China API key (platform.moonshot.cn). K3 models support 1M context.',
267
273
  },
268
274
  // ── Grok (xAI) ────────────────────────────────────────────────────
269
275
  // Pay-per-use today (console.x.ai key). The SuperGrok / X Premium+
@@ -276,8 +282,9 @@ export const PROVIDERS = {
276
282
  openai: { baseUrl: 'https://api.x.ai/v1', authHeader: 'Bearer', supportsNativeTools: true },
277
283
  },
278
284
  models: [
285
+ { id: 'grok-4.5', name: 'Grok 4.5', description: 'Flagship reasoning model — highest quality, 500K context' },
279
286
  { id: 'grok-build-0.1', name: 'Grok Build 0.1', description: 'Agentic coding model — fast, 256K context' },
280
- { id: 'grok-4.3', name: 'Grok 4.3', description: 'Flagship — highest quality, 1M context' },
287
+ { id: 'grok-4.3', name: 'Grok 4.3', description: 'Previous flagship, 1M context' },
281
288
  { id: 'grok-code-fast-1', name: 'Grok Code Fast 1', description: 'Low-cost speed-first coder (alias of Build 0.1)' },
282
289
  { id: 'grok-4-fast-reasoning', name: 'Grok 4 Fast (reasoning)', description: 'Cheap reasoning model, very large context' },
283
290
  ],
@@ -302,7 +309,7 @@ export const PROVIDERS = {
302
309
  models: [
303
310
  { id: 'qwen3-coder-plus', name: 'Qwen3-Coder Plus', description: 'Flagship coding model — best quality' },
304
311
  { id: 'qwen3-coder-next', name: 'Qwen3-Coder Next', description: 'Balanced quality/speed/cost' },
305
- { id: 'qwen3-max', name: 'Qwen3-Max', description: 'Flagship general model (code + reasoning)' },
312
+ { id: 'qwen3.7-max', name: 'Qwen3.7-Max', description: 'Flagship general model (code + reasoning)' },
306
313
  ],
307
314
  defaultModel: 'qwen3-coder-plus',
308
315
  defaultProtocol: 'openai',
@@ -323,7 +330,7 @@ export const PROVIDERS = {
323
330
  { id: 'qwen3-coder-plus', name: 'Qwen3-Coder Plus', description: 'Flagship coding model (256K, up to 1M)' },
324
331
  { id: 'qwen3-coder-next', name: 'Qwen3-Coder Next', description: 'Balanced quality/speed/cost' },
325
332
  { id: 'qwen3-coder-flash', name: 'Qwen3-Coder Flash', description: 'Fast/cheap coder' },
326
- { id: 'qwen3-max', name: 'Qwen3-Max', description: 'Flagship general model' },
333
+ { id: 'qwen3.7-max', name: 'Qwen3.7-Max', description: 'Flagship general model' },
327
334
  ],
328
335
  defaultModel: 'qwen3-coder-plus',
329
336
  defaultProtocol: 'openai',
@@ -343,7 +350,7 @@ export const PROVIDERS = {
343
350
  models: [
344
351
  { id: 'qwen3-coder-plus', name: 'Qwen3-Coder Plus', description: 'Flagship coding model — best quality' },
345
352
  { id: 'qwen3-coder-next', name: 'Qwen3-Coder Next', description: 'Balanced quality/speed/cost' },
346
- { id: 'qwen3-max', name: 'Qwen3-Max', description: 'Flagship general model' },
353
+ { id: 'qwen3.7-max', name: 'Qwen3.7-Max', description: 'Flagship general model' },
347
354
  ],
348
355
  defaultModel: 'qwen3-coder-plus',
349
356
  defaultProtocol: 'openai',
@@ -364,7 +371,7 @@ export const PROVIDERS = {
364
371
  { id: 'qwen3-coder-plus', name: 'Qwen3-Coder Plus', description: 'Flagship coding model' },
365
372
  { id: 'qwen3-coder-next', name: 'Qwen3-Coder Next', description: 'Balanced quality/speed/cost' },
366
373
  { id: 'qwen3-coder-flash', name: 'Qwen3-Coder Flash', description: 'Fast/cheap coder' },
367
- { id: 'qwen3-max', name: 'Qwen3-Max', description: 'Flagship general model' },
374
+ { id: 'qwen3.7-max', name: 'Qwen3.7-Max', description: 'Flagship general model' },
368
375
  ],
369
376
  defaultModel: 'qwen3-coder-plus',
370
377
  defaultProtocol: 'openai',
@@ -404,11 +411,14 @@ export const PROVIDERS = {
404
411
  },
405
412
  },
406
413
  models: [
407
- { id: 'gpt-5.5', name: 'GPT-5.5', description: 'Latest GPT model — best for coding' },
408
- { id: 'gpt-5.4', name: 'GPT-5.4', description: 'Previous generation GPT' },
414
+ { id: 'gpt-5.6-sol', name: 'GPT-5.6 Sol', description: 'Most capable GPT — best for coding & agentic work' },
415
+ { id: 'gpt-5.6-terra', name: 'GPT-5.6 Terra', description: 'Balanced — GPT-5.5 quality at about half the price' },
416
+ { id: 'gpt-5.6-luna', name: 'GPT-5.6 Luna', description: 'Fast and cheap — high-volume workloads' },
417
+ { id: 'gpt-5.5', name: 'GPT-5.5', description: 'Previous flagship GPT' },
418
+ { id: 'gpt-5.4', name: 'GPT-5.4', description: 'Older generation GPT' },
409
419
  { id: 'gpt-5.4-mini', name: 'GPT-5.4 Mini', description: 'Faster and cheaper GPT-5.4' },
410
420
  ],
411
- defaultModel: 'gpt-5.5',
421
+ defaultModel: 'gpt-5.6-sol',
412
422
  defaultProtocol: 'openai',
413
423
  useMaxCompletionTokens: true,
414
424
  requiresDefaultTemperature: true,
@@ -429,11 +439,11 @@ export const PROVIDERS = {
429
439
  },
430
440
  models: [
431
441
  { id: 'claude-fable-5', name: 'Claude Fable 5', description: 'Most capable — hardest reasoning & long-horizon agentic work' },
432
- { id: 'claude-opus-4-8', name: 'Claude Opus 4.8', description: 'Most capable Opus model' },
442
+ { id: 'claude-opus-5', name: 'Claude Opus 5', description: 'Complex agentic coding & deep reasoning — the Opus workhorse' },
433
443
  { id: 'claude-sonnet-5', name: 'Claude Sonnet 5', description: 'Best balance of speed and intelligence' },
434
444
  { id: 'claude-haiku-4-5-20251001', name: 'Claude Haiku', description: 'Fastest and most affordable' },
435
445
  ],
436
- defaultModel: 'claude-opus-4-8',
446
+ defaultModel: 'claude-opus-5',
437
447
  defaultProtocol: 'anthropic',
438
448
  envKey: 'ANTHROPIC_API_KEY',
439
449
  groupLabel: 'Anthropic',
@@ -452,6 +462,7 @@ export const PROVIDERS = {
452
462
  models: [
453
463
  { id: 'gemini-3.1-pro-preview', name: 'Gemini 3.1 Pro', description: 'Most capable Gemini model' },
454
464
  { id: 'gemini-3.5-flash', name: 'Gemini 3.5 Flash', description: 'Pro-level intelligence at Flash speed' },
465
+ { id: 'gemini-3.1-flash-lite', name: 'Gemini 3.1 Flash-Lite', description: 'Low-latency, low-cost workhorse (1M context)' },
455
466
  ],
456
467
  defaultModel: 'gemini-3.1-pro-preview',
457
468
  defaultProtocol: 'openai',
@@ -479,7 +490,7 @@ export const PROVIDERS = {
479
490
  { id: 'anthropic/claude-fable-5', name: 'Claude Fable 5', description: 'Anthropic — most capable' },
480
491
  { id: 'anthropic/claude-opus-4', name: 'Claude Opus 4', description: 'Anthropic — Opus tier' },
481
492
  { id: 'anthropic/claude-sonnet-5', name: 'Claude Sonnet 5', description: 'Anthropic — balanced' },
482
- { id: 'openai/gpt-5.5', name: 'GPT-5.5', description: 'OpenAI — flagship' },
493
+ { id: 'openai/gpt-5.6-sol', name: 'GPT-5.6 Sol', description: 'OpenAI — flagship' },
483
494
  { id: 'openai/gpt-5.4-mini', name: 'GPT-5.4 Mini', description: 'OpenAI — fast/cheap' },
484
495
  { id: 'google/gemini-3.1-pro', name: 'Gemini 3.1 Pro', description: 'Google — multimodal' },
485
496
  { id: 'meta-llama/llama-3.1-405b-instruct', name: 'Llama 3.1 405B', description: 'Meta — open weights, largest' },
@@ -487,7 +498,7 @@ export const PROVIDERS = {
487
498
  { id: 'deepseek/deepseek-v4', name: 'DeepSeek V4', description: 'DeepSeek via OpenRouter' },
488
499
  { id: 'mistralai/mistral-large', name: 'Mistral Large', description: 'Mistral — flagship' },
489
500
  { id: 'qwen/qwen-2.5-coder-32b-instruct', name: 'Qwen 2.5 Coder 32B', description: 'Alibaba — coding-tuned' },
490
- { id: 'x-ai/grok-2', name: 'Grok 2', description: 'xAI via OpenRouter' },
501
+ { id: 'x-ai/grok-4.5', name: 'Grok 4.5', description: 'xAI — flagship reasoning' },
491
502
  ],
492
503
  defaultModel: 'anthropic/claude-opus-4',
493
504
  defaultProtocol: 'openai',
@@ -661,8 +672,8 @@ export function providerNoStreamWithTools(providerId) {
661
672
  * internally and 400 on any custom value, so they're here too.
662
673
  */
663
674
  const SAMPLING_PARAMS_REJECTED = [
664
- 'claude-fable-5', 'claude-opus-4-8', 'claude-opus-4-7', 'claude-sonnet-5',
665
- 'kimi-k2.7-code', 'kimi-for-coding',
675
+ 'claude-fable-5', 'claude-opus-5', 'claude-opus-4-8', 'claude-opus-4-7', 'claude-sonnet-5',
676
+ 'kimi-k3-code', 'kimi-k3-thinking', 'kimi-k2.7-code', 'kimi-for-coding',
666
677
  ];
667
678
  export function modelRejectsSamplingParams(model) {
668
679
  return SAMPLING_PARAMS_REJECTED.some(id => model === id || model.startsWith(`${id}-`));
@@ -704,10 +715,10 @@ export function modelSupportsReasoningEffort(providerId, model) {
704
715
  const id = canonicalModelId(model);
705
716
  switch (providerId) {
706
717
  case 'anthropic':
707
- // Effort is GA on Opus 4.5+, Sonnet 4.6/5, Fable 5 — NOT Haiku or Sonnet 4.5.
718
+ // Effort is GA on Opus 5, Opus 4.5+, Sonnet 4.6/5, Fable 5 — NOT Haiku or Sonnet 4.5.
708
719
  if (idMatches(id, 'claude-haiku-4-5') || idMatches(id, 'claude-sonnet-4-5'))
709
720
  return false;
710
- return /^claude-(opus-4-([5-9]|\d\d)|sonnet-(4-6|5)|fable-5)/.test(id);
721
+ return /^claude-(opus-5|opus-4-([5-9]|\d\d)|sonnet-(4-6|5)|fable-5)/.test(id);
711
722
  case 'openai':
712
723
  // GPT-5.x are reasoning models — reasoning_effort across the family (incl. mini).
713
724
  return id.startsWith('gpt-5');
@@ -20,6 +20,37 @@ export interface ConfirmOptions {
20
20
  onConfirm: () => void;
21
21
  onCancel?: () => void;
22
22
  }
23
+ /**
24
+ * One hunk in the interactive `/apply --interactive` picker.
25
+ * `lines` are already-formatted diff lines (e.g. `+ added`, `- removed`).
26
+ */
27
+ export interface HunkPickerItem {
28
+ /** File path this hunk belongs to. */
29
+ path: string;
30
+ /** 0-based hunk index within the file diff. */
31
+ hunkIndex: number;
32
+ /** Human-readable hunk header, e.g. `@@ -12,3 +12,5 @@`. */
33
+ header: string;
34
+ /** Pre-formatted diff lines to display. */
35
+ lines: string[];
36
+ }
37
+ /**
38
+ * Options for the interactive hunk picker. The picker walks the user
39
+ * through `items` one at a time; for each they accept (`y`/Enter) or
40
+ * skip (`n`). `a` accepts all remaining, `q`/Esc quits.
41
+ *
42
+ * `onComplete` fires once with the set of accepted `[path, hunkIndex]`
43
+ * pairs (possibly empty) so the caller can apply them via
44
+ * `applyHunksToFiles`.
45
+ */
46
+ export interface HunkPickerOptions {
47
+ title: string;
48
+ items: HunkPickerItem[];
49
+ onComplete: (accepted: Array<{
50
+ path: string;
51
+ hunkIndex: number;
52
+ }>) => void;
53
+ }
23
54
  export interface AppOptions {
24
55
  onSubmit: (message: string) => Promise<void>;
25
56
  onCommand: (command: string, args: string[]) => void;
@@ -29,6 +60,8 @@ export interface AppOptions {
29
60
  getStatus: () => StatusInfo;
30
61
  hasWriteAccess?: () => boolean;
31
62
  hasProjectContext?: () => boolean;
63
+ /** Project root for `@mention` autocomplete suggestions. Falls back to cwd. */
64
+ getProjectRoot?: () => string;
32
65
  }
33
66
  export declare class App {
34
67
  private screen;
@@ -67,9 +100,19 @@ export declare class App {
67
100
  private showAutocomplete;
68
101
  private autocompleteIndex;
69
102
  private autocompleteItems;
103
+ private showMentionAutocomplete;
104
+ private mentionIndex;
105
+ private mentionItems;
106
+ private mentionAtStart;
107
+ /** Project root for resolving `suggestMentions`. Cached per update. */
108
+ private mentionRoot;
70
109
  private confirmOpen;
71
110
  private confirmOptions;
72
111
  private confirmSelection;
112
+ private hunkPickerOpen;
113
+ private hunkPickerOptions;
114
+ private hunkPickerIndex;
115
+ private hunkPickerAccepted;
73
116
  private menuOpen;
74
117
  private menuTitle;
75
118
  /** Filtered view shown to the user; derived from `menuItemsAll` + `menuFilter`. */
@@ -231,6 +274,11 @@ export declare class App {
231
274
  * Show confirmation dialog
232
275
  */
233
276
  showConfirm(options: ConfirmOptions): void;
277
+ /**
278
+ * Show the interactive hunk picker (`/apply --interactive`).
279
+ * The caller passes pre-built items + an `onComplete` callback.
280
+ */
281
+ showHunkPicker(options: HunkPickerOptions): void;
234
282
  /**
235
283
  * Show permission dialog (inline, below status bar)
236
284
  */
@@ -314,6 +362,13 @@ export declare class App {
314
362
  * Update autocomplete suggestions
315
363
  */
316
364
  private updateAutocomplete;
365
+ /**
366
+ * Replace the in-progress `@query` (from `mentionAtStart` to the
367
+ * cursor) with the selected mention's path. Keeps the `@` prefix and
368
+ * positions the cursor right after the inserted path so the user can
369
+ * keep typing the rest of the message.
370
+ */
371
+ private applyMentionSelection;
317
372
  /**
318
373
  * Handle inline status keys
319
374
  */
@@ -352,6 +407,15 @@ export declare class App {
352
407
  private handleInlinePermissionKey;
353
408
  private handleInlineSessionPickerKey;
354
409
  private handleInlineConfirmKey;
410
+ /**
411
+ * Handle keys in the interactive hunk picker.
412
+ * y / Enter / → accept this hunk, advance
413
+ * n / ← skip this hunk, advance
414
+ * a accept this + all remaining, finish
415
+ * q / Esc finish without accepting this hunk
416
+ * ↑ / ↓ navigate (preview only — no decision)
417
+ */
418
+ private handleHunkPickerKey;
355
419
  /**
356
420
  * Submit the current input buffer (used by Enter and Escape-in-multiline)
357
421
  */
@@ -373,6 +437,11 @@ export declare class App {
373
437
  * Render inline confirmation dialog below status bar
374
438
  */
375
439
  private renderInlineConfirm;
440
+ /**
441
+ * Render inline hunk picker (`/apply --interactive`).
442
+ * Shows the current hunk's diff + the y/n/a/q key legend.
443
+ */
444
+ private renderInlineHunkPicker;
376
445
  /**
377
446
  * Render input line
378
447
  */
@@ -394,6 +463,14 @@ export declare class App {
394
463
  * Render inline autocomplete below status bar
395
464
  */
396
465
  private renderInlineAutocomplete;
466
+ /**
467
+ * Render inline `@mention` file picker below the status bar.
468
+ *
469
+ * Mirrors the layout of `renderInlineAutocomplete` (separator → title →
470
+ * items → footer) but shows file paths with their parent directory as
471
+ * the description, and a `@` prefix instead of `/`.
472
+ */
473
+ private renderInlineMentionPicker;
397
474
  /**
398
475
  * Render inline permission dialog
399
476
  */
@@ -413,15 +490,6 @@ export declare class App {
413
490
  /**
414
491
  * Get color for action type
415
492
  */
416
- private getActionColor;
417
- /**
418
- * Format action target for display
419
- */
420
- private formatActionTarget;
421
- /**
422
- * Get action label for display
423
- */
424
- private getActionLabel;
425
493
  /**
426
494
  * Render status bar
427
495
  */
@@ -430,23 +498,6 @@ export declare class App {
430
498
  * Get visible messages (including streaming)
431
499
  */
432
500
  private getVisibleMessages;
433
- /**
434
- * Format message into lines with syntax highlighting for code blocks
435
- */
436
- private formatWelcomeMessage;
437
- private formatMessage;
438
- /**
439
- * Apply inline markdown formatting (bold, italic, inline code) to a line
440
- */
441
- private applyInlineMarkdown;
442
- /**
443
- * Format plain text lines with markdown support
444
- */
445
- private formatTextLines;
446
- /**
447
- * Format code block with syntax highlighting (no border)
448
- */
449
- private formatCodeBlock;
450
501
  /**
451
502
  * Render inline search screen
452
503
  */
@@ -471,8 +522,4 @@ export declare class App {
471
522
  * Get decrypted logo for intro animation
472
523
  */
473
524
  private getDecryptedLogo;
474
- /**
475
- * Word wrap
476
- */
477
- private wordWrap;
478
525
  }