@warlock.js/ai-workspace 4.5.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 (78) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/LICENSE +21 -0
  3. package/README.md +149 -0
  4. package/cjs/index.cjs +1609 -0
  5. package/cjs/index.cjs.map +1 -0
  6. package/esm/backends/local.d.mts +22 -0
  7. package/esm/backends/local.d.mts.map +1 -0
  8. package/esm/backends/local.mjs +208 -0
  9. package/esm/backends/local.mjs.map +1 -0
  10. package/esm/backends/mock.d.mts +62 -0
  11. package/esm/backends/mock.d.mts.map +1 -0
  12. package/esm/backends/mock.mjs +167 -0
  13. package/esm/backends/mock.mjs.map +1 -0
  14. package/esm/contracts/index.d.mts +5 -0
  15. package/esm/contracts/tool-io.type.d.mts +149 -0
  16. package/esm/contracts/tool-io.type.d.mts.map +1 -0
  17. package/esm/contracts/workspace-backend.contract.d.mts +69 -0
  18. package/esm/contracts/workspace-backend.contract.d.mts.map +1 -0
  19. package/esm/contracts/workspace-ops.contract.d.mts +72 -0
  20. package/esm/contracts/workspace-ops.contract.d.mts.map +1 -0
  21. package/esm/contracts/workspace-policy.type.d.mts +86 -0
  22. package/esm/contracts/workspace-policy.type.d.mts.map +1 -0
  23. package/esm/contracts/workspace.contract.d.mts +131 -0
  24. package/esm/contracts/workspace.contract.d.mts.map +1 -0
  25. package/esm/errors.d.mts +100 -0
  26. package/esm/errors.d.mts.map +1 -0
  27. package/esm/errors.mjs +58 -0
  28. package/esm/errors.mjs.map +1 -0
  29. package/esm/index.d.mts +20 -0
  30. package/esm/index.mjs +15 -0
  31. package/esm/ops.d.mts +25 -0
  32. package/esm/ops.d.mts.map +1 -0
  33. package/esm/ops.mjs +294 -0
  34. package/esm/ops.mjs.map +1 -0
  35. package/esm/policy/policy.d.mts +71 -0
  36. package/esm/policy/policy.d.mts.map +1 -0
  37. package/esm/policy/policy.mjs +184 -0
  38. package/esm/policy/policy.mjs.map +1 -0
  39. package/esm/tools/edit-file.d.mts +40 -0
  40. package/esm/tools/edit-file.d.mts.map +1 -0
  41. package/esm/tools/edit-file.mjs +57 -0
  42. package/esm/tools/edit-file.mjs.map +1 -0
  43. package/esm/tools/glob.d.mts +37 -0
  44. package/esm/tools/glob.d.mts.map +1 -0
  45. package/esm/tools/glob.mjs +45 -0
  46. package/esm/tools/glob.mjs.map +1 -0
  47. package/esm/tools/grep.d.mts +36 -0
  48. package/esm/tools/grep.d.mts.map +1 -0
  49. package/esm/tools/grep.mjs +51 -0
  50. package/esm/tools/grep.mjs.map +1 -0
  51. package/esm/tools/read-file.d.mts +35 -0
  52. package/esm/tools/read-file.d.mts.map +1 -0
  53. package/esm/tools/read-file.mjs +64 -0
  54. package/esm/tools/read-file.mjs.map +1 -0
  55. package/esm/tools/run-shell.d.mts +35 -0
  56. package/esm/tools/run-shell.d.mts.map +1 -0
  57. package/esm/tools/run-shell.mjs +65 -0
  58. package/esm/tools/run-shell.mjs.map +1 -0
  59. package/esm/tools/run-tests.d.mts +40 -0
  60. package/esm/tools/run-tests.d.mts.map +1 -0
  61. package/esm/tools/run-tests.mjs +67 -0
  62. package/esm/tools/run-tests.mjs.map +1 -0
  63. package/esm/tools/schema.mjs +111 -0
  64. package/esm/tools/schema.mjs.map +1 -0
  65. package/esm/tools/write-file.d.mts +33 -0
  66. package/esm/tools/write-file.d.mts.map +1 -0
  67. package/esm/tools/write-file.mjs +52 -0
  68. package/esm/tools/write-file.mjs.map +1 -0
  69. package/esm/workspace.d.mts +54 -0
  70. package/esm/workspace.d.mts.map +1 -0
  71. package/esm/workspace.mjs +210 -0
  72. package/esm/workspace.mjs.map +1 -0
  73. package/llms-full.txt +231 -0
  74. package/llms.txt +10 -0
  75. package/package.json +42 -0
  76. package/skills/README.md +13 -0
  77. package/skills/build-loop-agent/SKILL.md +100 -0
  78. package/skills/use-a-workspace/SKILL.md +117 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"edit-file.d.mts","names":[],"sources":["../../../../../../../@warlock.js/ai-workspace/src/tools/edit-file.ts"],"mappings":";;;;;;;;;AAkDA;;;;;;;;;;;;;;;;;AAG6C;;;;;;;;iBAH7B,gBAAA,CACd,GAAA,EAAK,YAAA,EACL,OAAA;EAAY,IAAA;AAAA,IACX,YAAA,CAAa,aAAA,EAAe,cAAA"}
