@posthog/agent 1.30.0 → 2.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.
package/src/agent.ts CHANGED
@@ -9,14 +9,12 @@ import { POSTHOG_NOTIFICATIONS } from "./acp-extensions.js";
9
9
  import {
10
10
  createAcpConnection,
11
11
  type InProcessAcpConnection,
12
- } from "./adapters/claude/claude.js";
12
+ } from "./adapters/connection.js";
13
13
  import { PostHogFileManager } from "./file-manager.js";
14
14
  import { GitManager } from "./git-manager.js";
15
15
  import { PostHogAPIClient } from "./posthog-api.js";
16
- import { PromptBuilder } from "./prompt-builder.js";
17
16
  import { SessionStore } from "./session-store.js";
18
17
  import { TaskManager } from "./task-manager.js";
19
- import { TemplateManager } from "./template-manager.js";
20
18
  import type {
21
19
  AgentConfig,
22
20
  CanUseTool,
@@ -25,8 +23,14 @@ import type {
25
23
  TaskExecutionOptions,
26
24
  } from "./types.js";
27
25
  import { Logger } from "./utils/logger.js";
28
- import { TASK_WORKFLOW } from "./workflow/config.js";
29
- import type { SendNotification, WorkflowRuntime } from "./workflow/types.js";
26
+
27
+ /**
28
+ * Type for sending ACP notifications
29
+ */
30
+ type SendNotification = (
31
+ method: string,
32
+ params: Record<string, unknown>,
33
+ ) => Promise<void>;
30
34
 
31
35
  export class Agent {
32
36
  private workingDirectory: string;
@@ -34,10 +38,8 @@ export class Agent {
34
38
  private posthogAPI?: PostHogAPIClient;
35
39
  private fileManager: PostHogFileManager;
36
40
  private gitManager: GitManager;
37
- private templateManager: TemplateManager;
38
41
  private logger: Logger;
39
42
  private acpConnection?: InProcessAcpConnection;
40
- private promptBuilder: PromptBuilder;
41
43
  private mcpServers?: Record<string, any>;
42
44
  private canUseTool?: CanUseTool;
43
45
  private currentRunId?: string;
@@ -89,7 +91,6 @@ export class Agent {
89
91
  repositoryPath: this.workingDirectory,
90
92
  logger: this.logger.child("GitManager"),
91
93
  });
92
- this.templateManager = new TemplateManager();
93
94
 
94
95
  if (
95
96
  config.posthogApiUrl &&
@@ -108,13 +109,6 @@ export class Agent {
108
109
  this.logger.child("SessionStore"),
109
110
  );
110
111
  }
111
-
112
- this.promptBuilder = new PromptBuilder({
113
- getTaskFiles: (taskId: string) => this.getTaskFiles(taskId),
114
- generatePlanTemplate: (vars) => this.templateManager.generatePlan(vars),
115
- posthogClient: this.posthogAPI,
116
- logger: this.logger.child("PromptBuilder"),
117
- });
118
112
  }
119
113
 
120
114
  /**
@@ -146,107 +140,18 @@ export class Agent {
146
140
  }
147
141
  }
148
142
 
149
- private getOrCreateConnection(): InProcessAcpConnection {
150
- if (!this.acpConnection) {
151
- this.acpConnection = createAcpConnection({
152
- sessionStore: this.sessionStore,
153
- });
154
- }
155
- return this.acpConnection;
156
- }
157
-
158
- // Adaptive task execution orchestrated via workflow steps
143
+ /**
144
+ * @deprecated Use runTaskV2() for local execution or runTaskCloud() for cloud execution.
145
+ * This method used the old workflow system which has been removed.
146
+ */
159
147
  async runTask(
160
- taskId: string,
161
- taskRunId: string,
162
- options: import("./types.js").TaskExecutionOptions = {},
148
+ _taskId: string,
149
+ _taskRunId: string,
150
+ _options: import("./types.js").TaskExecutionOptions = {},
163
151
  ): Promise<void> {
164
- // await this._configureLlmGateway();
165
-
166
- const task = await this.fetchTask(taskId);
167
- const cwd = options.repositoryPath || this.workingDirectory;
168
- const isCloudMode = options.isCloudMode ?? false;
169
- const taskSlug = (task as any).slug || task.id;
170
-
171
- // Use taskRunId as sessionId - they are the same identifier
172
- this.currentRunId = taskRunId;
173
-
174
- this.logger.info("Starting adaptive task execution", {
175
- taskId: task.id,
176
- taskSlug,
177
- taskRunId,
178
- isCloudMode,
179
- });
180
-
181
- const connection = this.getOrCreateConnection();
182
-
183
- // Create sendNotification using ACP connection's extNotification
184
- const sendNotification: SendNotification = async (method, params) => {
185
- this.logger.debug(`Notification: ${method}`, params);
186
- await connection.agentConnection.extNotification?.(method, params);
187
- };
188
-
189
- await sendNotification(POSTHOG_NOTIFICATIONS.RUN_STARTED, {
190
- sessionId: taskRunId,
191
- runId: taskRunId,
192
- });
193
-
194
- await this.prepareTaskBranch(taskSlug, isCloudMode, sendNotification);
195
-
196
- let taskError: Error | undefined;
197
- try {
198
- const workflowContext: WorkflowRuntime = {
199
- task,
200
- taskSlug,
201
- runId: taskRunId,
202
- cwd,
203
- isCloudMode,
204
- options,
205
- logger: this.logger,
206
- fileManager: this.fileManager,
207
- gitManager: this.gitManager,
208
- promptBuilder: this.promptBuilder,
209
- connection: connection.agentConnection,
210
- sessionId: taskRunId,
211
- sendNotification,
212
- mcpServers: this.mcpServers,
213
- posthogAPI: this.posthogAPI,
214
- stepResults: {},
215
- };
216
-
217
- for (const step of TASK_WORKFLOW) {
218
- const result = await step.run({ step, context: workflowContext });
219
- if (result.halt) {
220
- return;
221
- }
222
- }
223
-
224
- const shouldCreatePR = options.createPR ?? isCloudMode;
225
- if (shouldCreatePR) {
226
- await this.ensurePullRequest(
227
- task,
228
- workflowContext.stepResults,
229
- sendNotification,
230
- );
231
- }
232
-
233
- this.logger.info("Task execution complete", { taskId: task.id });
234
- await sendNotification(POSTHOG_NOTIFICATIONS.TASK_COMPLETE, {
235
- sessionId: taskRunId,
236
- taskId: task.id,
237
- });
238
- } catch (error) {
239
- taskError = error instanceof Error ? error : new Error(String(error));
240
- this.logger.error("Task execution failed", {
241
- taskId: task.id,
242
- error: taskError.message,
243
- });
244
- await sendNotification(POSTHOG_NOTIFICATIONS.ERROR, {
245
- sessionId: taskRunId,
246
- message: taskError.message,
247
- });
248
- throw taskError;
249
- }
152
+ throw new Error(
153
+ "runTask() is deprecated. Use runTaskV2() for local execution or runTaskCloud() for cloud execution.",
154
+ );
250
155
  }
251
156
 
252
157
  /**
@@ -263,8 +168,6 @@ export class Agent {
263
168
  ): Promise<InProcessAcpConnection> {
264
169
  await this._configureLlmGateway();
265
170
 
266
- const task = await this.fetchTask(taskId);
267
- const taskSlug = (task as any).slug || task.id;
268
171
  const isCloudMode = options.isCloudMode ?? false;
269
172
  const _cwd = options.repositoryPath || this.workingDirectory;
270
173
 
@@ -272,9 +175,10 @@ export class Agent {
272
175
  this.currentRunId = taskRunId;
273
176
 
274
177
  this.acpConnection = createAcpConnection({
178
+ framework: options.framework,
275
179
  sessionStore: this.sessionStore,
276
180
  sessionId: taskRunId,
277
- taskId: task.id,
181
+ taskId,
278
182
  });
279
183
 
280
184
  const sendNotification: SendNotification = async (method, params) => {
@@ -285,12 +189,17 @@ export class Agent {
285
189
  );
286
190
  };
287
191
 
288
- await sendNotification(POSTHOG_NOTIFICATIONS.RUN_STARTED, {
289
- sessionId: taskRunId,
290
- runId: taskRunId,
291
- });
192
+ if (!options.isReconnect) {
193
+ await sendNotification(POSTHOG_NOTIFICATIONS.RUN_STARTED, {
194
+ sessionId: taskRunId,
195
+ runId: taskRunId,
196
+ });
197
+ }
292
198
 
199
+ // Only fetch task when we need the slug for git branch creation
293
200
  if (!options.skipGitBranch) {
201
+ const task = options.task ?? (await this.fetchTask(taskId));
202
+ const taskSlug = (task as any).slug || task.id;
294
203
  try {
295
204
  await this.prepareTaskBranch(taskSlug, isCloudMode, sendNotification);
296
205
  } catch (error) {
@@ -753,61 +662,6 @@ This PR implements the changes described in the task.`;
753
662
  throw error;
754
663
  }
755
664
  }
756
-
757
- private async ensurePullRequest(
758
- task: Task,
759
- stepResults: Record<string, any>,
760
- sendNotification: SendNotification,
761
- ): Promise<void> {
762
- const latestRun = task.latest_run;
763
- const existingPr =
764
- latestRun?.output && typeof latestRun.output === "object"
765
- ? (latestRun.output as any).pr_url
766
- : null;
767
-
768
- if (existingPr) {
769
- this.logger.info("PR already exists, skipping creation", {
770
- taskId: task.id,
771
- prUrl: existingPr,
772
- });
773
- return;
774
- }
775
-
776
- const buildResult = stepResults.build;
777
- if (!buildResult?.commitCreated) {
778
- this.logger.warn(
779
- "Build step did not produce a commit; skipping PR creation",
780
- { taskId: task.id },
781
- );
782
- return;
783
- }
784
-
785
- const branchName = await this.gitManager.getCurrentBranch();
786
- const finalizeResult = stepResults.finalize;
787
- const prBody = finalizeResult?.prBody;
788
-
789
- const prUrl = await this.createPullRequest(
790
- task.id,
791
- branchName,
792
- task.title,
793
- task.description ?? "",
794
- prBody,
795
- );
796
-
797
- await sendNotification(POSTHOG_NOTIFICATIONS.PR_CREATED, { prUrl });
798
-
799
- try {
800
- await this.attachPullRequestToTask(task.id, prUrl, branchName);
801
- this.logger.info("PR attached to task successfully", {
802
- taskId: task.id,
803
- prUrl,
804
- });
805
- } catch (error) {
806
- this.logger.warn("Could not attach PR to task", {
807
- error: error instanceof Error ? error.message : String(error),
808
- });
809
- }
810
- }
811
665
  }
812
666
 
813
667
  export type {
@@ -1,7 +1,7 @@
1
1
  import { promises as fs } from "node:fs";
2
2
  import { extname, join } from "node:path";
3
3
  import z from "zod";
4
- import type { ResearchEvaluation, SupportingFile } from "./types.js";
4
+ import type { SupportingFile } from "./types.js";
5
5
  import { Logger } from "./utils/logger.js";
6
6
 
7
7
  export interface TaskFile {
@@ -162,39 +162,6 @@ export class PostHogFileManager {
162
162
  return await this.readTaskFile(taskId, "requirements.md");
163
163
  }
164
164
 
165
- async writeResearch(taskId: string, data: ResearchEvaluation): Promise<void> {
166
- this.logger.debug("Writing research", {
167
- taskId,
168
- score: data.actionabilityScore,
169
- hasQuestions: !!data.questions,
170
- questionCount: data.questions?.length ?? 0,
171
- answered: data.answered ?? false,
172
- });
173
-
174
- await this.writeTaskFile(taskId, {
175
- name: "research.json",
176
- content: JSON.stringify(data, null, 2),
177
- type: "artifact",
178
- });
179
-
180
- this.logger.info("Research file written", {
181
- taskId,
182
- score: data.actionabilityScore,
183
- hasQuestions: !!data.questions,
184
- answered: data.answered ?? false,
185
- });
186
- }
187
-
188
- async readResearch(taskId: string): Promise<ResearchEvaluation | null> {
189
- try {
190
- const content = await this.readTaskFile(taskId, "research.json");
191
- return content ? (JSON.parse(content) as ResearchEvaluation) : null;
192
- } catch (error) {
193
- this.logger.debug("Failed to parse research.json", { error });
194
- return null;
195
- }
196
- }
197
-
198
165
  async writeTodos(taskId: string, data: unknown): Promise<void> {
199
166
  const todos = z.object({
200
167
  metadata: z.object({
@@ -84,6 +84,11 @@ const TOOL_DEFINITIONS: Record<string, Tool> = {
84
84
  category: "assistant",
85
85
  description: "Exit plan mode and present plan to user",
86
86
  },
87
+ AskUserQuestion: {
88
+ name: "AskUserQuestion",
89
+ category: "assistant",
90
+ description: "Ask the user a clarifying question with options",
91
+ },
87
92
  SlashCommand: {
88
93
  name: "SlashCommand",
89
94
  category: "assistant",
@@ -100,6 +100,11 @@ export interface ExitPlanModeTool extends Tool {
100
100
  category: "assistant";
101
101
  }
102
102
 
103
+ export interface AskUserQuestionTool extends Tool {
104
+ name: "AskUserQuestion";
105
+ category: "assistant";
106
+ }
107
+
103
108
  export interface SlashCommandTool extends Tool {
104
109
  name: "SlashCommand";
105
110
  category: "assistant";
@@ -124,4 +129,5 @@ export type KnownTool =
124
129
  | TaskTool
125
130
  | TodoWriteTool
126
131
  | ExitPlanModeTool
132
+ | AskUserQuestionTool
127
133
  | SlashCommandTool;
package/src/types.ts CHANGED
@@ -141,6 +141,9 @@ export interface TaskExecutionOptions {
141
141
  // See: https://docs.claude.com/en/api/agent-sdk/permissions
142
142
  canUseTool?: CanUseTool;
143
143
  skipGitBranch?: boolean; // Skip creating a task-specific git branch
144
+ framework?: "claude"; // Agent framework to use (defaults to "claude")
145
+ task?: Task; // Pre-fetched task to avoid redundant API call
146
+ isReconnect?: boolean; // Session recreation - skip RUN_STARTED notification
144
147
  }
145
148
 
146
149
  export interface ExecutionResult {
@@ -248,29 +251,6 @@ export interface UrlMention {
248
251
  label?: string;
249
252
  }
250
253
 
251
- // Research evaluation types
252
- export interface ResearchQuestion {
253
- id: string;
254
- question: string;
255
- options: string[];
256
- }
257
-
258
- export interface ResearchAnswer {
259
- questionId: string;
260
- selectedOption: string;
261
- customInput?: string;
262
- }
263
-
264
- export interface ResearchEvaluation {
265
- actionabilityScore: number; // 0-1 confidence score
266
- context: string; // brief summary for planning
267
- keyFiles: string[]; // files needing modification
268
- blockers?: string[]; // what's preventing full confidence
269
- questions?: ResearchQuestion[]; // only if score < 0.7
270
- answered?: boolean; // whether questions have been answered
271
- answers?: ResearchAnswer[]; // user's answers to questions
272
- }
273
-
274
254
  // Worktree types for parallel task development
275
255
  export interface WorktreeInfo {
276
256
  worktreePath: string;
@@ -1,4 +1,4 @@
1
- import { exec, execFile } from "node:child_process";
1
+ import { execFile } from "node:child_process";
2
2
  import * as crypto from "node:crypto";
3
3
  import * as fs from "node:fs/promises";
4
4
  import * as path from "node:path";
@@ -6,7 +6,6 @@ import { promisify } from "node:util";
6
6
  import type { WorktreeInfo } from "./types.js";
7
7
  import { Logger } from "./utils/logger.js";
8
8
 
9
- const execAsync = promisify(exec);
10
9
  const execFileAsync = promisify(execFile);
11
10
 
12
11
  export interface WorktreeConfig {
@@ -512,14 +511,14 @@ export class WorktreeManager {
512
511
  return this.worktreeBasePath !== null;
513
512
  }
514
513
 
515
- private async runGitCommand(command: string): Promise<string> {
514
+ private async runGitCommand(args: string[]): Promise<string> {
516
515
  try {
517
- const { stdout } = await execAsync(`git ${command}`, {
516
+ const { stdout } = await execFileAsync("git", args, {
518
517
  cwd: this.mainRepoPath,
519
518
  });
520
519
  return stdout.trim();
521
520
  } catch (error) {
522
- throw new Error(`Git command failed: ${command}\n${error}`);
521
+ throw new Error(`Git command failed: git ${args.join(" ")}\n${error}`);
523
522
  }
524
523
  }
525
524
 
@@ -605,48 +604,68 @@ export class WorktreeManager {
605
604
  }
606
605
 
607
606
  private async getDefaultBranch(): Promise<string> {
608
- try {
609
- const remoteBranch = await this.runGitCommand(
610
- "symbolic-ref refs/remotes/origin/HEAD",
611
- );
612
- return remoteBranch.replace("refs/remotes/origin/", "");
613
- } catch {
614
- // Fallback: check if main exists, otherwise use master
615
- try {
616
- await this.runGitCommand("rev-parse --verify main");
617
- return "main";
618
- } catch {
619
- try {
620
- await this.runGitCommand("rev-parse --verify master");
621
- return "master";
622
- } catch {
623
- throw new Error(
624
- "Cannot determine default branch. No main or master branch found.",
625
- );
626
- }
627
- }
607
+ // Try all methods in parallel for speed
608
+ const [symbolicRef, mainExists, masterExists] = await Promise.allSettled([
609
+ this.runGitCommand(["symbolic-ref", "refs/remotes/origin/HEAD"]),
610
+ this.runGitCommand(["rev-parse", "--verify", "main"]),
611
+ this.runGitCommand(["rev-parse", "--verify", "master"]),
612
+ ]);
613
+
614
+ // Prefer symbolic ref (most accurate)
615
+ if (symbolicRef.status === "fulfilled") {
616
+ return symbolicRef.value.replace("refs/remotes/origin/", "");
628
617
  }
618
+
619
+ // Fallback to main if it exists
620
+ if (mainExists.status === "fulfilled") {
621
+ return "main";
622
+ }
623
+
624
+ // Fallback to master if it exists
625
+ if (masterExists.status === "fulfilled") {
626
+ return "master";
627
+ }
628
+
629
+ throw new Error(
630
+ "Cannot determine default branch. No main or master branch found.",
631
+ );
629
632
  }
630
633
 
631
634
  async createWorktree(options?: {
632
635
  baseBranch?: string;
633
636
  }): Promise<WorktreeInfo> {
637
+ const totalStart = Date.now();
638
+
639
+ // Run setup tasks in parallel for speed
640
+ const setupPromises: Promise<unknown>[] = [];
641
+
634
642
  // Only modify .git/info/exclude when using in-repo storage
635
643
  if (!this.usesExternalPath()) {
636
- await this.ensureArrayDirIgnored();
637
- }
638
-
639
- // Ensure the worktree folder exists when using external path
640
- if (this.usesExternalPath()) {
644
+ setupPromises.push(this.ensureArrayDirIgnored());
645
+ } else {
646
+ // Ensure the worktree folder exists when using external path
641
647
  const folderPath = this.getWorktreeFolderPath();
642
- await fs.mkdir(folderPath, { recursive: true });
648
+ setupPromises.push(fs.mkdir(folderPath, { recursive: true }));
643
649
  }
644
650
 
645
- // Generate unique worktree name
646
- const worktreeName = await this.generateUniqueWorktreeName();
651
+ // Generate unique worktree name (in parallel with above)
652
+ const worktreeNamePromise = this.generateUniqueWorktreeName();
653
+ setupPromises.push(worktreeNamePromise);
654
+
655
+ // Get default branch in parallel if not provided
656
+ const baseBranchPromise = options?.baseBranch
657
+ ? Promise.resolve(options.baseBranch)
658
+ : this.getDefaultBranch();
659
+ setupPromises.push(baseBranchPromise);
660
+
661
+ // Wait for all setup to complete
662
+ await Promise.all(setupPromises);
663
+ const setupTime = Date.now() - totalStart;
664
+
665
+ const worktreeName = await worktreeNamePromise;
666
+ const baseBranch = await baseBranchPromise;
647
667
  const worktreePath = this.getWorktreePath(worktreeName);
648
668
  const branchName = `array/${worktreeName}`;
649
- const baseBranch = options?.baseBranch ?? (await this.getDefaultBranch());
650
669
 
651
670
  this.logger.info("Creating worktree", {
652
671
  worktreeName,
@@ -654,21 +673,36 @@ export class WorktreeManager {
654
673
  branchName,
655
674
  baseBranch,
656
675
  external: this.usesExternalPath(),
676
+ setupTimeMs: setupTime,
657
677
  });
658
678
 
659
679
  // Create the worktree with a new branch
680
+ const gitStart = Date.now();
660
681
  if (this.usesExternalPath()) {
661
682
  // Use absolute path for external worktrees
662
- await this.runGitCommand(
663
- `worktree add -b "${branchName}" "${worktreePath}" "${baseBranch}"`,
664
- );
683
+ await this.runGitCommand([
684
+ "worktree",
685
+ "add",
686
+ "--quiet",
687
+ "-b",
688
+ branchName,
689
+ worktreePath,
690
+ baseBranch,
691
+ ]);
665
692
  } else {
666
693
  // Use relative path from repo root for in-repo worktrees
667
- const relativePath = `${WORKTREE_FOLDER_NAME}/${worktreeName}`;
668
- await this.runGitCommand(
669
- `worktree add -b "${branchName}" "./${relativePath}" "${baseBranch}"`,
670
- );
694
+ const relativePath = `./${WORKTREE_FOLDER_NAME}/${worktreeName}`;
695
+ await this.runGitCommand([
696
+ "worktree",
697
+ "add",
698
+ "--quiet",
699
+ "-b",
700
+ branchName,
701
+ relativePath,
702
+ baseBranch,
703
+ ]);
671
704
  }
705
+ const gitTime = Date.now() - gitStart;
672
706
 
673
707
  const createdAt = new Date().toISOString();
674
708
 
@@ -676,6 +710,9 @@ export class WorktreeManager {
676
710
  worktreeName,
677
711
  worktreePath,
678
712
  branchName,
713
+ setupTimeMs: setupTime,
714
+ gitWorktreeAddMs: gitTime,
715
+ totalMs: Date.now() - totalStart,
679
716
  });
680
717
 
681
718
  return {
@@ -758,7 +795,7 @@ export class WorktreeManager {
758
795
  try {
759
796
  await fs.rm(worktreePath, { recursive: true, force: true });
760
797
  // Also prune the worktree list
761
- await this.runGitCommand("worktree prune");
798
+ await this.runGitCommand(["worktree", "prune"]);
762
799
  this.logger.info("Worktree cleaned up manually", { worktreePath });
763
800
  } catch (cleanupError) {
764
801
  this.logger.error("Failed to cleanup worktree", {
@@ -773,7 +810,11 @@ export class WorktreeManager {
773
810
  async getWorktreeInfo(worktreePath: string): Promise<WorktreeInfo | null> {
774
811
  try {
775
812
  // Parse the worktree list to find info about this worktree
776
- const output = await this.runGitCommand("worktree list --porcelain");
813
+ const output = await this.runGitCommand([
814
+ "worktree",
815
+ "list",
816
+ "--porcelain",
817
+ ]);
777
818
  const worktrees = this.parseWorktreeList(output);
778
819
 
779
820
  const worktree = worktrees.find((w) => w.worktreePath === worktreePath);
@@ -786,7 +827,11 @@ export class WorktreeManager {
786
827
 
787
828
  async listWorktrees(): Promise<WorktreeInfo[]> {
788
829
  try {
789
- const output = await this.runGitCommand("worktree list --porcelain");
830
+ const output = await this.runGitCommand([
831
+ "worktree",
832
+ "list",
833
+ "--porcelain",
834
+ ]);
790
835
  return this.parseWorktreeList(output);
791
836
  } catch (error) {
792
837
  this.logger.debug("Failed to list worktrees", { error });
@@ -836,8 +881,9 @@ export class WorktreeManager {
836
881
 
837
882
  async isWorktree(repoPath: string): Promise<boolean> {
838
883
  try {
839
- const { stdout } = await execAsync(
840
- "git rev-parse --is-inside-work-tree",
884
+ const { stdout } = await execFileAsync(
885
+ "git",
886
+ ["rev-parse", "--is-inside-work-tree"],
841
887
  { cwd: repoPath },
842
888
  );
843
889
  if (stdout.trim() !== "true") {
@@ -1,41 +0,0 @@
1
- # Implementation Plan: {{task_title}}
2
-
3
- **Task ID:** {{task_id}}
4
- **Generated:** {{date}}
5
-
6
- ## Summary
7
-
8
- Brief description of what will be implemented and the overall approach.
9
-
10
- ## Implementation Steps
11
-
12
- ### 1. Analysis
13
- - [ ] Identify relevant files and components
14
- - [ ] Review existing patterns and constraints
15
-
16
- ### 2. Changes Required
17
- - [ ] Files to create/modify
18
- - [ ] Dependencies to add/update
19
-
20
- ### 3. Implementation
21
- - [ ] Core functionality changes
22
- - [ ] Tests and validation
23
- - [ ] Documentation updates
24
-
25
- ## File Changes
26
-
27
- ### New Files
28
- ```
29
- path/to/new/file.ts - Purpose
30
- ```
31
-
32
- ### Modified Files
33
- ```
34
- path/to/existing/file.ts - Changes needed
35
- ```
36
-
37
- ## Considerations
38
-
39
- - Key architectural decisions
40
- - Potential risks and mitigation
41
- - Testing approach