@theokit/sdk-tools 0.22.2 → 0.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +46 -28
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +79 -1
- package/dist/index.d.ts +79 -1
- package/dist/index.js +46 -28
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -227,8 +227,8 @@ ${chunk.oldLines.join("\n")}`
|
|
|
227
227
|
function createApplyPatchTool(opts) {
|
|
228
228
|
const { projectRoot } = opts;
|
|
229
229
|
return sdk.Tool.create({
|
|
230
|
-
name: "apply_patch",
|
|
231
|
-
description: "Apply a Codex-style V4A patch. The patch is `*** Begin Patch` \u2026 `*** End Patch` wrapping one or more hunks: `*** Add File: <path>` (then `+`lines), `*** Delete File: <path>`, or `*** Update File: <path>` (optional `*** Move to: <path>`) with `@@`-anchored `+` (add) / `-` (remove) / ` ` (context) lines. Read a file first so your context/removed lines match. Applied atomically \u2014 a mismatch anywhere aborts the whole patch with zero writes; each path is security-checked. Returns { ok, files_patched } or { ok: false, error }.",
|
|
230
|
+
name: opts.name ?? "apply_patch",
|
|
231
|
+
description: opts.description ?? "Apply a Codex-style V4A patch. The patch is `*** Begin Patch` \u2026 `*** End Patch` wrapping one or more hunks: `*** Add File: <path>` (then `+`lines), `*** Delete File: <path>`, or `*** Update File: <path>` (optional `*** Move to: <path>`) with `@@`-anchored `+` (add) / `-` (remove) / ` ` (context) lines. Read a file first so your context/removed lines match. Applied atomically \u2014 a mismatch anywhere aborts the whole patch with zero writes; each path is security-checked. Returns { ok, files_patched } or { ok: false, error }.",
|
|
232
232
|
inputSchema: zod.z.object({
|
|
233
233
|
patch: zod.z.string().min(1).describe("V4A patch: *** Begin Patch \u2026 *** End Patch.")
|
|
234
234
|
}),
|
|
@@ -429,8 +429,8 @@ function formatInTimezone(now, tz) {
|
|
|
429
429
|
function createCurrentTimeTool(opts = {}) {
|
|
430
430
|
const clock = opts.clock ?? (() => /* @__PURE__ */ new Date());
|
|
431
431
|
return sdk.Tool.create({
|
|
432
|
-
name: "current_time",
|
|
433
|
-
description: "Get the current date and time. Returns { current_time, iso, timezone } as a JSON string, where current_time is 'YYYY-MM-DD HH:MM:SS <timezone>' and iso is the ISO-8601 instant. Pass an optional IANA timezone (e.g. 'America/Sao_Paulo', 'Europe/Lisbon'); defaults to UTC. Never state the date or time from memory \u2014 always call this. Returns { ok: false, error: 'invalid_timezone' } for an unknown timezone.",
|
|
432
|
+
name: opts.name ?? "current_time",
|
|
433
|
+
description: opts.description ?? "Get the current date and time. Returns { current_time, iso, timezone } as a JSON string, where current_time is 'YYYY-MM-DD HH:MM:SS <timezone>' and iso is the ISO-8601 instant. Pass an optional IANA timezone (e.g. 'America/Sao_Paulo', 'Europe/Lisbon'); defaults to UTC. Never state the date or time from memory \u2014 always call this. Returns { ok: false, error: 'invalid_timezone' } for an unknown timezone.",
|
|
434
434
|
inputSchema: zod.z.object({
|
|
435
435
|
timezone: zod.z.string().optional().describe("IANA timezone, e.g. 'America/Sao_Paulo' or 'Europe/Lisbon'. Defaults to UTC.")
|
|
436
436
|
}),
|
|
@@ -720,6 +720,9 @@ function attachChildSettlers(child, gate, onClose, onError, resolve) {
|
|
|
720
720
|
}
|
|
721
721
|
|
|
722
722
|
// src/internal/git-exec.ts
|
|
723
|
+
function shq(arg) {
|
|
724
|
+
return `'${arg.replaceAll("'", `'\\''`)}'`;
|
|
725
|
+
}
|
|
723
726
|
function formatGitResult(result, timeoutMs) {
|
|
724
727
|
if (result.kind === "timeout") {
|
|
725
728
|
return JSON.stringify({ ok: false, error: "timeout", timeoutMs });
|
|
@@ -801,9 +804,6 @@ function ehProibidoEmQualquerProfundidade(path) {
|
|
|
801
804
|
// src/git-diff.ts
|
|
802
805
|
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
803
806
|
var DEFAULT_MAX_STDOUT_BYTES = 5 * 1024 * 1024;
|
|
804
|
-
function shq(arg) {
|
|
805
|
-
return `'${arg.replace(/'/g, `'\\''`)}'`;
|
|
806
|
-
}
|
|
807
807
|
async function diffViaSandbox(sandbox$1, ctx, cached, path, projectRoot, timeoutMs) {
|
|
808
808
|
const scopeCheck = checkPathScope(path, projectRoot);
|
|
809
809
|
if (scopeCheck !== null) return scopeCheck;
|
|
@@ -824,8 +824,8 @@ function createGitDiffTool(opts) {
|
|
|
824
824
|
sandbox
|
|
825
825
|
} = opts;
|
|
826
826
|
return sdk.Tool.create({
|
|
827
|
-
name: "git_diff",
|
|
828
|
-
description: "Return the unified diff of the project's working tree (or staged changes when cached=true). Scoped to a single file when 'path' is provided. Requires the project to be a git repository. Returns { ok, diff, truncated? } or { ok: false, error }.",
|
|
827
|
+
name: opts.name ?? "git_diff",
|
|
828
|
+
description: opts.description ?? "Return the unified diff of the project's working tree (or staged changes when cached=true). Scoped to a single file when 'path' is provided. Requires the project to be a git repository. Returns { ok, diff, truncated? } or { ok: false, error }.",
|
|
829
829
|
inputSchema: zod.z.object({
|
|
830
830
|
path: zod.z.string().optional().describe("Optional project-relative file or dir scope."),
|
|
831
831
|
cached: zod.z.boolean().optional().describe("If true, show staged changes (git diff --cached). Default false.")
|
|
@@ -859,26 +859,40 @@ function createGitStatusTool(opts) {
|
|
|
859
859
|
inputSchema: zod.z.object({
|
|
860
860
|
path: zod.z.string().optional().describe("Optional project-relative path to scope the status report.")
|
|
861
861
|
}),
|
|
862
|
-
handler: async ({ path: path$1 }) => {
|
|
862
|
+
handler: async ({ path: path$1 }, ctx) => {
|
|
863
863
|
if (!fs.existsSync(path.join(projectRoot, ".git"))) {
|
|
864
864
|
return JSON.stringify({ ok: false, error: "not_a_repo" });
|
|
865
865
|
}
|
|
866
866
|
const scopeCheck = checkPathScope(path$1, projectRoot);
|
|
867
867
|
if (scopeCheck !== null) return scopeCheck;
|
|
868
|
-
const args = ["status", "--porcelain"];
|
|
868
|
+
const args = ["status", "--porcelain=v1"];
|
|
869
|
+
if (opts.includeBranch !== false) args.push("-b");
|
|
869
870
|
if (path$1 !== void 0 && path$1 !== "") args.push("--", path$1);
|
|
871
|
+
if (opts.sandbox !== void 0) {
|
|
872
|
+
return statusViaSandbox(opts.sandbox, ctx, args, timeoutMs);
|
|
873
|
+
}
|
|
870
874
|
const result = await runGitProcess(projectRoot, args, timeoutMs, maxStdoutBytes);
|
|
871
875
|
return formatGitResult(result, timeoutMs);
|
|
872
876
|
}
|
|
873
877
|
});
|
|
874
878
|
}
|
|
879
|
+
async function statusViaSandbox(sandbox$1, ctx, args, timeoutMs) {
|
|
880
|
+
const command = ["git", ...args].map(shq).join(" ");
|
|
881
|
+
const backend = await sandbox.resolveSandbox(sandbox$1, ctx ?? {});
|
|
882
|
+
const r = await backend.execute(command, { timeoutMs });
|
|
883
|
+
if (r.timedOut) return JSON.stringify({ ok: false, error: "timeout", timeoutMs });
|
|
884
|
+
if (r.exitCode !== 0) {
|
|
885
|
+
return /not a git repository/i.test(r.stderr) ? JSON.stringify({ ok: false, error: "not_a_repo" }) : JSON.stringify({ ok: false, error: "git_failed", stderr: r.stderr });
|
|
886
|
+
}
|
|
887
|
+
return JSON.stringify({ ok: true, diff: r.stdout, truncated: false });
|
|
888
|
+
}
|
|
875
889
|
var DEFAULT_EXCLUDES = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", ".theo"]);
|
|
876
890
|
var MAX_BACKEND_WALK_DEPTH = 64;
|
|
877
891
|
function createGlobTool(opts) {
|
|
878
892
|
const { projectRoot, filesystem: filesystem$1 } = opts;
|
|
879
893
|
return sdk.Tool.create({
|
|
880
|
-
name: "glob_files",
|
|
881
|
-
description: "Find files by glob pattern across the project \u2014 fast at any repo size. Use glob_files when you know the filename SHAPE; use search_text when you know the file CONTENT; use read_file when you know the exact path. The pattern supports * and ** wildcards (e.g. '**/*.ts', 'src/**/*.json'); node_modules/.git/dist/.theo are excluded and results are relative paths. Returns { ok, files } or { ok: false, error }.",
|
|
894
|
+
name: opts.name ?? "glob_files",
|
|
895
|
+
description: opts.description ?? "Find files by glob pattern across the project \u2014 fast at any repo size. Use glob_files when you know the filename SHAPE; use search_text when you know the file CONTENT; use read_file when you know the exact path. The pattern supports * and ** wildcards (e.g. '**/*.ts', 'src/**/*.json'); node_modules/.git/dist/.theo are excluded and results are relative paths. Returns { ok, files } or { ok: false, error }.",
|
|
882
896
|
inputSchema: zod.z.object({
|
|
883
897
|
pattern: zod.z.string().min(1).describe("Glob pattern (e.g. '**/*.ts', 'src/**/*.json')."),
|
|
884
898
|
cwd: zod.z.string().optional().describe("Project-relative subdirectory to search from.")
|
|
@@ -997,8 +1011,8 @@ function toErrorJson(err) {
|
|
|
997
1011
|
function createInteractiveShellTool(opts) {
|
|
998
1012
|
const { interactive: interactive$1 } = opts;
|
|
999
1013
|
return sdk.Tool.create({
|
|
1000
|
-
name: "interactive_shell",
|
|
1001
|
-
description: "Start an interactive shell session for a command that PROMPTS for input or is a REPL (python, node, `git rebase -i`, a `read` prompt) \u2014 NOT for one-shot commands (use shell_exec). Returns a session_id; drive it with write_stdin, reading the incremental output each step. Returns { ok, session_id, output } or { ok: false, error }.",
|
|
1014
|
+
name: opts.name ?? "interactive_shell",
|
|
1015
|
+
description: opts.description ?? "Start an interactive shell session for a command that PROMPTS for input or is a REPL (python, node, `git rebase -i`, a `read` prompt) \u2014 NOT for one-shot commands (use shell_exec). Returns a session_id; drive it with write_stdin, reading the incremental output each step. Returns { ok, session_id, output } or { ok: false, error }.",
|
|
1002
1016
|
inputSchema: zod.z.object({
|
|
1003
1017
|
command: zod.z.string().min(1).describe("Command to run interactively, e.g. 'python3' or 'bash -i'."),
|
|
1004
1018
|
yield_time_ms: zod.z.number().int().positive().optional().describe("How long to wait for startup output before returning (clamped by the backend).")
|
|
@@ -1702,8 +1716,8 @@ function createPlanModeTool(options) {
|
|
|
1702
1716
|
}
|
|
1703
1717
|
const { artifactStore, artifactId = "plan" } = options;
|
|
1704
1718
|
return {
|
|
1705
|
-
name: "plan_mode",
|
|
1706
|
-
description: DESCRIPTION,
|
|
1719
|
+
name: options.name ?? "plan_mode",
|
|
1720
|
+
description: options.description ?? DESCRIPTION,
|
|
1707
1721
|
inputSchema: planModeSchema(true),
|
|
1708
1722
|
handler: async (input) => {
|
|
1709
1723
|
if (input.action === "enter") {
|
|
@@ -1761,10 +1775,14 @@ function createQuestionTool(opts) {
|
|
|
1761
1775
|
setTimeout(() => reject(new Error("timeout")), timeoutMs);
|
|
1762
1776
|
});
|
|
1763
1777
|
try {
|
|
1764
|
-
const answer = await Promise.race([
|
|
1778
|
+
const answer = await Promise.race([
|
|
1779
|
+
askUser(String(input.question ?? ""), ctx?.threadId),
|
|
1780
|
+
timeout
|
|
1781
|
+
]);
|
|
1765
1782
|
return JSON.stringify({ ok: true, answer });
|
|
1766
1783
|
} catch (err) {
|
|
1767
1784
|
if (err instanceof Error && err.message === "timeout") {
|
|
1785
|
+
opts.onAbandon?.(ctx?.threadId);
|
|
1768
1786
|
return JSON.stringify({
|
|
1769
1787
|
ok: false,
|
|
1770
1788
|
error: "timeout",
|
|
@@ -1809,8 +1827,8 @@ function createReadFileTool(opts) {
|
|
|
1809
1827
|
const numbered = lineNumbers === true ? " Returns a cat -n numbered view (`<n>\\t<line>`)." : "";
|
|
1810
1828
|
const abs = allowAbsolute === true ? " Absolute paths outside the project are honored." : "";
|
|
1811
1829
|
return sdk.Tool.create({
|
|
1812
|
-
name: "read_file",
|
|
1813
|
-
description: "Read a text file as UTF-8. ALWAYS read a file before you edit it (edit_file) or overwrite it (write_file), so your old_string / new content matches the real bytes exactly." + numbered + abs + " By default returns the whole file; use the optional offset (1-based first line) + limit to page through a large file, or search_text to locate a symbol. Refuses sensitive files (.env, .git/, node_modules/, .theo/, lock files) and binary files (null byte in the first 8 KB); caps at 5 MB. Returns { ok, content, size } or { ok: false, error }.",
|
|
1830
|
+
name: opts.name ?? "read_file",
|
|
1831
|
+
description: opts.description ?? "Read a text file as UTF-8. ALWAYS read a file before you edit it (edit_file) or overwrite it (write_file), so your old_string / new content matches the real bytes exactly." + numbered + abs + " By default returns the whole file; use the optional offset (1-based first line) + limit to page through a large file, or search_text to locate a symbol. Refuses sensitive files (.env, .git/, node_modules/, .theo/, lock files) and binary files (null byte in the first 8 KB); caps at 5 MB. Returns { ok, content, size } or { ok: false, error }.",
|
|
1814
1832
|
inputSchema: zod.z.object({
|
|
1815
1833
|
path: zod.z.string().min(1).describe("File path (project-relative; absolute when allowed)."),
|
|
1816
1834
|
offset: zod.z.number().int().min(1).optional().describe("1-based first line to read (default 1)."),
|
|
@@ -1984,8 +2002,8 @@ function createRunVitestTool(opts) {
|
|
|
1984
2002
|
maxStdoutBytes = DEFAULT_MAX_STDOUT_BYTES2
|
|
1985
2003
|
} = opts;
|
|
1986
2004
|
return sdk.Tool.create({
|
|
1987
|
-
name: "run_vitest",
|
|
1988
|
-
description: "Run the project's vitest suite, optionally scoped to a file or pattern via 'path'. Returns parsed { ok, summary } or { ok: false, error }. Vitest stdout warnings are stripped \u2014 the parser extracts the trailing JSON report.",
|
|
2005
|
+
name: opts.name ?? "run_vitest",
|
|
2006
|
+
description: opts.description ?? "Run the project's vitest suite, optionally scoped to a file or pattern via 'path'. Returns parsed { ok, summary } or { ok: false, error }. Vitest stdout warnings are stripped \u2014 the parser extracts the trailing JSON report.",
|
|
1989
2007
|
inputSchema: zod.z.object({
|
|
1990
2008
|
path: zod.z.string().optional().describe("Optional vitest pattern or file path (project-relative).")
|
|
1991
2009
|
}),
|
|
@@ -2582,8 +2600,8 @@ function createWebFetchTool(opts) {
|
|
|
2582
2600
|
const fetchImpl = opts?.fetchImpl;
|
|
2583
2601
|
const lookup = opts?.lookup;
|
|
2584
2602
|
return sdk.Tool.create({
|
|
2585
|
-
name: "web_fetch",
|
|
2586
|
-
description: "Fetch the contents of a URL via HTTP/HTTPS. Use only for URLs the user provided or that you are confident help with the task; never invent or guess URLs. Rejects non-http(s) URLs and is SSRF-guarded by default (private/loopback/link-local/cloud-metadata hosts are refused with an ssrf_blocked error). The response body is capped at 1 MB. Returns { ok, content, status_code, content_type } or { ok: false, error }.",
|
|
2603
|
+
name: opts?.name ?? "web_fetch",
|
|
2604
|
+
description: opts?.description ?? "Fetch the contents of a URL via HTTP/HTTPS. Use only for URLs the user provided or that you are confident help with the task; never invent or guess URLs. Rejects non-http(s) URLs and is SSRF-guarded by default (private/loopback/link-local/cloud-metadata hosts are refused with an ssrf_blocked error). The response body is capped at 1 MB. Returns { ok, content, status_code, content_type } or { ok: false, error }.",
|
|
2587
2605
|
inputSchema: zod.z.object({
|
|
2588
2606
|
url: zod.z.string().min(1).describe("URL to fetch (http or https only)."),
|
|
2589
2607
|
timeout_ms: zod.z.number().int().positive().optional().describe("Timeout in milliseconds (default 30000).")
|
|
@@ -2669,8 +2687,8 @@ function createWebFetchTool(opts) {
|
|
|
2669
2687
|
function createWebSearchTool(opts) {
|
|
2670
2688
|
const { search, defaultMaxResults = 5 } = opts;
|
|
2671
2689
|
return sdk.Tool.create({
|
|
2672
|
-
name: "web_search",
|
|
2673
|
-
description: "Search the web for a query \u2014 use when you need current information beyond the repo or your training cutoff (library docs, an error message, an API). Returns a list of results with title, URL, and snippet; follow up with web_fetch on a promising result to read it in full. The search provider is injected by the consumer. Returns { ok, results } or { ok: false, error }.",
|
|
2690
|
+
name: opts.name ?? "web_search",
|
|
2691
|
+
description: opts.description ?? "Search the web for a query \u2014 use when you need current information beyond the repo or your training cutoff (library docs, an error message, an API). Returns a list of results with title, URL, and snippet; follow up with web_fetch on a promising result to read it in full. The search provider is injected by the consumer. Returns { ok, results } or { ok: false, error }.",
|
|
2674
2692
|
inputSchema: zod.z.object({
|
|
2675
2693
|
query: zod.z.string().min(1).describe("Search query."),
|
|
2676
2694
|
max_results: zod.z.number().int().positive().max(20).optional().describe("Maximum results to return (default 5, max 20).")
|
|
@@ -2756,8 +2774,8 @@ function createWriteFileTool(opts) {
|
|
|
2756
2774
|
}
|
|
2757
2775
|
const guard = opts.requireReadBeforeWrite ? opts.readTracker : void 0;
|
|
2758
2776
|
return sdk.Tool.create({
|
|
2759
|
-
name: "write_file",
|
|
2760
|
-
description: "Write UTF-8 content to a project-relative file, creating parent directories as needed. OVERWRITES any existing file at the path. Prefer editing an existing file with edit_file over rewriting it; use write_file to create a NEW file or fully replace a small one. If the file already exists, read_file it first so you do not discard content you have not seen. Refuses paths that escape the write root and sensitive files (.env, .git/, node_modules/, .theo/, lock files); the default local root also refuses binary-file overwrites. Returns { ok, path, bytes } or { ok: false, error }.",
|
|
2777
|
+
name: opts.name ?? "write_file",
|
|
2778
|
+
description: opts.description ?? "Write UTF-8 content to a project-relative file, creating parent directories as needed. OVERWRITES any existing file at the path. Prefer editing an existing file with edit_file over rewriting it; use write_file to create a NEW file or fully replace a small one. If the file already exists, read_file it first so you do not discard content you have not seen. Refuses paths that escape the write root and sensitive files (.env, .git/, node_modules/, .theo/, lock files); the default local root also refuses binary-file overwrites. Returns { ok, path, bytes } or { ok: false, error }.",
|
|
2761
2779
|
inputSchema: zod.z.object({
|
|
2762
2780
|
path: zod.z.string().min(1).describe("Project-relative file path."),
|
|
2763
2781
|
content: zod.z.string().describe("UTF-8 content to write.")
|