@@ -0,0 +1,57 @@
1
+ import { objectSchema, optionalBooleanField, optionalStringField, stringField } from "./schema.mjs";
2
+ import { tool } from "@warlock.js/ai";
3
+
4
+ //#region ../@warlock.js/ai-workspace/src/tools/edit-file.ts
5
+ /** Default tool name exposed to the LLM. */
6
+ const DEFAULT_NAME = "edit_file";
7
+ /** Input schema for the `edit_file` tool. */
8
+ const inputSchema = objectSchema({
9
+ path: stringField(),
10
+ oldString: stringField(),
11
+ newString: stringField(),
12
+ replaceAll: optionalBooleanField(),
13
+ expectHash: optionalStringField()
14
+ });
15
+ /**
16
+ * Build the agent-facing `edit_file` tool over a workspace's policy-
17
+ * enforced {@link WorkspaceOps}.
18
+ *
19
+ * The tool validates `{ path, oldString, newString, replaceAll?,
20
+ * expectHash? }` against a Standard Schema, then delegates to
21
+ * `ops.editFile`, which applies the exact-string replacement under the
22
+ * read-before-edit guard and returns the replacement count plus the
23
+ * post-edit `hash`.
24
+ *
25
+ * **Errors flow as data.** A non-unique `oldString` (without
26
+ * `replaceAll`), a missing `oldString`, or a stale `expectHash` cause
27
+ * `ops` to throw a `WorkspaceEditError`; the `tool()` wrapper catches it
28
+ * and surfaces it in the returned `{ error }` field — `invoke()` never
29
+ * throws — so the agent can re-read and retry.
30
+ *
31
+ * @param ops - The shared, policy-enforced operation layer.
32
+ * @param options - Optional overrides; `name` renames the LLM-visible tool.
33
+ *
34
+ * @example
35
+ * const editTool = makeEditFileTool(ops);
36
+ * const { data, error } = await editTool.invoke({
37
+ * path: "src/index.ts",
38
+ * oldString: "const a = 1;",
39
+ * newString: "const a = 2;",
40
+ * expectHash,
41
+ * });
42
+ * if (error) console.warn(error.message); // e.g. stale-hash → re-read
43
+ */
44
+ function makeEditFileTool(ops, options) {
45
+ return tool({
46
+ name: options?.name ?? DEFAULT_NAME,
47
+ description: "Replace an exact substring in a workspace file. oldString must match uniquely unless replaceAll is set. Pass expectHash (from read_file) to reject the edit if the file changed since you read it. Returns the number of replacements and the new content hash.",
48
+ input: inputSchema,
49
+ async execute(input) {
50
+ return ops.editFile(input);
51
+ }
52
+ });
53
+ }
54
+
55
+ //#endregion
56
+ export { makeEditFileTool };
57
+ //# sourceMappingURL=edit-file.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"edit-file.mjs","names":[],"sources":["../../../../../../../@warlock.js/ai-workspace/src/tools/edit-file.ts"],"sourcesContent":["import { tool, type ToolContract } from \"@warlock.js/ai\";\nimport {\n objectSchema,\n optionalBooleanField,\n optionalStringField,\n stringField,\n} from \"./schema\";\nimport type { EditFileInput, EditFileResult, WorkspaceOps } from \"../contracts\";\n\n/** Default tool name exposed to the LLM. */\nconst DEFAULT_NAME = \"edit_file\";\n\n/** Input schema for the `edit_file` tool. */\nconst inputSchema = objectSchema<EditFileInput>({\n path: stringField(),\n oldString: stringField(),\n newString: stringField(),\n replaceAll: optionalBooleanField(),\n expectHash: optionalStringField(),\n});\n\n/**\n * Build the agent-facing `edit_file` tool over a workspace's policy-\n * enforced {@link WorkspaceOps}.\n *\n * The tool validates `{ path, oldString, newString, replaceAll?,\n * expectHash? }` against a Standard Schema, then delegates to\n * `ops.editFile`, which applies the exact-string replacement under the\n * read-before-edit guard and returns the replacement count plus the\n * post-edit `hash`.\n *\n * **Errors flow as data.** A non-unique `oldString` (without\n * `replaceAll`), a missing `oldString`, or a stale `expectHash` cause\n * `ops` to throw a `WorkspaceEditError`; the `tool()` wrapper catches it\n * and surfaces it in the returned `{ error }` field — `invoke()` never\n * throws — so the agent can re-read and retry.\n *\n * @param ops - The shared, policy-enforced operation layer.\n * @param options - Optional overrides; `name` renames the LLM-visible tool.\n *\n * @example\n * const editTool = makeEditFileTool(ops);\n * const { data, error } = await editTool.invoke({\n * path: \"src/index.ts\",\n * oldString: \"const a = 1;\",\n * newString: \"const a = 2;\",\n * expectHash,\n * });\n * if (error) console.warn(error.message); // e.g. stale-hash → re-read\n */\nexport function makeEditFileTool(\n ops: WorkspaceOps,\n options?: { name?: string },\n): ToolContract<EditFileInput, EditFileResult> {\n return tool<EditFileInput, EditFileResult>({\n name: options?.name ?? DEFAULT_NAME,\n description:\n \"Replace an exact substring in a workspace file. oldString must match \" +\n \"uniquely unless replaceAll is set. Pass expectHash (from read_file) to \" +\n \"reject the edit if the file changed since you read it. Returns the \" +\n \"number of replacements and the new content hash.\",\n input: inputSchema,\n async execute(input) {\n return ops.editFile(input);\n },\n });\n}\n"],"mappings":";;;;;AAUA,MAAM,eAAe;;AAGrB,MAAM,cAAc,aAA4B;CAC9C,MAAM,YAAY;CAClB,WAAW,YAAY;CACvB,WAAW,YAAY;CACvB,YAAY,qBAAqB;CACjC,YAAY,oBAAoB;AAClC,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+BD,SAAgB,iBACd,KACA,SAC6C;CAC7C,OAAO,KAAoC;EACzC,MAAM,SAAS,QAAQ;EACvB,aACE;EAIF,OAAO;EACP,MAAM,QAAQ,OAAO;GACnB,OAAO,IAAI,SAAS,KAAK;EAC3B;CACF,CAAC;AACH"}
@@ -0,0 +1,37 @@
1
+ import { GlobInput, GlobResult } from "../contracts/tool-io.type.mjs";
2
+ import { WorkspaceOps } from "../contracts/workspace-ops.contract.mjs";
3
+ import { ToolContract } from "@warlock.js/ai";
4
+
5
+ //#region ../@warlock.js/ai-workspace/src/tools/glob.d.ts
6
+ /** Options accepted by {@link makeGlobTool} to customize the vended tool. */
7
+ interface MakeGlobToolOptions {
8
+ /**
9
+ * Override the tool name the LLM sees. Defaults to `"glob"`. Use a
10
+ * custom name when wiring several workspaces into one agent so each
11
+ * path-match surface is addressable.
12
+ */
13
+ name?: string;
14
+ }
15
+ /**
16
+ * Build the agent-facing `glob` tool — resolve a glob pattern to the
17
+ * matching workspace-relative paths within the jail. The returned
18
+ * {@link ToolContract} validates the LLM's arguments, delegates to
19
+ * {@link WorkspaceOps.glob} (which returns a bare sorted `string[]`), and
20
+ * wraps the result in a {@link GlobResult} so the agent always reads a
21
+ * stable `{ paths }` envelope. The jail and `denyPaths` filtering are
22
+ * enforced in the shared ops layer; a policy violation surfaces as typed
23
+ * tool-error *data* via the runtime's `invoke()` wrapper.
24
+ *
25
+ * @param ops - The policy-enforced operation layer to delegate to.
26
+ * @param options - Optional `{ name }` override for the vended tool name.
27
+ * @returns A {@link ToolContract} the agent can call as `glob`.
28
+ *
29
+ * @example
30
+ * const glob = makeGlobTool(ops);
31
+ * const { data } = await glob.invoke({ pattern: "src/models/**\/*.ts" });
32
+ * console.log(data?.paths);
33
+ */
34
+ declare function makeGlobTool(ops: WorkspaceOps, options?: MakeGlobToolOptions): ToolContract<GlobInput, GlobResult>;
35
+ //#endregion
36
+ export { MakeGlobToolOptions, makeGlobTool };
37
+ //# sourceMappingURL=glob.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"glob.d.mts","names":[],"sources":["../../../../../../../@warlock.js/ai-workspace/src/tools/glob.ts"],"mappings":";;;;;;UAKiB,mBAAA;;;AAAjB;;;EAME,IAAI;AAAA;AAgCN;;;;;;;;;;;;;;;;;;AAGqC;AAHrC,iBAAgB,YAAA,CACd,GAAA,EAAK,YAAA,EACL,OAAA,GAAU,mBAAA,GACT,YAAA,CAAa,SAAA,EAAW,UAAA"}
@@ -0,0 +1,45 @@
1
+ import { objectSchema, stringField } from "./schema.mjs";
2
+ import { tool } from "@warlock.js/ai";
3
+
4
+ //#region ../@warlock.js/ai-workspace/src/tools/glob.ts
5
+ /**
6
+ * Standard Schema for {@link GlobInput} — a single required `pattern`
7
+ * string. Built on the package's shared, dependency-free schema builders
8
+ * (no schema library, matching the validator idiom the `@warlock.js/ai`
9
+ * tool runtime expects).
10
+ */
11
+ const globInputSchema = objectSchema({ pattern: stringField() });
12
+ /**
13
+ * Build the agent-facing `glob` tool — resolve a glob pattern to the
14
+ * matching workspace-relative paths within the jail. The returned
15
+ * {@link ToolContract} validates the LLM's arguments, delegates to
16
+ * {@link WorkspaceOps.glob} (which returns a bare sorted `string[]`), and
17
+ * wraps the result in a {@link GlobResult} so the agent always reads a
18
+ * stable `{ paths }` envelope. The jail and `denyPaths` filtering are
19
+ * enforced in the shared ops layer; a policy violation surfaces as typed
20
+ * tool-error *data* via the runtime's `invoke()` wrapper.
21
+ *
22
+ * @param ops - The policy-enforced operation layer to delegate to.
23
+ * @param options - Optional `{ name }` override for the vended tool name.
24
+ * @returns A {@link ToolContract} the agent can call as `glob`.
25
+ *
26
+ * @example
27
+ * const glob = makeGlobTool(ops);
28
+ * const { data } = await glob.invoke({ pattern: "src/models/**\/*.ts" });
29
+ * console.log(data?.paths);
30
+ */
31
+ function makeGlobTool(ops, options) {
32
+ return tool({
33
+ name: options?.name ?? "glob",
34
+ description: "Find files in the workspace whose path matches a glob pattern (supports `*`, `**`, and `?`). Returns the matching workspace-relative paths, sorted.",
35
+ action: (input) => `Finding files matching ${input.pattern}`,
36
+ input: globInputSchema,
37
+ async execute(input) {
38
+ return { paths: await ops.glob(input.pattern) };
39
+ }
40
+ });
41
+ }
42
+
43
+ //#endregion
44
+ export { makeGlobTool };
45
+ //# sourceMappingURL=glob.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"glob.mjs","names":[],"sources":["../../../../../../../@warlock.js/ai-workspace/src/tools/glob.ts"],"sourcesContent":["import { type ToolContract, tool } from \"@warlock.js/ai\";\nimport { objectSchema, stringField } from \"./schema\";\nimport type { GlobInput, GlobResult, WorkspaceOps } from \"../contracts\";\n\n/** Options accepted by {@link makeGlobTool} to customize the vended tool. */\nexport interface MakeGlobToolOptions {\n /**\n * Override the tool name the LLM sees. Defaults to `\"glob\"`. Use a\n * custom name when wiring several workspaces into one agent so each\n * path-match surface is addressable.\n */\n name?: string;\n}\n\n/**\n * Standard Schema for {@link GlobInput} — a single required `pattern`\n * string. Built on the package's shared, dependency-free schema builders\n * (no schema library, matching the validator idiom the `@warlock.js/ai`\n * tool runtime expects).\n */\nconst globInputSchema = objectSchema<GlobInput>({\n pattern: stringField(),\n});\n\n/**\n * Build the agent-facing `glob` tool — resolve a glob pattern to the\n * matching workspace-relative paths within the jail. The returned\n * {@link ToolContract} validates the LLM's arguments, delegates to\n * {@link WorkspaceOps.glob} (which returns a bare sorted `string[]`), and\n * wraps the result in a {@link GlobResult} so the agent always reads a\n * stable `{ paths }` envelope. The jail and `denyPaths` filtering are\n * enforced in the shared ops layer; a policy violation surfaces as typed\n * tool-error *data* via the runtime's `invoke()` wrapper.\n *\n * @param ops - The policy-enforced operation layer to delegate to.\n * @param options - Optional `{ name }` override for the vended tool name.\n * @returns A {@link ToolContract} the agent can call as `glob`.\n *\n * @example\n * const glob = makeGlobTool(ops);\n * const { data } = await glob.invoke({ pattern: \"src/models/**\\/*.ts\" });\n * console.log(data?.paths);\n */\nexport function makeGlobTool(\n ops: WorkspaceOps,\n options?: MakeGlobToolOptions,\n): ToolContract<GlobInput, GlobResult> {\n return tool<GlobInput, GlobResult>({\n name: options?.name ?? \"glob\",\n description:\n \"Find files in the workspace whose path matches a glob pattern \" +\n \"(supports `*`, `**`, and `?`). Returns the matching \" +\n \"workspace-relative paths, sorted.\",\n action: (input) => `Finding files matching ${input.pattern}`,\n input: globInputSchema,\n async execute(input) {\n const paths = await ops.glob(input.pattern);\n\n return { paths };\n },\n });\n}\n"],"mappings":";;;;;;;;;;AAoBA,MAAM,kBAAkB,aAAwB,EAC9C,SAAS,YAAY,EACvB,CAAC;;;;;;;;;;;;;;;;;;;;AAqBD,SAAgB,aACd,KACA,SACqC;CACrC,OAAO,KAA4B;EACjC,MAAM,SAAS,QAAQ;EACvB,aACE;EAGF,SAAS,UAAU,0BAA0B,MAAM;EACnD,OAAO;EACP,MAAM,QAAQ,OAAO;GAGnB,OAAO,EAAE,aAFW,IAAI,KAAK,MAAM,OAAO,EAE3B;EACjB;CACF,CAAC;AACH"}
@@ -0,0 +1,36 @@
1
+ import { GrepInput, GrepResult } from "../contracts/tool-io.type.mjs";
2
+ import { WorkspaceOps } from "../contracts/workspace-ops.contract.mjs";
3
+ import { ToolContract } from "@warlock.js/ai";
4
+
5
+ //#region ../@warlock.js/ai-workspace/src/tools/grep.d.ts
6
+ /** Options accepted by {@link makeGrepTool} to customize the vended tool. */
7
+ interface MakeGrepToolOptions {
8
+ /**
9
+ * Override the tool name the LLM sees. Defaults to `"grep"`. Use a
10
+ * custom name when wiring several workspaces into one agent so each
11
+ * search surface is addressable.
12
+ */
13
+ name?: string;
14
+ }
15
+ /**
16
+ * Build the agent-facing `grep` tool — a regex content search across the
17
+ * jailed file set. The returned {@link ToolContract} validates the LLM's
18
+ * arguments, then delegates verbatim to {@link WorkspaceOps.grep}, so the
19
+ * policy jail, `denyPaths` filtering, and match cap are enforced in the
20
+ * single shared ops layer rather than duplicated here. A policy violation
21
+ * (e.g. a jail-resolution failure) surfaces as typed tool-error *data*
22
+ * via the runtime's `invoke()` wrapper, never as a thrown run-killer.
23
+ *
24
+ * @param ops - The policy-enforced operation layer to delegate to.
25
+ * @param options - Optional `{ name }` override for the vended tool name.
26
+ * @returns A {@link ToolContract} the agent can call as `grep`.
27
+ *
28
+ * @example
29
+ * const grep = makeGrepTool(ops);
30
+ * const { data } = await grep.invoke({ pattern: "TODO", glob: "src/*.ts" });
31
+ * console.log(data?.total, data?.matches);
32
+ */
33
+ declare function makeGrepTool(ops: WorkspaceOps, options?: MakeGrepToolOptions): ToolContract<GrepInput, GrepResult>;
34
+ //#endregion
35
+ export { MakeGrepToolOptions, makeGrepTool };
36
+ //# sourceMappingURL=grep.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grep.d.mts","names":[],"sources":["../../../../../../../@warlock.js/ai-workspace/src/tools/grep.ts"],"mappings":";;;;;;UAUiB,mBAAA;;;AAAjB;;;EAME,IAAI;AAAA;AAiCN;;;;;;;;;;;;;;;;;;AAAA,iBAAgB,YAAA,CACd,GAAA,EAAK,YAAA,EACL,OAAA,GAAU,mBAAA,GACT,YAAA,CAAa,SAAA,EAAW,UAAA"}
@@ -0,0 +1,51 @@
1
+ import { objectSchema, optionalBooleanField, optionalStringField, stringField } from "./schema.mjs";
2
+ import { tool } from "@warlock.js/ai";
3
+
4
+ //#region ../@warlock.js/ai-workspace/src/tools/grep.ts
5
+ /**
6
+ * Standard Schema for {@link GrepInput} — `pattern` is a required string;
7
+ * `glob` and `ignoreCase` are optional. Built on the package's shared,
8
+ * dependency-free schema builders (no schema library, matching the
9
+ * validator idiom the `@warlock.js/ai` tool runtime expects).
10
+ */
11
+ const grepInputSchema = objectSchema({
12
+ pattern: stringField(),
13
+ glob: optionalStringField(),
14
+ ignoreCase: optionalBooleanField()
15
+ });
16
+ /**
17
+ * Build the agent-facing `grep` tool — a regex content search across the
18
+ * jailed file set. The returned {@link ToolContract} validates the LLM's
19
+ * arguments, then delegates verbatim to {@link WorkspaceOps.grep}, so the
20
+ * policy jail, `denyPaths` filtering, and match cap are enforced in the
21
+ * single shared ops layer rather than duplicated here. A policy violation
22
+ * (e.g. a jail-resolution failure) surfaces as typed tool-error *data*
23
+ * via the runtime's `invoke()` wrapper, never as a thrown run-killer.
24
+ *
25
+ * @param ops - The policy-enforced operation layer to delegate to.
26
+ * @param options - Optional `{ name }` override for the vended tool name.
27
+ * @returns A {@link ToolContract} the agent can call as `grep`.
28
+ *
29
+ * @example
30
+ * const grep = makeGrepTool(ops);
31
+ * const { data } = await grep.invoke({ pattern: "TODO", glob: "src/*.ts" });
32
+ * console.log(data?.total, data?.matches);
33
+ */
34
+ function makeGrepTool(ops, options) {
35
+ return tool({
36
+ name: options?.name ?? "grep",
37
+ description: "Search file contents across the workspace for a regular-expression pattern. Optionally narrow the scanned files with a glob and match case-insensitively. Returns every matching line with its file path and 1-based line number.",
38
+ action: (input) => `Searching for /${input.pattern}/`,
39
+ input: grepInputSchema,
40
+ async execute(input) {
41
+ return ops.grep(input.pattern, {
42
+ glob: input.glob,
43
+ ignoreCase: input.ignoreCase
44
+ });
45
+ }
46
+ });
47
+ }
48
+
49
+ //#endregion
50
+ export { makeGrepTool };
51
+ //# sourceMappingURL=grep.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grep.mjs","names":[],"sources":["../../../../../../../@warlock.js/ai-workspace/src/tools/grep.ts"],"sourcesContent":["import { type ToolContract, tool } from \"@warlock.js/ai\";\nimport {\n objectSchema,\n optionalBooleanField,\n optionalStringField,\n stringField,\n} from \"./schema\";\nimport type { GrepInput, GrepResult, WorkspaceOps } from \"../contracts\";\n\n/** Options accepted by {@link makeGrepTool} to customize the vended tool. */\nexport interface MakeGrepToolOptions {\n /**\n * Override the tool name the LLM sees. Defaults to `\"grep\"`. Use a\n * custom name when wiring several workspaces into one agent so each\n * search surface is addressable.\n */\n name?: string;\n}\n\n/**\n * Standard Schema for {@link GrepInput} — `pattern` is a required string;\n * `glob` and `ignoreCase` are optional. Built on the package's shared,\n * dependency-free schema builders (no schema library, matching the\n * validator idiom the `@warlock.js/ai` tool runtime expects).\n */\nconst grepInputSchema = objectSchema<GrepInput>({\n pattern: stringField(),\n glob: optionalStringField(),\n ignoreCase: optionalBooleanField(),\n});\n\n/**\n * Build the agent-facing `grep` tool — a regex content search across the\n * jailed file set. The returned {@link ToolContract} validates the LLM's\n * arguments, then delegates verbatim to {@link WorkspaceOps.grep}, so the\n * policy jail, `denyPaths` filtering, and match cap are enforced in the\n * single shared ops layer rather than duplicated here. A policy violation\n * (e.g. a jail-resolution failure) surfaces as typed tool-error *data*\n * via the runtime's `invoke()` wrapper, never as a thrown run-killer.\n *\n * @param ops - The policy-enforced operation layer to delegate to.\n * @param options - Optional `{ name }` override for the vended tool name.\n * @returns A {@link ToolContract} the agent can call as `grep`.\n *\n * @example\n * const grep = makeGrepTool(ops);\n * const { data } = await grep.invoke({ pattern: \"TODO\", glob: \"src/*.ts\" });\n * console.log(data?.total, data?.matches);\n */\nexport function makeGrepTool(\n ops: WorkspaceOps,\n options?: MakeGrepToolOptions,\n): ToolContract<GrepInput, GrepResult> {\n return tool<GrepInput, GrepResult>({\n name: options?.name ?? \"grep\",\n description:\n \"Search file contents across the workspace for a regular-expression \" +\n \"pattern. Optionally narrow the scanned files with a glob and match \" +\n \"case-insensitively. Returns every matching line with its file path \" +\n \"and 1-based line number.\",\n action: (input) => `Searching for /${input.pattern}/`,\n input: grepInputSchema,\n async execute(input) {\n return ops.grep(input.pattern, {\n glob: input.glob,\n ignoreCase: input.ignoreCase,\n });\n },\n });\n}\n"],"mappings":";;;;;;;;;;AAyBA,MAAM,kBAAkB,aAAwB;CAC9C,SAAS,YAAY;CACrB,MAAM,oBAAoB;CAC1B,YAAY,qBAAqB;AACnC,CAAC;;;;;;;;;;;;;;;;;;;AAoBD,SAAgB,aACd,KACA,SACqC;CACrC,OAAO,KAA4B;EACjC,MAAM,SAAS,QAAQ;EACvB,aACE;EAIF,SAAS,UAAU,kBAAkB,MAAM,QAAQ;EACnD,OAAO;EACP,MAAM,QAAQ,OAAO;GACnB,OAAO,IAAI,KAAK,MAAM,SAAS;IAC7B,MAAM,MAAM;IACZ,YAAY,MAAM;GACpB,CAAC;EACH;CACF,CAAC;AACH"}
@@ -0,0 +1,35 @@
1
+ import { ReadFileInput, ReadFileResult } from "../contracts/tool-io.type.mjs";
2
+ import { WorkspaceOps } from "../contracts/workspace-ops.contract.mjs";
3
+ import { ToolContract } from "@warlock.js/ai";
4
+
5
+ //#region ../@warlock.js/ai-workspace/src/tools/read-file.d.ts
6
+ /**
7
+ * Build the agent-facing `read_file` tool over a workspace's policy-
8
+ * enforced {@link WorkspaceOps}.
9
+ *
10
+ * The tool validates `{ path, startLine?, limit? }` against a Standard
11
+ * Schema, then delegates to `ops.readFile`, mapping the result into the
12
+ * agent wire shape {@link ReadFileResult} — the `hash` an agent must
13
+ * carry into a later `edit_file` (read-before-edit), plus the `startLine`
14
+ * / `endLine` / `truncated` window metadata derived from the requested
15
+ * range and the file's `totalLines`.
16
+ *
17
+ * **Errors flow as data.** Policy violations (a jail escape) are thrown
18
+ * by `ops`; the `tool()` wrapper catches them and surfaces them in the
19
+ * returned `{ error }` field — `invoke()` never throws — so the agent can
20
+ * read the failure and self-correct.
21
+ *
22
+ * @param ops - The shared, policy-enforced operation layer.
23
+ * @param options - Optional overrides; `name` renames the LLM-visible tool.
24
+ *
25
+ * @example
26
+ * const readTool = makeReadFileTool(ops);
27
+ * const { data, error } = await readTool.invoke({ path: "src/index.ts" });
28
+ * if (!error) console.log(data.hash); // feed into edit_file's expectHash
29
+ */
30
+ declare function makeReadFileTool(ops: WorkspaceOps, options?: {
31
+ name?: string;
32
+ }): ToolContract<ReadFileInput, ReadFileResult>;
33
+ //#endregion
34
+ export { makeReadFileTool };
35
+ //# sourceMappingURL=read-file.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"read-file.d.mts","names":[],"sources":["../../../../../../../@warlock.js/ai-workspace/src/tools/read-file.ts"],"mappings":";;;;;;;;;AAsCA;;;;;;;;;;;;;;;;;AAG6C;;;iBAH7B,gBAAA,CACd,GAAA,EAAK,YAAA,EACL,OAAA;EAAY,IAAA;AAAA,IACX,YAAA,CAAa,aAAA,EAAe,cAAA"}
@@ -0,0 +1,64 @@
1
+ import { objectSchema, optionalNumberField, stringField } from "./schema.mjs";
2
+ import { tool } from "@warlock.js/ai";
3
+
4
+ //#region ../@warlock.js/ai-workspace/src/tools/read-file.ts
5
+ /** Default tool name exposed to the LLM. */
6
+ const DEFAULT_NAME = "read_file";
7
+ /** Input schema for the `read_file` tool. */
8
+ const inputSchema = objectSchema({
9
+ path: stringField(),
10
+ startLine: optionalNumberField(),
11
+ limit: optionalNumberField()
12
+ });
13
+ /**
14
+ * Build the agent-facing `read_file` tool over a workspace's policy-
15
+ * enforced {@link WorkspaceOps}.
16
+ *
17
+ * The tool validates `{ path, startLine?, limit? }` against a Standard
18
+ * Schema, then delegates to `ops.readFile`, mapping the result into the
19
+ * agent wire shape {@link ReadFileResult} — the `hash` an agent must
20
+ * carry into a later `edit_file` (read-before-edit), plus the `startLine`
21
+ * / `endLine` / `truncated` window metadata derived from the requested
22
+ * range and the file's `totalLines`.
23
+ *
24
+ * **Errors flow as data.** Policy violations (a jail escape) are thrown
25
+ * by `ops`; the `tool()` wrapper catches them and surfaces them in the
26
+ * returned `{ error }` field — `invoke()` never throws — so the agent can
27
+ * read the failure and self-correct.
28
+ *
29
+ * @param ops - The shared, policy-enforced operation layer.
30
+ * @param options - Optional overrides; `name` renames the LLM-visible tool.
31
+ *
32
+ * @example
33
+ * const readTool = makeReadFileTool(ops);
34
+ * const { data, error } = await readTool.invoke({ path: "src/index.ts" });
35
+ * if (!error) console.log(data.hash); // feed into edit_file's expectHash
36
+ */
37
+ function makeReadFileTool(ops, options) {
38
+ return tool({
39
+ name: options?.name ?? DEFAULT_NAME,
40
+ description: "Read a file from the workspace, returning a numbered line window plus the file's content hash. Pass the hash to edit_file's expectHash to guard against editing a stale version. Use startLine/limit to page through large files.",
41
+ input: inputSchema,
42
+ async execute(input) {
43
+ const startLine = input.startLine !== void 0 ? Math.max(1, input.startLine) : 1;
44
+ const { content, hash, totalLines } = await ops.readFile(input.path, {
45
+ offset: startLine,
46
+ limit: input.limit
47
+ });
48
+ const returnedLines = content.length === 0 ? 0 : content.split("\n").length;
49
+ const endLine = Math.min(totalLines, startLine + Math.max(returnedLines, 1) - 1);
50
+ return {
51
+ content,
52
+ startLine,
53
+ endLine,
54
+ totalLines,
55
+ truncated: endLine < totalLines,
56
+ hash
57
+ };
58
+ }
59
+ });
60
+ }
61
+
62
+ //#endregion
63
+ export { makeReadFileTool };
64
+ //# sourceMappingURL=read-file.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"read-file.mjs","names":[],"sources":["../../../../../../../@warlock.js/ai-workspace/src/tools/read-file.ts"],"sourcesContent":["import { tool, type ToolContract } from \"@warlock.js/ai\";\nimport { objectSchema, optionalNumberField, stringField } from \"./schema\";\nimport type { ReadFileInput, ReadFileResult, WorkspaceOps } from \"../contracts\";\n\n/** Default tool name exposed to the LLM. */\nconst DEFAULT_NAME = \"read_file\";\n\n/** Input schema for the `read_file` tool. */\nconst inputSchema = objectSchema<ReadFileInput>({\n path: stringField(),\n startLine: optionalNumberField(),\n limit: optionalNumberField(),\n});\n\n/**\n * Build the agent-facing `read_file` tool over a workspace's policy-\n * enforced {@link WorkspaceOps}.\n *\n * The tool validates `{ path, startLine?, limit? }` against a Standard\n * Schema, then delegates to `ops.readFile`, mapping the result into the\n * agent wire shape {@link ReadFileResult} — the `hash` an agent must\n * carry into a later `edit_file` (read-before-edit), plus the `startLine`\n * / `endLine` / `truncated` window metadata derived from the requested\n * range and the file's `totalLines`.\n *\n * **Errors flow as data.** Policy violations (a jail escape) are thrown\n * by `ops`; the `tool()` wrapper catches them and surfaces them in the\n * returned `{ error }` field — `invoke()` never throws — so the agent can\n * read the failure and self-correct.\n *\n * @param ops - The shared, policy-enforced operation layer.\n * @param options - Optional overrides; `name` renames the LLM-visible tool.\n *\n * @example\n * const readTool = makeReadFileTool(ops);\n * const { data, error } = await readTool.invoke({ path: \"src/index.ts\" });\n * if (!error) console.log(data.hash); // feed into edit_file's expectHash\n */\nexport function makeReadFileTool(\n ops: WorkspaceOps,\n options?: { name?: string },\n): ToolContract<ReadFileInput, ReadFileResult> {\n return tool<ReadFileInput, ReadFileResult>({\n name: options?.name ?? DEFAULT_NAME,\n description:\n \"Read a file from the workspace, returning a numbered line window plus \" +\n \"the file's content hash. Pass the hash to edit_file's expectHash to \" +\n \"guard against editing a stale version. Use startLine/limit to page \" +\n \"through large files.\",\n input: inputSchema,\n async execute(input) {\n const startLine = input.startLine !== undefined ? Math.max(1, input.startLine) : 1;\n const { content, hash, totalLines } = await ops.readFile(input.path, {\n offset: startLine,\n limit: input.limit,\n });\n\n // The window's last line is the start plus however many lines the\n // ops layer actually returned (it caps at `limit` / the policy\n // default), bounded by the file's end.\n const returnedLines = content.length === 0 ? 0 : content.split(\"\\n\").length;\n const endLine = Math.min(totalLines, startLine + Math.max(returnedLines, 1) - 1);\n const truncated = endLine < totalLines;\n\n return { content, startLine, endLine, totalLines, truncated, hash };\n },\n });\n}\n"],"mappings":";;;;;AAKA,MAAM,eAAe;;AAGrB,MAAM,cAAc,aAA4B;CAC9C,MAAM,YAAY;CAClB,WAAW,oBAAoB;CAC/B,OAAO,oBAAoB;AAC7B,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;AA0BD,SAAgB,iBACd,KACA,SAC6C;CAC7C,OAAO,KAAoC;EACzC,MAAM,SAAS,QAAQ;EACvB,aACE;EAIF,OAAO;EACP,MAAM,QAAQ,OAAO;GACnB,MAAM,YAAY,MAAM,cAAc,SAAY,KAAK,IAAI,GAAG,MAAM,SAAS,IAAI;GACjF,MAAM,EAAE,SAAS,MAAM,eAAe,MAAM,IAAI,SAAS,MAAM,MAAM;IACnE,QAAQ;IACR,OAAO,MAAM;GACf,CAAC;GAKD,MAAM,gBAAgB,QAAQ,WAAW,IAAI,IAAI,QAAQ,MAAM,IAAI,CAAC,CAAC;GACrE,MAAM,UAAU,KAAK,IAAI,YAAY,YAAY,KAAK,IAAI,eAAe,CAAC,IAAI,CAAC;GAG/E,OAAO;IAAE;IAAS;IAAW;IAAS;IAAY,WAFhC,UAAU;IAEiC;GAAK;EACpE;CACF,CAAC;AACH"}
@@ -0,0 +1,35 @@
1
+ import { RunShellInput, RunShellResult } from "../contracts/tool-io.type.mjs";
2
+ import { WorkspaceOps } from "../contracts/workspace-ops.contract.mjs";
3
+ import { ToolContract } from "@warlock.js/ai";
4
+
5
+ //#region ../@warlock.js/ai-workspace/src/tools/run-shell.d.ts
6
+ /** Options for {@link makeRunShellTool}. */
7
+ interface MakeRunShellToolOptions {
8
+ /** Override the tool name exposed to the LLM (default `"run_shell"`). */
9
+ name?: string;
10
+ }
11
+ /**
12
+ * Build the `run_shell` tool — a {@link ToolContract} that runs a single
13
+ * shell command through the policy-enforced {@link WorkspaceOps} layer.
14
+ *
15
+ * The command's leading executable basename is gated against the shell
16
+ * allow/deny policy by `ops.exec`; a blocked command throws a
17
+ * `WorkspacePolicyError` which the `tool()` runtime catches and surfaces
18
+ * in the result's `error` field (never a thrown run-killer), so the agent
19
+ * reads the refusal as tool data and self-corrects. A command that runs
20
+ * but exits non-zero is *not* an error — its `exitCode`/`stderr` come back
21
+ * in `data` for the agent to inspect.
22
+ *
23
+ * @param ops - The policy-enforced operation layer to delegate `exec` to.
24
+ * @param options - Optional tool-name override.
25
+ *
26
+ * @example
27
+ * const runShell = makeRunShellTool(ops);
28
+ * const { data, error } = await runShell.invoke({ command: "npm run build" });
29
+ * if (error) handleDenied(error);
30
+ * else console.log(data.exitCode, data.stdout);
31
+ */
32
+ declare function makeRunShellTool(ops: WorkspaceOps, options?: MakeRunShellToolOptions): ToolContract<RunShellInput, RunShellResult>;
33
+ //#endregion
34
+ export { MakeRunShellToolOptions, makeRunShellTool };
35
+ //# sourceMappingURL=run-shell.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-shell.d.mts","names":[],"sources":["../../../../../../../@warlock.js/ai-workspace/src/tools/run-shell.ts"],"mappings":";;;;;;UAsDiB,uBAAA;;EAEf,IAAI;AAAA;;;;AAAA;AAwBN;;;;;;;;;;;;;;;;;iBAAgB,gBAAA,CACd,GAAA,EAAK,YAAA,EACL,OAAA,GAAU,uBAAA,GACT,YAAA,CAAa,aAAA,EAAe,cAAA"}
@@ -0,0 +1,65 @@
1
+ import { tool } from "@warlock.js/ai";
2
+
3
+ //#region ../@warlock.js/ai-workspace/src/tools/run-shell.ts
4
+ /** The default tool name `run_shell` is exposed to the LLM under. */
5
+ const DEFAULT_RUN_SHELL_TOOL_NAME = "run_shell";
6
+ /**
7
+ * Hand-rolled Standard Schema for {@link RunShellInput}. We validate the
8
+ * model's arguments without a runtime schema dependency: `command` must be
9
+ * a non-empty string, and `timeoutMs` (when present) a positive number.
10
+ * Invalid args surface as a `SchemaValidationError` in the tool result's
11
+ * `error` field rather than reaching `ops.exec`.
12
+ */
13
+ const runShellInputSchema = { "~standard": {
14
+ version: 1,
15
+ vendor: "@warlock.js/ai-workspace",
16
+ validate: (value) => {
17
+ if (typeof value !== "object" || value === null) return { issues: [{ message: "expected an object" }] };
18
+ const candidate = value;
19
+ if (typeof candidate.command !== "string" || candidate.command.length === 0) return { issues: [{
20
+ message: "command must be a non-empty string",
21
+ path: ["command"]
22
+ }] };
23
+ if (candidate.timeoutMs !== void 0 && (typeof candidate.timeoutMs !== "number" || candidate.timeoutMs <= 0)) return { issues: [{
24
+ message: "timeoutMs must be a positive number",
25
+ path: ["timeoutMs"]
26
+ }] };
27
+ const result = { command: candidate.command };
28
+ if (candidate.timeoutMs !== void 0) result.timeoutMs = candidate.timeoutMs;
29
+ return { value: result };
30
+ }
31
+ } };
32
+ /**
33
+ * Build the `run_shell` tool — a {@link ToolContract} that runs a single
34
+ * shell command through the policy-enforced {@link WorkspaceOps} layer.
35
+ *
36
+ * The command's leading executable basename is gated against the shell
37
+ * allow/deny policy by `ops.exec`; a blocked command throws a
38
+ * `WorkspacePolicyError` which the `tool()` runtime catches and surfaces
39
+ * in the result's `error` field (never a thrown run-killer), so the agent
40
+ * reads the refusal as tool data and self-corrects. A command that runs
41
+ * but exits non-zero is *not* an error — its `exitCode`/`stderr` come back
42
+ * in `data` for the agent to inspect.
43
+ *
44
+ * @param ops - The policy-enforced operation layer to delegate `exec` to.
45
+ * @param options - Optional tool-name override.
46
+ *
47
+ * @example
48
+ * const runShell = makeRunShellTool(ops);
49
+ * const { data, error } = await runShell.invoke({ command: "npm run build" });
50
+ * if (error) handleDenied(error);
51
+ * else console.log(data.exitCode, data.stdout);
52
+ */
53
+ function makeRunShellTool(ops, options) {
54
+ return tool({
55
+ name: options?.name ?? DEFAULT_RUN_SHELL_TOOL_NAME,
56
+ description: "Run a single shell command inside the workspace. The command's executable must be permitted by the shell policy; output is byte-capped and the run is time-limited. A non-zero exit code is returned as data, not an error.",
57
+ action: (input) => `Running \`${input.command}\``,
58
+ input: runShellInputSchema,
59
+ execute: (input) => ops.exec(input.command, { timeoutMs: input.timeoutMs })
60
+ });
61
+ }
62
+
63
+ //#endregion
64
+ export { makeRunShellTool };
65
+ //# sourceMappingURL=run-shell.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-shell.mjs","names":[],"sources":["../../../../../../../@warlock.js/ai-workspace/src/tools/run-shell.ts"],"sourcesContent":["import type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport { tool, type ToolContract } from \"@warlock.js/ai\";\nimport type {\n RunShellInput,\n RunShellResult,\n WorkspaceOps,\n} from \"../contracts\";\n\n/** The default tool name `run_shell` is exposed to the LLM under. */\nconst DEFAULT_RUN_SHELL_TOOL_NAME = \"run_shell\";\n\n/**\n * Hand-rolled Standard Schema for {@link RunShellInput}. We validate the\n * model's arguments without a runtime schema dependency: `command` must be\n * a non-empty string, and `timeoutMs` (when present) a positive number.\n * Invalid args surface as a `SchemaValidationError` in the tool result's\n * `error` field rather than reaching `ops.exec`.\n */\nconst runShellInputSchema: StandardSchemaV1<RunShellInput> = {\n \"~standard\": {\n version: 1,\n vendor: \"@warlock.js/ai-workspace\",\n validate: (value) => {\n if (typeof value !== \"object\" || value === null) {\n return { issues: [{ message: \"expected an object\" }] };\n }\n\n const candidate = value as Record<string, unknown>;\n\n if (typeof candidate.command !== \"string\" || candidate.command.length === 0) {\n return { issues: [{ message: \"command must be a non-empty string\", path: [\"command\"] }] };\n }\n\n if (\n candidate.timeoutMs !== undefined &&\n (typeof candidate.timeoutMs !== \"number\" || candidate.timeoutMs <= 0)\n ) {\n return {\n issues: [{ message: \"timeoutMs must be a positive number\", path: [\"timeoutMs\"] }],\n };\n }\n\n const result: RunShellInput = { command: candidate.command };\n\n if (candidate.timeoutMs !== undefined) {\n result.timeoutMs = candidate.timeoutMs as number;\n }\n\n return { value: result };\n },\n },\n};\n\n/** Options for {@link makeRunShellTool}. */\nexport interface MakeRunShellToolOptions {\n /** Override the tool name exposed to the LLM (default `\"run_shell\"`). */\n name?: string;\n}\n\n/**\n * Build the `run_shell` tool — a {@link ToolContract} that runs a single\n * shell command through the policy-enforced {@link WorkspaceOps} layer.\n *\n * The command's leading executable basename is gated against the shell\n * allow/deny policy by `ops.exec`; a blocked command throws a\n * `WorkspacePolicyError` which the `tool()` runtime catches and surfaces\n * in the result's `error` field (never a thrown run-killer), so the agent\n * reads the refusal as tool data and self-corrects. A command that runs\n * but exits non-zero is *not* an error — its `exitCode`/`stderr` come back\n * in `data` for the agent to inspect.\n *\n * @param ops - The policy-enforced operation layer to delegate `exec` to.\n * @param options - Optional tool-name override.\n *\n * @example\n * const runShell = makeRunShellTool(ops);\n * const { data, error } = await runShell.invoke({ command: \"npm run build\" });\n * if (error) handleDenied(error);\n * else console.log(data.exitCode, data.stdout);\n */\nexport function makeRunShellTool(\n ops: WorkspaceOps,\n options?: MakeRunShellToolOptions,\n): ToolContract<RunShellInput, RunShellResult> {\n return tool<RunShellInput, RunShellResult>({\n name: options?.name ?? DEFAULT_RUN_SHELL_TOOL_NAME,\n description:\n \"Run a single shell command inside the workspace. The command's \" +\n \"executable must be permitted by the shell policy; output is \" +\n \"byte-capped and the run is time-limited. A non-zero exit code is \" +\n \"returned as data, not an error.\",\n action: (input) => `Running \\`${input.command}\\``,\n input: runShellInputSchema,\n execute: (input) => ops.exec(input.command, { timeoutMs: input.timeoutMs }),\n });\n}\n"],"mappings":";;;;AASA,MAAM,8BAA8B;;;;;;;;AASpC,MAAM,sBAAuD,EAC3D,aAAa;CACX,SAAS;CACT,QAAQ;CACR,WAAW,UAAU;EACnB,IAAI,OAAO,UAAU,YAAY,UAAU,MACzC,OAAO,EAAE,QAAQ,CAAC,EAAE,SAAS,qBAAqB,CAAC,EAAE;EAGvD,MAAM,YAAY;EAElB,IAAI,OAAO,UAAU,YAAY,YAAY,UAAU,QAAQ,WAAW,GACxE,OAAO,EAAE,QAAQ,CAAC;GAAE,SAAS;GAAsC,MAAM,CAAC,SAAS;EAAE,CAAC,EAAE;EAG1F,IACE,UAAU,cAAc,WACvB,OAAO,UAAU,cAAc,YAAY,UAAU,aAAa,IAEnE,OAAO,EACL,QAAQ,CAAC;GAAE,SAAS;GAAuC,MAAM,CAAC,WAAW;EAAE,CAAC,EAClF;EAGF,MAAM,SAAwB,EAAE,SAAS,UAAU,QAAQ;EAE3D,IAAI,UAAU,cAAc,QAC1B,OAAO,YAAY,UAAU;EAG/B,OAAO,EAAE,OAAO,OAAO;CACzB;AACF,EACF;;;;;;;;;;;;;;;;;;;;;;AA6BA,SAAgB,iBACd,KACA,SAC6C;CAC7C,OAAO,KAAoC;EACzC,MAAM,SAAS,QAAQ;EACvB,aACE;EAIF,SAAS,UAAU,aAAa,MAAM,QAAQ;EAC9C,OAAO;EACP,UAAU,UAAU,IAAI,KAAK,MAAM,SAAS,EAAE,WAAW,MAAM,UAAU,CAAC;CAC5E,CAAC;AACH"}
@@ -0,0 +1,40 @@
1
+ import { RunShellResult, RunTestsInput } from "../contracts/tool-io.type.mjs";
2
+ import { WorkspaceOps } from "../contracts/workspace-ops.contract.mjs";
3
+ import { ToolContract } from "@warlock.js/ai";
4
+
5
+ //#region ../@warlock.js/ai-workspace/src/tools/run-tests.d.ts
6
+ /** Options for {@link makeRunTestsTool}. */
7
+ interface MakeRunTestsToolOptions {
8
+ /** Override the tool name exposed to the LLM (default `"run_tests"`). */
9
+ name?: string;
10
+ /**
11
+ * The base test command to run (default `"npm test"`). When the model
12
+ * supplies a `pattern`, it is appended to this command.
13
+ */
14
+ command?: string;
15
+ }
16
+ /**
17
+ * Build the `run_tests` tool — a {@link ToolContract} convenience over
18
+ * `run_shell` that runs the workspace's configured test command through
19
+ * the policy-enforced {@link WorkspaceOps} layer.
20
+ *
21
+ * The base command defaults to `"npm test"` and can be overridden via
22
+ * `options.command`. When the model passes a `pattern`, it is appended to
23
+ * the command as a path/suite filter forwarded to the runner (e.g.
24
+ * `"npm test src/cart"`). Like `run_shell`, the resolved command's
25
+ * executable is gated by the shell policy — a denial surfaces in the
26
+ * result's `error` field — and a non-zero exit (failing tests) comes back
27
+ * as `data` for the agent to read and fix.
28
+ *
29
+ * @param ops - The policy-enforced operation layer to delegate `exec` to.
30
+ * @param options - Optional tool-name and base-command overrides.
31
+ *
32
+ * @example
33
+ * const runTests = makeRunTestsTool(ops, { command: "pnpm test" });
34
+ * const { data } = await runTests.invoke({ pattern: "cart-total" });
35
+ * if (data.exitCode !== 0) inspect(data.stderr);
36
+ */
37
+ declare function makeRunTestsTool(ops: WorkspaceOps, options?: MakeRunTestsToolOptions): ToolContract<RunTestsInput, RunShellResult>;
38
+ //#endregion
39
+ export { MakeRunTestsToolOptions, makeRunTestsTool };
40
+ //# sourceMappingURL=run-tests.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-tests.d.mts","names":[],"sources":["../../../../../../../@warlock.js/ai-workspace/src/tools/run-tests.ts"],"mappings":";;;;;;UAqDiB,uBAAA;;EAEf,IAAA;EAFe;;;;EAOf,OAAO;AAAA;;;;;;;;;;;;;;;;;;AA2BoC;;;;iBAH7B,gBAAA,CACd,GAAA,EAAK,YAAA,EACL,OAAA,GAAU,uBAAA,GACT,YAAA,CAAa,aAAA,EAAe,cAAA"}
@@ -0,0 +1,67 @@
1
+ import { tool } from "@warlock.js/ai";
2
+
3
+ //#region ../@warlock.js/ai-workspace/src/tools/run-tests.ts
4
+ /** The default tool name `run_tests` is exposed to the LLM under. */
5
+ const DEFAULT_RUN_TESTS_TOOL_NAME = "run_tests";
6
+ /** The default command run when no `command` override is configured. */
7
+ const DEFAULT_TEST_COMMAND = "npm test";
8
+ /**
9
+ * Hand-rolled Standard Schema for {@link RunTestsInput}. `pattern` is the
10
+ * only field and is optional; when present it must be a string. Validation
11
+ * happens without a runtime schema dependency, mirroring the wider tool
12
+ * layer.
13
+ */
14
+ const runTestsInputSchema = { "~standard": {
15
+ version: 1,
16
+ vendor: "@warlock.js/ai-workspace",
17
+ validate: (value) => {
18
+ if (value === void 0 || value === null) return { value: {} };
19
+ if (typeof value !== "object") return { issues: [{ message: "expected an object" }] };
20
+ const candidate = value;
21
+ if (candidate.pattern !== void 0 && typeof candidate.pattern !== "string") return { issues: [{
22
+ message: "pattern must be a string",
23
+ path: ["pattern"]
24
+ }] };
25
+ const result = {};
26
+ if (candidate.pattern !== void 0) result.pattern = candidate.pattern;
27
+ return { value: result };
28
+ }
29
+ } };
30
+ /**
31
+ * Build the `run_tests` tool — a {@link ToolContract} convenience over
32
+ * `run_shell` that runs the workspace's configured test command through
33
+ * the policy-enforced {@link WorkspaceOps} layer.
34
+ *
35
+ * The base command defaults to `"npm test"` and can be overridden via
36
+ * `options.command`. When the model passes a `pattern`, it is appended to
37
+ * the command as a path/suite filter forwarded to the runner (e.g.
38
+ * `"npm test src/cart"`). Like `run_shell`, the resolved command's
39
+ * executable is gated by the shell policy — a denial surfaces in the
40
+ * result's `error` field — and a non-zero exit (failing tests) comes back
41
+ * as `data` for the agent to read and fix.
42
+ *
43
+ * @param ops - The policy-enforced operation layer to delegate `exec` to.
44
+ * @param options - Optional tool-name and base-command overrides.
45
+ *
46
+ * @example
47
+ * const runTests = makeRunTestsTool(ops, { command: "pnpm test" });
48
+ * const { data } = await runTests.invoke({ pattern: "cart-total" });
49
+ * if (data.exitCode !== 0) inspect(data.stderr);
50
+ */
51
+ function makeRunTestsTool(ops, options) {
52
+ const baseCommand = options?.command ?? DEFAULT_TEST_COMMAND;
53
+ return tool({
54
+ name: options?.name ?? DEFAULT_RUN_TESTS_TOOL_NAME,
55
+ description: "Run the workspace's test suite, optionally narrowed to a path or name pattern forwarded to the test runner. Failing tests return a non-zero exit code as data, not an error.",
56
+ action: (input) => input.pattern ? `Running tests matching "${input.pattern}"` : "Running tests",
57
+ input: runTestsInputSchema,
58
+ execute: (input) => {
59
+ const command = input.pattern ? `${baseCommand} ${input.pattern}` : baseCommand;
60
+ return ops.exec(command);
61
+ }
62
+ });
63
+ }
64
+
65
+ //#endregion
66
+ export { makeRunTestsTool };
67
+ //# sourceMappingURL=run-tests.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-tests.mjs","names":[],"sources":["../../../../../../../@warlock.js/ai-workspace/src/tools/run-tests.ts"],"sourcesContent":["import type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport { tool, type ToolContract } from \"@warlock.js/ai\";\nimport type {\n RunShellResult,\n RunTestsInput,\n WorkspaceOps,\n} from \"../contracts\";\n\n/** The default tool name `run_tests` is exposed to the LLM under. */\nconst DEFAULT_RUN_TESTS_TOOL_NAME = \"run_tests\";\n\n/** The default command run when no `command` override is configured. */\nconst DEFAULT_TEST_COMMAND = \"npm test\";\n\n/**\n * Hand-rolled Standard Schema for {@link RunTestsInput}. `pattern` is the\n * only field and is optional; when present it must be a string. Validation\n * happens without a runtime schema dependency, mirroring the wider tool\n * layer.\n */\nconst runTestsInputSchema: StandardSchemaV1<RunTestsInput> = {\n \"~standard\": {\n version: 1,\n vendor: \"@warlock.js/ai-workspace\",\n validate: (value) => {\n // A no-argument call (the common case) is valid and runs the bare\n // test command.\n if (value === undefined || value === null) {\n return { value: {} };\n }\n\n if (typeof value !== \"object\") {\n return { issues: [{ message: \"expected an object\" }] };\n }\n\n const candidate = value as Record<string, unknown>;\n\n if (candidate.pattern !== undefined && typeof candidate.pattern !== \"string\") {\n return { issues: [{ message: \"pattern must be a string\", path: [\"pattern\"] }] };\n }\n\n const result: RunTestsInput = {};\n\n if (candidate.pattern !== undefined) {\n result.pattern = candidate.pattern as string;\n }\n\n return { value: result };\n },\n },\n};\n\n/** Options for {@link makeRunTestsTool}. */\nexport interface MakeRunTestsToolOptions {\n /** Override the tool name exposed to the LLM (default `\"run_tests\"`). */\n name?: string;\n /**\n * The base test command to run (default `\"npm test\"`). When the model\n * supplies a `pattern`, it is appended to this command.\n */\n command?: string;\n}\n\n/**\n * Build the `run_tests` tool — a {@link ToolContract} convenience over\n * `run_shell` that runs the workspace's configured test command through\n * the policy-enforced {@link WorkspaceOps} layer.\n *\n * The base command defaults to `\"npm test\"` and can be overridden via\n * `options.command`. When the model passes a `pattern`, it is appended to\n * the command as a path/suite filter forwarded to the runner (e.g.\n * `\"npm test src/cart\"`). Like `run_shell`, the resolved command's\n * executable is gated by the shell policy — a denial surfaces in the\n * result's `error` field — and a non-zero exit (failing tests) comes back\n * as `data` for the agent to read and fix.\n *\n * @param ops - The policy-enforced operation layer to delegate `exec` to.\n * @param options - Optional tool-name and base-command overrides.\n *\n * @example\n * const runTests = makeRunTestsTool(ops, { command: \"pnpm test\" });\n * const { data } = await runTests.invoke({ pattern: \"cart-total\" });\n * if (data.exitCode !== 0) inspect(data.stderr);\n */\nexport function makeRunTestsTool(\n ops: WorkspaceOps,\n options?: MakeRunTestsToolOptions,\n): ToolContract<RunTestsInput, RunShellResult> {\n const baseCommand = options?.command ?? DEFAULT_TEST_COMMAND;\n\n return tool<RunTestsInput, RunShellResult>({\n name: options?.name ?? DEFAULT_RUN_TESTS_TOOL_NAME,\n description:\n \"Run the workspace's test suite, optionally narrowed to a path or \" +\n \"name pattern forwarded to the test runner. Failing tests return a \" +\n \"non-zero exit code as data, not an error.\",\n action: (input) =>\n input.pattern ? `Running tests matching \"${input.pattern}\"` : \"Running tests\",\n input: runTestsInputSchema,\n execute: (input) => {\n const command = input.pattern ? `${baseCommand} ${input.pattern}` : baseCommand;\n\n return ops.exec(command);\n },\n });\n}\n"],"mappings":";;;;AASA,MAAM,8BAA8B;;AAGpC,MAAM,uBAAuB;;;;;;;AAQ7B,MAAM,sBAAuD,EAC3D,aAAa;CACX,SAAS;CACT,QAAQ;CACR,WAAW,UAAU;EAGnB,IAAI,UAAU,UAAa,UAAU,MACnC,OAAO,EAAE,OAAO,CAAC,EAAE;EAGrB,IAAI,OAAO,UAAU,UACnB,OAAO,EAAE,QAAQ,CAAC,EAAE,SAAS,qBAAqB,CAAC,EAAE;EAGvD,MAAM,YAAY;EAElB,IAAI,UAAU,YAAY,UAAa,OAAO,UAAU,YAAY,UAClE,OAAO,EAAE,QAAQ,CAAC;GAAE,SAAS;GAA4B,MAAM,CAAC,SAAS;EAAE,CAAC,EAAE;EAGhF,MAAM,SAAwB,CAAC;EAE/B,IAAI,UAAU,YAAY,QACxB,OAAO,UAAU,UAAU;EAG7B,OAAO,EAAE,OAAO,OAAO;CACzB;AACF,EACF;;;;;;;;;;;;;;;;;;;;;;AAkCA,SAAgB,iBACd,KACA,SAC6C;CAC7C,MAAM,cAAc,SAAS,WAAW;CAExC,OAAO,KAAoC;EACzC,MAAM,SAAS,QAAQ;EACvB,aACE;EAGF,SAAS,UACP,MAAM,UAAU,2BAA2B,MAAM,QAAQ,KAAK;EAChE,OAAO;EACP,UAAU,UAAU;GAClB,MAAM,UAAU,MAAM,UAAU,GAAG,YAAY,GAAG,MAAM,YAAY;GAEpE,OAAO,IAAI,KAAK,OAAO;EACzB;CACF,CAAC;AACH"}