@posthog/agent 1.24.0 → 1.24.2

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 (116) hide show
  1. package/LICENSE +33 -0
  2. package/dist/index.d.ts +11 -11
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +3 -3
  5. package/dist/src/adapters/claude/claude-adapter.d.ts +3 -3
  6. package/dist/src/adapters/claude/claude-adapter.d.ts.map +1 -1
  7. package/dist/src/adapters/claude/claude-adapter.js +111 -156
  8. package/dist/src/adapters/claude/claude-adapter.js.map +1 -1
  9. package/dist/src/adapters/claude/tool-mapper.d.ts +1 -1
  10. package/dist/src/adapters/claude/tool-mapper.d.ts.map +1 -1
  11. package/dist/src/adapters/claude/tool-mapper.js.map +1 -1
  12. package/dist/src/adapters/types.d.ts +1 -1
  13. package/dist/src/adapters/types.d.ts.map +1 -1
  14. package/dist/src/agent.d.ts +7 -7
  15. package/dist/src/agent.d.ts.map +1 -1
  16. package/dist/src/agent.js +85 -143
  17. package/dist/src/agent.js.map +1 -1
  18. package/dist/src/agents/execution.js.map +1 -1
  19. package/dist/src/agents/planning.js.map +1 -1
  20. package/dist/src/agents/research.js.map +1 -1
  21. package/dist/src/file-manager.d.ts +4 -4
  22. package/dist/src/file-manager.d.ts.map +1 -1
  23. package/dist/src/file-manager.js +58 -59
  24. package/dist/src/file-manager.js.map +1 -1
  25. package/dist/src/git-manager.d.ts +1 -1
  26. package/dist/src/git-manager.d.ts.map +1 -1
  27. package/dist/src/git-manager.js +70 -87
  28. package/dist/src/git-manager.js.map +1 -1
  29. package/dist/src/posthog-api.d.ts +3 -2
  30. package/dist/src/posthog-api.d.ts.map +1 -1
  31. package/dist/src/posthog-api.js +22 -22
  32. package/dist/src/posthog-api.js.map +1 -1
  33. package/dist/src/prompt-builder.d.ts +3 -3
  34. package/dist/src/prompt-builder.d.ts.map +1 -1
  35. package/dist/src/prompt-builder.js +93 -123
  36. package/dist/src/prompt-builder.js.map +1 -1
  37. package/dist/src/task-manager.d.ts +4 -4
  38. package/dist/src/task-manager.d.ts.map +1 -1
  39. package/dist/src/task-manager.js +18 -19
  40. package/dist/src/task-manager.js.map +1 -1
  41. package/dist/src/task-progress-reporter.d.ts +4 -3
  42. package/dist/src/task-progress-reporter.d.ts.map +1 -1
  43. package/dist/src/task-progress-reporter.js +54 -59
  44. package/dist/src/task-progress-reporter.js.map +1 -1
  45. package/dist/src/template-manager.d.ts +1 -1
  46. package/dist/src/template-manager.d.ts.map +1 -1
  47. package/dist/src/template-manager.js +28 -30
  48. package/dist/src/template-manager.js.map +1 -1
  49. package/dist/src/todo-manager.d.ts +3 -3
  50. package/dist/src/todo-manager.d.ts.map +1 -1
  51. package/dist/src/todo-manager.js +24 -29
  52. package/dist/src/todo-manager.js.map +1 -1
  53. package/dist/src/tools/registry.d.ts +1 -1
  54. package/dist/src/tools/registry.js +60 -60
  55. package/dist/src/tools/registry.js.map +1 -1
  56. package/dist/src/tools/types.d.ts +31 -31
  57. package/dist/src/types.d.ts +33 -33
  58. package/dist/src/types.d.ts.map +1 -1
  59. package/dist/src/types.js.map +1 -1
  60. package/dist/src/utils/logger.d.ts +4 -4
  61. package/dist/src/utils/logger.d.ts.map +1 -1
  62. package/dist/src/utils/logger.js +8 -8
  63. package/dist/src/utils/logger.js.map +1 -1
  64. package/dist/src/workflow/config.d.ts +1 -1
  65. package/dist/src/workflow/config.d.ts.map +1 -1
  66. package/dist/src/workflow/config.js +18 -18
  67. package/dist/src/workflow/config.js.map +1 -1
  68. package/dist/src/workflow/steps/build.d.ts +1 -1
  69. package/dist/src/workflow/steps/build.d.ts.map +1 -1
  70. package/dist/src/workflow/steps/build.js +38 -46
  71. package/dist/src/workflow/steps/build.js.map +1 -1
  72. package/dist/src/workflow/steps/finalize.d.ts +1 -1
  73. package/dist/src/workflow/steps/finalize.d.ts.map +1 -1
  74. package/dist/src/workflow/steps/finalize.js +48 -54
  75. package/dist/src/workflow/steps/finalize.js.map +1 -1
  76. package/dist/src/workflow/steps/plan.d.ts +1 -1
  77. package/dist/src/workflow/steps/plan.d.ts.map +1 -1
  78. package/dist/src/workflow/steps/plan.js +46 -58
  79. package/dist/src/workflow/steps/plan.js.map +1 -1
  80. package/dist/src/workflow/steps/research.d.ts +1 -1
  81. package/dist/src/workflow/steps/research.d.ts.map +1 -1
  82. package/dist/src/workflow/steps/research.js +56 -68
  83. package/dist/src/workflow/steps/research.js.map +1 -1
  84. package/dist/src/workflow/types.d.ts +12 -12
  85. package/dist/src/workflow/types.d.ts.map +1 -1
  86. package/dist/src/workflow/utils.d.ts +1 -1
  87. package/dist/src/workflow/utils.d.ts.map +1 -1
  88. package/dist/src/workflow/utils.js +4 -7
  89. package/dist/src/workflow/utils.js.map +1 -1
  90. package/package.json +6 -6
  91. package/src/adapters/claude/claude-adapter.ts +168 -220
  92. package/src/adapters/claude/tool-mapper.ts +2 -2
  93. package/src/adapters/types.ts +1 -1
  94. package/src/agent.ts +444 -579
  95. package/src/agents/execution.ts +1 -1
  96. package/src/agents/planning.ts +1 -1
  97. package/src/agents/research.ts +1 -0
  98. package/src/file-manager.ts +63 -64
  99. package/src/git-manager.ts +88 -144
  100. package/src/posthog-api.ts +82 -122
  101. package/src/prompt-builder.ts +135 -180
  102. package/src/task-manager.ts +30 -38
  103. package/src/task-progress-reporter.ts +59 -70
  104. package/src/template-manager.ts +45 -98
  105. package/src/todo-manager.ts +30 -35
  106. package/src/tools/registry.ts +62 -62
  107. package/src/tools/types.ts +36 -36
  108. package/src/types.ts +71 -93
  109. package/src/utils/logger.ts +56 -62
  110. package/src/workflow/config.ts +48 -48
  111. package/src/workflow/steps/build.ts +113 -122
  112. package/src/workflow/steps/finalize.ts +182 -214
  113. package/src/workflow/steps/plan.ts +131 -151
  114. package/src/workflow/steps/research.ts +186 -205
  115. package/src/workflow/types.ts +36 -38
  116. package/src/workflow/utils.ts +34 -37
