@posthog/agent 1.0.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 (97) hide show
  1. package/CLAUDE.md +296 -0
  2. package/README.md +142 -0
  3. package/dist/example.d.ts +3 -0
  4. package/dist/example.d.ts.map +1 -0
  5. package/dist/example.js +49 -0
  6. package/dist/example.js.map +1 -0
  7. package/dist/index.d.ts +7 -0
  8. package/dist/index.d.ts.map +1 -0
  9. package/dist/index.js +5 -0
  10. package/dist/index.js.map +1 -0
  11. package/dist/src/agent-registry.d.ts +16 -0
  12. package/dist/src/agent-registry.d.ts.map +1 -0
  13. package/dist/src/agent-registry.js +54 -0
  14. package/dist/src/agent-registry.js.map +1 -0
  15. package/dist/src/agent.d.ts +60 -0
  16. package/dist/src/agent.d.ts.map +1 -0
  17. package/dist/src/agent.js +371 -0
  18. package/dist/src/agent.js.map +1 -0
  19. package/dist/src/agents/execution.d.ts +2 -0
  20. package/dist/src/agents/execution.d.ts.map +1 -0
  21. package/dist/src/agents/execution.js +54 -0
  22. package/dist/src/agents/execution.js.map +1 -0
  23. package/dist/src/agents/planning.d.ts +2 -0
  24. package/dist/src/agents/planning.d.ts.map +1 -0
  25. package/dist/src/agents/planning.js +68 -0
  26. package/dist/src/agents/planning.js.map +1 -0
  27. package/dist/src/event-transformer.d.ts +7 -0
  28. package/dist/src/event-transformer.d.ts.map +1 -0
  29. package/dist/src/event-transformer.js +174 -0
  30. package/dist/src/event-transformer.js.map +1 -0
  31. package/dist/src/file-manager.d.ts +30 -0
  32. package/dist/src/file-manager.d.ts.map +1 -0
  33. package/dist/src/file-manager.js +181 -0
  34. package/dist/src/file-manager.js.map +1 -0
  35. package/dist/src/git-manager.d.ts +49 -0
  36. package/dist/src/git-manager.d.ts.map +1 -0
  37. package/dist/src/git-manager.js +278 -0
  38. package/dist/src/git-manager.js.map +1 -0
  39. package/dist/src/posthog-api.d.ts +48 -0
  40. package/dist/src/posthog-api.d.ts.map +1 -0
  41. package/dist/src/posthog-api.js +110 -0
  42. package/dist/src/posthog-api.js.map +1 -0
  43. package/dist/src/prompt-builder.d.ts +17 -0
  44. package/dist/src/prompt-builder.d.ts.map +1 -0
  45. package/dist/src/prompt-builder.js +75 -0
  46. package/dist/src/prompt-builder.js.map +1 -0
  47. package/dist/src/stage-executor.d.ts +16 -0
  48. package/dist/src/stage-executor.d.ts.map +1 -0
  49. package/dist/src/stage-executor.js +119 -0
  50. package/dist/src/stage-executor.js.map +1 -0
  51. package/dist/src/task-manager.d.ts +25 -0
  52. package/dist/src/task-manager.d.ts.map +1 -0
  53. package/dist/src/task-manager.js +119 -0
  54. package/dist/src/task-manager.js.map +1 -0
  55. package/dist/src/template-manager.d.ts +30 -0
  56. package/dist/src/template-manager.d.ts.map +1 -0
  57. package/dist/src/template-manager.js +118 -0
  58. package/dist/src/template-manager.js.map +1 -0
  59. package/dist/src/types.d.ts +163 -0
  60. package/dist/src/types.d.ts.map +1 -0
  61. package/dist/src/types.js +9 -0
  62. package/dist/src/types.js.map +1 -0
  63. package/dist/src/utils/logger.d.ts +29 -0
  64. package/dist/src/utils/logger.d.ts.map +1 -0
  65. package/dist/src/utils/logger.js +66 -0
  66. package/dist/src/utils/logger.js.map +1 -0
  67. package/dist/src/utils/mcp.d.ts +10 -0
  68. package/dist/src/utils/mcp.d.ts.map +1 -0
  69. package/dist/src/utils/mcp.js +16 -0
  70. package/dist/src/utils/mcp.js.map +1 -0
  71. package/dist/src/workflow-registry.d.ts +11 -0
  72. package/dist/src/workflow-registry.d.ts.map +1 -0
  73. package/dist/src/workflow-registry.js +26 -0
  74. package/dist/src/workflow-registry.js.map +1 -0
  75. package/dist/src/workflow-types.d.ts +45 -0
  76. package/dist/src/workflow-types.d.ts.map +1 -0
  77. package/dist/src/workflow-types.js +2 -0
  78. package/dist/src/workflow-types.js.map +1 -0
  79. package/package.json +61 -0
  80. package/src/agent-registry.ts +60 -0
  81. package/src/agent.ts +428 -0
  82. package/src/agents/execution.ts +53 -0
  83. package/src/agents/planning.ts +67 -0
  84. package/src/event-transformer.ts +189 -0
  85. package/src/file-manager.ts +204 -0
  86. package/src/git-manager.ts +344 -0
  87. package/src/posthog-api.ts +169 -0
  88. package/src/prompt-builder.ts +93 -0
  89. package/src/stage-executor.ts +137 -0
  90. package/src/task-manager.ts +155 -0
  91. package/src/template-manager.ts +149 -0
  92. package/src/templates/plan-template.md +45 -0
  93. package/src/types.ts +223 -0
  94. package/src/utils/logger.ts +79 -0
  95. package/src/utils/mcp.ts +15 -0
  96. package/src/workflow-registry.ts +31 -0
  97. package/src/workflow-types.ts +53 -0
