@theokit/sdk-tools 0.18.0 → 0.19.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/CHANGELOG.md +10 -0
- package/dist/index.cjs +37 -15
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +33 -11
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -780,6 +780,12 @@ interface CreateSearchTextToolOptions {
|
|
|
780
780
|
/** Optional injected filesystem (`@theokit/sdk/filesystem`) — when provided, the recursive walk reads
|
|
781
781
|
* through the backend (surface-agnostic); omitted ⇒ the local `readdir`/`readFile` walk (unchanged). */
|
|
782
782
|
filesystem?: FilesystemProvider;
|
|
783
|
+
/** M17 — treat `query` as a JavaScript RegExp instead of a literal substring (grep semantics). An
|
|
784
|
+
* invalid pattern returns `{ ok: false, error: 'invalid_regex' }`. Default `false` ⇒ literal (unchanged). */
|
|
785
|
+
regex?: boolean;
|
|
786
|
+
/** M17 — opt-in "reads-anywhere": honor an ABSOLUTE `path` scope outside `projectRoot` (Codex read-only
|
|
787
|
+
* sandbox). Forbidden dirs are still skipped. Default `false` ⇒ absolute scope rejected (unchanged). */
|
|
788
|
+
allowAbsolute?: boolean;
|
|
783
789
|
}
|
|
784
790
|
declare function createSearchTextTool(opts: CreateSearchTextToolOptions): CustomTool;
|
|
785
791
|
|
package/dist/index.d.ts
CHANGED
|
@@ -780,6 +780,12 @@ interface CreateSearchTextToolOptions {
|
|
|
780
780
|
/** Optional injected filesystem (`@theokit/sdk/filesystem`) — when provided, the recursive walk reads
|
|
781
781
|
* through the backend (surface-agnostic); omitted ⇒ the local `readdir`/`readFile` walk (unchanged). */
|
|
782
782
|
filesystem?: FilesystemProvider;
|
|
783
|
+
/** M17 — treat `query` as a JavaScript RegExp instead of a literal substring (grep semantics). An
|
|
784
|
+
* invalid pattern returns `{ ok: false, error: 'invalid_regex' }`. Default `false` ⇒ literal (unchanged). */
|
|
785
|
+
regex?: boolean;
|
|
786
|
+
/** M17 — opt-in "reads-anywhere": honor an ABSOLUTE `path` scope outside `projectRoot` (Codex read-only
|
|
787
|
+
* sandbox). Forbidden dirs are still skipped. Default `false` ⇒ absolute scope rejected (unchanged). */
|
|
788
|
+
allowAbsolute?: boolean;
|
|
783
789
|
}
|
|
784
790
|
declare function createSearchTextTool(opts: CreateSearchTextToolOptions): CustomTool;
|
|
785
791
|
|
package/dist/index.js
CHANGED
|
@@ -1891,27 +1891,36 @@ function createSearchTextTool(opts) {
|
|
|
1891
1891
|
projectRoot,
|
|
1892
1892
|
maxMatches = DEFAULT_MAX_MATCHES,
|
|
1893
1893
|
maxFileSize = DEFAULT_MAX_FILE_SIZE,
|
|
1894
|
-
filesystem
|
|
1894
|
+
filesystem,
|
|
1895
|
+
regex = false,
|
|
1896
|
+
allowAbsolute = false
|
|
1895
1897
|
} = opts;
|
|
1898
|
+
const queryKind = regex ? "a JavaScript REGULAR EXPRESSION" : "LITERAL, CASE-SENSITIVE text";
|
|
1899
|
+
const queryMatch = regex ? "matched as a regex" : "matched as a substring, not a regex";
|
|
1896
1900
|
return Tool.create({
|
|
1897
1901
|
name: "search_text",
|
|
1898
|
-
description: `Search file CONTENTS for
|
|
1902
|
+
description: `Search file CONTENTS for ${queryKind} across the project tree (the query is ${queryMatch}). Use search_text when you know the content; use glob_files when you know the filename shape; use read_file when you know the exact path. Skips sensitive dirs (.env/.git/node_modules/.theo), binary files, and files over 1 MB; 'path' scopes the search to a subdirectory. Returns up to ${String(maxMatches)} matches as { file, line, preview } \u2014 cite locations to the user as file:line. Returns { ok, matches } or { ok: false, error }.`,
|
|
1899
1903
|
inputSchema: z.object({
|
|
1900
|
-
query: z.string().min(1).describe("Literal text to search for. Case-sensitive."),
|
|
1901
|
-
path: z.string().optional().describe(
|
|
1904
|
+
query: regex ? z.string().min(1).describe("A JavaScript regular expression, e.g. 'function\\\\s+main'.") : z.string().min(1).describe("Literal text to search for. Case-sensitive."),
|
|
1905
|
+
path: z.string().optional().describe(
|
|
1906
|
+
"Optional directory to scope the search (project-relative; absolute when allowed)."
|
|
1907
|
+
)
|
|
1902
1908
|
}),
|
|
1903
1909
|
handler: async ({ query, path }, ctx) => {
|
|
1910
|
+
const built = buildMatcher(query, regex);
|
|
1911
|
+
if ("error" in built) return built.error;
|
|
1912
|
+
const matcher = built.matcher;
|
|
1904
1913
|
const state = {
|
|
1905
1914
|
matches: [],
|
|
1906
1915
|
totalMatches: 0,
|
|
1907
1916
|
truncated: false,
|
|
1908
|
-
|
|
1917
|
+
matcher,
|
|
1909
1918
|
maxMatches,
|
|
1910
1919
|
maxFileSize,
|
|
1911
1920
|
projectRoot
|
|
1912
1921
|
};
|
|
1913
1922
|
if (filesystem !== void 0) {
|
|
1914
|
-
const scopeRel = resolveScopeRel(path, projectRoot);
|
|
1923
|
+
const scopeRel = resolveScopeRel(path, projectRoot, allowAbsolute);
|
|
1915
1924
|
if ("error" in scopeRel) return scopeRel.error;
|
|
1916
1925
|
const backend = await resolveFilesystem(filesystem, ctx ?? {});
|
|
1917
1926
|
await walkBackend(backend, scopeRel.rel, state, 0);
|
|
@@ -1922,7 +1931,7 @@ function createSearchTextTool(opts) {
|
|
|
1922
1931
|
totalMatches: state.totalMatches
|
|
1923
1932
|
});
|
|
1924
1933
|
}
|
|
1925
|
-
const scope = resolveSearchScope(path, projectRoot);
|
|
1934
|
+
const scope = resolveSearchScope(path, projectRoot, allowAbsolute);
|
|
1926
1935
|
if ("error" in scope) return scope.error;
|
|
1927
1936
|
await walk(scope.scopeAbs, state);
|
|
1928
1937
|
return JSON.stringify({
|
|
@@ -1934,8 +1943,20 @@ function createSearchTextTool(opts) {
|
|
|
1934
1943
|
}
|
|
1935
1944
|
});
|
|
1936
1945
|
}
|
|
1937
|
-
function
|
|
1946
|
+
function buildMatcher(query, regex) {
|
|
1947
|
+
if (!regex) return { matcher: (line) => line.includes(query) };
|
|
1948
|
+
try {
|
|
1949
|
+
const re = new RegExp(query);
|
|
1950
|
+
return { matcher: (line) => re.test(line) };
|
|
1951
|
+
} catch {
|
|
1952
|
+
return { error: JSON.stringify({ ok: false, error: "invalid_regex", query }) };
|
|
1953
|
+
}
|
|
1954
|
+
}
|
|
1955
|
+
function resolveSearchScope(path, projectRoot, allowAbsolute) {
|
|
1938
1956
|
const scopeRel = path === void 0 || path === "" || path === "." ? "." : path;
|
|
1957
|
+
if (allowAbsolute && isAbsolute(scopeRel)) {
|
|
1958
|
+
return { scopeAbs: scopeRel };
|
|
1959
|
+
}
|
|
1939
1960
|
try {
|
|
1940
1961
|
const scopeAbs = scopeRel === "." ? projectRoot : safePathJoin(projectRoot, scopeRel);
|
|
1941
1962
|
assertNoSymlinkEscape(scopeAbs, projectRoot);
|
|
@@ -2007,13 +2028,14 @@ async function scanFile(absPath, relPath, state) {
|
|
|
2007
2028
|
const lines = buffer.toString("utf-8").split("\n");
|
|
2008
2029
|
for (let i = 0; i < lines.length; i += 1) {
|
|
2009
2030
|
const line = lines[i];
|
|
2010
|
-
if (!
|
|
2031
|
+
if (!state.matcher(line)) continue;
|
|
2011
2032
|
if (!recordMatch(state, relPath, i + 1, line)) return;
|
|
2012
2033
|
}
|
|
2013
2034
|
}
|
|
2014
|
-
function resolveScopeRel(path, projectRoot) {
|
|
2035
|
+
function resolveScopeRel(path, projectRoot, allowAbsolute) {
|
|
2015
2036
|
const scopeRel = path === void 0 || path === "" || path === "." ? "" : path;
|
|
2016
2037
|
if (scopeRel === "") return { rel: "" };
|
|
2038
|
+
if (allowAbsolute && isAbsolute(scopeRel)) return { rel: scopeRel };
|
|
2017
2039
|
try {
|
|
2018
2040
|
assertNoSymlinkEscape(safePathJoin(projectRoot, scopeRel), projectRoot);
|
|
2019
2041
|
return { rel: scopeRel };
|
|
@@ -2065,7 +2087,7 @@ async function scanFileBackend(backend, relPath, size, state) {
|
|
|
2065
2087
|
const lines = content.split("\n");
|
|
2066
2088
|
for (let i = 0; i < lines.length; i += 1) {
|
|
2067
2089
|
const line = lines[i];
|
|
2068
|
-
if (!
|
|
2090
|
+
if (!state.matcher(line)) continue;
|
|
2069
2091
|
if (!recordMatch(state, relPath, i + 1, line)) return;
|
|
2070
2092
|
}
|
|
2071
2093
|
}
|