@@ -1,30 +1,27 @@
1
- import { promises as fs } from "node:fs";
2
- import { join } from "node:path";
3
- import type { TemplateVariables } from "./template-manager.js";
4
- import type { PostHogResource, Task, UrlMention } from "./types.js";
5
- import { Logger } from "./utils/logger.js";
1
+ import type { Task, UrlMention, PostHogResource } from './types.js';
2
+ import type { TemplateVariables } from './template-manager.js';
3
+ import { Logger } from './utils/logger.js';
4
+ import { promises as fs } from 'fs';
5
+ import { join } from 'path';
6
6
 
7
7
  export interface PromptBuilderDeps {
8
8
  getTaskFiles: (taskId: string) => Promise<any[]>;
9
9
  generatePlanTemplate: (vars: TemplateVariables) => Promise<string>;
10
- posthogClient?: {
11
- fetchResourceByUrl: (mention: UrlMention) => Promise<PostHogResource>;
12
- };
10
+ posthogClient?: { fetchResourceByUrl: (mention: UrlMention) => Promise<PostHogResource> };
13
11
  logger?: Logger;
14
12
  }
15
13
 
16
14
  export class PromptBuilder {
17
- private getTaskFiles: PromptBuilderDeps["getTaskFiles"];
18
- private generatePlanTemplate: PromptBuilderDeps["generatePlanTemplate"];
19
- private posthogClient?: PromptBuilderDeps["posthogClient"];
15
+ private getTaskFiles: PromptBuilderDeps['getTaskFiles'];
16
+ private generatePlanTemplate: PromptBuilderDeps['generatePlanTemplate'];
17
+ private posthogClient?: PromptBuilderDeps['posthogClient'];
20
18
  private logger: Logger;
21
19
 
22
20
  constructor(deps: PromptBuilderDeps) {
23
21
  this.getTaskFiles = deps.getTaskFiles;
24
22
  this.generatePlanTemplate = deps.generatePlanTemplate;
25
23
  this.posthogClient = deps.posthogClient;
26
- this.logger =
27
- deps.logger || new Logger({ debug: false, prefix: "[PromptBuilder]" });
24
+ this.logger = deps.logger || new Logger({ debug: false, prefix: '[PromptBuilder]' });
28
25
  }
29
26
 
30
27
  /**
@@ -36,10 +33,8 @@ export class PromptBuilder {
36
33
  const paths: string[] = [];
37
34
  let match: RegExpExecArray | null;
38
35
 
39
- match = fileTagRegex.exec(description);
40
- while (match !== null) {
36
+ while ((match = fileTagRegex.exec(description)) !== null) {
41
37
  paths.push(match[1]);
42
- match = fileTagRegex.exec(description);
43
38
  }
44
39
 
45
40
  return paths;
@@ -48,18 +43,13 @@ export class PromptBuilder {
48
43
  /**
49
44
  * Read file contents from repository
50
45
  */
51
- private async readFileContent(
52
- repositoryPath: string,
53
- filePath: string,
54
- ): Promise<string | null> {
46
+ private async readFileContent(repositoryPath: string, filePath: string): Promise<string | null> {
55
47
  try {
56
48
  const fullPath = join(repositoryPath, filePath);
57
- const content = await fs.readFile(fullPath, "utf8");
49
+ const content = await fs.readFile(fullPath, 'utf8');
58
50
  return content;
59
51
  } catch (error) {
60
- this.logger.warn(`Failed to read referenced file: ${filePath}`, {
61
- error,
62
- });
52
+ this.logger.warn(`Failed to read referenced file: ${filePath}`, { error });
63
53
  return null;
64
54
  }
65
55
  }
@@ -70,35 +60,30 @@ export class PromptBuilder {
70
60
  */
71
61
  private extractUrlMentions(description: string): UrlMention[] {
72
62
  const mentions: UrlMention[] = [];
73
-
63
+
74
64
  // PostHog resource mentions: <error id="..." />, <experiment id="..." />, etc.
75
- const resourceRegex =
76
- /<(error|experiment|insight|feature_flag)\s+id="([^"]+)"\s*\/>/g;
65
+ const resourceRegex = /<(error|experiment|insight|feature_flag)\s+id="([^"]+)"\s*\/>/g;
77
66
  let match: RegExpExecArray | null;
