@empiricalrun/test-gen 0.74.2 → 0.76.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 (155) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/dist/agent/base/index.d.ts +43 -0
  3. package/dist/agent/base/index.d.ts.map +1 -0
  4. package/dist/agent/base/index.js +106 -0
  5. package/dist/agent/chat/agent-loop.d.ts +7 -7
  6. package/dist/agent/chat/agent-loop.d.ts.map +1 -1
  7. package/dist/agent/chat/agent-loop.js +5 -18
  8. package/dist/agent/chat/exports.d.ts +6 -4
  9. package/dist/agent/chat/exports.d.ts.map +1 -1
  10. package/dist/agent/chat/exports.js +9 -8
  11. package/dist/agent/chat/index.d.ts +6 -10
  12. package/dist/agent/chat/index.d.ts.map +1 -1
  13. package/dist/agent/chat/index.js +130 -200
  14. package/dist/agent/chat/prompt/index.d.ts +5 -4
  15. package/dist/agent/chat/prompt/index.d.ts.map +1 -1
  16. package/dist/agent/chat/prompt/index.js +79 -68
  17. package/dist/agent/chat/state.d.ts +3 -5
  18. package/dist/agent/chat/state.d.ts.map +1 -1
  19. package/dist/agent/chat/state.js +2 -10
  20. package/dist/agent/chat/utils.d.ts +2 -4
  21. package/dist/agent/chat/utils.d.ts.map +1 -1
  22. package/dist/agent/chat/utils.js +2 -16
  23. package/dist/agent/cli.d.ts +11 -0
  24. package/dist/agent/cli.d.ts.map +1 -0
  25. package/dist/agent/cli.js +209 -0
  26. package/dist/agent/code-review/index.d.ts +7 -0
  27. package/dist/agent/code-review/index.d.ts.map +1 -0
  28. package/dist/agent/code-review/index.js +65 -0
  29. package/dist/agent/code-review/prompt.d.ts +1 -1
  30. package/dist/agent/code-review/prompt.d.ts.map +1 -1
  31. package/dist/agent/code-review/prompt.js +52 -16
  32. package/dist/agent/index.d.ts +10 -0
  33. package/dist/agent/index.d.ts.map +1 -0
  34. package/dist/agent/index.js +19 -0
  35. package/dist/agent/triage/index.d.ts +7 -0
  36. package/dist/agent/triage/index.d.ts.map +1 -0
  37. package/dist/agent/triage/index.js +102 -0
  38. package/dist/agent/video-analysis/index.d.ts +7 -0
  39. package/dist/agent/video-analysis/index.d.ts.map +1 -0
  40. package/dist/agent/video-analysis/index.js +35 -0
  41. package/dist/bin/index.js +6 -6
  42. package/dist/file-info/adapters/github/index.d.ts +2 -2
  43. package/dist/file-info/adapters/github/index.d.ts.map +1 -1
  44. package/dist/file-info/adapters/github/index.js +4 -4
  45. package/dist/file-info/adapters/github/reader.d.ts +5 -10
  46. package/dist/file-info/adapters/github/reader.d.ts.map +1 -1
  47. package/dist/file-info/adapters/github/reader.js +168 -138
  48. package/dist/tools/create-pull-request/index.d.ts.map +1 -0
  49. package/dist/tools/{definitions/commit-and-create-pr.js → create-pull-request/index.js} +30 -1
  50. package/dist/tools/create-pull-request/utils.d.ts +21 -0
  51. package/dist/tools/create-pull-request/utils.d.ts.map +1 -0
  52. package/dist/tools/create-pull-request/utils.js +83 -0
  53. package/dist/tools/definitions/extract-frames-from-video.d.ts +39 -0
  54. package/dist/tools/definitions/extract-frames-from-video.d.ts.map +1 -0
  55. package/dist/tools/definitions/extract-frames-from-video.js +60 -0
  56. package/dist/tools/definitions/fetch-video-analysis.d.ts +28 -0
  57. package/dist/tools/definitions/fetch-video-analysis.d.ts.map +1 -1
  58. package/dist/tools/definitions/fetch-video-analysis.js +39 -4
  59. package/dist/tools/definitions/rename-file.d.ts +3 -0
  60. package/dist/tools/definitions/rename-file.d.ts.map +1 -0
  61. package/dist/tools/definitions/rename-file.js +23 -0
  62. package/dist/tools/delete-file/index.d.ts.map +1 -1
  63. package/dist/tools/delete-file/index.js +13 -1
  64. package/dist/tools/executor/index.d.ts +1 -1
  65. package/dist/tools/executor/index.d.ts.map +1 -1
  66. package/dist/tools/executor/index.js +22 -7
  67. package/dist/tools/executor/utils/checkpoint.d.ts +1 -3
  68. package/dist/tools/executor/utils/checkpoint.d.ts.map +1 -1
  69. package/dist/tools/executor/utils/checkpoint.js +17 -17
  70. package/dist/tools/executor/utils/git.d.ts +9 -1
  71. package/dist/tools/executor/utils/git.d.ts.map +1 -1
  72. package/dist/tools/executor/utils/git.js +72 -2
  73. package/dist/tools/extract-frames-from-video/index.d.ts +7 -0
  74. package/dist/tools/extract-frames-from-video/index.d.ts.map +1 -0
  75. package/dist/tools/extract-frames-from-video/index.js +145 -0
  76. package/dist/tools/{fetch-image → fetch-file}/index.d.ts +2 -2
  77. package/dist/tools/fetch-file/index.d.ts.map +1 -0
  78. package/dist/tools/fetch-file/index.js +97 -0
  79. package/dist/tools/fetch-session-diff/index.d.ts +3 -0
  80. package/dist/tools/fetch-session-diff/index.d.ts.map +1 -0
  81. package/dist/tools/fetch-session-diff/index.js +46 -0
  82. package/dist/tools/fetch-video-analysis/index.d.ts +3 -3
  83. package/dist/tools/fetch-video-analysis/index.d.ts.map +1 -1
  84. package/dist/tools/fetch-video-analysis/index.js +84 -24
  85. package/dist/tools/fetch-video-analysis/open-ai.d.ts +6 -0
  86. package/dist/tools/fetch-video-analysis/open-ai.d.ts.map +1 -0
  87. package/dist/tools/fetch-video-analysis/open-ai.js +37 -0
  88. package/dist/tools/fetch-video-analysis/utils.d.ts +9 -3
  89. package/dist/tools/fetch-video-analysis/utils.d.ts.map +1 -1
  90. package/dist/tools/fetch-video-analysis/utils.js +64 -15
  91. package/dist/tools/fetch-video-analysis/video-analysis.d.ts +2 -2
  92. package/dist/tools/fetch-video-analysis/video-analysis.d.ts.map +1 -1
  93. package/dist/tools/fetch-video-analysis/video-analysis.js +24 -8
  94. package/dist/tools/file-operations/create.d.ts.map +1 -1
  95. package/dist/tools/file-operations/create.js +6 -3
  96. package/dist/tools/file-operations/insert.d.ts.map +1 -1
  97. package/dist/tools/file-operations/insert.js +6 -3
  98. package/dist/tools/file-operations/replace.d.ts.map +1 -1
  99. package/dist/tools/file-operations/replace.js +6 -3
  100. package/dist/tools/file-operations/shared/git-helper.d.ts.map +1 -1
  101. package/dist/tools/file-operations/shared/git-helper.js +1 -1
  102. package/dist/tools/file-operations/view/index.d.ts +2 -5
  103. package/dist/tools/file-operations/view/index.d.ts.map +1 -1
  104. package/dist/tools/file-operations/view/index.js +2 -22
  105. package/dist/tools/index.d.ts +28 -2
  106. package/dist/tools/index.d.ts.map +1 -1
  107. package/dist/tools/index.js +50 -22
  108. package/dist/tools/issues/update-issue.d.ts.map +1 -1
  109. package/dist/tools/issues/update-issue.js +16 -9
  110. package/dist/tools/merge-conflicts/index.js +1 -1
  111. package/dist/tools/rename-file/index.d.ts +3 -0
  112. package/dist/tools/rename-file/index.d.ts.map +1 -0
  113. package/dist/tools/rename-file/index.js +88 -0
  114. package/dist/tools/review-pull-request/index.d.ts +3 -0
  115. package/dist/tools/review-pull-request/index.d.ts.map +1 -0
  116. package/dist/tools/review-pull-request/index.js +103 -0
  117. package/dist/tools/run-test.js +2 -2
  118. package/dist/tools/test-run-fetcher/index.d.ts.map +1 -1
  119. package/dist/tools/test-run-fetcher/index.js +4 -14
  120. package/dist/tools/trace-dot-zip/index.d.ts.map +1 -1
  121. package/dist/tools/trace-dot-zip/index.js +2 -1
  122. package/dist/tools/trace-dot-zip/types.d.ts +35 -3
  123. package/dist/tools/trace-dot-zip/types.d.ts.map +1 -1
  124. package/dist/tools/trace-dot-zip/utils/network-trace.d.ts +7 -2
  125. package/dist/tools/trace-dot-zip/utils/network-trace.d.ts.map +1 -1
  126. package/dist/tools/trace-dot-zip/utils/network-trace.js +130 -10
  127. package/dist/tools/upgrade-packages/index.js +1 -1
  128. package/dist/tools/utils/urls.d.ts +5 -0
  129. package/dist/tools/utils/urls.d.ts.map +1 -0
  130. package/dist/tools/utils/urls.js +19 -0
  131. package/dist/tools/view-failed-test-run-report/index.d.ts.map +1 -1
  132. package/dist/tools/view-failed-test-run-report/index.js +3 -15
  133. package/dist/utils/file.d.ts +1 -0
  134. package/dist/utils/file.d.ts.map +1 -1
  135. package/dist/utils/file.js +45 -1
  136. package/dist/utils/index.d.ts +0 -1
  137. package/dist/utils/index.d.ts.map +1 -1
  138. package/dist/utils/index.js +1 -3
  139. package/dist/utils/local-ffmpeg-client.d.ts +27 -0
  140. package/dist/utils/local-ffmpeg-client.d.ts.map +1 -0
  141. package/dist/{tools/fetch-video-analysis → utils}/local-ffmpeg-client.js +117 -27
  142. package/package.json +5 -5
  143. package/tsconfig.tsbuildinfo +1 -1
  144. package/dist/agent/chat/utils/tool-calls.d.ts +0 -21
  145. package/dist/agent/chat/utils/tool-calls.d.ts.map +0 -1
  146. package/dist/agent/chat/utils/tool-calls.js +0 -64
  147. package/dist/tools/commit-and-create-pr/index.d.ts.map +0 -1
  148. package/dist/tools/commit-and-create-pr/index.js +0 -83
  149. package/dist/tools/definitions/commit-and-create-pr.d.ts +0 -3
  150. package/dist/tools/definitions/commit-and-create-pr.d.ts.map +0 -1
  151. package/dist/tools/fetch-image/index.d.ts.map +0 -1
  152. package/dist/tools/fetch-image/index.js +0 -63
  153. package/dist/tools/fetch-video-analysis/local-ffmpeg-client.d.ts +0 -24
  154. package/dist/tools/fetch-video-analysis/local-ffmpeg-client.d.ts.map +0 -1
  155. /package/dist/tools/{commit-and-create-pr → create-pull-request}/index.d.ts +0 -0
