@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.
Files changed (131) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +221 -219
  3. package/dist/adapters/claude/conversion/tool-use-to-acp.d.ts +21 -0
  4. package/dist/adapters/claude/conversion/tool-use-to-acp.js +547 -0
  5. package/dist/adapters/claude/conversion/tool-use-to-acp.js.map +1 -0
  6. package/dist/adapters/claude/permissions/permission-options.d.ts +13 -0
  7. package/dist/adapters/claude/permissions/permission-options.js +117 -0
  8. package/dist/adapters/claude/permissions/permission-options.js.map +1 -0
  9. package/dist/adapters/claude/questions/utils.d.ts +132 -0
  10. package/dist/adapters/claude/questions/utils.js +63 -0
  11. package/dist/adapters/claude/questions/utils.js.map +1 -0
  12. package/dist/adapters/claude/tools.d.ts +18 -0
  13. package/dist/adapters/claude/tools.js +95 -0
  14. package/dist/adapters/claude/tools.js.map +1 -0
  15. package/dist/agent-DBQY1BfC.d.ts +123 -0
  16. package/dist/agent.d.ts +5 -0
  17. package/dist/agent.js +3656 -0
  18. package/dist/agent.js.map +1 -0
  19. package/dist/claude-cli/cli.js +3695 -2746
  20. package/dist/claude-cli/vendor/ripgrep/COPYING +3 -0
  21. package/dist/claude-cli/vendor/ripgrep/arm64-darwin/rg +0 -0
  22. package/dist/claude-cli/vendor/ripgrep/arm64-darwin/ripgrep.node +0 -0
  23. package/dist/claude-cli/vendor/ripgrep/arm64-linux/rg +0 -0
  24. package/dist/claude-cli/vendor/ripgrep/arm64-linux/ripgrep.node +0 -0
  25. package/dist/claude-cli/vendor/ripgrep/x64-darwin/rg +0 -0
  26. package/dist/claude-cli/vendor/ripgrep/x64-darwin/ripgrep.node +0 -0
  27. package/dist/claude-cli/vendor/ripgrep/x64-linux/rg +0 -0
  28. package/dist/claude-cli/vendor/ripgrep/x64-linux/ripgrep.node +0 -0
  29. package/dist/claude-cli/vendor/ripgrep/x64-win32/rg.exe +0 -0
  30. package/dist/claude-cli/vendor/ripgrep/x64-win32/ripgrep.node +0 -0
  31. package/dist/gateway-models.d.ts +24 -0
  32. package/dist/gateway-models.js +93 -0
  33. package/dist/gateway-models.js.map +1 -0
  34. package/dist/index.d.ts +170 -1157
  35. package/dist/index.js +3252 -5074
  36. package/dist/index.js.map +1 -1
  37. package/dist/logger-DDBiMOOD.d.ts +24 -0
  38. package/dist/posthog-api.d.ts +40 -0
  39. package/dist/posthog-api.js +175 -0
  40. package/dist/posthog-api.js.map +1 -0
  41. package/dist/server/agent-server.d.ts +41 -0
  42. package/dist/server/agent-server.js +4451 -0
  43. package/dist/server/agent-server.js.map +1 -0
  44. package/dist/server/bin.d.ts +1 -0
  45. package/dist/server/bin.js +4507 -0
  46. package/dist/server/bin.js.map +1 -0
  47. package/dist/types.d.ts +129 -0
  48. package/dist/types.js +1 -0
  49. package/dist/types.js.map +1 -0
  50. package/package.json +66 -14
  51. package/src/acp-extensions.ts +98 -16
  52. package/src/adapters/acp-connection.ts +494 -0
  53. package/src/adapters/base-acp-agent.ts +150 -0
  54. package/src/adapters/claude/claude-agent.ts +596 -0
  55. package/src/adapters/claude/conversion/acp-to-sdk.ts +102 -0
  56. package/src/adapters/claude/conversion/sdk-to-acp.ts +571 -0
  57. package/src/adapters/claude/conversion/tool-use-to-acp.ts +618 -0
  58. package/src/adapters/claude/hooks.ts +64 -0
  59. package/src/adapters/claude/mcp/tool-metadata.ts +102 -0
  60. package/src/adapters/claude/permissions/permission-handlers.ts +433 -0
  61. package/src/adapters/claude/permissions/permission-options.ts +103 -0
  62. package/src/adapters/claude/plan/utils.ts +56 -0
  63. package/src/adapters/claude/questions/utils.ts +92 -0
  64. package/src/adapters/claude/session/commands.ts +38 -0
  65. package/src/adapters/claude/session/mcp-config.ts +37 -0
  66. package/src/adapters/claude/session/models.ts +12 -0
  67. package/src/adapters/claude/session/options.ts +236 -0
  68. package/src/adapters/claude/tool-meta.ts +143 -0
  69. package/src/adapters/claude/tools.ts +53 -688
  70. package/src/adapters/claude/types.ts +61 -0
  71. package/src/adapters/codex/spawn.ts +130 -0
  72. package/src/agent.ts +96 -587
  73. package/src/execution-mode.ts +43 -0
  74. package/src/gateway-models.ts +135 -0
  75. package/src/index.ts +79 -0
  76. package/src/otel-log-writer.test.ts +105 -0
  77. package/src/otel-log-writer.ts +94 -0
  78. package/src/posthog-api.ts +75 -235
  79. package/src/resume.ts +115 -0
  80. package/src/sagas/apply-snapshot-saga.test.ts +690 -0
  81. package/src/sagas/apply-snapshot-saga.ts +88 -0
  82. package/src/sagas/capture-tree-saga.test.ts +892 -0
  83. package/src/sagas/capture-tree-saga.ts +141 -0
  84. package/src/sagas/resume-saga.test.ts +558 -0
  85. package/src/sagas/resume-saga.ts +332 -0
  86. package/src/sagas/test-fixtures.ts +250 -0
  87. package/src/server/agent-server.test.ts +220 -0
  88. package/src/server/agent-server.ts +748 -0
  89. package/src/server/bin.ts +88 -0
  90. package/src/server/jwt.ts +65 -0
  91. package/src/server/schemas.ts +47 -0
  92. package/src/server/types.ts +13 -0
  93. package/src/server/utils/retry.test.ts +122 -0
  94. package/src/server/utils/retry.ts +61 -0
  95. package/src/server/utils/sse-parser.test.ts +93 -0
  96. package/src/server/utils/sse-parser.ts +46 -0
  97. package/src/session-log-writer.test.ts +140 -0
  98. package/src/session-log-writer.ts +137 -0
  99. package/src/test/assertions.ts +114 -0
  100. package/src/test/controllers/sse-controller.ts +107 -0
  101. package/src/test/fixtures/api.ts +111 -0
  102. package/src/test/fixtures/config.ts +33 -0
  103. package/src/test/fixtures/notifications.ts +92 -0
  104. package/src/test/mocks/claude-sdk.ts +251 -0
  105. package/src/test/mocks/msw-handlers.ts +48 -0
  106. package/src/test/setup.ts +114 -0
  107. package/src/test/wait.ts +41 -0
  108. package/src/tree-tracker.ts +173 -0
  109. package/src/types.ts +54 -137
  110. package/src/utils/acp-content.ts +58 -0
  111. package/src/utils/async-mutex.test.ts +104 -0
  112. package/src/utils/async-mutex.ts +31 -0
  113. package/src/utils/common.ts +15 -0
  114. package/src/utils/gateway.ts +9 -6
  115. package/src/utils/logger.ts +0 -30
  116. package/src/utils/streams.ts +220 -0
  117. package/CLAUDE.md +0 -331
  118. package/src/adapters/claude/claude.ts +0 -1947
  119. package/src/adapters/claude/mcp-server.ts +0 -810
  120. package/src/adapters/claude/utils.ts +0 -267
  121. package/src/adapters/connection.ts +0 -95
  122. package/src/file-manager.ts +0 -273
  123. package/src/git-manager.ts +0 -577
  124. package/src/schemas.ts +0 -241
  125. package/src/session-store.ts +0 -259
  126. package/src/task-manager.ts +0 -163
  127. package/src/todo-manager.ts +0 -180
  128. package/src/tools/registry.ts +0 -134
  129. package/src/tools/types.ts +0 -133
  130. package/src/utils/tapped-stream.ts +0 -60
  131. 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