@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/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.19.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- `createSearchTextTool` gains two ADDITIVE, opt-in options (both default OFF ⇒ existing literal, project-
|
|
8
|
+
scoped behavior unchanged): `regex` — match `query` as a JavaScript RegExp (grep semantics; an invalid
|
|
9
|
+
pattern returns `{ ok: false, error: 'invalid_regex' }` before walking), and `allowAbsolute` — honor an
|
|
10
|
+
absolute `path` scope outside `projectRoot` (Codex read-only "reads-anywhere"; forbidden dirs still
|
|
11
|
+
skipped). Together they let one built-in cover both literal content search and grep-style regex search.
|
|
12
|
+
|
|
3
13
|
## 0.18.0
|
|
4
14
|
|
|
5
15
|
### Minor Changes
|
package/dist/index.cjs
CHANGED
|
@@ -1893,27 +1893,36 @@ function createSearchTextTool(opts) {
|
|
|
1893
1893
|
projectRoot,
|
|
1894
1894
|
maxMatches = DEFAULT_MAX_MATCHES,
|
|
1895
1895
|
maxFileSize = DEFAULT_MAX_FILE_SIZE,
|
|
1896
|
-
filesystem: filesystem$1
|
|
1896
|
+
filesystem: filesystem$1,
|
|
1897
|
+
regex = false,
|
|
1898
|
+
allowAbsolute = false
|
|
1897
1899
|
} = opts;
|
|
1900
|
+
const queryKind = regex ? "a JavaScript REGULAR EXPRESSION" : "LITERAL, CASE-SENSITIVE text";
|
|
1901
|
+
const queryMatch = regex ? "matched as a regex" : "matched as a substring, not a regex";
|
|
1898
1902
|
return sdk.Tool.create({
|
|
1899
1903
|
name: "search_text",
|
|
1900
|
-
description: `Search file CONTENTS for
|
|
1904
|
+
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 }.`,
|
|
1901
1905
|
inputSchema: zod.z.object({
|
|
1902
|
-
query: zod.z.string().min(1).describe("Literal text to search for. Case-sensitive."),
|
|
1903
|
-
path: zod.z.string().optional().describe(
|
|
1906
|
+
query: regex ? zod.z.string().min(1).describe("A JavaScript regular expression, e.g. 'function\\\\s+main'.") : zod.z.string().min(1).describe("Literal text to search for. Case-sensitive."),
|
|
1907
|
+
path: zod.z.string().optional().describe(
|
|
1908
|
+
"Optional directory to scope the search (project-relative; absolute when allowed)."
|
|
1909
|
+
)
|
|
1904
1910
|
}),
|
|
1905
1911
|
handler: async ({ query, path }, ctx) => {
|
|
1912
|
+
const built = buildMatcher(query, regex);
|
|
1913
|
+
if ("error" in built) return built.error;
|
|
1914
|
+
const matcher = built.matcher;
|
|
1906
1915
|
const state = {
|
|
1907
1916
|
matches: [],
|
|
1908
1917
|
totalMatches: 0,
|
|
1909
1918
|
truncated: false,
|
|
1910
|
-
|
|
1919
|
+
matcher,
|
|
1911
1920
|
maxMatches,
|
|
1912
1921
|
maxFileSize,
|
|
1913
1922
|
projectRoot
|
|
1914
1923
|
};
|
|
1915
1924
|
if (filesystem$1 !== void 0) {
|
|
1916
|
-
const scopeRel = resolveScopeRel(path, projectRoot);
|
|
1925
|
+
const scopeRel = resolveScopeRel(path, projectRoot, allowAbsolute);
|
|
1917
1926
|
if ("error" in scopeRel) return scopeRel.error;
|
|
1918
1927
|
const backend = await filesystem.resolveFilesystem(filesystem$1, ctx ?? {});
|
|
1919
1928
|
await walkBackend(backend, scopeRel.rel, state, 0);
|
|
@@ -1924,7 +1933,7 @@ function createSearchTextTool(opts) {
|
|
|
1924
1933
|
totalMatches: state.totalMatches
|
|
1925
1934
|
});
|
|
1926
1935
|
}
|
|
1927
|
-
const scope = resolveSearchScope(path, projectRoot);
|
|
1936
|
+
const scope = resolveSearchScope(path, projectRoot, allowAbsolute);
|
|
1928
1937
|
if ("error" in scope) return scope.error;
|
|
1929
1938
|
await walk(scope.scopeAbs, state);
|
|
1930
1939
|
return JSON.stringify({
|
|
@@ -1936,15 +1945,27 @@ function createSearchTextTool(opts) {
|
|
|
1936
1945
|
}
|
|
1937
1946
|
});
|
|
1938
1947
|
}
|
|
1939
|
-
function
|
|
1940
|
-
|
|
1948
|
+
function buildMatcher(query, regex) {
|
|
1949
|
+
if (!regex) return { matcher: (line) => line.includes(query) };
|
|
1950
|
+
try {
|
|
1951
|
+
const re = new RegExp(query);
|
|
1952
|
+
return { matcher: (line) => re.test(line) };
|
|
1953
|
+
} catch {
|
|
1954
|
+
return { error: JSON.stringify({ ok: false, error: "invalid_regex", query }) };
|
|
1955
|
+
}
|
|
1956
|
+
}
|
|
1957
|
+
function resolveSearchScope(path$1, projectRoot, allowAbsolute) {
|
|
1958
|
+
const scopeRel = path$1 === void 0 || path$1 === "" || path$1 === "." ? "." : path$1;
|
|
1959
|
+
if (allowAbsolute && path.isAbsolute(scopeRel)) {
|
|
1960
|
+
return { scopeAbs: scopeRel };
|
|
1961
|
+
}
|
|
1941
1962
|
try {
|
|
1942
1963
|
const scopeAbs = scopeRel === "." ? projectRoot : safePathJoin(projectRoot, scopeRel);
|
|
1943
1964
|
assertNoSymlinkEscape(scopeAbs, projectRoot);
|
|
1944
1965
|
return { scopeAbs };
|
|
1945
1966
|
} catch (err) {
|
|
1946
1967
|
if (err instanceof PathTraversalError || err instanceof ForbiddenPathError) {
|
|
1947
|
-
return { error: JSON.stringify({ ok: false, error: "path_traversal", path }) };
|
|
1968
|
+
return { error: JSON.stringify({ ok: false, error: "path_traversal", path: path$1 }) };
|
|
1948
1969
|
}
|
|
1949
1970
|
throw err;
|
|
1950
1971
|
}
|
|
@@ -2009,19 +2030,20 @@ async function scanFile(absPath, relPath, state) {
|
|
|
2009
2030
|
const lines = buffer.toString("utf-8").split("\n");
|
|
2010
2031
|
for (let i = 0; i < lines.length; i += 1) {
|
|
2011
2032
|
const line = lines[i];
|
|
2012
|
-
if (!
|
|
2033
|
+
if (!state.matcher(line)) continue;
|
|
2013
2034
|
if (!recordMatch(state, relPath, i + 1, line)) return;
|
|
2014
2035
|
}
|
|
2015
2036
|
}
|
|
2016
|
-
function resolveScopeRel(path, projectRoot) {
|
|
2017
|
-
const scopeRel = path === void 0 || path === "" || path === "." ? "" : path;
|
|
2037
|
+
function resolveScopeRel(path$1, projectRoot, allowAbsolute) {
|
|
2038
|
+
const scopeRel = path$1 === void 0 || path$1 === "" || path$1 === "." ? "" : path$1;
|
|
2018
2039
|
if (scopeRel === "") return { rel: "" };
|
|
2040
|
+
if (allowAbsolute && path.isAbsolute(scopeRel)) return { rel: scopeRel };
|
|
2019
2041
|
try {
|
|
2020
2042
|
assertNoSymlinkEscape(safePathJoin(projectRoot, scopeRel), projectRoot);
|
|
2021
2043
|
return { rel: scopeRel };
|
|
2022
2044
|
} catch (err) {
|
|
2023
2045
|
if (err instanceof PathTraversalError || err instanceof ForbiddenPathError) {
|
|
2024
|
-
return { error: JSON.stringify({ ok: false, error: "path_traversal", path }) };
|
|
2046
|
+
return { error: JSON.stringify({ ok: false, error: "path_traversal", path: path$1 }) };
|
|
2025
2047
|
}
|
|
2026
2048
|
throw err;
|
|
2027
2049
|
}
|
|
@@ -2067,7 +2089,7 @@ async function scanFileBackend(backend, relPath, size, state) {
|
|
|
2067
2089
|
const lines = content.split("\n");
|
|
2068
2090
|
for (let i = 0; i < lines.length; i += 1) {
|
|
2069
2091
|
const line = lines[i];
|
|
2070
|
-
if (!
|
|
2092
|
+
if (!state.matcher(line)) continue;
|
|
2071
2093
|
if (!recordMatch(state, relPath, i + 1, line)) return;
|
|
2072
2094
|
}
|
|
2073
2095
|
}
|