package/dist/bin/index.js CHANGED
@@ -7,8 +7,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
7
7
  const commander_1 = require("commander");
8
8
  const dotenv_1 = __importDefault(require("dotenv"));
9
9
  const fs_1 = __importDefault(require("fs"));
10
- const chat_1 = require("../agent/chat");
11
10
  const models_1 = require("../agent/chat/models");
11
+ const cli_1 = require("../agent/cli");
12
12
  const auth_1 = require("../auth");
13
13
  const client_1 = require("../dashboard/client");
14
14
  const recorder_1 = require("../recorder");
@@ -20,13 +20,13 @@ const utils_1 = require("./utils");
20
20
  dotenv_1.default.config({
21
21
  path: [".env.local", ".env"],
22
22
  });
23
- async function runChatAgent({ modelInput, useDiskForChatState, prompt: initialPromptContent, useTriage, resetChat, useFSCache, }) {
23
+ async function runChatAgent({ modelInput, useDiskForChatState, prompt: initialPromptContent, agentMode = "chat", resetChat, useFSCache, }) {
24
24
  const resolvedModel = (0, models_1.resolveChatModelBasedOnInput)(modelInput);
25
- return await (0, chat_1.runChatAgentForCLI)({
25
+ return await (0, cli_1.runChatAgentForCLI)({
26
26
  selectedModel: resolvedModel,
27
27
  useDiskForChatState: useDiskForChatState || false,
28
28
  initialPromptContent,
29
- useTriage: useTriage || false,
29
+ agentMode,
30
30
  resetChat: resetChat || false,
31
31
  useFSCache: useFSCache || false,
32
32
  });
@@ -178,7 +178,7 @@ async function main() {
178
178
  .option("--model <model>", "LLM to use (gpt-5, claude-4 or gemini-2.5)")
179
179
  .option("--use-disk", "Save and load chat state from disk")
180
180
  .option("--prompt <string>", "String to pass as user prompt")
181
- .option("--use-triage", "run the model in triage mode, different set of tools")
181
+ .option("--agent-mode <mode>", "Mode of the agent: 'chat' or 'triage' or 'video' or 'code-review' (Defaults to 'chat')")
182
182
  .option("--use-cache", "Use filesystem cache for LLM responses (Claude-only, and will disable streaming)")
183
183
  .option("--reset-chat", "Clear any saved chat state (last-chat.json) before starting")
184
184
  .action(async (options) => {
@@ -186,7 +186,7 @@ async function main() {
186
186
  modelInput: options.model,
187
187
  useDiskForChatState: options.useDisk,
188
188
  prompt: options.prompt,
189
- useTriage: options.useTriage,
189
+ agentMode: options.agentMode,
190
190
  resetChat: options.resetChat,
191
191
  useFSCache: options.useCache,
192
192
  });
@@ -1,11 +1,11 @@
1
1
  import { IDashboardAPIClient, ToolResult } from "@empiricalrun/shared-types";
2
2
  import { type StrReplaceInputParams } from "../../../tools/file-operations/shared/helpers";
3
3
  export { getFileInfoFromGitHub } from "./reader";
4
- export declare function viewFileUsingGitHub({ input, filePath, repoName, apiClient, branchName, }: {
4
+ export declare function viewFileUsingGitHub({ input, repoName, apiClient, branchName, baseBranch, }: {
5
5
  input: StrReplaceInputParams;
6
- filePath: string;
7
6
  repoName: string;
8
7
  apiClient: IDashboardAPIClient;
9
8
  branchName: string;
9
+ baseBranch: string;
10
10
  }): Promise<ToolResult>;
11
11
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/file-info/adapters/github/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAE7E,OAAO,EAEL,KAAK,qBAAqB,EAC3B,MAAM,+CAA+C,CAAC;AAEvD,OAAO,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AAIjD,wBAAsB,mBAAmB,CAAC,EACxC,KAAK,EACL,QAAQ,EACR,QAAQ,EACR,SAAS,EACT,UAAU,GACX,EAAE;IACD,KAAK,EAAE,qBAAqB,CAAC;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,mBAAmB,CAAC;IAC/B,UAAU,EAAE,MAAM,CAAC;CACpB,GAAG,OAAO,CAAC,UAAU,CAAC,CAsBtB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/file-info/adapters/github/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAE7E,OAAO,EAEL,KAAK,qBAAqB,EAC3B,MAAM,+CAA+C,CAAC;AAEvD,OAAO,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AAEjD,wBAAsB,mBAAmB,CAAC,EACxC,KAAK,EACL,QAAQ,EACR,SAAS,EACT,UAAU,EACV,UAAU,GACX,EAAE;IACD,KAAK,EAAE,qBAAqB,CAAC;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,mBAAmB,CAAC;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB,GAAG,OAAO,CAAC,UAAU,CAAC,CA2BtB"}
@@ -6,10 +6,10 @@ const helpers_1 = require("../../../tools/file-operations/shared/helpers");
6
6
  const reader_1 = require("./reader");
7
7
  var reader_2 = require("./reader");
8
8
  Object.defineProperty(exports, "getFileInfoFromGitHub", { enumerable: true, get: function () { return reader_2.getFileInfoFromGitHub; } });
9
- const REPO_OWNER = "empirical-run";
10
- async function viewFileUsingGitHub({ input, filePath, repoName, apiClient, branchName, }) {
11
- const githubReader = new reader_1.GitHubFileReader(repoName, apiClient, REPO_OWNER);
12
- const fileData = await githubReader.readFile(filePath, branchName);
9
+ async function viewFileUsingGitHub({ input, repoName, apiClient, branchName, baseBranch, }) {
10
+ const filePath = input.path;
11
+ const githubReader = new reader_1.GitHubFileReader(repoName, apiClient);
12
+ const fileData = await githubReader.readFile(filePath, branchName, baseBranch);
13
13
  if (!fileData) {
14
14
  return {
15
15
  result: `Error: File or directory ${filePath} is not found.`,
@@ -2,15 +2,10 @@ import { FileInfo, FileReadResult, IDashboardAPIClient } from "@empiricalrun/sha
2
2
  export declare class GitHubFileReader {
3
3
  private repoName;
4
4
  private apiClient;
5
- private repoOwner;
6
- constructor(repoName: string, apiClient: IDashboardAPIClient, repoOwner: string);
7
- private fetchFileFromGitHub;
8
- private fetchTreeFromGitHub;
9
- private resolveBranchName;
10
- readFile(filePath: string, branchName: string): Promise<FileReadResult | null>;
11
- private getFileContent;
12
- private getContentsForDirectory;
13
- getFileInfo(branchName: string): Promise<FileInfo>;
5
+ constructor(repoName: string, apiClient: IDashboardAPIClient);
6
+ resolveBranchName(branchName: string | undefined, baseBranch?: string): Promise<string>;
7
+ readFile(filePath: string, branchName: string, baseBranch: string): Promise<FileReadResult | null>;
8
+ private readDirectory;
14
9
  }
15
- export declare function getFileInfoFromGitHub(repoName: string, apiClient: IDashboardAPIClient, branchName: string): Promise<FileInfo>;
10
+ export declare function getFileInfoFromGitHub(repoName: string, apiClient: IDashboardAPIClient, branchName: string, baseBranchName: string): Promise<FileInfo>;
16
11
  //# sourceMappingURL=reader.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"reader.d.ts","sourceRoot":"","sources":["../../../../src/file-info/adapters/github/reader.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,QAAQ,EACR,cAAc,EACd,mBAAmB,EACpB,MAAM,4BAA4B,CAAC;AA4CpC,qBAAa,gBAAgB;IAEzB,OAAO,CAAC,QAAQ;IAChB,OAAO,CAAC,SAAS;IACjB,OAAO,CAAC,SAAS;gBAFT,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,mBAAmB,EAC9B,SAAS,EAAE,MAAM;YAGb,mBAAmB;YAUnB,mBAAmB;YASnB,iBAAiB;IAkBzB,QAAQ,CACZ,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC;YAmCnB,cAAc;YAiBd,uBAAuB;IAuB/B,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;CA+EzD;AAED,wBAAsB,qBAAqB,CACzC,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,mBAAmB,EAC9B,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,QAAQ,CAAC,CAGnB"}
1
+ {"version":3,"file":"reader.d.ts","sourceRoot":"","sources":["../../../../src/file-info/adapters/github/reader.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,QAAQ,EACR,cAAc,EACd,mBAAmB,EACpB,MAAM,4BAA4B,CAAC;AAcpC,qBAAa,gBAAgB;IAEzB,OAAO,CAAC,QAAQ;IAChB,OAAO,CAAC,SAAS;gBADT,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,mBAAmB;IAGlC,iBAAiB,CACrB,UAAU,EAAE,MAAM,GAAG,SAAS,EAC9B,UAAU,GAAE,MAAe,GAC1B,OAAO,CAAC,MAAM,CAAC;IAsBZ,QAAQ,CACZ,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC;YA4CnB,aAAa;CA8C5B;AAyED,wBAAsB,qBAAqB,CACzC,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,mBAAmB,EAC9B,UAAU,EAAE,MAAM,EAClB,cAAc,EAAE,MAAM,GACrB,OAAO,CAAC,QAAQ,CAAC,CAyFnB"}
@@ -6,180 +6,210 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.GitHubFileReader = void 0;
7
7
  exports.getFileInfoFromGitHub = getFileInfoFromGitHub;
8
8
  const path_1 = __importDefault(require("path"));
9
- const dashboard_1 = require("../../../dashboard");
10
- const REPO_OWNER = "empirical-run";
11
9
  class GitHubFileReader {
12
10
  repoName;
13
11
  apiClient;
14
- repoOwner;
15
- constructor(repoName, apiClient, repoOwner) {
12
+ constructor(repoName, apiClient) {
16
13
  this.repoName = repoName;
17
14
  this.apiClient = apiClient;
18
- this.repoOwner = repoOwner;
19
15
  }
20
- async fetchFileFromGitHub(filePath, branch) {
21
- return (await this.apiClient.callGitHubProxy({
22
- method: "GET",
23
- url: `/repos/${this.repoOwner}/${this.repoName}/contents/${filePath}?ref=${branch}`,
24
- }));
25
- }
26
- async fetchTreeFromGitHub(branch) {
27
- return (await this.apiClient.callGitHubProxy({
28
- method: "GET",
29
- url: `/repos/${this.repoOwner}/${this.repoName}/git/trees/${branch}?recursive=1`,
30
- }));
31
- }
32
- async resolveBranchName(branchName) {
33
- if (!branchName || branchName === "main") {
34
- return "main";
16
+ async resolveBranchName(branchName, baseBranch = "main") {
17
+ if (!branchName || branchName === baseBranch) {
18
+ return baseBranch;
35
19
  }
36
20
  try {
37
- // Try to fetch the branch to see if it exists
21
+ // Try to fetch the branch to see if it exists via GitHub proxy
38
22
  await this.apiClient.callGitHubProxy({
39
23
  method: "GET",
40
- url: `/repos/${this.repoOwner}/${this.repoName}/branches/${branchName}`,
24
+ url: `/repos/empirical-run/${this.repoName}/branches/${branchName}`,
41
25
  });
26
+ console.log(`[GitHubFileReader] Branch ${branchName} exists`);
42
27
  return branchName;
43
28
  }
44
29
  catch {
45
- // Branch doesn't exist, fallback to main
46
- return "main";
30
+ // Branch doesn't exist, fallback to base branch
31
+ console.log(`[GitHubFileReader] Branch ${branchName} not found, falling back to ${baseBranch}`);
32
+ return baseBranch;
47
33
  }
48
34
  }
49
- async readFile(filePath, branchName) {
50
- const branch = await this.resolveBranchName(branchName);
51
- const normalizedPath = filePath === "" ? "." : filePath;
35
+ async readFile(filePath, branchName, baseBranch) {
36
+ const resolvedBranch = await this.resolveBranchName(branchName, baseBranch);
37
+ // Check if path has file extension - if not, treat as directory
38
+ const hasFileExtension = path_1.default.extname(filePath) !== "";
39
+ if (!hasFileExtension) {
40
+ console.log(`[GitHubFileReader] Reading as directory: ${filePath}`);
41
+ return this.readDirectory(filePath, resolvedBranch);
42
+ }
52
43
  try {
53
- const response = await this.fetchFileFromGitHub(normalizedPath, branch);
54
- const isDirectory = Array.isArray(response);
55
- if (!isDirectory) {
56
- if (response.type === "file" && response.content) {
57
- const content = Buffer.from(response.content, "base64").toString("utf-8");
58
- return {
59
- content,
60
- isDirectory: false,
61
- };
62
- }
63
- }
64
- else {
44
+ console.log(`[GitHubFileReader] Reading as file: ${filePath}, resolved branch: ${resolvedBranch}`);
45
+ const params = {
46
+ repo: this.repoName,
47
+ path: filePath,
48
+ ref: resolvedBranch,
49
+ };
50
+ const response = await this.apiClient.request(`/api/github/files`, {
51
+ method: "GET",
52
+ params,
53
+ });
54
+ if (response.data?.fileContents.available) {
65
55
  return {
66
- content: response.map((item) => item.name).join("\n"),
67
- isDirectory: true,
56
+ content: response.data.fileContents.content,
57
+ isDirectory: false,
68
58
  };
69
59
  }
60
+ console.log(`[GitHubFileReader] File not available: ${filePath}`);
61
+ // For file paths (with extensions), don't fall back to directory reading
62
+ return null;
70
63
  }
71
64
  catch (error) {
72
- if (error instanceof dashboard_1.NonRetryableHTTPError && error.status === 404) {
73
- return null; // File or directory not found
74
- }
75
- throw error; // Re-throw other errors
65
+ console.log(`[GitHubFileReader] Error reading file ${filePath}:`, error);
66
+ // For file paths, return null (file not found)
67
+ return null;
76
68
  }
77
- return null;
78
69
  }
79
- async getFileContent(path, branchName) {
80
- const branch = await this.resolveBranchName(branchName);
81
- const response = await this.fetchFileFromGitHub(path, branch);
82
- const isDirectory = Array.isArray(response);
83
- if (isDirectory) {
84
- throw new Error(`${path} is a directory, not a file`);
70
+ async readDirectory(filePath, resolvedBranch) {
71
+ try {
72
+ console.log(`[GitHubFileReader] Reading directory: ${filePath}, resolved branch: ${resolvedBranch}`);
73
+ const params = {
74
+ repo: this.repoName,
75
+ path: filePath || ".", // Handle empty path
76
+ ref: resolvedBranch,
77
+ };
78
+ const treeResponse = await this.apiClient.request(`/api/github/tree`, {
79
+ method: "GET",
80
+ params,
81
+ });
82
+ if (treeResponse.data?.tree) {
83
+ const targetPath = filePath === "." || filePath === "" ? "" : filePath;
84
+ const files = treeResponse.data.tree.tree
85
+ .map((item) => item.path)
86
+ .filter((itemPath) => {
87
+ if (!itemPath)
88
+ return false;
89
+ // Check if item is a direct child of the target directory
90
+ const itemDir = path_1.default.dirname(itemPath);
91
+ // Normalize "." to "" for root directory comparison
92
+ const normalizedItemDir = itemDir === "." ? "" : itemDir;
93
+ return normalizedItemDir === targetPath;
94
+ })
95
+ .map((itemPath) => path_1.default.basename(itemPath))
96
+ .sort()
97
+ .join("\n");
98
+ return { content: files, isDirectory: true };
99
+ }
85
100
  }
86
- if (response.type === "file" && response.content) {
87
- return Buffer.from(response.content, "base64").toString("utf-8");
101
+ catch (error) {
102
+ console.log(`[GitHubFileReader] Error reading directory ${filePath}:`, error);
88
103
  }
89
- throw new Error(`Unable to fetch file content for ${path}`);
104
+ return null;
90
105
  }
91
- async getContentsForDirectory(path, branchName) {
92
- const branch = await this.resolveBranchName(branchName);
93
- const response = await this.fetchTreeFromGitHub(branch);
94
- if (response.tree) {
95
- // Convert to expected RepoTree format
96
- return {
97
- sha: response.sha,
98
- truncated: false,
99
- tree: response.tree.map((item) => ({
100
- path: item.path || "",
101
- mode: item.mode || "",
102
- type: item.type === "blob" ? "blob" : "tree",
103
- sha: item.sha,
104
- })),
105
- };
106
- }
107
- throw new Error(`Unable to fetch directory contents for ${path}`);
106
+ }
107
+ exports.GitHubFileReader = GitHubFileReader;
108
+ async function getFileContent(repoName, path, apiClient, branchName) {
109
+ const params = {
110
+ repo: repoName,
111
+ path: path,
112
+ };
113
+ if (branchName) {
114
+ params.ref = branchName;
115
+ }
116
+ const response = await apiClient.request(`/api/github/files`, {
117
+ method: "GET",
118
+ params,
119
+ });
120
+ if (!response.data) {
121
+ throw new Error(`Unable to fetch file for FileInfo`);
122
+ }
123
+ return response.data.fileContents.content;
124
+ }
125
+ async function getContentsForDirectory({ repo, path, }, apiClient, branchName) {
126
+ const params = {
127
+ repo: repo,
128
+ path: path,
129
+ };
130
+ if (branchName) {
131
+ params.ref = branchName;
132
+ }
133
+ const response = await apiClient.request(`/api/github/tree`, {
134
+ method: "GET",
135
+ params,
136
+ });
137
+ if (!response.data) {
138
+ throw new Error(`Unable to fetch file for FileInfo`);
139
+ }
140
+ return response.data.tree;
141
+ }
142
+ async function getFileInfoFromGitHub(repoName, apiClient, branchName, baseBranchName) {
143
+ // Use GitHubFileReader to resolve the branch
144
+ const fileReader = new GitHubFileReader(repoName, apiClient);
145
+ const resolvedBranch = await fileReader.resolveBranchName(branchName, baseBranchName);
146
+ const files = await getContentsForDirectory({ repo: repoName, path: "" }, apiClient, resolvedBranch);
147
+ const root = [];
148
+ const nodeMap = {};
149
+ if (!files) {
150
+ return {
151
+ type: "directory",
152
+ path: repoName,
153
+ name: repoName,
154
+ children: [],
155
+ };
108
156
  }
109
- async getFileInfo(branchName) {
110
- const files = await this.getContentsForDirectory("", branchName);
111
- const root = [];
112
- const nodeMap = {};
113
- if (!files) {
114
- return {
157
+ files.tree.forEach((file) => {
158
+ if (!file.path) {
159
+ return;
160
+ }
161
+ const pathParts = file.path.split("/");
162
+ const fileName = path_1.default.basename(file.path);
163
+ const isFile = file.type === "blob";
164
+ const currentPath = file.path;
165
+ const currentNode = isFile
166
+ ? {
167
+ type: "file",
168
+ path: currentPath,
169
+ name: fileName,
170
+ getContent: async () => getFileContent(repoName, currentPath, apiClient, resolvedBranch),
171
+ }
172
+ : {
115
173
  type: "directory",
116
- path: this.repoName,
117
- name: this.repoName,
174
+ path: currentPath,
175
+ name: fileName,
118
176
  children: [],
119
177
  };
178
+ nodeMap[currentPath] = currentNode;
179
+ if (pathParts.length === 1) {
180
+ root.push(currentNode);
120
181
  }
121
- files.tree.forEach((file) => {
122
- if (!file.path) {
123
- return;
124
- }
125
- const pathParts = file.path.split("/");
126
- const fileName = path_1.default.basename(file.path);
127
- const isFile = file.type === "blob";
128
- const currentPath = file.path;
129
- const currentNode = isFile
130
- ? {
131
- type: "file",
132
- path: currentPath,
133
- name: fileName,
134
- getContent: async () => this.getFileContent(currentPath, branchName),
135
- }
136
- : {
182
+ else {
183
+ const parentPath = path_1.default.dirname(currentPath);
184
+ if (!nodeMap[parentPath]) {
185
+ const parentNode = {
137
186
  type: "directory",
138
- path: currentPath,
139
- name: fileName,
187
+ path: parentPath,
188
+ name: parentPath.split("/").pop() || "",
140
189
  children: [],
141
190
  };
142
- nodeMap[currentPath] = currentNode;
143
- if (pathParts.length === 1) {
144
- root.push(currentNode);
145
- }
146
- else {
147
- const parentPath = path_1.default.dirname(currentPath);
148
- if (!nodeMap[parentPath]) {
149
- const parentNode = {
150
- type: "directory",
151
- path: parentPath,
152
- name: parentPath.split("/").pop() || "",
153
- children: [],
154
- };
155
- nodeMap[parentPath] = parentNode;
156
- const parentPathParts = parentPath.split("/");
157
- if (parentPathParts.length === 1) {
158
- root.push(parentNode);
159
- }
160
- else {
161
- const grandparentPath = path_1.default.dirname(parentPath);
162
- if (nodeMap[grandparentPath] &&
163
- nodeMap[grandparentPath].type === "directory") {
164
- nodeMap[grandparentPath].children.push(parentNode);
165
- }
166
- }
191
+ nodeMap[parentPath] = parentNode;
192
+ const parentPathParts = parentPath.split("/");
193
+ if (parentPathParts.length === 1) {
194
+ root.push(parentNode);
167
195
  }
168
- if (nodeMap[parentPath].type === "directory") {
169
- nodeMap[parentPath].children.push(currentNode);
196
+ else {
197
+ const grandparentPath = path_1.default.dirname(parentPath);
198
+ if (nodeMap[grandparentPath] &&
199
+ nodeMap[grandparentPath].type === "directory") {
200
+ nodeMap[grandparentPath].children.push(parentNode);
201
+ }
170
202
  }
171
203
  }
172
- });
173
- return {
174
- type: "directory",
175
- path: this.repoName,
176
- name: this.repoName,
177
- children: root,
178
- };
179
- }
180
- }
181
- exports.GitHubFileReader = GitHubFileReader;
182
- async function getFileInfoFromGitHub(repoName, apiClient, branchName) {
183
- const fileReader = new GitHubFileReader(repoName, apiClient, REPO_OWNER);
184
- return fileReader.getFileInfo(branchName);
204
+ if (nodeMap[parentPath].type === "directory") {
205
+ nodeMap[parentPath].children.push(currentNode);
206
+ }
207
+ }
208
+ });
209
+ return {
210
+ type: "directory",
211
+ path: repoName,
212
+ name: repoName,
213
+ children: root,
214
+ };
185
215
  }
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/tools/create-pull-request/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,4BAA4B,CAAC;AA6BvD,eAAO,MAAM,qBAAqB,EAAE,IAwDnC,CAAC"}
@@ -2,6 +2,9 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createPullRequestTool = void 0;
4
4
  const zod_1 = require("zod");
5
+ const utils_1 = require("../executor/utils");
6
+ const utils_2 = require("./utils");
7
+ const REPO_OWNER = "empirical-run";
5
8
  const createPullRequestSchema = zod_1.z.object({
6
9
  pullRequestTitle: zod_1.z
7
10
  .string()
@@ -27,5 +30,31 @@ Don't ask the user for this information, just come up with it yourself.
27
30
  parameters: createPullRequestSchema,
28
31
  },
29
32
  needsBrowser: false,
30
- isInlineTool: false,
33
+ isInlineTool: true,
34
+ execute: async ({ input, apiClient, chatSession }) => {
35
+ const owner = REPO_OWNER;
36
+ try {
37
+ const valids = await (0, utils_2.validateInputs)({ input, apiClient, chatSession });
38
+ const { pullRequestTitle, pullRequestDescription, branchName } = valids;
39
+ const repo = await (0, utils_2.getRepoName)(chatSession, apiClient);
40
+ const existingPR = await (0, utils_1.getExistingPR)({
41
+ owner,
42
+ repo,
43
+ branchName,
44
+ apiClient: apiClient,
45
+ });
46
+ if (existingPR) {
47
+ return await (0, utils_2.handleExistingPullRequest)(existingPR, owner, repo, pullRequestDescription, chatSession, apiClient);
48
+ }
49
+ else {
50
+ return await (0, utils_2.handleNewPullRequest)(owner, repo, pullRequestTitle, branchName, chatSession, pullRequestDescription, apiClient);
51
+ }
52
+ }
53
+ catch (error) {
54
+ return {
55
+ isError: true,
56
+ result: `Failed to commit and push changes: ${error instanceof Error ? error.message : String(error)}`,
57
+ };
58
+ }
59
+ },
31
60
  };
@@ -0,0 +1,21 @@
1
+ import { ChatSessionInfo, IDashboardAPIClient } from "@empiricalrun/shared-types";
2
+ export interface ValidatedInputs {
3
+ pullRequestTitle: string;
4
+ pullRequestDescription: string;
5
+ branchName: string;
6
+ }
7
+ export declare function validateInputs({ input, apiClient, chatSession, }: any): Promise<ValidatedInputs>;
8
+ export declare function getRepoName(chatSession: ChatSessionInfo | null | undefined, apiClient: IDashboardAPIClient): Promise<string>;
9
+ export declare function getMergeableStateInfo(owner: string, repo: string, pullRequest: any, apiClient: any): Promise<{
10
+ mergeableState: "unknown" | "clean" | "dirty" | "unstable";
11
+ stateDescription: string;
12
+ }>;
13
+ export declare function handleExistingPullRequest(pullRequest: any, owner: string, repo: string, pullRequestDescription: string, chatSession: any, apiClient: any): Promise<{
14
+ isError: boolean;
15
+ result: string;
16
+ }>;
17
+ export declare function handleNewPullRequest(owner: string, repo: string, pullRequestTitle: string, branchName: string, chatSession: any, pullRequestDescription: string, apiClient: any): Promise<{
18
+ isError: boolean;
19
+ result: string;
20
+ }>;
21
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/tools/create-pull-request/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,mBAAmB,EAEpB,MAAM,4BAA4B,CAAC;AAUpC,MAAM,WAAW,eAAe;IAC9B,gBAAgB,EAAE,MAAM,CAAC;IACzB,sBAAsB,EAAE,MAAM,CAAC;IAC/B,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,wBAAsB,cAAc,CAAC,EACnC,KAAK,EACL,SAAS,EACT,WAAW,GACZ,EAAE,GAAG,GAAG,OAAO,CAAC,eAAe,CAAC,CAehC;AAED,wBAAsB,WAAW,CAC/B,WAAW,EAAE,eAAe,GAAG,IAAI,GAAG,SAAS,EAC/C,SAAS,EAAE,mBAAmB,GAC7B,OAAO,CAAC,MAAM,CAAC,CA2BjB;AAED,wBAAsB,qBAAqB,CACzC,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,GAAG,EAChB,SAAS,EAAE,GAAG;;;GAUf;AAED,wBAAsB,yBAAyB,CAC7C,WAAW,EAAE,GAAG,EAChB,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,sBAAsB,EAAE,MAAM,EAC9B,WAAW,EAAE,GAAG,EAChB,SAAS,EAAE,GAAG;;;GAqBf;AAED,wBAAsB,oBAAoB,CACxC,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,gBAAgB,EAAE,MAAM,EACxB,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,GAAG,EAChB,sBAAsB,EAAE,MAAM,EAC9B,SAAS,EAAE,GAAG;;;GAuBf"}
@@ -0,0 +1,83 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.validateInputs = validateInputs;
4
+ exports.getRepoName = getRepoName;
5
+ exports.getMergeableStateInfo = getMergeableStateInfo;
6
+ exports.handleExistingPullRequest = handleExistingPullRequest;
7
+ exports.handleNewPullRequest = handleNewPullRequest;
8
+ const utils_1 = require("../executor/utils");
9
+ const pr_description_1 = require("../executor/utils/pr-description");
10
+ async function validateInputs({ input, apiClient, chatSession, }) {
11
+ const { pullRequestTitle, pullRequestDescription } = input;
12
+ const branchName = chatSession?.branchName;
13
+ if (!apiClient) {
14
+ throw new Error("Dashboard API client is not available.");
15
+ }
16
+ if (!branchName) {
17
+ throw new Error("Branch name is not available in the chat session.");
18
+ }
19
+ return {
20
+ pullRequestTitle,
21
+ pullRequestDescription,
22
+ branchName,
23
+ };
24
+ }
25
+ async function getRepoName(chatSession, apiClient) {
26
+ if (!chatSession?.id) {
27
+ throw new Error("Cannot create pull request without repo name or chat session ID.");
28
+ }
29
+ try {
30
+ const url = `/api/chat-sessions/${chatSession.id}/details`;
31
+ const sessionDetails = await apiClient.request(url, { method: "GET" });
32
+ if ("error" in sessionDetails) {
33
+ throw new Error(`Failed to fetch session details: ${sessionDetails.error}`);
34
+ }
35
+ if (!sessionDetails.gitPayload?.repoName) {
36
+ throw new Error("Repository name not found in session details.");
37
+ }
38
+ return sessionDetails.gitPayload.repoName;
39
+ }
40
+ catch (error) {
41
+ throw new Error(`Failed to fetch repository name: ${error instanceof Error ? error.message : String(error)}`);
42
+ }
43
+ }
44
+ async function getMergeableStateInfo(owner, repo, pullRequest, apiClient) {
45
+ const mergeableState = await (0, utils_1.getMergeableState)({
46
+ owner,
47
+ repo,
48
+ pullRequest,
49
+ apiClient,
50
+ });
51
+ const stateDescription = (0, utils_1.getMergeableStateDescription)(mergeableState);
52
+ return { mergeableState, stateDescription };
53
+ }
54
+ async function handleExistingPullRequest(pullRequest, owner, repo, pullRequestDescription, chatSession, apiClient) {
55
+ const updatedPR = await (0, utils_1.updatePullRequest)({
56
+ owner,
57
+ repo,
58
+ prNumber: pullRequest.number,
59
+ body: (0, pr_description_1.addMetadataToPRDescription)(pullRequestDescription, chatSession),
60
+ apiClient,
61
+ });
62
+ const { mergeableState, stateDescription } = await getMergeableStateInfo(owner, repo, updatedPR, apiClient);
63
+ return {
64
+ isError: false,
65
+ result: `Updated existing PR: ${updatedPR.html_url}\n\nMergeable state: ${mergeableState} - ${stateDescription}`,
66
+ };
67
+ }
68
+ async function handleNewPullRequest(owner, repo, pullRequestTitle, branchName, chatSession, pullRequestDescription, apiClient) {
69
+ const newPR = await (0, utils_1.createPullRequest)({
70
+ owner,
71
+ repo,
72
+ title: pullRequestTitle,
73
+ head: branchName,
74
+ base: chatSession?.baseBranchName || "main",
75
+ body: (0, pr_description_1.addMetadataToPRDescription)(pullRequestDescription, chatSession),
76
+ apiClient,
77
+ });
78
+ const { mergeableState, stateDescription } = await getMergeableStateInfo(owner, repo, newPR, apiClient);
79
+ return {
80
+ isError: false,
81
+ result: `Created a new PR: ${newPR.html_url}\n\nMergeable state: ${mergeableState} - ${stateDescription}`,
82
+ };
83
+ }