78
67
 
79
- match = resourceRegex.exec(description);
80
- while (match !== null) {
68
+ while ((match = resourceRegex.exec(description)) !== null) {
81
69
  const [, type, id] = match;
82
70
  mentions.push({
83
- url: "", // Will be reconstructed if needed
71
+ url: '', // Will be reconstructed if needed
84
72
  type: type as any,
85
73
  id,
86
- label: this.generateUrlLabel("", type as any),
74
+ label: this.generateUrlLabel('', type as any),
87
75
  });
88
- match = resourceRegex.exec(description);
89
76
  }
90
77
 
91
78
  // Generic URL mentions: <url href="..." />
92
79
  const urlRegex = /<url\s+href="([^"]+)"\s*\/>/g;
93
- match = urlRegex.exec(description);
94
- while (match !== null) {
80
+ while ((match = urlRegex.exec(description)) !== null) {
95
81
  const [, url] = match;
96
82
  mentions.push({
97
83
  url,
98
- type: "generic",
99
- label: this.generateUrlLabel(url, "generic"),
84
+ type: 'generic',
85
+ label: this.generateUrlLabel(url, 'generic'),
100
86
  });
101
- match = urlRegex.exec(description);
102
87
  }
103
88
 
104
89
  return mentions;
@@ -111,23 +96,21 @@ export class PromptBuilder {
111
96
  try {
112
97
  const urlObj = new URL(url);
113
98
  switch (type) {
114
- case "error": {
99
+ case 'error':
115
100
  const errorMatch = url.match(/error_tracking\/([a-f0-9-]+)/);
116
- return errorMatch ? `Error ${errorMatch[1].slice(0, 8)}...` : "Error";
117
- }
118
- case "experiment": {
101
+ return errorMatch ? `Error ${errorMatch[1].slice(0, 8)}...` : 'Error';
102
+ case 'experiment':
119
103
  const expMatch = url.match(/experiments\/(\d+)/);
120
- return expMatch ? `Experiment #${expMatch[1]}` : "Experiment";
121
- }
122
- case "insight":
123
- return "Insight";
124
- case "feature_flag":
125
- return "Feature Flag";
104
+ return expMatch ? `Experiment #${expMatch[1]}` : 'Experiment';
105
+ case 'insight':
106
+ return 'Insight';
107
+ case 'feature_flag':
108
+ return 'Feature Flag';
126
109
  default:
127
110
  return urlObj.hostname;
128
111
  }
129
112
  } catch {
130
- return "URL";
113
+ return 'URL';
131
114
  }
132
115
  }
133
116
 
@@ -135,7 +118,7 @@ export class PromptBuilder {
135
118
  * Process URL references and fetch their content
136
119
  */
137
120
  private async processUrlReferences(
138
- description: string,
121
+ description: string
139
122
  ): Promise<{ description: string; referencedResources: PostHogResource[] }> {
140
123
  const urlMentions = this.extractUrlMentions(description);
141
124
  const referencedResources: PostHogResource[] = [];
@@ -150,15 +133,13 @@ export class PromptBuilder {
150
133
  const resource = await this.posthogClient.fetchResourceByUrl(mention);
151
134
  referencedResources.push(resource);
152
135
  } catch (error) {
153
- this.logger.warn(`Failed to fetch resource from URL: ${mention.url}`, {
154
- error,
155
- });
136
+ this.logger.warn(`Failed to fetch resource from URL: ${mention.url}`, { error });
156
137
  // Add a placeholder resource for failed fetches
157
138
  referencedResources.push({
158
139
  type: mention.type,
159
- id: mention.id || "",
140
+ id: mention.id || '',
160
141
  url: mention.url,
161
- title: mention.label || "Unknown Resource",
142
+ title: mention.label || 'Unknown Resource',
162
143
  content: `Failed to fetch resource from ${mention.url}: ${error}`,
163
144
  metadata: {},
164
145
  });
@@ -168,22 +149,20 @@ export class PromptBuilder {
168
149
  // Replace URL tags with just the label for readability
169
150
  let processedDescription = description;
170
151
  for (const mention of urlMentions) {
171
- if (mention.type === "generic") {
152
+ if (mention.type === 'generic') {
172
153
  // Generic URLs: <url href="..." />
173
- const escapedUrl = mention.url.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
154
+ const escapedUrl = mention.url.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
174
155
  processedDescription = processedDescription.replace(
175
- new RegExp(`<url\\s+href="${escapedUrl}"\\s*/>`, "g"),
176
- `@${mention.label}`,
156
+ new RegExp(`<url\\s+href="${escapedUrl}"\\s*/>`, 'g'),
157
+ `@${mention.label}`
177
158
  );
178
159
  } else {
179
160
  // PostHog resources: <error id="..." />, <experiment id="..." />, etc.
180
- const escapedType = mention.type.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
181
- const escapedId = mention.id
182
- ? mention.id.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")
183
- : "";
161
+ const escapedType = mention.type.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
162
+ const escapedId = mention.id ? mention.id.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') : '';
184
163
  processedDescription = processedDescription.replace(
185
- new RegExp(`<${escapedType}\\s+id="${escapedId}"\\s*/>`, "g"),
186
- `@${mention.label}`,
164
+ new RegExp(`<${escapedType}\\s+id="${escapedId}"\\s*/>`, 'g'),
165
+ `@${mention.label}`
187
166
  );
188
167
  }
189
168
  }
@@ -197,11 +176,8 @@ export class PromptBuilder {
197
176
  */
198
177
  private async processFileReferences(
199
178
  description: string,
200
- repositoryPath?: string,
201
- ): Promise<{
202
- description: string;
203
- referencedFiles: Array<{ path: string; content: string }>;
204
- }> {
179
+ repositoryPath?: string
180
+ ): Promise<{ description: string; referencedFiles: Array<{ path: string; content: string }> }> {
205
181
  const filePaths = this.extractFilePaths(description);
206
182
  const referencedFiles: Array<{ path: string; content: string }> = [];
207
183
 
@@ -220,221 +196,211 @@ export class PromptBuilder {
220
196
  // Replace file tags with just the filename for readability
221
197
  let processedDescription = description;
222
198
  for (const filePath of filePaths) {
223
- const fileName = filePath.split("/").pop() || filePath;
199
+ const fileName = filePath.split('/').pop() || filePath;
224
200
  processedDescription = processedDescription.replace(
225
- new RegExp(
226
- `<file\\s+path="${filePath.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}"\\s*/>`,
227
- "g",
228
- ),
229
- `@${fileName}`,
201
+ new RegExp(`<file\\s+path="${filePath.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}"\\s*/>`, 'g'),
202
+ `@${fileName}`
230
203
  );
231
204
  }
232
205
 
233
206
  return { description: processedDescription, referencedFiles };
234
207
  }
235
208
 
236
- async buildResearchPrompt(
237
- task: Task,
238
- repositoryPath?: string,
239
- ): Promise<string> {
209
+ async buildResearchPrompt(task: Task, repositoryPath?: string): Promise<string> {
240
210
  // Process file references in description
241
- const { description: descriptionAfterFiles, referencedFiles } =
242
- await this.processFileReferences(task.description, repositoryPath);
211
+ const { description: descriptionAfterFiles, referencedFiles } = await this.processFileReferences(
212
+ task.description,
213
+ repositoryPath
214
+ );
243
215
 
244
216
  // Process URL references in description
245
- const { description: processedDescription, referencedResources } =
246
- await this.processUrlReferences(descriptionAfterFiles);
217
+ const { description: processedDescription, referencedResources } = await this.processUrlReferences(
218
+ descriptionAfterFiles
219
+ );
247
220
 
248
- let prompt = "<task>\n";
221
+ let prompt = '<task>\n';
249
222
  prompt += `<title>${task.title}</title>\n`;
250
223
  prompt += `<description>${processedDescription}</description>\n`;
251
224
 
252
225
  if ((task as any).primary_repository) {
253
226
  prompt += `<repository>${(task as any).primary_repository}</repository>\n`;
254
227
  }
255
- prompt += "</task>\n";
228
+ prompt += '</task>\n';
256
229
 
257
230
  // Add referenced files from @ mentions
258
231
  if (referencedFiles.length > 0) {
259
- prompt += "\n<referenced_files>\n";
232
+ prompt += '\n<referenced_files>\n';
260
233
  for (const file of referencedFiles) {
261
234
  prompt += `<file path="${file.path}">\n\`\`\`\n${file.content}\n\`\`\`\n</file>\n`;
262
235
  }
263
- prompt += "</referenced_files>\n";
236
+ prompt += '</referenced_files>\n';
264
237
  }
265
238
 
266
239
  // Add referenced resources from URL mentions
267
240
  if (referencedResources.length > 0) {
268
- prompt += "\n<referenced_resources>\n";
241
+ prompt += '\n<referenced_resources>\n';
269
242
  for (const resource of referencedResources) {
270
243
  prompt += `<resource type="${resource.type}" url="${resource.url}">\n`;
271
244
  prompt += `<title>${resource.title}</title>\n`;
272
245
  prompt += `<content>${resource.content}</content>\n`;
273
- prompt += "</resource>\n";
246
+ prompt += '</resource>\n';
274
247
  }
275
- prompt += "</referenced_resources>\n";
248
+ prompt += '</referenced_resources>\n';
276
249
  }
277
250
 
278
251
  try {
279
252
  const taskFiles = await this.getTaskFiles(task.id);
280
- const contextFiles = taskFiles.filter(
281
- (f: any) => f.type === "context" || f.type === "reference",
282
- );
253
+ const contextFiles = taskFiles.filter((f: any) => f.type === 'context' || f.type === 'reference');
283
254
  if (contextFiles.length > 0) {
284
- prompt += "\n<supporting_files>\n";
255
+ prompt += '\n<supporting_files>\n';
285
256
  for (const file of contextFiles) {
286
257
  prompt += `<file name="${file.name}" type="${file.type}">\n${file.content}\n</file>\n`;
287
258
  }
288
- prompt += "</supporting_files>\n";
259
+ prompt += '</supporting_files>\n';
289
260
  }
290
- } catch (_error) {
291
- this.logger.debug("No existing task files found for research", {
292
- taskId: task.id,
293
- });
261
+ } catch (error) {
262
+ this.logger.debug('No existing task files found for research', { taskId: task.id });
294
263
  }
295
264
 
296
265
  return prompt;
297
266
  }
298
267
 
299
- async buildPlanningPrompt(
300
- task: Task,
301
- repositoryPath?: string,
302
- ): Promise<string> {
268
+ async buildPlanningPrompt(task: Task, repositoryPath?: string): Promise<string> {
303
269
  // Process file references in description
304
- const { description: descriptionAfterFiles, referencedFiles } =
305
- await this.processFileReferences(task.description, repositoryPath);
270
+ const { description: descriptionAfterFiles, referencedFiles } = await this.processFileReferences(
271
+ task.description,
272
+ repositoryPath
273
+ );
306
274
 
307
275
  // Process URL references in description
308
- const { description: processedDescription, referencedResources } =
309
- await this.processUrlReferences(descriptionAfterFiles);
276
+ const { description: processedDescription, referencedResources } = await this.processUrlReferences(
277
+ descriptionAfterFiles
278
+ );
310
279
 
311
- let prompt = "<task>\n";
280
+ let prompt = '<task>\n';
312
281
  prompt += `<title>${task.title}</title>\n`;
313
282
  prompt += `<description>${processedDescription}</description>\n`;
314
283
 
315
284
  if ((task as any).primary_repository) {
316
285
  prompt += `<repository>${(task as any).primary_repository}</repository>\n`;
317
286
  }
318
- prompt += "</task>\n";
287
+ prompt += '</task>\n';
319
288
 
320
289
  // Add referenced files from @ mentions
321
290
  if (referencedFiles.length > 0) {
322
- prompt += "\n<referenced_files>\n";
291
+ prompt += '\n<referenced_files>\n';
323
292
  for (const file of referencedFiles) {
324
293
  prompt += `<file path="${file.path}">\n\`\`\`\n${file.content}\n\`\`\`\n</file>\n`;
325
294
  }
326
- prompt += "</referenced_files>\n";
295
+ prompt += '</referenced_files>\n';
327
296
  }
328
297
 
329
298
  // Add referenced resources from URL mentions
330
299
  if (referencedResources.length > 0) {
331
- prompt += "\n<referenced_resources>\n";
300
+ prompt += '\n<referenced_resources>\n';
332
301
  for (const resource of referencedResources) {
333
302
  prompt += `<resource type="${resource.type}" url="${resource.url}">\n`;
334
303
  prompt += `<title>${resource.title}</title>\n`;
335
304
  prompt += `<content>${resource.content}</content>\n`;
336
- prompt += "</resource>\n";
305
+ prompt += '</resource>\n';
337
306
  }
338
- prompt += "</referenced_resources>\n";
307
+ prompt += '</referenced_resources>\n';
339
308
  }
340
309
 
341
310
  try {
342
311
  const taskFiles = await this.getTaskFiles(task.id);
343
- const contextFiles = taskFiles.filter(
344
- (f: any) => f.type === "context" || f.type === "reference",
345
- );
312
+ const contextFiles = taskFiles.filter((f: any) => f.type === 'context' || f.type === 'reference');
346
313
  if (contextFiles.length > 0) {
347
- prompt += "\n<supporting_files>\n";
314
+ prompt += '\n<supporting_files>\n';
348
315
  for (const file of contextFiles) {
349
316
  prompt += `<file name="${file.name}" type="${file.type}">\n${file.content}\n</file>\n`;
350
317
  }
351
- prompt += "</supporting_files>\n";
318
+ prompt += '</supporting_files>\n';
352
319
  }
353
- } catch (_error) {
354
- this.logger.debug("No existing task files found for planning", {
355
- taskId: task.id,
356
- });
320
+ } catch (error) {
321
+ this.logger.debug('No existing task files found for planning', { taskId: task.id });
357
322
  }
358
323
 
359
324
  const templateVariables = {
360
325
  task_id: task.id,
361
326
  task_title: task.title,
362
327
  task_description: processedDescription,
363
- date: new Date().toISOString().split("T")[0],
364
- repository: ((task as any).primary_repository || "") as string,
328
+ date: new Date().toISOString().split('T')[0],
329
+ repository: ((task as any).primary_repository || '') as string,
365
330
  };
366
331
 
367
332
  const planTemplate = await this.generatePlanTemplate(templateVariables);
368
333
 
369
- prompt += "\n<instructions>\n";
370
- prompt +=
371
- "Analyze the codebase and create a detailed implementation plan. Use the template structure below, filling each section with specific, actionable information.\n";
372
- prompt += "</instructions>\n\n";
373
- prompt += "<plan_template>\n";
334
+ prompt += '\n<instructions>\n';
335
+ prompt += 'Analyze the codebase and create a detailed implementation plan. Use the template structure below, filling each section with specific, actionable information.\n';
336
+ prompt += '</instructions>\n\n';
337
+ prompt += '<plan_template>\n';
374
338
  prompt += planTemplate;
375
- prompt += "\n</plan_template>";
339
+ prompt += '\n</plan_template>';
376
340
 
377
341
  return prompt;
378
342
  }
379
343
 
380
- async buildExecutionPrompt(
381
- task: Task,
382
- repositoryPath?: string,
383
- ): Promise<string> {
344
+ async buildExecutionPrompt(task: Task, repositoryPath?: string): Promise<string> {
384
345
  // Process file references in description
385
- const { description: descriptionAfterFiles, referencedFiles } =
386
- await this.processFileReferences(task.description, repositoryPath);
346
+ const { description: descriptionAfterFiles, referencedFiles } = await this.processFileReferences(
347
+ task.description,
348
+ repositoryPath
349
+ );
387
350
 
388
351
  // Process URL references in description
389
- const { description: processedDescription, referencedResources } =
390
- await this.processUrlReferences(descriptionAfterFiles);
352
+ const { description: processedDescription, referencedResources } = await this.processUrlReferences(
353
+ descriptionAfterFiles
354
+ );
391
355
 
392
- let prompt = "<task>\n";
356
+ let prompt = '<task>\n';
393
357
  prompt += `<title>${task.title}</title>\n`;
394
358
  prompt += `<description>${processedDescription}</description>\n`;
395
359
 
396
360
  if ((task as any).primary_repository) {
397
361
  prompt += `<repository>${(task as any).primary_repository}</repository>\n`;
398
362
  }
399
- prompt += "</task>\n";
363
+ prompt += '</task>\n';
400
364
 
401
365
  // Add referenced files from @ mentions
402
366
  if (referencedFiles.length > 0) {
403
- prompt += "\n<referenced_files>\n";
367
+ prompt += '\n<referenced_files>\n';
404
368
  for (const file of referencedFiles) {
405
369
  prompt += `<file path="${file.path}">\n\`\`\`\n${file.content}\n\`\`\`\n</file>\n`;
406
370
  }
407
- prompt += "</referenced_files>\n";
371
+ prompt += '</referenced_files>\n';
408
372
  }
409
373
 
410
374
  // Add referenced resources from URL mentions
411
375
  if (referencedResources.length > 0) {
412
- prompt += "\n<referenced_resources>\n";
376
+ prompt += '\n<referenced_resources>\n';
413
377
  for (const resource of referencedResources) {
414
378
  prompt += `<resource type="${resource.type}" url="${resource.url}">\n`;
415
379
  prompt += `<title>${resource.title}</title>\n`;
416
380
  prompt += `<content>${resource.content}</content>\n`;
417
- prompt += "</resource>\n";
381
+ prompt += '</resource>\n';
418
382
  }
419
- prompt += "</referenced_resources>\n";
383
+ prompt += '</referenced_resources>\n';
420
384
  }
421
385
 
422
386
  try {
423
387
  const taskFiles = await this.getTaskFiles(task.id);
424
- const hasPlan = taskFiles.some((f: any) => f.type === "plan");
425
- const todosFile = taskFiles.find((f: any) => f.name === "todos.json");
388
+ const hasPlan = taskFiles.some((f: any) => f.type === 'plan');
389
+ const todosFile = taskFiles.find((f: any) => f.name === 'todos.json');
426
390
 
427
391
  if (taskFiles.length > 0) {
428
- prompt += "\n<context>\n";
392
+ prompt += '\n<context>\n';
429
393
  for (const file of taskFiles) {
430
- if (file.type === "plan") {
394
+ if (file.type === 'plan') {
431
395
  prompt += `<plan>\n${file.content}\n</plan>\n`;
432
- } else if (file.name === "todos.json") {
396
+ } else if (file.name === 'todos.json') {
397
+ // skip - we do this below
398
+ continue;
433
399
  } else {
434
400
  prompt += `<file name="${file.name}" type="${file.type}">\n${file.content}\n</file>\n`;
435
401
  }
436
402
  }
437
- prompt += "</context>\n";
403
+ prompt += '</context>\n';
438
404
  }
439
405
 
440
406
  // Add todos context if resuming work
@@ -442,48 +408,37 @@ export class PromptBuilder {
442
408
  try {
443
409
  const todos = JSON.parse(todosFile.content);
444
410
  if (todos.items && todos.items.length > 0) {
445
- prompt += "\n<previous_todos>\n";
446
- prompt +=
447
- "You previously created the following todo list for this task:\n\n";
411
+ prompt += '\n<previous_todos>\n';
412
+ prompt += 'You previously created the following todo list for this task:\n\n';
448
413
  for (const item of todos.items) {
449
- const statusIcon =
450
- item.status === "completed"
451
- ? "✓"
452
- : item.status === "in_progress"
453
- ? "▶"
454
- : "○";
414
+ const statusIcon = item.status === 'completed' ? '✓' : item.status === 'in_progress' ? '▶' : '○';
455
415
  prompt += `${statusIcon} [${item.status}] ${item.content}\n`;
456
416
  }
457
417
  prompt += `\nProgress: ${todos.metadata.completed}/${todos.metadata.total} completed\n`;
458
- prompt +=
459
- "\nYou can reference this list when resuming work or create an updated list as needed.\n";
460
- prompt += "</previous_todos>\n";
418
+ prompt += '\nYou can reference this list when resuming work or create an updated list as needed.\n';
419
+ prompt += '</previous_todos>\n';
461
420
  }
462
421
  } catch (error) {
463
- this.logger.debug("Failed to parse todos.json for context", {
464
- error,
465
- });
422
+ this.logger.debug('Failed to parse todos.json for context', { error });
466
423
  }
467
424
  }
468
425
 
469
- prompt += "\n<instructions>\n";
426
+ prompt += '\n<instructions>\n';
470
427
  if (hasPlan) {
471
- prompt +=
472
- "Implement the changes described in the execution plan. Follow the plan step-by-step and make the necessary file modifications.\n";
428
+ prompt += 'Implement the changes described in the execution plan. Follow the plan step-by-step and make the necessary file modifications.\n';
473
429
  } else {
474
- prompt +=
475
- "Implement the changes described in the task. Make the necessary file modifications to complete the task.\n";
430
+ prompt += 'Implement the changes described in the task. Make the necessary file modifications to complete the task.\n';
476
431
  }
477
- prompt += "</instructions>";
478
- } catch (_error) {
479
- this.logger.debug("No supporting files found for execution", {
480
- taskId: task.id,
481
- });
482
- prompt += "\n<instructions>\n";
483
- prompt += "Implement the changes described in the task.\n";
484
- prompt += "</instructions>";
432
+ prompt += '</instructions>';
433
+ } catch (error) {
434
+ this.logger.debug('No supporting files found for execution', { taskId: task.id });
435
+ prompt += '\n<instructions>\n';
436
+ prompt += 'Implement the changes described in the task.\n';
437
+ prompt += '</instructions>';
485
438
  }
486
439
 
487
440
  return prompt;
488
441
  }
489
442
  }
443
+
444
+