@empiricalrun/test-gen 0.60.0 → 0.62.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 (92) hide show
  1. package/CHANGELOG.md +52 -0
  2. package/dist/agent/browsing/run.d.ts +2 -0
  3. package/dist/agent/browsing/run.d.ts.map +1 -1
  4. package/dist/agent/browsing/run.js +11 -7
  5. package/dist/agent/browsing/utils.d.ts.map +1 -1
  6. package/dist/agent/browsing/utils.js +1 -1
  7. package/dist/agent/chat/agent-loop.js +2 -3
  8. package/dist/agent/chat/exports.d.ts +2 -2
  9. package/dist/agent/chat/exports.d.ts.map +1 -1
  10. package/dist/agent/chat/exports.js +1 -1
  11. package/dist/agent/chat/index.d.ts.map +1 -1
  12. package/dist/agent/chat/index.js +16 -2
  13. package/dist/agent/chat/models.d.ts +1 -3
  14. package/dist/agent/chat/models.d.ts.map +1 -1
  15. package/dist/agent/chat/models.js +4 -25
  16. package/dist/agent/chat/prompt.d.ts.map +1 -1
  17. package/dist/agent/chat/prompt.js +58 -0
  18. package/dist/agent/cua/computer.d.ts +6 -6
  19. package/dist/agent/cua/computer.d.ts.map +1 -1
  20. package/dist/agent/cua/computer.js +38 -83
  21. package/dist/agent/cua/index.d.ts +2 -1
  22. package/dist/agent/cua/index.d.ts.map +1 -1
  23. package/dist/agent/cua/index.js +26 -33
  24. package/dist/agent/cua/pw-codegen/element-from-point.d.ts +8 -0
  25. package/dist/agent/cua/pw-codegen/element-from-point.d.ts.map +1 -0
  26. package/dist/agent/cua/pw-codegen/element-from-point.js +118 -0
  27. package/dist/agent/cua/pw-codegen/pw-pause/index.d.ts +15 -0
  28. package/dist/agent/cua/pw-codegen/pw-pause/index.d.ts.map +1 -0
  29. package/dist/agent/cua/pw-codegen/pw-pause/index.js +84 -0
  30. package/dist/agent/cua/pw-codegen/pw-pause/utils.d.ts +16 -0
  31. package/dist/agent/cua/pw-codegen/pw-pause/utils.d.ts.map +1 -0
  32. package/dist/agent/cua/pw-codegen/pw-pause/utils.js +98 -0
  33. package/dist/agent/cua/pw-codegen/types.d.ts +46 -0
  34. package/dist/agent/cua/pw-codegen/types.d.ts.map +1 -0
  35. package/dist/agent/cua/pw-codegen/types.js +2 -0
  36. package/dist/agent/master/browser-tests/cua.spec.js +13 -1
  37. package/dist/artifacts/index.d.ts +43 -0
  38. package/dist/artifacts/index.d.ts.map +1 -0
  39. package/dist/artifacts/index.js +209 -0
  40. package/dist/bin/index.js +7 -11
  41. package/dist/bin/utils/index.d.ts +5 -3
  42. package/dist/bin/utils/index.d.ts.map +1 -1
  43. package/dist/bin/utils/index.js +13 -0
  44. package/dist/bin/utils/platform/web/index.d.ts +4 -1
  45. package/dist/bin/utils/platform/web/index.d.ts.map +1 -1
  46. package/dist/bin/utils/platform/web/index.js +24 -8
  47. package/dist/bin/utils/scenarios/index.d.ts +3 -3
  48. package/dist/file/client.d.ts +2 -0
  49. package/dist/file/client.d.ts.map +1 -1
  50. package/dist/file/client.js +16 -0
  51. package/dist/file/server.d.ts +3 -1
  52. package/dist/file/server.d.ts.map +1 -1
  53. package/dist/file/server.js +27 -3
  54. package/dist/index.d.ts.map +1 -1
  55. package/dist/index.js +4 -1
  56. package/dist/test-build/index.d.ts +6 -2
  57. package/dist/test-build/index.d.ts.map +1 -1
  58. package/dist/test-build/index.js +9 -7
  59. package/dist/tool-call-service/index.d.ts +14 -7
  60. package/dist/tool-call-service/index.d.ts.map +1 -1
  61. package/dist/tool-call-service/index.js +19 -8
  62. package/dist/tools/commit-and-create-pr.d.ts.map +1 -1
  63. package/dist/tools/commit-and-create-pr.js +5 -1
  64. package/dist/tools/download-build.d.ts.map +1 -1
  65. package/dist/tools/download-build.js +3 -3
  66. package/dist/tools/grep/index.d.ts.map +1 -1
  67. package/dist/tools/grep/index.js +4 -2
  68. package/dist/tools/str_replace_editor.d.ts.map +1 -1
  69. package/dist/tools/str_replace_editor.js +25 -8
  70. package/dist/tools/test-gen-browser.d.ts.map +1 -1
  71. package/dist/tools/test-gen-browser.js +21 -4
  72. package/dist/tools/test-run.d.ts.map +1 -1
  73. package/dist/tools/test-run.js +11 -8
  74. package/dist/tools/utils/index.d.ts +13 -0
  75. package/dist/tools/utils/index.d.ts.map +1 -1
  76. package/dist/tools/utils/index.js +47 -0
  77. package/dist/utils/exec.d.ts +2 -0
  78. package/dist/utils/exec.d.ts.map +1 -1
  79. package/dist/utils/exec.js +4 -1
  80. package/dist/utils/git.d.ts.map +1 -1
  81. package/dist/utils/git.js +1 -1
  82. package/dist/utils/slug.d.ts +16 -0
  83. package/dist/utils/slug.d.ts.map +1 -1
  84. package/dist/utils/slug.js +27 -1
  85. package/dist/utils/stripAnsi.d.ts +2 -0
  86. package/dist/utils/stripAnsi.d.ts.map +1 -0
  87. package/dist/utils/stripAnsi.js +9 -0
  88. package/package.json +6 -4
  89. package/tsconfig.tsbuildinfo +1 -1
  90. package/dist/utils/pw-test.d.ts +0 -2
  91. package/dist/utils/pw-test.d.ts.map +0 -1
  92. package/dist/utils/pw-test.js +0 -13
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tool-call-service/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAEL,eAAe,EACf,mBAAmB,EACnB,IAAI,EACJ,UAAU,EACX,MAAM,wBAAwB,CAAC;AAiBhC,YAAY,EAAE,mBAAmB,EAAE,CAAC;AAEpC,KAAK,aAAa,GAAG;IACnB,CAAC,GAAG,EAAE,MAAM,GAAG,CAAC,KAAK,EAAE,GAAG,EAAE,KAAK,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;CACzE,CAAC;AAEF,qBAAa,eAAe;IAC1B,KAAK,EAAE,IAAI,EAAE,CAAM;IACnB,aAAa,EAAE,aAAa,CAAM;IAClC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,aAAa,EAAE,mBAAmB,CAAC;IACnC,UAAU,EAAE,MAAM,CAAC;gBAEjB,aAAa,EAAE,MAAM,GAAG,IAAI,EAC5B,aAAa,EAAE,mBAAmB,EAClC,UAAU,EAAE,MAAM;IAiBd,QAAQ;;;IAaR,WAAW,CAAC,SAAS,EAAE,eAAe,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAcxD,OAAO,CACX,SAAS,EAAE,eAAe,EAAE,EAC5B,KAAK,CAAC,EAAE,WAAW,GAClB,OAAO,CAAC,UAAU,EAAE,CAAC;CA2CzB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tool-call-service/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAEL,eAAe,EACf,mBAAmB,EACnB,IAAI,EACJ,WAAW,EACX,UAAU,EACX,MAAM,wBAAwB,CAAC;AAiBhC,YAAY,EAAE,mBAAmB,EAAE,CAAC;AAEpC,KAAK,aAAa,GAAG;IACnB,CAAC,GAAG,EAAE,MAAM,GAAG,WAAW,CAAC;CAC5B,CAAC;AAEF,qBAAa,eAAe;IAC1B,KAAK,EAAE,IAAI,EAAE,CAAM;IACnB,aAAa,EAAE,aAAa,CAAM;IAClC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,aAAa,EAAE,mBAAmB,CAAC;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,KAAK,CAAC,EAAE,WAAW,CAAC;gBACR,EACV,aAAa,EACb,aAAa,EACb,UAAU,EACV,QAAQ,EACR,MAAM,EACN,KAAK,GACN,EAAE;QACD,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;QAC7B,aAAa,EAAE,mBAAmB,CAAC;QACnC,UAAU,EAAE,MAAM,CAAC;QACnB,QAAQ,EAAE,MAAM,CAAC;QACjB,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;QAC3B,KAAK,CAAC,EAAE,WAAW,CAAC;KACrB;IAiCK,WAAW,CAAC,SAAS,EAAE,eAAe,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAcxD,OAAO,CAAC,SAAS,EAAE,eAAe,EAAE,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC;CAmDnE"}
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ToolCallService = void 0;
4
4
  const chat_1 = require("@empiricalrun/llm/chat");
