@posthog/agent 2.3.15 → 2.3.21

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@posthog/agent",
3
- "version": "2.3.15",
3
+ "version": "2.3.21",
4
4
  "repository": "https://github.com/PostHog/code",
5
5
  "description": "TypeScript agent framework wrapping Claude Agent SDK with Git-based task execution for PostHog",
6
6
  "exports": {
@@ -1,3 +1,4 @@
1
+ import fs from "node:fs";
1
2
  import path from "node:path";
2
3
  import type {
3
4
  PlanEntry,
@@ -179,18 +180,22 @@ export function toolInfoFromToolUse(
179
180
  : null;
180
181
  let newText: string = input?.new_string ? String(input.new_string) : "";
181
182
 
182
- // If we have cached file content, show a full-file diff
183
- if (
184
- filePath &&
185
- options?.cachedFileContent &&
186
- filePath in options.cachedFileContent
187
- ) {
188
- const oldContent = options.cachedFileContent[filePath];
189
- const newContent = input?.replace_all
190
- ? oldContent.replaceAll(oldText ?? "", newText)
191
- : oldContent.replace(oldText ?? "", newText);
192
- oldText = oldContent;
193
- newText = newContent;
183
+ // try to display a rich diff by first checking if file content is cached
184
+ // and valid (old_text exists in the content), then fall back to reading
185
+ // file from disk, then fall back to fragemented snippet diff
186
+ if (filePath && oldText !== null) {
187
+ const fileContent = resolveFileContent(
188
+ filePath,
189
+ oldText,
190
+ options?.cachedFileContent,
191
+ );
192
+ if (fileContent) {
193
+ const newContent = input?.replace_all
194
+ ? fileContent.replaceAll(oldText, newText)
195
+ : fileContent.replace(oldText, newText);
196
+ oldText = fileContent;
197
+ newText = newContent;
198
+ }
194
199
  }
195
200
 
196
201
  return {
@@ -759,6 +764,37 @@ export function planEntries(input: { todos: ClaudePlanEntry[] }): PlanEntry[] {
759
764
  }));
760
765
  }
761
766
 
767
+ /**
768
+ * attempt to resolve full file contents for diff generation
769
+ *
770
+ * 1) check file content cache exists, and is valid (old_text in content)
771
+ * 2) if missing or invalid, read file from disk
772
+ * 3) if both fail, return null, we'll fall back to fragmented snippet diff
773
+ */
774
+ function resolveFileContent(
775
+ filePath: string,
776
+ oldText: string,
777
+ cachedFileContent?: Record<string, string>,
778
+ ): string | null {
779
+ if (cachedFileContent && filePath in cachedFileContent) {
780
+ const cached = cachedFileContent[filePath];
781
+ if (cached.includes(oldText)) {
782
+ return cached;
783
+ }
784
+ }
785
+
786
+ try {
787
+ const content = fs.readFileSync(filePath, "utf-8");
788
+ if (content.includes(oldText)) {
789
+ return content;
790
+ }
791
+ } catch {
792
+ return null;
793
+ }
794
+
795
+ return null;
796
+ }
797
+
762
798
  function markdownEscape(text: string): string {
763
799
  let escapedText = "```";
764
800
  for (const [m] of text.matchAll(/^```+/gm)) {
@@ -0,0 +1,19 @@
1
+ const BRANCH_NAMING = `
2
+ # Branch Naming
3
+
4
+ When working in a detached HEAD state, create a descriptive branch name based on the work being done before committing. Do this automatically without asking the user.
5
+ `;
6
+
7
+ const PLAN_MODE = `
8
+ # Plan Mode
9
+
10
+ Only enter plan mode (EnterPlanMode) when the user is requesting a significant change in approach or direction mid-task. Do NOT enter plan mode for:
11
+ - Confirmations or approvals ("yes", "looks good", "continue", "go ahead")
12
+ - Minor clarifications or small adjustments
13
+ - Answers to questions you asked (unless you are still in the initial planning phase and have not yet started executing)
14
+ - Feedback that does not require replanning
15
+
16
+ When in doubt, continue executing and incorporate the feedback inline.
17
+ `;
18
+
19
+ export const APPENDED_INSTRUCTIONS = BRANCH_NAMING + PLAN_MODE;
@@ -18,6 +18,7 @@ import {
18
18
  } from "../hooks";
19
19
  import type { CodeExecutionMode } from "../tools";
20
20
  import type { EffortLevel } from "../types";
21
+ import { APPENDED_INSTRUCTIONS } from "./instructions";
21
22
  import { DEFAULT_MODEL } from "./models";
22
23
  import type { SettingsManager } from "./settings";
23
24
 
@@ -47,19 +48,13 @@ export interface BuildOptionsParams {
47
48
  effort?: EffortLevel;
48
49
  }
49
50
 
50
- const BRANCH_NAMING_INSTRUCTIONS = `
51
- # Branch Naming
52
-
53
- When working in a detached HEAD state, create a descriptive branch name based on the work being done before committing. Do this automatically without asking the user.
54
- `;
55
-
56
51
  export function buildSystemPrompt(
57
52
  customPrompt?: unknown,
58
53
  ): Options["systemPrompt"] {
59
54
  const defaultPrompt: Options["systemPrompt"] = {
60
55
  type: "preset",
61
56
  preset: "claude_code",
62
- append: BRANCH_NAMING_INSTRUCTIONS,
57
+ append: APPENDED_INSTRUCTIONS,
63
58
  };
64
59
 
65
60
  if (!customPrompt) {
@@ -67,7 +62,7 @@ export function buildSystemPrompt(
67
62
  }
68
63
 
69
64
  if (typeof customPrompt === "string") {
70
- return customPrompt + BRANCH_NAMING_INSTRUCTIONS;
65
+ return customPrompt + APPENDED_INSTRUCTIONS;
71
66
  }
72
67
 
73
68
  if (
@@ -78,7 +73,7 @@ export function buildSystemPrompt(
78
73
  ) {
79
74
  return {
80
75
  ...defaultPrompt,
81
- append: customPrompt.append + BRANCH_NAMING_INSTRUCTIONS,
76
+ append: customPrompt.append + APPENDED_INSTRUCTIONS,
82
77
  };
83
78
  }
84
79