codeep 1.2.19 → 1.2.20

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 CHANGED
@@ -764,3 +764,37 @@ Contributions are welcome! Please open an issue or submit a pull request on [Git
764
764
  ## Support
765
765
 
766
766
  - **Issues**: [GitHub Issues](https://github.com/VladoIvankovic/Codeep/issues)
767
+
768
+ ## Zed Editor Integration (ACP)
769
+
770
+ Codeep supports the [Agent Client Protocol (ACP)](https://agentclientprotocol.com), letting you use it as an AI coding agent directly inside [Zed](https://zed.dev).
771
+
772
+ ### Setup
773
+
774
+ 1. Install Codeep globally:
775
+
776
+ ```bash
777
+ npm install -g codeep
778
+ ```
779
+
780
+ 2. Add to your Zed `settings.json`:
781
+
782
+ ```json
783
+ {
784
+ "agent_servers": {
785
+ "Codeep": {
786
+ "type": "custom",
787
+ "command": "codeep",
788
+ "args": ["acp"]
789
+ }
790
+ }
791
+ }
792
+ ```
793
+
794
+ 3. Make sure your API key is set in the environment Zed uses:
795
+
796
+ ```bash
797
+ export DEEPSEEK_API_KEY=your_key # or whichever provider
798
+ ```
799
+
800
+ 4. Open Zed's AI panel and select **Codeep** as the agent.
@@ -0,0 +1,54 @@
1
+ export interface JsonRpcRequest {
2
+ jsonrpc: '2.0';
3
+ id: number | string;
4
+ method: string;
5
+ params?: unknown;
6
+ }
7
+ export interface JsonRpcResponse {
8
+ jsonrpc: '2.0';
9
+ id: number | string;
10
+ result?: unknown;
11
+ error?: {
12
+ code: number;
13
+ message: string;
14
+ };
15
+ }
16
+ export interface JsonRpcNotification {
17
+ jsonrpc: '2.0';
18
+ method: string;
19
+ params?: unknown;
20
+ }
21
+ export interface InitializeParams {
22
+ capabilities?: Record<string, unknown>;
23
+ workspaceFolders?: {
24
+ uri: string;
25
+ name: string;
26
+ }[];
27
+ }
28
+ export interface InitializeResult {
29
+ capabilities: {
30
+ streaming?: boolean;
31
+ fileEditing?: boolean;
32
+ };
33
+ serverInfo: {
34
+ name: string;
35
+ version: string;
36
+ };
37
+ }
38
+ export interface AgentRunParams {
39
+ prompt: string;
40
+ workspaceRoot?: string;
41
+ conversationId?: string;
42
+ }
43
+ export interface AgentStreamParams {
44
+ conversationId: string;
45
+ text: string;
46
+ done?: boolean;
47
+ }
48
+ export interface ApplyEditParams {
49
+ changes: {
50
+ uri: string;
51
+ newText: string;
52
+ }[];
53
+ }
54
+ export type AcpMessage = JsonRpcRequest | JsonRpcResponse | JsonRpcNotification;
@@ -0,0 +1,3 @@
1
+ // acp/protocol.ts
2
+ // ACP JSON-RPC message types for Zed Agent Client Protocol
3
+ export {};
@@ -0,0 +1 @@
1
+ export declare function startAcpServer(): Promise<void>;
@@ -0,0 +1,81 @@
1
+ // src/acp/server.ts
2
+ // Codeep ACP adapter — started via `codeep acp` CLI subcommand
3
+ import { StdioTransport } from './transport.js';
4
+ import { runAgentSession } from './session.js';
5
+ export function startAcpServer() {
6
+ const transport = new StdioTransport();
7
+ const activeSessions = new Map();
8
+ transport.start((msg) => {
9
+ switch (msg.method) {
10
+ case 'initialize':
11
+ handleInitialize(msg);
12
+ break;
13
+ case 'initialized':
14
+ // no-op acknowledgment
15
+ break;
16
+ case 'agent/run':
17
+ handleAgentRun(msg);
18
+ break;
19
+ case 'agent/cancel':
20
+ handleAgentCancel(msg);
21
+ break;
22
+ default:
23
+ transport.error(msg.id, -32601, `Method not found: ${msg.method}`);
24
+ }
25
+ });
26
+ function handleInitialize(msg) {
27
+ const _params = msg.params;
28
+ const result = {
29
+ capabilities: {
30
+ streaming: true,
31
+ fileEditing: true,
32
+ },
33
+ serverInfo: {
34
+ name: 'codeep',
35
+ version: '1.0.0',
36
+ },
37
+ };
38
+ transport.respond(msg.id, result);
39
+ }
40
+ function handleAgentRun(msg) {
41
+ const params = msg.params;
42
+ const conversationId = params.conversationId ?? String(msg.id);
43
+ const abortController = new AbortController();
44
+ activeSessions.set(conversationId, abortController);
45
+ runAgentSession({
46
+ prompt: params.prompt,
47
+ workspaceRoot: params.workspaceRoot ?? process.cwd(),
48
+ conversationId,
49
+ abortSignal: abortController.signal,
50
+ onChunk: (text) => {
51
+ transport.notify('agent/stream', { conversationId, text });
52
+ },
53
+ onFileEdit: (uri, newText) => {
54
+ transport.notify('workspace/applyEdit', {
55
+ changes: [{ uri, newText }],
56
+ });
57
+ },
58
+ }).then(() => {
59
+ transport.respond(msg.id, { done: true });
60
+ }).catch((err) => {
61
+ if (err.name === 'AbortError') {
62
+ transport.respond(msg.id, { cancelled: true });
63
+ }
64
+ else {
65
+ transport.error(msg.id, -32000, err.message);
66
+ }
67
+ }).finally(() => {
68
+ activeSessions.delete(conversationId);
69
+ });
70
+ }
71
+ function handleAgentCancel(msg) {
72
+ const { conversationId } = msg.params;
73
+ activeSessions.get(conversationId)?.abort();
74
+ activeSessions.delete(conversationId);
75
+ transport.respond(msg.id, { cancelled: true });
76
+ }
77
+ // Keep process alive until stdin closes (Zed terminates us)
78
+ return new Promise((resolve) => {
79
+ process.stdin.on('end', resolve);
80
+ });
81
+ }
@@ -0,0 +1,15 @@
1
+ export interface AgentSessionOptions {
2
+ prompt: string;
3
+ workspaceRoot: string;
4
+ conversationId: string;
5
+ abortSignal: AbortSignal;
6
+ onChunk: (text: string) => void;
7
+ onFileEdit: (uri: string, newText: string) => void;
8
+ }
9
+ /**
10
+ * Run a single agent session driven by ACP parameters.
11
+ *
12
+ * onFileEdit is reserved for future use (v1 emits everything via onChunk).
13
+ */
14
+ export declare function runAgentSession(opts: AgentSessionOptions): Promise<void>;
15
+ export declare function pathToUri(absolutePath: string): string;
@@ -0,0 +1,61 @@
1
+ // acp/session.ts
2
+ // Bridges ACP parameters to the Codeep agent loop.
3
+ import { pathToFileURL } from 'url';
4
+ import { runAgent } from '../utils/agent.js';
5
+ import { getProjectContext } from '../utils/project.js';
6
+ /**
7
+ * Build a ProjectContext from a workspace root directory.
8
+ * Falls back to a minimal synthetic context if scanning fails.
9
+ */
10
+ function buildProjectContext(workspaceRoot) {
11
+ const ctx = getProjectContext(workspaceRoot);
12
+ if (ctx) {
13
+ return ctx;
14
+ }
15
+ // Minimal fallback so runAgent never receives null
16
+ return {
17
+ root: workspaceRoot,
18
+ name: workspaceRoot.split('/').pop() ?? 'workspace',
19
+ type: 'Unknown',
20
+ structure: '',
21
+ keyFiles: [],
22
+ fileCount: 0,
23
+ summary: `Workspace at ${workspaceRoot}`,
24
+ };
25
+ }
26
+ /**
27
+ * Run a single agent session driven by ACP parameters.
28
+ *
29
+ * onFileEdit is reserved for future use (v1 emits everything via onChunk).
30
+ */
31
+ export async function runAgentSession(opts) {
32
+ const projectContext = buildProjectContext(opts.workspaceRoot);
33
+ const result = await runAgent(opts.prompt, projectContext, {
34
+ abortSignal: opts.abortSignal,
35
+ // Stream iteration status and thinking text back to the caller via onChunk
36
+ onIteration: (_iteration, message) => {
37
+ opts.onChunk(message + '\n');
38
+ },
39
+ onThinking: (text) => {
40
+ opts.onChunk(text);
41
+ },
42
+ });
43
+ // Emit the final response text if present
44
+ if (result.finalResponse) {
45
+ opts.onChunk(result.finalResponse);
46
+ }
47
+ // Surface errors as thrown exceptions so index.ts can handle them correctly
48
+ if (!result.success && !result.aborted) {
49
+ throw new Error(result.error ?? 'Agent run failed without a specific error message');
50
+ }
51
+ if (result.aborted) {
52
+ const abortError = new Error('Agent session was cancelled');
53
+ abortError.name = 'AbortError';
54
+ throw abortError;
55
+ }
56
+ }
57
+ // Utility: convert an absolute file-system path to a file:// URI string.
58
+ // Exported for use by callers that need to construct applyEdit URIs.
59
+ export function pathToUri(absolutePath) {
60
+ return pathToFileURL(absolutePath).href;
61
+ }
@@ -0,0 +1,13 @@
1
+ import { JsonRpcRequest, JsonRpcResponse, JsonRpcNotification } from './protocol.js';
2
+ type MessageHandler = (msg: JsonRpcRequest) => void;
3
+ export declare class StdioTransport {
4
+ private buffer;
5
+ private handler;
6
+ start(handler: MessageHandler): void;
7
+ private onData;
8
+ send(msg: JsonRpcResponse | JsonRpcNotification): void;
9
+ respond(id: number | string, result: unknown): void;
10
+ error(id: number | string, code: number, message: string): void;
11
+ notify(method: string, params: unknown): void;
12
+ }
13
+ export {};
@@ -0,0 +1,41 @@
1
+ // acp/transport.ts
2
+ // Newline-delimited JSON-RPC over stdio
3
+ export class StdioTransport {
4
+ buffer = '';
5
+ handler = null;
6
+ start(handler) {
7
+ this.handler = handler;
8
+ process.stdin.setEncoding('utf8');
9
+ process.stdin.on('data', (chunk) => this.onData(chunk));
10
+ process.stdin.on('end', () => process.exit(0));
11
+ }
12
+ onData(chunk) {
13
+ this.buffer += chunk;
14
+ const lines = this.buffer.split('\n');
15
+ this.buffer = lines.pop() ?? '';
16
+ for (const line of lines) {
17
+ const trimmed = line.trim();
18
+ if (!trimmed)
19
+ continue;
20
+ try {
21
+ const msg = JSON.parse(trimmed);
22
+ this.handler?.(msg);
23
+ }
24
+ catch {
25
+ // ignore malformed messages
26
+ }
27
+ }
28
+ }
29
+ send(msg) {
30
+ process.stdout.write(JSON.stringify(msg) + '\n');
31
+ }
32
+ respond(id, result) {
33
+ this.send({ jsonrpc: '2.0', id, result });
34
+ }
35
+ error(id, code, message) {
36
+ this.send({ jsonrpc: '2.0', id, error: { code, message } });
37
+ }
38
+ notify(method, params) {
39
+ this.send({ jsonrpc: '2.0', method, params });
40
+ }
41
+ }
@@ -321,6 +321,7 @@ Codeep - AI-powered coding assistant TUI
321
321
 
322
322
  Usage:
323
323
  codeep Start interactive chat
324
+ codeep acp Start ACP server (for Zed editor integration)
324
325
  codeep --version Show version
325
326
  codeep --help Show this help
326
327
 
@@ -332,6 +333,12 @@ Commands (in chat):
332
333
  `);
333
334
  process.exit(0);
334
335
  }
336
+ // ACP server mode — started by Zed via Agent Client Protocol
337
+ if (args[0] === 'acp') {
338
+ const { startAcpServer } = await import('../acp/server.js');
339
+ await startAcpServer();
340
+ return;
341
+ }
335
342
  await loadAllApiKeys();
336
343
  let apiKey = await loadApiKey();
337
344
  if (!apiKey) {
@@ -4,8 +4,10 @@
4
4
  * Private chat/stream logic lives in agentChat.ts and agentStream.ts.
5
5
  */
6
6
  import { ProjectContext } from './project';
7
- import { loadProjectRules, formatChatHistoryForAgent, AgentChatResponse } from './agentChat';
8
- export { loadProjectRules, formatChatHistoryForAgent, AgentChatResponse };
7
+ import { loadProjectRules, formatChatHistoryForAgent } from './agentChat';
8
+ import type { AgentChatResponse } from './agentChat';
9
+ export { loadProjectRules, formatChatHistoryForAgent };
10
+ export type { AgentChatResponse };
9
11
  import { ToolCall, ToolResult, ActionLog } from './tools';
10
12
  import { undoLastAction, undoAllActions, getCurrentSession, getRecentSessions, formatSession, ActionSession } from './history';
11
13
  import { VerifyResult } from './verify';
@@ -13,8 +13,8 @@
13
13
  */
14
14
  import { ProjectContext } from './project';
15
15
  import { Message } from '../config/index';
16
- import { AgentChatResponse } from './agentStream';
17
- export { AgentChatResponse };
16
+ import type { AgentChatResponse } from './agentStream';
17
+ export type { AgentChatResponse };
18
18
  /**
19
19
  * Custom error class for timeout
20
20
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeep",
3
- "version": "1.2.19",
3
+ "version": "1.2.20",
4
4
  "description": "AI-powered coding assistant built for the terminal. Multiple LLM providers, project-aware context, and a seamless development workflow.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",