@posthog/agent 2.0.0 → 2.0.1
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/LICENSE +1 -1
- package/README.md +221 -219
- package/dist/adapters/claude/conversion/tool-use-to-acp.d.ts +21 -0
- package/dist/adapters/claude/conversion/tool-use-to-acp.js +547 -0
- package/dist/adapters/claude/conversion/tool-use-to-acp.js.map +1 -0
- package/dist/adapters/claude/permissions/permission-options.d.ts +13 -0
- package/dist/adapters/claude/permissions/permission-options.js +117 -0
- package/dist/adapters/claude/permissions/permission-options.js.map +1 -0
- package/dist/adapters/claude/questions/utils.d.ts +132 -0
- package/dist/adapters/claude/questions/utils.js +63 -0
- package/dist/adapters/claude/questions/utils.js.map +1 -0
- package/dist/adapters/claude/tools.d.ts +18 -0
- package/dist/adapters/claude/tools.js +95 -0
- package/dist/adapters/claude/tools.js.map +1 -0
- package/dist/agent-DBQY1BfC.d.ts +123 -0
- package/dist/agent.d.ts +5 -0
- package/dist/agent.js +3656 -0
- package/dist/agent.js.map +1 -0
- package/dist/claude-cli/cli.js +3695 -2746
- package/dist/claude-cli/vendor/ripgrep/COPYING +3 -0
- package/dist/claude-cli/vendor/ripgrep/arm64-darwin/rg +0 -0
- package/dist/claude-cli/vendor/ripgrep/arm64-darwin/ripgrep.node +0 -0
- package/dist/claude-cli/vendor/ripgrep/arm64-linux/rg +0 -0
- package/dist/claude-cli/vendor/ripgrep/arm64-linux/ripgrep.node +0 -0
- package/dist/claude-cli/vendor/ripgrep/x64-darwin/rg +0 -0
- package/dist/claude-cli/vendor/ripgrep/x64-darwin/ripgrep.node +0 -0
- package/dist/claude-cli/vendor/ripgrep/x64-linux/rg +0 -0
- package/dist/claude-cli/vendor/ripgrep/x64-linux/ripgrep.node +0 -0
- package/dist/claude-cli/vendor/ripgrep/x64-win32/rg.exe +0 -0
- package/dist/claude-cli/vendor/ripgrep/x64-win32/ripgrep.node +0 -0
- package/dist/gateway-models.d.ts +24 -0
- package/dist/gateway-models.js +93 -0
- package/dist/gateway-models.js.map +1 -0
- package/dist/index.d.ts +170 -1157
- package/dist/index.js +3252 -5074
- package/dist/index.js.map +1 -1
- package/dist/logger-DDBiMOOD.d.ts +24 -0
- package/dist/posthog-api.d.ts +40 -0
- package/dist/posthog-api.js +175 -0
- package/dist/posthog-api.js.map +1 -0
- package/dist/server/agent-server.d.ts +41 -0
- package/dist/server/agent-server.js +4451 -0
- package/dist/server/agent-server.js.map +1 -0
- package/dist/server/bin.d.ts +1 -0
- package/dist/server/bin.js +4507 -0
- package/dist/server/bin.js.map +1 -0
- package/dist/types.d.ts +129 -0
- package/dist/types.js +1 -0
- package/dist/types.js.map +1 -0
- package/package.json +66 -14
- package/src/acp-extensions.ts +98 -16
- package/src/adapters/acp-connection.ts +494 -0
- package/src/adapters/base-acp-agent.ts +150 -0
- package/src/adapters/claude/claude-agent.ts +596 -0
- package/src/adapters/claude/conversion/acp-to-sdk.ts +102 -0
- package/src/adapters/claude/conversion/sdk-to-acp.ts +571 -0
- package/src/adapters/claude/conversion/tool-use-to-acp.ts +618 -0
- package/src/adapters/claude/hooks.ts +64 -0
- package/src/adapters/claude/mcp/tool-metadata.ts +102 -0
- package/src/adapters/claude/permissions/permission-handlers.ts +433 -0
- package/src/adapters/claude/permissions/permission-options.ts +103 -0
- package/src/adapters/claude/plan/utils.ts +56 -0
- package/src/adapters/claude/questions/utils.ts +92 -0
- package/src/adapters/claude/session/commands.ts +38 -0
- package/src/adapters/claude/session/mcp-config.ts +37 -0
- package/src/adapters/claude/session/models.ts +12 -0
- package/src/adapters/claude/session/options.ts +236 -0
- package/src/adapters/claude/tool-meta.ts +143 -0
- package/src/adapters/claude/tools.ts +53 -688
- package/src/adapters/claude/types.ts +61 -0
- package/src/adapters/codex/spawn.ts +130 -0
- package/src/agent.ts +96 -587
- package/src/execution-mode.ts +43 -0
- package/src/gateway-models.ts +135 -0
- package/src/index.ts +79 -0
- package/src/otel-log-writer.test.ts +105 -0
- package/src/otel-log-writer.ts +94 -0
- package/src/posthog-api.ts +75 -235
- package/src/resume.ts +115 -0
- package/src/sagas/apply-snapshot-saga.test.ts +690 -0
- package/src/sagas/apply-snapshot-saga.ts +88 -0
- package/src/sagas/capture-tree-saga.test.ts +892 -0
- package/src/sagas/capture-tree-saga.ts +141 -0
- package/src/sagas/resume-saga.test.ts +558 -0
- package/src/sagas/resume-saga.ts +332 -0
- package/src/sagas/test-fixtures.ts +250 -0
- package/src/server/agent-server.test.ts +220 -0
- package/src/server/agent-server.ts +748 -0
- package/src/server/bin.ts +88 -0
- package/src/server/jwt.ts +65 -0
- package/src/server/schemas.ts +47 -0
- package/src/server/types.ts +13 -0
- package/src/server/utils/retry.test.ts +122 -0
- package/src/server/utils/retry.ts +61 -0
- package/src/server/utils/sse-parser.test.ts +93 -0
- package/src/server/utils/sse-parser.ts +46 -0
- package/src/session-log-writer.test.ts +140 -0
- package/src/session-log-writer.ts +137 -0
- package/src/test/assertions.ts +114 -0
- package/src/test/controllers/sse-controller.ts +107 -0
- package/src/test/fixtures/api.ts +111 -0
- package/src/test/fixtures/config.ts +33 -0
- package/src/test/fixtures/notifications.ts +92 -0
- package/src/test/mocks/claude-sdk.ts +251 -0
- package/src/test/mocks/msw-handlers.ts +48 -0
- package/src/test/setup.ts +114 -0
- package/src/test/wait.ts +41 -0
- package/src/tree-tracker.ts +173 -0
- package/src/types.ts +54 -137
- package/src/utils/acp-content.ts +58 -0
- package/src/utils/async-mutex.test.ts +104 -0
- package/src/utils/async-mutex.ts +31 -0
- package/src/utils/common.ts +15 -0
- package/src/utils/gateway.ts +9 -6
- package/src/utils/logger.ts +0 -30
- package/src/utils/streams.ts +220 -0
- package/CLAUDE.md +0 -331
- package/src/adapters/claude/claude.ts +0 -1947
- package/src/adapters/claude/mcp-server.ts +0 -810
- package/src/adapters/claude/utils.ts +0 -267
- package/src/adapters/connection.ts +0 -95
- package/src/file-manager.ts +0 -273
- package/src/git-manager.ts +0 -577
- package/src/schemas.ts +0 -241
- package/src/session-store.ts +0 -259
- package/src/task-manager.ts +0 -163
- package/src/todo-manager.ts +0 -180
- package/src/tools/registry.ts +0 -134
- package/src/tools/types.ts +0 -133
- package/src/utils/tapped-stream.ts +0 -60
- package/src/worktree-manager.ts +0 -974
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import type { Readable, Writable } from "node:stream";
|
|
2
|
+
import { ReadableStream, WritableStream } from "node:stream/web";
|
|
3
|
+
import type { Logger } from "./logger.js";
|
|
4
|
+
|
|
5
|
+
export class Pushable<T> implements AsyncIterable<T> {
|
|
6
|
+
private queue: T[] = [];
|
|
7
|
+
private resolvers: ((value: IteratorResult<T>) => void)[] = [];
|
|
8
|
+
private done = false;
|
|
9
|
+
|
|
10
|
+
push(item: T) {
|
|
11
|
+
const resolve = this.resolvers.shift();
|
|
12
|
+
if (resolve) {
|
|
13
|
+
resolve({ value: item, done: false });
|
|
14
|
+
} else {
|
|
15
|
+
this.queue.push(item);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
end() {
|
|
20
|
+
this.done = true;
|
|
21
|
+
for (const resolve of this.resolvers) {
|
|
22
|
+
resolve({ value: undefined as unknown as T, done: true });
|
|
23
|
+
}
|
|
24
|
+
this.resolvers = [];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
[Symbol.asyncIterator](): AsyncIterator<T> {
|
|
28
|
+
return {
|
|
29
|
+
next: (): Promise<IteratorResult<T>> => {
|
|
30
|
+
if (this.queue.length > 0) {
|
|
31
|
+
const value = this.queue.shift() as T;
|
|
32
|
+
return Promise.resolve({ value, done: false });
|
|
33
|
+
}
|
|
34
|
+
if (this.done) {
|
|
35
|
+
return Promise.resolve({
|
|
36
|
+
value: undefined as unknown as T,
|
|
37
|
+
done: true,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
return new Promise<IteratorResult<T>>((resolve) => {
|
|
41
|
+
this.resolvers.push(resolve);
|
|
42
|
+
});
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export type StreamPair = {
|
|
49
|
+
readable: globalThis.ReadableStream<Uint8Array>;
|
|
50
|
+
writable: globalThis.WritableStream<Uint8Array>;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export type BidirectionalStreamPair = {
|
|
54
|
+
client: StreamPair;
|
|
55
|
+
agent: StreamPair;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
function pushableToReadableStream(
|
|
59
|
+
pushable: Pushable<Uint8Array>,
|
|
60
|
+
): globalThis.ReadableStream<Uint8Array> {
|
|
61
|
+
const iterator = pushable[Symbol.asyncIterator]();
|
|
62
|
+
return new ReadableStream<Uint8Array>({
|
|
63
|
+
async pull(controller) {
|
|
64
|
+
const { value, done } = await iterator.next();
|
|
65
|
+
if (done) {
|
|
66
|
+
controller.close();
|
|
67
|
+
} else {
|
|
68
|
+
controller.enqueue(value);
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
}) as unknown as globalThis.ReadableStream<Uint8Array>;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function createBidirectionalStreams(): BidirectionalStreamPair {
|
|
75
|
+
const clientToAgentPushable = new Pushable<Uint8Array>();
|
|
76
|
+
const agentToClientPushable = new Pushable<Uint8Array>();
|
|
77
|
+
|
|
78
|
+
const clientToAgentReadable = pushableToReadableStream(clientToAgentPushable);
|
|
79
|
+
const agentToClientReadable = pushableToReadableStream(agentToClientPushable);
|
|
80
|
+
|
|
81
|
+
const clientToAgentWritable = new WritableStream<Uint8Array>({
|
|
82
|
+
write(chunk) {
|
|
83
|
+
clientToAgentPushable.push(chunk);
|
|
84
|
+
},
|
|
85
|
+
close() {
|
|
86
|
+
clientToAgentPushable.end();
|
|
87
|
+
},
|
|
88
|
+
}) as globalThis.WritableStream<Uint8Array>;
|
|
89
|
+
|
|
90
|
+
const agentToClientWritable = new WritableStream<Uint8Array>({
|
|
91
|
+
write(chunk) {
|
|
92
|
+
agentToClientPushable.push(chunk);
|
|
93
|
+
},
|
|
94
|
+
close() {
|
|
95
|
+
agentToClientPushable.end();
|
|
96
|
+
},
|
|
97
|
+
}) as globalThis.WritableStream<Uint8Array>;
|
|
98
|
+
|
|
99
|
+
return {
|
|
100
|
+
client: {
|
|
101
|
+
readable: agentToClientReadable,
|
|
102
|
+
writable: clientToAgentWritable,
|
|
103
|
+
},
|
|
104
|
+
agent: {
|
|
105
|
+
readable: clientToAgentReadable,
|
|
106
|
+
writable: agentToClientWritable,
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
type MessageCallback = (line: string) => void;
|
|
112
|
+
|
|
113
|
+
export interface TappedStreamOptions {
|
|
114
|
+
onMessage: MessageCallback;
|
|
115
|
+
logger?: Logger;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export function createTappedWritableStream(
|
|
119
|
+
underlying: WritableStream<Uint8Array>,
|
|
120
|
+
options: TappedStreamOptions,
|
|
121
|
+
): WritableStream<Uint8Array> {
|
|
122
|
+
const { onMessage, logger } = options;
|
|
123
|
+
const decoder = new TextDecoder();
|
|
124
|
+
let buffer = "";
|
|
125
|
+
let _messageCount = 0;
|
|
126
|
+
|
|
127
|
+
return new WritableStream({
|
|
128
|
+
async write(chunk: Uint8Array) {
|
|
129
|
+
buffer += decoder.decode(chunk, { stream: true });
|
|
130
|
+
|
|
131
|
+
const lines = buffer.split("\n");
|
|
132
|
+
buffer = lines.pop() ?? "";
|
|
133
|
+
|
|
134
|
+
for (const line of lines) {
|
|
135
|
+
if (!line.trim()) continue;
|
|
136
|
+
_messageCount++;
|
|
137
|
+
|
|
138
|
+
onMessage(line);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
try {
|
|
142
|
+
const writer = underlying.getWriter();
|
|
143
|
+
await writer.write(chunk);
|
|
144
|
+
writer.releaseLock();
|
|
145
|
+
} catch (err) {
|
|
146
|
+
// Stream may be closed if subprocess crashed - log but don't throw
|
|
147
|
+
logger?.error("ACP write error", err);
|
|
148
|
+
}
|
|
149
|
+
},
|
|
150
|
+
async close() {
|
|
151
|
+
try {
|
|
152
|
+
const writer = underlying.getWriter();
|
|
153
|
+
await writer.close();
|
|
154
|
+
writer.releaseLock();
|
|
155
|
+
} catch {
|
|
156
|
+
// Stream may already be closed
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
async abort(reason: unknown) {
|
|
160
|
+
logger?.warn("Tapped stream aborted", { reason });
|
|
161
|
+
try {
|
|
162
|
+
const writer = underlying.getWriter();
|
|
163
|
+
await writer.abort(reason);
|
|
164
|
+
writer.releaseLock();
|
|
165
|
+
} catch {
|
|
166
|
+
// Stream may already be closed
|
|
167
|
+
}
|
|
168
|
+
},
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export function nodeReadableToWebReadable(
|
|
173
|
+
nodeStream: Readable,
|
|
174
|
+
): globalThis.ReadableStream<Uint8Array> {
|
|
175
|
+
return new ReadableStream<Uint8Array>({
|
|
176
|
+
start(controller) {
|
|
177
|
+
nodeStream.on("data", (chunk: Buffer) => {
|
|
178
|
+
controller.enqueue(new Uint8Array(chunk));
|
|
179
|
+
});
|
|
180
|
+
nodeStream.on("end", () => {
|
|
181
|
+
controller.close();
|
|
182
|
+
});
|
|
183
|
+
nodeStream.on("error", (err) => {
|
|
184
|
+
controller.error(err);
|
|
185
|
+
});
|
|
186
|
+
},
|
|
187
|
+
cancel() {
|
|
188
|
+
nodeStream.destroy();
|
|
189
|
+
},
|
|
190
|
+
}) as unknown as globalThis.ReadableStream<Uint8Array>;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export function nodeWritableToWebWritable(
|
|
194
|
+
nodeStream: Writable,
|
|
195
|
+
): globalThis.WritableStream<Uint8Array> {
|
|
196
|
+
return new WritableStream<Uint8Array>({
|
|
197
|
+
write(chunk) {
|
|
198
|
+
return new Promise((resolve, reject) => {
|
|
199
|
+
const ok = nodeStream.write(Buffer.from(chunk), (err) => {
|
|
200
|
+
if (err) reject(err);
|
|
201
|
+
});
|
|
202
|
+
if (ok) {
|
|
203
|
+
resolve();
|
|
204
|
+
} else {
|
|
205
|
+
nodeStream.once("drain", resolve);
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
},
|
|
209
|
+
close() {
|
|
210
|
+
return new Promise((resolve) => {
|
|
211
|
+
nodeStream.end(resolve);
|
|
212
|
+
});
|
|
213
|
+
},
|
|
214
|
+
abort(reason) {
|
|
215
|
+
nodeStream.destroy(
|
|
216
|
+
reason instanceof Error ? reason : new Error(String(reason)),
|
|
217
|
+
);
|
|
218
|
+
},
|
|
219
|
+
}) as globalThis.WritableStream<Uint8Array>;
|
|
220
|
+
}
|
package/CLAUDE.md
DELETED
|
@@ -1,331 +0,0 @@
|
|
|
1
|
-
# CLAUDE.md
|
|
2
|
-
|
|
3
|
-
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
-
|
|
5
|
-
## Overview
|
|
6
|
-
|
|
7
|
-
This is a TypeScript-based agent framework that wraps the Anthropic Claude Agent SDK, providing plan-and-execute capabilities for PostHog's Array desktop app and future backend services. It uses a Git-based workflow with file system storage for task artifacts.
|
|
8
|
-
|
|
9
|
-
## Essential Commands
|
|
10
|
-
|
|
11
|
-
```bash
|
|
12
|
-
# Install dependencies
|
|
13
|
-
pnpm install
|
|
14
|
-
|
|
15
|
-
# Run example usage (demonstrates all features)
|
|
16
|
-
pnpm run example-usage.ts
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
Note: No test, lint, or build scripts are currently defined in package.json.
|
|
20
|
-
|
|
21
|
-
## Architecture
|
|
22
|
-
|
|
23
|
-
### Core Components
|
|
24
|
-
|
|
25
|
-
1. **Agent Class** (`src/agent.ts`):
|
|
26
|
-
- Main interface to Claude API using `@anthropic-ai/claude-agent-sdk`
|
|
27
|
-
- Supports three execution modes: PLAN_AND_BUILD, PLAN_ONLY, BUILD_ONLY
|
|
28
|
-
- Git-based workflow with branch creation and commits
|
|
29
|
-
- Event streaming with Array-compatible event format
|
|
30
|
-
- Default model: claude-4-5-sonnet
|
|
31
|
-
|
|
32
|
-
2. **PostHog API Client** (`src/posthog-api.ts`):
|
|
33
|
-
- Fetches existing tasks from PostHog Django backend
|
|
34
|
-
- Authenticates using API keys and team resolution
|
|
35
|
-
- Does not create tasks - only reads existing ones
|
|
36
|
-
|
|
37
|
-
3. **File Manager** (`src/file-manager.ts`):
|
|
38
|
-
- Manages .posthog/{taskId}/ folder structure
|
|
39
|
-
- Stores plans, context files, and supporting documents
|
|
40
|
-
- Handles gitignore configuration for PostHog folders
|
|
41
|
-
|
|
42
|
-
4. **Git Manager** (`src/git-manager.ts`):
|
|
43
|
-
- Creates task-specific branches for planning and implementation
|
|
44
|
-
- Commits plans and implementations with descriptive messages
|
|
45
|
-
- Branch naming: posthog/task-{id}-planning, posthog/task-{id}-implementation
|
|
46
|
-
|
|
47
|
-
5. **Template Manager** (`src/template-manager.ts`):
|
|
48
|
-
- Generates standardized plan and context files from templates
|
|
49
|
-
- Supports variable substitution in markdown templates
|
|
50
|
-
- Creates consistent .posthog/ folder structures
|
|
51
|
-
|
|
52
|
-
6. **Task Manager** (`src/task-manager.ts`):
|
|
53
|
-
- Tracks execution state (running, completed, failed, canceled)
|
|
54
|
-
- Manages timeouts and cancellation
|
|
55
|
-
- Does not store task data - only execution state
|
|
56
|
-
|
|
57
|
-
3. **Event System** (`src/event-transformer.ts`):
|
|
58
|
-
- Maps Claude SDK events to Array's expected format
|
|
59
|
-
- Supports: token, status, tool_call, tool_result, diff, file_write, metric, artifact, error, done
|
|
60
|
-
|
|
61
|
-
4. **System Prompts** (`src/agents/` directory):
|
|
62
|
-
- `planning.ts`: System prompt for planning mode (read-only analysis)
|
|
63
|
-
- `execution.ts`: System prompt for execution mode (implementation)
|
|
64
|
-
- `ENGINEER.md`: Legacy prompt template (kept for reference)
|
|
65
|
-
|
|
66
|
-
5. **Entry Point** (`index.ts`):
|
|
67
|
-
- Re-exports all public APIs from src
|
|
68
|
-
- Clean import interface for consumers
|
|
69
|
-
|
|
70
|
-
### Key Patterns
|
|
71
|
-
|
|
72
|
-
- **Git-Based Workflow**: Plans and implementations are committed to separate branches
|
|
73
|
-
- **Plan-and-Execute Workflow**: Planning phase generates plans stored in .posthog/ folders
|
|
74
|
-
- **File System Storage**: Task artifacts stored in .posthog/{taskId}/ directories
|
|
75
|
-
- **PostHog Integration**: Fetches existing tasks from PostHog API, doesn't create new ones
|
|
76
|
-
- **Event Streaming**: Real-time event streaming compatible with Array app
|
|
77
|
-
- **Permission Modes**: Support for plan, default, acceptEdits, bypassPermissions
|
|
78
|
-
|
|
79
|
-
### Project Structure
|
|
80
|
-
|
|
81
|
-
```
|
|
82
|
-
/
|
|
83
|
-
├── example-usage.ts # Comprehensive usage examples
|
|
84
|
-
├── tsconfig.json # TypeScript configuration
|
|
85
|
-
└── src/ # Source code
|
|
86
|
-
├── agent.ts # Main Agent class
|
|
87
|
-
├── types.ts # TypeScript interfaces and enums
|
|
88
|
-
├── posthog-api.ts # PostHog API client
|
|
89
|
-
├── file-manager.ts # .posthog/ folder management
|
|
90
|
-
├── git-manager.ts # Git operations and branch management
|
|
91
|
-
├── template-manager.ts # Plan and context templates
|
|
92
|
-
├── task-manager.ts # Execution state tracking
|
|
93
|
-
├── event-transformer.ts # Event mapping logic
|
|
94
|
-
├── agents/ # System prompts
|
|
95
|
-
│ ├── planning.ts # Planning mode system prompt
|
|
96
|
-
│ └── execution.ts # Execution mode system prompt
|
|
97
|
-
└── templates/ # Template files
|
|
98
|
-
├── plan-template.md # Plan generation template
|
|
99
|
-
└── context-template.md # Context file template
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
## Usage Patterns
|
|
103
|
-
|
|
104
|
-
### Basic Task Execution with PostHog
|
|
105
|
-
```typescript
|
|
106
|
-
const agent = new Agent({
|
|
107
|
-
workingDirectory: "/path/to/repo",
|
|
108
|
-
posthogApiUrl: "https://app.posthog.com",
|
|
109
|
-
posthogApiKey: process.env.POSTHOG_API_KEY,
|
|
110
|
-
posthogProjectId: 1
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
// Fetch existing PostHog task
|
|
114
|
-
const task = await agent.fetchTask("task_abc123");
|
|
115
|
-
const result = await agent.runTask(task, ExecutionMode.PLAN_AND_BUILD);
|
|
116
|
-
```
|
|
117
|
-
|
|
118
|
-
### Progress Updates
|
|
119
|
-
```typescript
|
|
120
|
-
const posthogClient = agent.getPostHogClient();
|
|
121
|
-
const poller = setInterval(async () => {
|
|
122
|
-
const progress = await posthogClient?.getTaskProgress(taskId);
|
|
123
|
-
if (progress?.has_progress) {
|
|
124
|
-
updateUI(progress.status, progress.current_step, progress.completed_steps, progress.total_steps);
|
|
125
|
-
}
|
|
126
|
-
}, 3000);
|
|
127
|
-
|
|
128
|
-
try {
|
|
129
|
-
await agent.runWorkflow(taskId, workflowId, {
|
|
130
|
-
repositoryPath: selectedRepoPath,
|
|
131
|
-
permissionMode: PermissionMode.DEFAULT,
|
|
132
|
-
autoProgress: true,
|
|
133
|
-
});
|
|
134
|
-
} finally {
|
|
135
|
-
clearInterval(poller);
|
|
136
|
-
}
|
|
137
|
-
```
|
|
138
|
-
|
|
139
|
-
> The agent still emits transformed events via the `onEvent` callback, so UI layers can combine streaming updates with periodic polling if desired.
|
|
140
|
-
|
|
141
|
-
```typescript
|
|
142
|
-
// Handle the hook provided when constructing the Agent
|
|
143
|
-
import type { AgentEvent } from '@posthog/agent';
|
|
144
|
-
|
|
145
|
-
private handleLiveEvent(event: AgentEvent) {
|
|
146
|
-
switch (event.type) {
|
|
147
|
-
case 'status':
|
|
148
|
-
this.updateUI(event.phase, event.stage);
|
|
149
|
-
break;
|
|
150
|
-
case 'error':
|
|
151
|
-
this.showError(event.message);
|
|
152
|
-
break;
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
```
|
|
156
|
-
|
|
157
|
-
### Working with Task Files
|
|
158
|
-
```typescript
|
|
159
|
-
// Add context files to task folder
|
|
160
|
-
await agent.writeTaskFile(taskId, "requirements.md",
|
|
161
|
-
"Must be backwards compatible...", "context");
|
|
162
|
-
|
|
163
|
-
// Read plan after planning phase
|
|
164
|
-
const plan = await agent.readPlan(taskId);
|
|
165
|
-
|
|
166
|
-
// All files are in .posthog/{taskId}/ and version controlled
|
|
167
|
-
```
|
|
168
|
-
|
|
169
|
-
### Git Workflow
|
|
170
|
-
```typescript
|
|
171
|
-
// Plan-only mode creates planning branch and commits plan
|
|
172
|
-
const planResult = await agent.runTask(taskId, ExecutionMode.PLAN_ONLY);
|
|
173
|
-
// Creates branch: posthog/task-{id}-planning
|
|
174
|
-
// Commits: .posthog/{taskId}/plan.md
|
|
175
|
-
|
|
176
|
-
// Build-only mode creates implementation branch
|
|
177
|
-
const buildResult = await agent.runTask(taskId, ExecutionMode.BUILD_ONLY);
|
|
178
|
-
// Creates branch: posthog/task-{id}-implementation
|
|
179
|
-
// Commits: all implementation changes
|
|
180
|
-
```
|
|
181
|
-
|
|
182
|
-
## Development Notes
|
|
183
|
-
|
|
184
|
-
- Replaces the previous `@posthog/code-agent` package
|
|
185
|
-
- Designed for both Array app and future backend integration
|
|
186
|
-
- Uses Claude SDK's native plan mode for proper planning workflow
|
|
187
|
-
- Git-based artifact storage replaces database-backed supporting files
|
|
188
|
-
- All task artifacts (.posthog/ folders) are version controlled alongside code
|
|
189
|
-
- PostHog tasks are read-only - SDK doesn't create tasks, only executes existing ones
|
|
190
|
-
- Event system compatible with existing Array app expectations
|
|
191
|
-
|
|
192
|
-
## File System Layout
|
|
193
|
-
|
|
194
|
-
When working with tasks, the agent creates this structure:
|
|
195
|
-
|
|
196
|
-
```
|
|
197
|
-
your-repo/
|
|
198
|
-
├── .posthog/
|
|
199
|
-
│ ├── README.md # Auto-generated documentation
|
|
200
|
-
│ ├── .gitignore # Controls what gets committed
|
|
201
|
-
│ └── {task-id}/ # Per-task folder
|
|
202
|
-
│ ├── plan.md # Generated implementation plan
|
|
203
|
-
│ ├── context.md # Additional context (optional)
|
|
204
|
-
│ └── *.md # Other supporting files
|
|
205
|
-
└── (your regular code)
|
|
206
|
-
```
|
|
207
|
-
|
|
208
|
-
## Git Workflow
|
|
209
|
-
|
|
210
|
-
Each task execution creates specific Git branches:
|
|
211
|
-
|
|
212
|
-
1. **Planning Phase**: `posthog/task-{id}-planning`
|
|
213
|
-
- Contains .posthog/{id}/ folder with plan files
|
|
214
|
-
- Committed after plan generation
|
|
215
|
-
- Ready for review before implementation
|
|
216
|
-
|
|
217
|
-
2. **Implementation Phase**: `posthog/task-{id}-implementation`
|
|
218
|
-
- Contains actual code changes
|
|
219
|
-
- Includes updated .posthog/ files if needed
|
|
220
|
-
- Ready for PR creation and code review
|
|
221
|
-
|
|
222
|
-
## Workflow Examples
|
|
223
|
-
|
|
224
|
-
### Complete Task Execution Flow
|
|
225
|
-
|
|
226
|
-
```typescript
|
|
227
|
-
// 1. Initialize agent with PostHog credentials
|
|
228
|
-
const agent = new Agent({
|
|
229
|
-
workingDirectory: "/path/to/repo",
|
|
230
|
-
posthogApiUrl: "https://app.posthog.com",
|
|
231
|
-
posthogApiKey: process.env.POSTHOG_API_KEY,
|
|
232
|
-
posthogProjectId: 1
|
|
233
|
-
});
|
|
234
|
-
|
|
235
|
-
// 2. Fetch existing task from PostHog
|
|
236
|
-
const task = await agent.fetchTask("task_abc123");
|
|
237
|
-
|
|
238
|
-
// 3. Add context files before execution (optional)
|
|
239
|
-
await agent.writeTaskFile(task.id, "requirements.md",
|
|
240
|
-
"- Maintain backwards compatibility\n- Add comprehensive tests",
|
|
241
|
-
"context"
|
|
242
|
-
);
|
|
243
|
-
|
|
244
|
-
// 4. Execute with PLAN_AND_BUILD mode and rely on PostHog polling for progress
|
|
245
|
-
const result = await agent.runWorkflow(task.id, workflowId, {
|
|
246
|
-
repositoryPath: "/path/to/repo",
|
|
247
|
-
permissionMode: PermissionMode.DEFAULT,
|
|
248
|
-
autoProgress: true,
|
|
249
|
-
});
|
|
250
|
-
|
|
251
|
-
// 5. Review results
|
|
252
|
-
console.log("Planning branch:", `posthog/task-${task.id}-planning`);
|
|
253
|
-
console.log("Implementation branch:", `posthog/task-${task.id}-implementation`);
|
|
254
|
-
console.log("Plan location:", `.posthog/${task.id}/plan.md`);
|
|
255
|
-
```
|
|
256
|
-
|
|
257
|
-
### Array App Integration Pattern
|
|
258
|
-
|
|
259
|
-
```typescript
|
|
260
|
-
class ArrayTaskExecution {
|
|
261
|
-
async executeTask(taskId: string, workflowId: string, repoPath: string) {
|
|
262
|
-
const poller = setInterval(() => this.pollProgress(taskId), 3000);
|
|
263
|
-
try {
|
|
264
|
-
await this.agent.runWorkflow(taskId, workflowId, {
|
|
265
|
-
repositoryPath: repoPath,
|
|
266
|
-
permissionMode: PermissionMode.DEFAULT,
|
|
267
|
-
autoProgress: true,
|
|
268
|
-
});
|
|
269
|
-
} finally {
|
|
270
|
-
clearInterval(poller);
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
this.showBranchesForReview(taskId);
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
private async pollProgress(taskId: string) {
|
|
277
|
-
const client = this.agent.getPostHogClient();
|
|
278
|
-
if (!client) {
|
|
279
|
-
return;
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
const progress = await client.getTaskProgress(taskId);
|
|
283
|
-
if (progress.has_progress) {
|
|
284
|
-
this.updateProgressBar({
|
|
285
|
-
status: progress.status,
|
|
286
|
-
currentStep: progress.current_step,
|
|
287
|
-
completed: progress.completed_steps,
|
|
288
|
-
total: progress.total_steps,
|
|
289
|
-
});
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
```
|
|
294
|
-
|
|
295
|
-
### File System Operations
|
|
296
|
-
|
|
297
|
-
```typescript
|
|
298
|
-
// Working with task files
|
|
299
|
-
await agent.writeTaskFile(taskId, "context.md", contextContent, "context");
|
|
300
|
-
await agent.writeTaskFile(taskId, "requirements.md", requirements, "reference");
|
|
301
|
-
|
|
302
|
-
// Reading files
|
|
303
|
-
const plan = await agent.readPlan(taskId);
|
|
304
|
-
const files = await agent.getTaskFiles(taskId);
|
|
305
|
-
|
|
306
|
-
// Files are stored in .posthog/{taskId}/ and committed to Git
|
|
307
|
-
```
|
|
308
|
-
|
|
309
|
-
## Error Handling Patterns
|
|
310
|
-
|
|
311
|
-
```typescript
|
|
312
|
-
try {
|
|
313
|
-
const result = await agent.runTask(taskId, ExecutionMode.PLAN_AND_BUILD);
|
|
314
|
-
} catch (error) {
|
|
315
|
-
if (error.message.includes('Git command failed')) {
|
|
316
|
-
// Handle Git-related errors (branch conflicts, etc.)
|
|
317
|
-
} else if (error.message.includes('PostHog API')) {
|
|
318
|
-
// Handle API-related errors (authentication, task not found)
|
|
319
|
-
} else if (error.message.includes('File system')) {
|
|
320
|
-
// Handle file permission or disk space issues
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
```
|
|
324
|
-
|
|
325
|
-
## Performance Considerations
|
|
326
|
-
|
|
327
|
-
- **Branch Creation**: Fast Git operations using local commands
|
|
328
|
-
- **File I/O**: Efficient `.posthog/` folder management with minimal disk usage
|
|
329
|
-
- **API Calls**: Cached PostHog task data to minimize network requests
|
|
330
|
-
- **Event Streaming**: Real-time updates without blocking execution
|
|
331
|
-
- **Template Processing**: Lazy-loaded templates with variable substitution
|