@@ -0,0 +1,344 @@
1
+ import { exec } from 'child_process';
2
+ import { promisify } from 'util';
3
+ import { Logger } from './utils/logger';
4
+
5
+ const execAsync = promisify(exec);
6
+
7
+ export interface GitConfig {
8
+ repositoryPath: string;
9
+ authorName?: string;
10
+ authorEmail?: string;
11
+ logger?: Logger;
12
+ }
13
+
14
+ export interface BranchInfo {
15
+ name: string;
16
+ exists: boolean;
17
+ isCurrentBranch: boolean;
18
+ }
19
+
20
+ export class GitManager {
21
+ private repositoryPath: string;
22
+ private authorName?: string;
23
+ private authorEmail?: string;
24
+ private logger: Logger;
25
+
26
+ constructor(config: GitConfig) {
27
+ this.repositoryPath = config.repositoryPath;
28
+ this.authorName = config.authorName;
29
+ this.authorEmail = config.authorEmail;
30
+ this.logger = config.logger || new Logger({ debug: false, prefix: '[GitManager]' });
31
+ }
32
+
33
+ private async runGitCommand(command: string): Promise<string> {
34
+ try {
35
+ const { stdout } = await execAsync(`cd "${this.repositoryPath}" && git ${command}`);
36
+ return stdout.trim();
37
+ } catch (error) {
38
+ throw new Error(`Git command failed: ${command}\n${error}`);
39
+ }
40
+ }
41
+
42
+ private async runCommand(command: string): Promise<string> {
43
+ try {
44
+ const { stdout } = await execAsync(`cd "${this.repositoryPath}" && ${command}`);
45
+ return stdout.trim();
46
+ } catch (error) {
47
+ throw new Error(`Command failed: ${command}\n${error}`);
48
+ }
49
+ }
50
+
51
+ async isGitRepository(): Promise<boolean> {
52
+ try {
53
+ await this.runGitCommand('rev-parse --git-dir');
54
+ return true;
55
+ } catch {
56
+ return false;
57
+ }
58
+ }
59
+
60
+ async getCurrentBranch(): Promise<string> {
61
+ return await this.runGitCommand('branch --show-current');
62
+ }
63
+
64
+ async getDefaultBranch(): Promise<string> {
65
+ try {
66
+ // Try to get the default branch from remote
67
+ const remoteBranch = await this.runGitCommand('symbolic-ref refs/remotes/origin/HEAD');
68
+ return remoteBranch.replace('refs/remotes/origin/', '');
69
+ } catch {
70
+ // Fallback: check if main exists, otherwise use master
71
+ if (await this.branchExists('main')) {
72
+ return 'main';
73
+ } else if (await this.branchExists('master')) {
74
+ return 'master';
75
+ } else {
76
+ throw new Error('Cannot determine default branch. No main or master branch found.');
77
+ }
78
+ }
79
+ }
80
+
81
+ async branchExists(branchName: string): Promise<boolean> {
82
+ try {
83
+ await this.runGitCommand(`rev-parse --verify ${branchName}`);
84
+ return true;
85
+ } catch {
86
+ return false;
87
+ }
88
+ }
89
+
90
+ async createBranch(branchName: string, baseBranch?: string): Promise<void> {
91
+ const base = baseBranch || await this.getCurrentBranch();
92
+ await this.runGitCommand(`checkout -b ${branchName} ${base}`);
93
+ }
94
+
95
+ async switchToBranch(branchName: string): Promise<void> {
96
+ await this.runGitCommand(`checkout ${branchName}`);
97
+ }
98
+
99
+ async createOrSwitchToBranch(branchName: string, baseBranch?: string): Promise<void> {
100
+ const exists = await this.branchExists(branchName);
101
+ if (exists) {
102
+ await this.switchToBranch(branchName);
103
+ } else {
104
+ await this.createBranch(branchName, baseBranch);
105
+ }
106
+ }
107
+
108
+ async addFiles(paths: string[]): Promise<void> {
109
+ const pathList = paths.map(p => `"${p}"`).join(' ');
110
+ await this.runGitCommand(`add ${pathList}`);
111
+ }
112
+
113
+ async addAllPostHogFiles(): Promise<void> {
114
+ await this.runGitCommand('add .posthog/');
115
+ }
116
+
117
+ async commitChanges(message: string, options?: {
118
+ authorName?: string;
119
+ authorEmail?: string;
120
+ }): Promise<string> {
121
+ let command = 'commit -m "' + message.replace(/"/g, '\\"') + '"';
122
+
123
+ const authorName = options?.authorName || this.authorName;
124
+ const authorEmail = options?.authorEmail || this.authorEmail;
125
+
126
+ if (authorName && authorEmail) {
127
+ command += ` --author="${authorName} <${authorEmail}>"`;
128
+ }
129
+
130
+ return await this.runGitCommand(command);
131
+ }
132
+
133
+ async hasChanges(): Promise<boolean> {
134
+ try {
135
+ const status = await this.runGitCommand('status --porcelain');
136
+ return status.length > 0;
137
+ } catch {
138
+ return false;
139
+ }
140
+ }
141
+
142
+ async hasStagedChanges(): Promise<boolean> {
143
+ try {
144
+ const status = await this.runGitCommand('diff --cached --name-only');
145
+ return status.length > 0;
146
+ } catch {
147
+ return false;
148
+ }
149
+ }
150
+
151
+ async getRemoteUrl(): Promise<string | null> {
152
+ try {
153
+ return await this.runGitCommand('remote get-url origin');
154
+ } catch {
155
+ return null;
156
+ }
157
+ }
158
+
159
+ async pushBranch(branchName: string, force: boolean = false): Promise<void> {
160
+ const forceFlag = force ? '--force' : '';
161
+ await this.runGitCommand(`push ${forceFlag} -u origin ${branchName}`);
162
+ }
163
+
164
+ // Utility methods for PostHog task workflow
165
+ async createTaskPlanningBranch(taskId: string, baseBranch?: string): Promise<string> {
166
+ let branchName = `posthog/task-${taskId}-planning`;
167
+ let counter = 1;
168
+
169
+ // Find a unique branch name if the base name already exists
170
+ while (await this.branchExists(branchName)) {
171
+ branchName = `posthog/task-${taskId}-planning-${counter}`;
172
+ counter++;
173
+ }
174
+
175
+ this.logger.debug('Creating unique planning branch', { branchName, taskId });
176
+
177
+ // If no base branch specified, ensure we're on main/master
178
+ if (!baseBranch) {
179
+ baseBranch = await this.getDefaultBranch();
180
+ await this.switchToBranch(baseBranch);
181
+
182
+ // Check for uncommitted changes
183
+ if (await this.hasChanges()) {
184
+ throw new Error(`Uncommitted changes detected. Please commit or stash changes before running tasks.`);
185
+ }
186
+ }
187
+
188
+ await this.createBranch(branchName, baseBranch); // Use createBranch instead of createOrSwitchToBranch for new branches
189
+ return branchName;
190
+ }
191
+
192
+ async createTaskImplementationBranch(taskId: string, planningBranchName?: string): Promise<string> {
193
+ let branchName = `posthog/task-${taskId}-implementation`;
194
+ let counter = 1;
195
+
196
+ // Find a unique branch name if the base name already exists
197
+ while (await this.branchExists(branchName)) {
198
+ branchName = `posthog/task-${taskId}-implementation-${counter}`;
199
+ counter++;
200
+ }
201
+
202
+ const currentBranchBefore = await this.getCurrentBranch();
203
+ this.logger.debug('Creating unique implementation branch', {
204
+ branchName,
205
+ taskId,
206
+ currentBranch: currentBranchBefore
207
+ });
208
+
209
+ // Implementation branch should branch from the specific planning branch
210
+ let baseBranch = planningBranchName;
211
+
212
+ if (!baseBranch) {
213
+ // Try to find the corresponding planning branch
214
+ const currentBranch = await this.getCurrentBranch();
215
+ if (currentBranch.includes('-planning')) {
216
+ baseBranch = currentBranch; // Use current planning branch
217
+ this.logger.debug('Using current planning branch', { baseBranch });
218
+ } else {
219
+ // Fallback to default branch
220
+ baseBranch = await this.getDefaultBranch();
221
+ this.logger.debug('No planning branch found, using default', { baseBranch });
222
+ await this.switchToBranch(baseBranch);
223
+ }
224
+ }
225
+
226
+ this.logger.debug('Creating implementation branch from base', { baseBranch, branchName });
227
+ await this.createBranch(branchName, baseBranch); // Create fresh branch from base
228
+
229
+ const currentBranchAfter = await this.getCurrentBranch();
230
+ this.logger.info('Implementation branch created', {
231
+ branchName,
232
+ currentBranch: currentBranchAfter
233
+ });
234
+
235
+ return branchName;
236
+ }
237
+
238
+ async commitPlan(taskId: string, taskTitle: string): Promise<string> {
239
+ const currentBranch = await this.getCurrentBranch();
240
+ this.logger.debug('Committing plan', { taskId, currentBranch });
241
+
242
+ await this.addAllPostHogFiles();
243
+
244
+ const hasChanges = await this.hasStagedChanges();
245
+ this.logger.debug('Checking for staged changes', { hasChanges });
246
+
247
+ if (!hasChanges) {
248
+ this.logger.info('No plan changes to commit', { taskId });
249
+ return 'No changes to commit';
250
+ }
251
+
252
+ const message = `📋 Add plan for task: ${taskTitle}
253
+
254
+ Task ID: ${taskId}
255
+ Generated by PostHog Agent
256
+
257
+ This commit contains the implementation plan and supporting documentation
258
+ for the task. Review the plan before proceeding with implementation.`;
259
+
260
+ const result = await this.commitChanges(message);
261
+ this.logger.info('Plan committed', { taskId, taskTitle });
262
+ return result;
263
+ }
264
+
265
+ async commitImplementation(taskId: string, taskTitle: string, planSummary?: string): Promise<string> {
266
+ await this.runGitCommand('add .');
267
+
268
+ const hasChanges = await this.hasStagedChanges();
269
+ if (!hasChanges) {
270
+ this.logger.warn('No implementation changes to commit', { taskId });
271
+ return 'No changes to commit';
272
+ }
273
+
274
+ let message = `✨ Implement task: ${taskTitle}
275
+
276
+ Task ID: ${taskId}
277
+ Generated by PostHog Agent`;
278
+
279
+ if (planSummary) {
280
+ message += `\n\nPlan Summary:\n${planSummary}`;
281
+ }
282
+
283
+ message += `\n\nThis commit implements the changes described in the task plan.`;
284
+
285
+ const result = await this.commitChanges(message);
286
+ this.logger.info('Implementation committed', { taskId, taskTitle });
287
+ return result;
288
+ }
289
+
290
+ async deleteBranch(branchName: string, force: boolean = false): Promise<void> {
291
+ const forceFlag = force ? '-D' : '-d';
292
+ await this.runGitCommand(`branch ${forceFlag} ${branchName}`);
293
+ }
294
+
295
+ async deleteRemoteBranch(branchName: string): Promise<void> {
296
+ await this.runGitCommand(`push origin --delete ${branchName}`);
297
+ }
298
+
299
+ async getBranchInfo(branchName: string): Promise<BranchInfo> {
300
+ const exists = await this.branchExists(branchName);
301
+ const currentBranch = await this.getCurrentBranch();
302
+
303
+ return {
304
+ name: branchName,
305
+ exists,
306
+ isCurrentBranch: branchName === currentBranch
307
+ };
308
+ }
309
+
310
+ async getCommitSha(ref: string = 'HEAD'): Promise<string> {
311
+ return await this.runGitCommand(`rev-parse ${ref}`);
312
+ }
313
+
314
+ async getCommitMessage(ref: string = 'HEAD'): Promise<string> {
315
+ return await this.runGitCommand(`log -1 --pretty=%B ${ref}`);
316
+ }
317
+
318
+ async createPullRequest(
319
+ branchName: string,
320
+ title: string,
321
+ body: string,
322
+ baseBranch?: string
323
+ ): Promise<string> {
324
+ const currentBranch = await this.getCurrentBranch();
325
+ if (currentBranch !== branchName) {
326
+ await this.switchToBranch(branchName);
327
+ }
328
+
329
+ await this.pushBranch(branchName);
330
+
331
+ let command = `gh pr create --title "${title.replace(/"/g, '\\"')}" --body "${body.replace(/"/g, '\\"')}"`;
332
+
333
+ if (baseBranch) {
334
+ command += ` --base ${baseBranch}`;
335
+ }
336
+
337
+ try {
338
+ const prUrl = await this.runCommand(command);
339
+ return prUrl.trim();
340
+ } catch (error) {
341
+ throw new Error(`Failed to create PR: ${error}`);
342
+ }
343
+ }
344
+ }
@@ -0,0 +1,169 @@
1
+ import type { Task, SupportingFile, PostHogAPIConfig } from './types';
2
+ import type { WorkflowDefinition, AgentDefinition } from './workflow-types';
3
+
4
+ interface PostHogApiResponse<T> {
5
+ results?: T[];
6
+ count?: number;
7
+ next?: string | null;
8
+ previous?: string | null;
9
+ }
10
+
11
+ interface TaskProgressResponse {
12
+ has_progress: boolean;
13
+ id?: string;
14
+ status?: "started" | "in_progress" | "completed" | "failed";
15
+ current_step?: string;
16
+ completed_steps?: number;
17
+ total_steps?: number;
18
+ progress_percentage?: number;
19
+ output_log?: string;
20
+ error_message?: string;
21
+ created_at?: string;
22
+ updated_at?: string;
23
+ completed_at?: string;
24
+ workflow_id?: string;
25
+ workflow_run_id?: string;
26
+ message?: string;
27
+ }
28
+
29
+ export class PostHogAPIClient {
30
+ private config: PostHogAPIConfig;
31
+ private _teamId: number | null = null;
32
+
33
+ constructor(config: PostHogAPIConfig) {
34
+ this.config = config;
35
+ }
36
+
37
+ private get baseUrl(): string {
38
+ const host = this.config.apiUrl.endsWith("/")
39
+ ? this.config.apiUrl.slice(0, -1)
40
+ : this.config.apiUrl;
41
+ return host;
42
+ }
43
+
44
+ private get headers(): Record<string, string> {
45
+ return {
46
+ 'Authorization': `Bearer ${this.config.apiKey}`,
47
+ 'Content-Type': 'application/json',
48
+ };
49
+ }
50
+
51
+ private async apiRequest<T>(
52
+ endpoint: string,
53
+ options: RequestInit = {}
54
+ ): Promise<T> {
55
+ const url = `${this.baseUrl}${endpoint}`;
56
+
57
+ const response = await fetch(url, {
58
+ ...options,
59
+ headers: {
60
+ ...this.headers,
61
+ ...options.headers,
62
+ },
63
+ });
64
+
65
+ if (!response.ok) {
66
+ let errorMessage: string;
67
+ try {
68
+ const errorResponse = await response.json();
69
+ errorMessage = `Failed request: [${response.status}] ${JSON.stringify(errorResponse)}`;
70
+ } catch {
71
+ errorMessage = `Failed request: [${response.status}] ${response.statusText}`;
72
+ }
73
+ throw new Error(errorMessage);
74
+ }
75
+
76
+ return response.json();
77
+ }
78
+
79
+ private async getTeamId(): Promise<number> {
80
+ if (this._teamId !== null) {
81
+ return this._teamId;
82
+ }
83
+
84
+ // Fetch user info to get team ID (following Array's pattern)
85
+ const userResponse = await this.apiRequest<any>('/api/users/@me/');
86
+
87
+ if (!userResponse.team?.id) {
88
+ throw new Error('No team found for user');
89
+ }
90
+
91
+ const teamId = Number(userResponse.team.id);
92
+ this._teamId = teamId;
93
+ return teamId;
94
+ }
95
+
96
+ async fetchTask(taskId: string): Promise<Task> {
97
+ const teamId = await this.getTeamId();
98
+ return this.apiRequest<Task>(`/api/projects/${teamId}/tasks/${taskId}/`);
99
+ }
100
+
101
+ async listTasks(filters?: {
102
+ repository?: string;
103
+ organization?: string;
104
+ origin_product?: string;
105
+ workflow?: string;
106
+ current_stage?: string;
107
+ }): Promise<Task[]> {
108
+ const teamId = await this.getTeamId();
109
+ const url = new URL(`${this.baseUrl}/api/projects/${teamId}/tasks/`);
110
+
111
+ if (filters) {
112
+ Object.entries(filters).forEach(([key, value]) => {
113
+ if (value) url.searchParams.append(key, value);
114
+ });
115
+ }
116
+
117
+ const response = await this.apiRequest<PostHogApiResponse<Task>>(
118
+ url.pathname + url.search
119
+ );
120
+
121
+ return response.results || [];
122
+ }
123
+
124
+ async updateTask(taskId: string, updates: Partial<Task>): Promise<Task> {
125
+ const teamId = await this.getTeamId();
126
+ return this.apiRequest<Task>(`/api/projects/${teamId}/tasks/${taskId}/`, {
127
+ method: 'PATCH',
128
+ body: JSON.stringify(updates),
129
+ });
130
+ }
131
+
132
+ async updateTaskStage(taskId: string, stageId: string): Promise<Task> {
133
+ const teamId = await this.getTeamId();
134
+ return this.apiRequest<Task>(`/api/projects/${teamId}/tasks/${taskId}/update_stage/`, {
135
+ method: 'PATCH',
136
+ body: JSON.stringify({ current_stage: stageId }),
137
+ });
138
+ }
139
+
140
+ async getTaskProgress(taskId: string): Promise<TaskProgressResponse> {
141
+ const teamId = await this.getTeamId();
142
+ return this.apiRequest<TaskProgressResponse>(`/api/projects/${teamId}/tasks/${taskId}/progress/`);
143
+ }
144
+
145
+ // Workflow endpoints
146
+ async fetchWorkflow(workflowId: string): Promise<WorkflowDefinition> {
147
+ const teamId = await this.getTeamId();
148
+ return this.apiRequest<WorkflowDefinition>(`/api/projects/${teamId}/task_workflows/${workflowId}/`);
149
+ }
150
+
151
+ async listWorkflows(): Promise<WorkflowDefinition[]> {
152
+ const teamId = await this.getTeamId();
153
+ const response = await this.apiRequest<PostHogApiResponse<WorkflowDefinition>>(`/api/projects/${teamId}/task_workflows/`);
154
+ return response.results || [];
155
+ }
156
+
157
+ // Agent catalog exposure
158
+ async listAgents(): Promise<AgentDefinition[]> {
159
+ return this.apiRequest<AgentDefinition[]>(`/api/agents/`);
160
+ }
161
+
162
+ async progressTask(taskId: string, options?: { next_stage_id?: string; auto?: boolean }): Promise<Task> {
163
+ const teamId = await this.getTeamId();
164
+ return this.apiRequest<Task>(`/api/projects/${teamId}/tasks/${taskId}/progress_task/`, {
165
+ method: 'POST',
166
+ body: JSON.stringify(options || {}),
167
+ });
168
+ }
169
+ }
@@ -0,0 +1,93 @@
1
+ import type { Task } from './types';
2
+ import type { TemplateVariables } from './template-manager';
3
+ import { Logger } from './utils/logger';
4
+
5
+ export interface PromptBuilderDeps {
6
+ getTaskFiles: (taskId: string) => Promise<any[]>;
7
+ generatePlanTemplate: (vars: TemplateVariables) => Promise<string>;
8
+ logger?: Logger;
9
+ }
10
+
11
+ export class PromptBuilder {
12
+ private getTaskFiles: PromptBuilderDeps['getTaskFiles'];
13
+ private generatePlanTemplate: PromptBuilderDeps['generatePlanTemplate'];
14
+ private logger: Logger;
15
+
16
+ constructor(deps: PromptBuilderDeps) {
17
+ this.getTaskFiles = deps.getTaskFiles;
18
+ this.generatePlanTemplate = deps.generatePlanTemplate;
19
+ this.logger = deps.logger || new Logger({ debug: false, prefix: '[PromptBuilder]' });
20
+ }
21
+
22
+ async buildPlanningPrompt(task: Task): Promise<string> {
23
+ let prompt = '';
24
+ prompt += `## Current Task\n\n**Task**: ${task.title}\n**Description**: ${task.description}`;
25
+
26
+ if ((task as any).primary_repository) {
27
+ prompt += `\n**Repository**: ${(task as any).primary_repository}`;
28
+ }
29
+
30
+ try {
31
+ const taskFiles = await this.getTaskFiles(task.id);
32
+ const contextFiles = taskFiles.filter((f: any) => f.type === 'context' || f.type === 'reference');
33
+ if (contextFiles.length > 0) {
34
+ prompt += `\n\n## Supporting Files`;
35
+ for (const file of contextFiles) {
36
+ prompt += `\n\n### ${file.name} (${file.type})\n${file.content}`;
37
+ }
38
+ }
39
+ } catch (error) {
40
+ this.logger.debug('No existing task files found for planning', { taskId: task.id });
41
+ }
42
+
43
+ const templateVariables = {
44
+ task_id: task.id,
45
+ task_title: task.title,
46
+ task_description: task.description,
47
+ date: new Date().toISOString().split('T')[0],
48
+ repository: ((task as any).primary_repository || '') as string,
49
+ };
50
+
51
+ const planTemplate = await this.generatePlanTemplate(templateVariables);
52
+
53
+ prompt += `\n\nPlease analyze the codebase and create a detailed implementation plan for this task. Use the following template structure for your plan:\n\n${planTemplate}\n\nFill in each section with specific, actionable information based on your analysis. Replace all placeholder content with actual details about this task.`;
54
+
55
+ return prompt;
56
+ }
57
+
58
+ async buildExecutionPrompt(task: Task): Promise<string> {
59
+ let prompt = '';
60
+ prompt += `## Current Task\n\n**Task**: ${task.title}\n**Description**: ${task.description}`;
61
+
62
+ if ((task as any).primary_repository) {
63
+ prompt += `\n**Repository**: ${(task as any).primary_repository}`;
64
+ }
65
+
66
+ try {
67
+ const taskFiles = await this.getTaskFiles(task.id);
68
+ const hasPlan = taskFiles.some((f: any) => f.type === 'plan');
69
+ if (taskFiles.length > 0) {
70
+ prompt += `\n\n## Context and Supporting Information`;
71
+ for (const file of taskFiles) {
72
+ if (file.type === 'plan') {
73
+ prompt += `\n\n### Execution Plan\n${file.content}`;
74
+ } else {
75
+ prompt += `\n\n### ${file.name} (${file.type})\n${file.content}`;
76
+ }
77
+ }
78
+ }
79
+ if (hasPlan) {
80
+ prompt += `\n\nPlease implement the changes described in the execution plan above. Follow the plan step-by-step and make the necessary file modifications. You must actually edit files and make changes - do not just analyze or review.`;
81
+ } else {
82
+ prompt += `\n\nPlease implement the changes described in the task above. You must actually edit files and make changes - do not just analyze or review.`;
83
+ }
84
+ } catch (error) {
85
+ this.logger.debug('No supporting files found for execution', { taskId: task.id });
86
+ prompt += `\n\nPlease implement the changes described in the task above.`;
87
+ }
88
+ return prompt;
89
+ }
90
+ }
91
+
92
+
93
+