5
+ const artifacts_1 = require("../artifacts");
5
6
  const commit_and_create_pr_1 = require("../tools/commit-and-create-pr");
6
7
  const diagnosis_fetcher_1 = require("../tools/diagnosis-fetcher");
7
8
  const download_build_1 = require("../tools/download-build");
@@ -19,10 +20,16 @@ class ToolCallService {
19
20
  chatSessionId;
20
21
  selectedModel;
21
22
  branchName;
22
- constructor(chatSessionId, selectedModel, branchName) {
23
+ repoPath;
24
+ apiKey;
25
+ trace;
26
+ constructor({ chatSessionId, selectedModel, branchName, repoPath, apiKey, trace, }) {
23
27
  this.chatSessionId = chatSessionId;
24
28
  this.selectedModel = selectedModel;
25
29
  this.branchName = branchName;
30
+ this.trace = trace;
31
+ this.repoPath = repoPath;
32
+ this.apiKey = apiKey;
26
33
  this.tools = [
27
34
  grep_1.grepTool,
28
35
  test_run_1.runTestTool,
@@ -33,8 +40,6 @@ class ToolCallService {
33
40
  environment_crud_1.getEnvironmentTool,
34
41
  download_build_1.downloadBuildTool,
35
42
  ];
36
- }
37
- async getTools() {
38
43
  if ((0, chat_1.getProviderForModel)(this.selectedModel) !== "claude") {
39
44
  this.tools.push(...str_replace_editor_1.textEditorTools);
40
45
  }
@@ -42,9 +47,13 @@ class ToolCallService {
42
47
  this.toolExecutors[tool.schema.name] = tool.execute;
43
48
  });
44
49
  if ((0, chat_1.getProviderForModel)(this.selectedModel) === "claude") {
45
- this.toolExecutors["str_replace_editor"] = str_replace_editor_1.strReplaceEditorExecutor;
50
+ this.toolExecutors = {
51
+ ...this.toolExecutors,
52
+ // Support for Claude 3x and 4: They use different tool names
53
+ str_replace_editor: str_replace_editor_1.strReplaceEditorExecutor,
54
+ str_replace_based_edit_tool: str_replace_editor_1.strReplaceEditorExecutor,
55
+ };
46
56
  }
47
- return { tools: this.tools };
48
57
  }
49
58
  async sendToQueue(toolCalls) {
50
59
  const requestId = toolCalls[0]?.id;
@@ -59,8 +68,8 @@ class ToolCallService {
59
68
  branchName: this.branchName,
60
69
  });
61
70
  }
62
- async execute(toolCalls, trace) {
63
- const executeSpan = trace?.span({
71
+ async execute(toolCalls) {
72
+ const executeSpan = this.trace?.span({
64
73
  name: "execute_tools",
65
74
  input: { toolCalls: toolCalls.map((tc) => ({ name: tc.name })) },
66
75
  });
@@ -75,13 +84,14 @@ class ToolCallService {
75
84
  const errorResult = {
76
85
  isError: true,
77
86
  result: `Invalid function/tool call: ${toolCall.name} not found`,
87
+ artifacts: null,
78
88
  };
79
89
  toolResults.push(errorResult);
80
90
  span?.end({ output: errorResult });
81
91
  continue;
82
92
  }
83
93
  try {
84
- const result = await toolExecutor(toolCall.input, trace);
94
+ const result = await toolExecutor(toolCall.input, this.repoPath, this.apiKey, this.trace, artifacts_1.collectArtifacts);
85
95
  toolResults.push(result);
86
96
  span?.end({ output: result });
87
97
  }
@@ -89,6 +99,7 @@ class ToolCallService {
89
99
  const errorResult = {
90
100
  isError: true,
91
101
  result: error instanceof Error ? error.message : String(error),
102
+ artifacts: null,
92
103
  };
93
104
  toolResults.push(errorResult);
94
105
  span?.end({ output: errorResult });
@@ -1 +1 @@
1
- {"version":3,"file":"commit-and-create-pr.d.ts","sourceRoot":"","sources":["../../src/tools/commit-and-create-pr.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAC;AAOnD,UAAU,cAAc;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,cAAc,CAiB1D;AA2CD,eAAO,MAAM,qBAAqB,EAAE,IAuEnC,CAAC"}
1
+ {"version":3,"file":"commit-and-create-pr.d.ts","sourceRoot":"","sources":["../../src/tools/commit-and-create-pr.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAC;AAOnD,UAAU,cAAc;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,cAAc,CAiB1D;AA2CD,eAAO,MAAM,qBAAqB,EAAE,IA2EnC,CAAC"}
@@ -48,7 +48,11 @@ exports.createPullRequestTool = {
48
48
  description: `Creates a new Pull Request on GitHub.
49
49
  If the current branch already has an open PR, commits and pushes changes to that PR.
50
50
  Uses the empiricalrun[bot] credentials for git operations.
51
- Returns the URL of the created or updated pull request.`,
51
+ Returns the URL of the created or updated pull request.
52
+
53
+ You must come up with a title and description for the pull request, based on the changes made.
54
+ Don't ask the user for this information, just come up with it yourself.
55
+ `,
52
56
  parameters: createPullRequestSchema,
53
57
  },
54
58
  execute: async (input) => {
@@ -1 +1 @@
1
- {"version":3,"file":"download-build.d.ts","sourceRoot":"","sources":["../../src/tools/download-build.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,eAAO,MAAM,uBAAuB;;;;;;EAElC,CAAC;AAEH,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAE7E,eAAO,MAAM,iBAAiB,EAAE,IA8B/B,CAAC"}
1
+ {"version":3,"file":"download-build.d.ts","sourceRoot":"","sources":["../../src/tools/download-build.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,eAAO,MAAM,uBAAuB;;;;;;EAElC,CAAC;AAEH,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAE7E,eAAO,MAAM,iBAAiB,EAAE,IAkC/B,CAAC"}
@@ -14,8 +14,8 @@ have a build URL, you can try getting the environment details with the getEnviro
14
14
  Environment details will include the build URL.`,
15
15
  parameters: exports.downloadBuildToolSchema,
16
16
  },
17
- execute: async (input) => {
18
- if (!(await (0, test_build_1.hasDownloadScript)())) {
17
+ execute: async (input, repoPath, apiKey) => {
18
+ if (!(await (0, test_build_1.hasDownloadScript)(repoPath))) {
19
19
  return {
20
20
  isError: true,
21
21
  result: `This repo does not have a download script in package.json.
@@ -24,7 +24,7 @@ You probably don't need to worry about this, since it means this repo does not h
24
24
  }
25
25
  const { buildUrl } = input;
26
26
  try {
27
- await (0, test_build_1.downloadBuild)(buildUrl);
27
+ await (0, test_build_1.downloadBuild)({ buildUrl, repoPath, apiKey });
28
28
  return {
29
29
  isError: false,
30
30
  result: "Build downloaded successfully",
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/tools/grep/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAc,MAAM,wBAAwB,CAAC;AAyH/D,eAAO,MAAM,QAAQ,EAAE,IAmBtB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/tools/grep/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAc,MAAM,wBAAwB,CAAC;AA2H/D,eAAO,MAAM,QAAQ,EAAE,IAmBtB,CAAC"}
@@ -74,8 +74,10 @@ async function usingSystemGrep(input) {
74
74
  async function usingRipgrep(input) {
75
75
  try {
76
76
  const dir = path_1.default.join(process.cwd(), input.directory || "");
77
- const escapedPattern = input.pattern.replace(/\s+/g, "\\ ");
78
- const results = await (0, ripgrep_1.ripgrep)(dir, {
77
+ const escapedPattern = input.pattern
78
+ .replace(/\s+/g, "\\ ")
79
+ .replace(/([[\]()])/g, "\\$1");
80
+ const results = (0, ripgrep_1.ripgrep)(dir, {
79
81
  string: escapedPattern,
80
82
  globs: input.filePattern ? [input.filePattern] : undefined,
81
83
  });
@@ -1 +1 @@
1
- {"version":3,"file":"str_replace_editor.d.ts","sourceRoot":"","sources":["../../src/tools/str_replace_editor.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AA2B1D,UAAU,eAAe;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAqED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAwC1D;AAMD;;;GAGG;AACH,wBAAsB,wBAAwB,CAC5C,KAAK,EAAE,eAAe,GACrB,OAAO,CAAC,UAAU,CAAC,CA6LrB;AAiGD,eAAO,MAAM,eAAe,EAAE,IAAI,EAKjC,CAAC"}
1
+ {"version":3,"file":"str_replace_editor.d.ts","sourceRoot":"","sources":["../../src/tools/str_replace_editor.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AA2B1D,UAAU,eAAe;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAqED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAwC1D;AAMD;;;GAGG;AACH,wBAAsB,wBAAwB,CAC5C,KAAK,EAAE,eAAe,GACrB,OAAO,CAAC,UAAU,CAAC,CAiNrB;AAiGD,eAAO,MAAM,eAAe,EAAE,IAAI,EAKjC,CAAC"}
@@ -140,12 +140,12 @@ function escapeRegExp(text) {
140
140
  * https://docs.anthropic.com/en/docs/build-with-claude/tool-use/text-editor-tool
141
141
  */
142
142
  async function strReplaceEditorExecutor(input) {
143
+ const repoDir = process.cwd();
143
144
  const { path: filePath } = input;
144
145
  try {
145
146
  let content;
146
147
  let lines;
147
148
  let newContent;
148
- let typeCheckErrors;
149
149
  switch (input.command) {
150
150
  case "view":
151
151
  if (!fs_1.default.existsSync(filePath)) {
@@ -195,8 +195,25 @@ async function strReplaceEditorExecutor(input) {
195
195
  if (input.file_text === undefined || input.file_text === null) {
196
196
  throw new Error("file_text is required for create command");
197
197
  }
198
+ if (filePath.endsWith("test.ts")) {
199
+ throw new Error("Creating test.ts files is not allowed. Did you mean spec.ts?");
200
+ }
201
+ if (filePath.endsWith("spec.ts") && !filePath.startsWith("tests/")) {
202
+ throw new Error("Creating spec.ts files is not allowed outside tests/ directory");
203
+ }
204
+ const parentDir = path_1.default.dirname(filePath);
205
+ if (parentDir !== "." && !fs_1.default.existsSync(parentDir)) {
206
+ // Ensure parent directory exists
207
+ fs_1.default.mkdirSync(parentDir, { recursive: true });
208
+ }
198
209
  fs_1.default.writeFileSync(filePath, input.file_text);
199
- // TODO: Add type checking
210
+ let createTypescriptResult = await (0, web_1.runTypescriptCompiler)(repoDir);
211
+ if (!createTypescriptResult.success) {
212
+ return {
213
+ result: `File ${filePath} has been created. However, type checks are failing with errors:\n${createTypescriptResult.errors.join("\n")}`,
214
+ isError: true,
215
+ };
216
+ }
200
217
  return {
201
218
  result: `Successfully created file ${filePath}`,
202
219
  isError: false,
@@ -243,10 +260,10 @@ async function strReplaceEditorExecutor(input) {
243
260
  }
244
261
  newContent = normalizedContent.replace(normalizedOldStr, input.new_str);
245
262
  fs_1.default.writeFileSync(filePath, newContent);
246
- typeCheckErrors = (0, web_1.validateTypescript)(filePath);
247
- if (typeCheckErrors.length > 0) {
263
+ let strReplaceTypescriptResult = await (0, web_1.runTypescriptCompiler)(repoDir);
264
+ if (!strReplaceTypescriptResult.success) {
248
265
  return {
249
- result: `Edits to file ${filePath} have been applied. However, type checks are failing with errors:\n${typeCheckErrors.join("\n")}`,
266
+ result: `Edits to file ${filePath} have been applied. However, type checks are failing with errors:\n${strReplaceTypescriptResult.errors.join("\n")}`,
250
267
  isError: true,
251
268
  };
252
269
  }
@@ -272,10 +289,10 @@ async function strReplaceEditorExecutor(input) {
272
289
  }
273
290
  lines.splice(input.insert_line - 1, 0, input.new_str);
274
291
  fs_1.default.writeFileSync(filePath, lines.join("\n"));
275
- typeCheckErrors = (0, web_1.validateTypescript)(filePath);
276
- if (typeCheckErrors.length > 0) {
292
+ let insertTypescriptResult = await (0, web_1.runTypescriptCompiler)(repoDir);
293
+ if (!insertTypescriptResult.success) {
277
294
  return {
278
- result: `Insertion in file ${filePath} was applied. However, type checks are failing with errors:\n${typeCheckErrors.join("\n")}`,
295
+ result: `Insertion in file ${filePath} was applied. However, type checks are failing with errors:\n${insertTypescriptResult.errors.join("\n")}`,
279
296
  isError: true,
280
297
  };
281
298
  }
@@ -1 +1 @@
1
- {"version":3,"file":"test-gen-browser.d.ts","sourceRoot":"","sources":["../../src/tools/test-gen-browser.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAC;AAsFnD,eAAO,MAAM,4BAA4B,EAAE,IAqF1C,CAAC"}
1
+ {"version":3,"file":"test-gen-browser.d.ts","sourceRoot":"","sources":["../../src/tools/test-gen-browser.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAC;AA4FnD,eAAO,MAAM,4BAA4B,EAAE,IAoH1C,CAAC"}
@@ -9,8 +9,10 @@ const promises_1 = __importDefault(require("fs/promises"));
9
9
  const zod_1 = require("zod");
10
10
  const run_1 = require("../agent/browsing/run");
11
11
  const utils_1 = require("../agent/browsing/utils");
12
+ const pw_pause_1 = require("../agent/cua/pw-codegen/pw-pause");
12
13
  const web_1 = require("../bin/utils/platform/web");
13
14
  const scenarios_1 = require("../bin/utils/scenarios");
15
+ const slug_1 = require("../utils/slug");
14
16
  const BrowserAgentSchema = zod_1.z.object({
15
17
  testName: zod_1.z.string().describe("The name of the test to create or modify"),
16
18
  testSuites: zod_1.z
@@ -87,8 +89,8 @@ exports.generateTestWithBrowserAgent = {
87
89
  description: BROWSER_AGENT_DESCRIPTION,
88
90
  parameters: BrowserAgentSchema,
89
91
  },
90
- execute: async (input, trace) => {
91
- const repoDir = process.cwd();
92
+ execute: async (input, repoPath, apiKey, trace, collectArtifacts) => {
93
+ const repoDir = repoPath;
92
94
  const { testName, testSuites, fileName, changeToMake, project } = input;
93
95
  try {
94
96
  const { projects } = await (0, test_run_1.getAllPlaywrightProjects)(repoDir);
@@ -113,6 +115,7 @@ exports.generateTestWithBrowserAgent = {
113
115
  result: `Test block not found for test name: "${testName}" in file: "${fileName}" with describe blocks: "${testSuites.join(", ")}"`,
114
116
  };
115
117
  }
118
+ // Prepare the file for the browser agent
116
119
  const fileBackup = await promises_1.default.readFile(fileName, "utf-8");
117
120
  try {
118
121
  await (0, utils_1.replaceTodoWithCreateTest)(fileName);
@@ -125,6 +128,14 @@ exports.generateTestWithBrowserAgent = {
125
128
  result: `Error running tool: ${error}`,
126
129
  };
127
130
  }
131
+ try {
132
+ // Prepare playwright for codegen
133
+ console.log("[generateTestWithBrowserAgent] Preparing playwright for codegen");
134
+ await (0, pw_pause_1.preparePlaywrightForCodegen)(repoDir);
135
+ }
136
+ catch (err) {
137
+ console.warn("[generateTestWithBrowserAgent] Error preparing playwright for codegen", err);
138
+ }
128
139
  const testGenToken = (0, scenarios_1.buildTokenFromOptions)({ name: testName, file: fileName, prompt: changeToMake }, { useComputerUseAgent: true });
129
140
  console.log("[generateTestWithBrowserAgent] Validations passed, starting agent");
130
141
  const toolResult = await (0, run_1.generateTestsUsingMasterAgent)({
@@ -138,9 +149,14 @@ exports.generateTestWithBrowserAgent = {
138
149
  repoDir,
139
150
  editFileWithGeneratedCode: false,
140
151
  });
141
- // Undo the TODO -> createTest changes
152
+ // Cleanup: Undo the TODO -> createTest changes
153
+ await (0, pw_pause_1.revertToOriginalPwCode)(repoDir);
142
154
  await promises_1.default.writeFile(fileName, fileBackup, "utf-8");
143
- const { isError, error, actionsSummary } = toolResult;
155
+ const { isError, error, actionsSummary, artifacts } = toolResult;
156
+ let collectedArtifacts;
157
+ if (artifacts) {
158
+ collectedArtifacts = await collectArtifacts?.(artifacts, repoDir, (0, slug_1.slugify)(testName));
159
+ }
144
160
  if (!isError) {
145
161
  return {
146
162
  isError,
@@ -149,6 +165,7 @@ and the generated Playwright code:
149
165
 
150
166
  ${actionsSummary}
151
167
  `,
168
+ artifacts: collectedArtifacts,
152
169
  };
153
170
  }
154
171
  else {
@@ -1 +1 @@
1
- {"version":3,"file":"test-run.d.ts","sourceRoot":"","sources":["../../src/tools/test-run.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAC;AAgDnD,eAAO,MAAM,WAAW,EAAE,IAiDzB,CAAC"}
1
+ {"version":3,"file":"test-run.d.ts","sourceRoot":"","sources":["../../src/tools/test-run.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAc,MAAM,wBAAwB,CAAC;AA4C/D,eAAO,MAAM,WAAW,EAAE,IAsEzB,CAAC"}
@@ -3,6 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.runTestTool = void 0;
4
4
  const test_run_1 = require("@empiricalrun/test-run");
5
5
  const zod_1 = require("zod");
6
+ const artifacts_1 = require("../artifacts");
7
+ const utils_1 = require("./utils");
6
8
  const RunTestSchema = zod_1.z.object({
7
9
  testName: zod_1.z.string().describe("The name of the test to run"),
8
10
  suites: zod_1.z
@@ -13,11 +15,6 @@ const RunTestSchema = zod_1.z.object({
13
15
  .describe("The name of the file where the test is located. File name must end with .spec.ts"),
14
16
  project: zod_1.z.string().describe("The project to run the test on"),
15
17
  });
16
- function hasCloudflareCredentials() {
17
- return (process.env.R2_ACCOUNT_ID &&
18
- process.env.R2_ACCESS_KEY_ID &&
19
- process.env.R2_SECRET_ACCESS_KEY);
20
- }
21
18
  function buildReportUrl(projectName, testRunId) {
22
19
  return `https://reports.empirical.run/${projectName}/${testRunId}/index.html`;
23
20
  }
@@ -37,10 +34,10 @@ exports.runTestTool = {
37
34
  description: "Run a test",
38
35
  parameters: RunTestSchema,
39
36
  },
40
- execute: async (input) => {
37
+ execute: async (input, repoPath, apiKey, trace, collectArtifacts) => {
41
38
  let reportUrl = undefined;
42
39
  let envOverrides = undefined;
43
- if (hasCloudflareCredentials()) {
40
+ if ((0, artifacts_1.isArtifactCollectionEnabled)()) {
44
41
  const projectName = "test-gen-chat-agent";
45
42
  const testRunId = Date.now().toString();
46
43
  reportUrl = buildReportUrl(projectName, testRunId);
@@ -53,6 +50,7 @@ exports.runTestTool = {
53
50
  console.warn("R2 credentials not found: report artifacts will not be uploaded");
54
51
  }
55
52
  const { testName, suites, fileName, project } = input;
53
+ const repoDir = process.cwd();
56
54
  try {
57
55
  const result = await (0, test_run_1.runSingleTest)({
58
56
  testName,
@@ -60,20 +58,25 @@ exports.runTestTool = {
60
58
  fileName,
61
59
  projects: [project],
62
60
  envOverrides,
61
+ repoDir,
63
62
  });
63
+ const attachments = (0, utils_1.extractAttachmentsFromPlaywrightJSONReport)(result.summaryJson);
64
+ const artifacts = await collectArtifacts?.(attachments, repoDir, Date.now().toString());
64
65
  return {
66
+ isError: false,
65
67
  result: buildResult({
66
68
  hasTestPassed: result.hasTestPassed,
67
69
  summaryJson: result.summaryJson,
68
70
  reportUrl: reportUrl,
69
71
  }),
70
- isError: false,
72
+ artifacts,
71
73
  };
72
74
  }
73
75
  catch (error) {
74
76
  // Ensure we capture the full error message regardless of error type
75
77
  const errorMessage = error instanceof Error ? error.message : String(error);
76
78
  return {
79
+ artifacts: null,
77
80
  result: JSON.stringify({ error: errorMessage }),
78
81
  isError: true,
79
82
  };
@@ -1,3 +1,4 @@
1
+ import { JSONReport as PlaywrightJSONReport } from "@playwright/test/reporter";
1
2
  export declare function makeDashboardRequest<T>({ path, method, body, }: {
2
3
  path: string;
3
4
  method?: string;
@@ -8,4 +9,16 @@ export declare function callGitHubProxy({ method, url, body, }: {
8
9
  url: string;
9
10
  body?: any;
10
11
  }): Promise<unknown>;
12
+ type AttachmentInfo = {
13
+ name: string;
14
+ path: string;
15
+ contentType: string;
16
+ };
17
+ /**
18
+ * Extracts attachment information from a Playwright JSON report.
19
+ * @param report The Playwright JSON report to extract attachments from
20
+ * @returns An array of objects containing path and contentType for each attachment
21
+ */
22
+ export declare function extractAttachmentsFromPlaywrightJSONReport(report: PlaywrightJSONReport): AttachmentInfo[];
23
+ export {};
11
24
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/tools/utils/index.ts"],"names":[],"mappings":"AAAA,wBAAsB,oBAAoB,CAAC,CAAC,EAAE,EAC5C,IAAI,EACJ,MAAc,EACd,IAAI,GACL,EAAE;IACD,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,GAAG,CAAC;CACZ,GAAG,OAAO,CAAC,CAAC,CAAC,CAuBb;AAED,wBAAsB,eAAe,CAAC,EACpC,MAAM,EACN,GAAG,EACH,IAAI,GACL,EAAE;IACD,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,GAAG,CAAC;CACZ,oBAWA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/tools/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,IAAI,oBAAoB,EAEnC,MAAM,2BAA2B,CAAC;AAEnC,wBAAsB,oBAAoB,CAAC,CAAC,EAAE,EAC5C,IAAI,EACJ,MAAc,EACd,IAAI,GACL,EAAE;IACD,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,GAAG,CAAC;CACZ,GAAG,OAAO,CAAC,CAAC,CAAC,CAuBb;AAED,wBAAsB,eAAe,CAAC,EACpC,MAAM,EACN,GAAG,EACH,IAAI,GACL,EAAE;IACD,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,GAAG,CAAC;CACZ,oBAWA;AAED,KAAK,cAAc,GAAG;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,0CAA0C,CACxD,MAAM,EAAE,oBAAoB,GAC3B,cAAc,EAAE,CA4ClB"}
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.makeDashboardRequest = makeDashboardRequest;
4
4
  exports.callGitHubProxy = callGitHubProxy;
5
+ exports.extractAttachmentsFromPlaywrightJSONReport = extractAttachmentsFromPlaywrightJSONReport;
5
6
  async function makeDashboardRequest({ path, method = "GET", body, }) {
6
7
  const API_KEY = process.env.EMPIRICALRUN_API_KEY;
7
8
  if (!API_KEY) {
@@ -36,3 +37,49 @@ async function callGitHubProxy({ method, url, body, }) {
36
37
  },
37
38
  });
38
39
  }
40
+ /**
41
+ * Extracts attachment information from a Playwright JSON report.
42
+ * @param report The Playwright JSON report to extract attachments from
43
+ * @returns An array of objects containing path and contentType for each attachment
44
+ */
45
+ function extractAttachmentsFromPlaywrightJSONReport(report) {
46
+ const attachments = [];
47
+ if (!report) {
48
+ return attachments;
49
+ }
50
+ function processSpec(spec) {
51
+ const testTitle = spec.title;
52
+ for (const test of spec.tests) {
53
+ for (const result of test.results) {
54
+ if (!result.attachments)
55
+ continue;
56
+ for (const attachment of result.attachments) {
57
+ if (attachment.path && attachment.contentType) {
58
+ attachments.push({
59
+ name: `${testTitle} ${attachment.name}`,
60
+ path: attachment.path,
61
+ contentType: attachment.contentType,
62
+ });
63
+ }
64
+ }
65
+ }
66
+ }
67
+ }
68
+ for (const suite of report.suites) {
69
+ // Process nested suites
70
+ if (suite.suites) {
71
+ for (const nestedSuite of suite.suites) {
72
+ for (const spec of nestedSuite.specs) {
73
+ processSpec(spec);
74
+ }
75
+ }
76
+ }
77
+ // Process specs in current suite
78
+ if (suite.specs) {
79
+ for (const spec of suite.specs) {
80
+ processSpec(spec);
81
+ }
82
+ }
83
+ }
84
+ return attachments;
85
+ }
@@ -2,11 +2,13 @@ export declare class ProcessManager {
2
2
  private childProcess;
3
3
  execute(command: string, args: string[], options: {
4
4
  env?: Record<string, string>;
5
+ cwd?: string;
5
6
  }): Promise<number>;
6
7
  terminate(): void;
7
8
  isRunning(): boolean;
8
9
  }
9
10
  export declare function cmd(command: string, args: string[], options: {
10
11
  env?: Record<string, string>;
12
+ cwd?: string;
11
13
  }): Promise<number>;
12
14
  //# sourceMappingURL=exec.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"exec.d.ts","sourceRoot":"","sources":["../../src/utils/exec.ts"],"names":[],"mappings":"AAmBA,qBAAa,cAAc;IACzB,OAAO,CAAC,YAAY,CAA6B;IAE3C,OAAO,CACX,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,EAAE;QAAE,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,GACxC,OAAO,CAAC,MAAM,CAAC;IAmDlB,SAAS,IAAI,IAAI;IASjB,SAAS,IAAI,OAAO;CAGrB;AAED,wBAAsB,GAAG,CACvB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,GACxC,OAAO,CAAC,MAAM,CAAC,CAGjB"}
1
+ {"version":3,"file":"exec.d.ts","sourceRoot":"","sources":["../../src/utils/exec.ts"],"names":[],"mappings":"AAmBA,qBAAa,cAAc;IACzB,OAAO,CAAC,YAAY,CAA6B;IAE3C,OAAO,CACX,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,EAEd,OAAO,EAAE;QAAE,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,GACtD,OAAO,CAAC,MAAM,CAAC;IAoDlB,SAAS,IAAI,IAAI;IASjB,SAAS,IAAI,OAAO;CAGrB;AAED,wBAAsB,GAAG,CACvB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GACtD,OAAO,CAAC,MAAM,CAAC,CAGjB"}
@@ -21,7 +21,9 @@ function isAcceptableExit(exitCode, signal) {
21
21
  }
22
22
  class ProcessManager {
23
23
  childProcess = null;
24
- async execute(command, args, options) {
24
+ async execute(command, args,
25
+ // TODO: Make these required when all tools use repoPath
26
+ options) {
25
27
  console.log(`Executing command: ${command} with args: ${args}`);
26
28
  if (this.childProcess) {
27
29
  throw new Error("Process is already running");
@@ -34,6 +36,7 @@ class ProcessManager {
34
36
  }
35
37
  const p = (0, child_process_1.spawn)(command, args, {
36
38
  env: { ...process_1.default.env, ...options.env },
39
+ cwd: options.cwd,
37
40
  detached: true, // Create process group so we can terminate all child processes
38
41
  });
39
42
  this.childProcess = p;
@@ -1 +1 @@
1
- {"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../../src/utils/git.ts"],"names":[],"mappings":"AAKA,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAKnD;AAED,wBAAsB,cAAc,CAAC,UAAU,EAAE,MAAM,iBAQtD;AAED,wBAAsB,eAAe,CAAC,aAAa,EAAE,MAAM,iBAS1D;AAED,wBAAsB,oBAAoB,oBAGzC;AAED,wBAAsB,UAAU,CAAC,UAAU,EAAE,MAAM,iBAElD;AAED,wBAAsB,wBAAwB,CAC5C,aAAa,EAAE,MAAM,EACrB,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,MAAM,EAAE,iBAKhB;AAED,wBAAsB,eAAe,sBAQpC"}
1
+ {"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../../src/utils/git.ts"],"names":[],"mappings":"AAKA,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAKnD;AAED,wBAAsB,cAAc,CAAC,UAAU,EAAE,MAAM,iBAQtD;AAED,wBAAsB,eAAe,CAAC,aAAa,EAAE,MAAM,iBAS1D;AAED,wBAAsB,oBAAoB,oBAGzC;AAED,wBAAsB,UAAU,CAAC,UAAU,EAAE,MAAM,iBAElD;AAED,wBAAsB,wBAAwB,CAC5C,aAAa,EAAE,MAAM,EACrB,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,MAAM,EAAE,iBAKhB;AAED,wBAAsB,eAAe,sBAUpC"}
package/dist/utils/git.js CHANGED
@@ -47,7 +47,7 @@ async function commitFilesAndPushBranch(commitMessage, branchName, files) {
47
47
  (0, child_process_1.execSync)(`git push origin ${branchName}`);
48
48
  }
49
49
  async function getFilesChanged() {
50
- const output = (0, child_process_1.execSync)("git status --porcelain").toString();
50
+ const output = (0, child_process_1.execSync)("git status --porcelain --untracked-files").toString();
51
51
  let filesChanged = output
52
52
  .split("\n")
53
53
  .map((line) => line.trim().split(" ").pop())
@@ -1,2 +1,18 @@
1
1
  export declare const asyncGenerateRandomSlug: (size: number) => Promise<string>;
2
+ /**
3
+ * Converts a string into a URL-friendly slug by:
4
+ * 1. Converting to lowercase
5
+ * 2. Replacing non-alphanumeric characters with hyphens
6
+ * 3. Collapsing multiple hyphens into single hyphens
7
+ * 4. Removing empty segments
8
+ * 5. Removing leading and trailing hyphens
9
+ *
10
+ * @param str - The input string to convert into a slug
11
+ * @returns A URL-friendly slug string
12
+ * @example
13
+ * slugify("Hello World!") // returns "hello-world"
14
+ * slugify("Test & Case") // returns "test-case"
15
+ * slugify("Multiple---Hyphens") // returns "multiple-hyphens"
16
+ */
17
+ export declare const slugify: (str: string) => string;
2
18
  //# sourceMappingURL=slug.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"slug.d.ts","sourceRoot":"","sources":["../../src/utils/slug.ts"],"names":[],"mappings":"AAQA,eAAO,MAAM,uBAAuB,GAAU,MAAM,MAAM,oBAOzD,CAAC"}
1
+ {"version":3,"file":"slug.d.ts","sourceRoot":"","sources":["../../src/utils/slug.ts"],"names":[],"mappings":"AAQA,eAAO,MAAM,uBAAuB,GAAU,MAAM,MAAM,oBAOzD,CAAC;AAEF;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,OAAO,GAAI,KAAK,MAAM,KAAG,MASrC,CAAC"}
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.asyncGenerateRandomSlug = void 0;
3
+ exports.slugify = exports.asyncGenerateRandomSlug = void 0;
4
4
  // @ts-ignore
5
5
  let customAlphabet;
6
6
  const loadNanoid = async () => {
@@ -16,3 +16,29 @@ const asyncGenerateRandomSlug = async (size) => {
16
16
  return nanoid();
17
17
  };
18
18
  exports.asyncGenerateRandomSlug = asyncGenerateRandomSlug;
19
+ /**
20
+ * Converts a string into a URL-friendly slug by:
21
+ * 1. Converting to lowercase
22
+ * 2. Replacing non-alphanumeric characters with hyphens
23
+ * 3. Collapsing multiple hyphens into single hyphens
24
+ * 4. Removing empty segments
25
+ * 5. Removing leading and trailing hyphens
26
+ *
27
+ * @param str - The input string to convert into a slug
28
+ * @returns A URL-friendly slug string
29
+ * @example
30
+ * slugify("Hello World!") // returns "hello-world"
31
+ * slugify("Test & Case") // returns "test-case"
32
+ * slugify("Multiple---Hyphens") // returns "multiple-hyphens"
33
+ */
34
+ const slugify = (str) => {
35
+ const join = "-";
36
+ return str
37
+ .toLowerCase()
38
+ .replace(/[^a-z0-9-]+/g, join)
39
+ .replace(/-+/g, join)
40
+ .split(join)
41
+ .filter(Boolean)
42
+ .join(join);
43
+ };
44
+ exports.slugify = slugify;
@@ -0,0 +1,2 @@
1
+ export declare function stripAnsiEscapes(str: string): string;
2
+ //# sourceMappingURL=stripAnsi.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stripAnsi.d.ts","sourceRoot":"","sources":["../../src/utils/stripAnsi.ts"],"names":[],"mappings":"AAMA,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEpD"}
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.stripAnsiEscapes = stripAnsiEscapes;
4
+ const ansiRegex = new RegExp(
5
+ // eslint-disable-next-line no-control-regex
6
+ "([\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~])))", "g");
7
+ function stripAnsiEscapes(str) {
8
+ return str.replace(ansiRegex, "");
9
+ }