reasonix 0.4.24 → 0.4.27
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/cli/{chunk-K6MR4SWS.js → chunk-ANMDY236.js} +137 -10
- package/dist/cli/chunk-ANMDY236.js.map +1 -0
- package/dist/cli/index.js +619 -58
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/{prompt-VDN5U3YE.js → prompt-75XLIUTO.js} +2 -2
- package/dist/index.d.ts +146 -2
- package/dist/index.js +541 -12
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/cli/chunk-K6MR4SWS.js.map +0 -1
- /package/dist/cli/{prompt-VDN5U3YE.js.map → prompt-75XLIUTO.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -1814,6 +1814,10 @@ var CacheFirstLoop = class {
|
|
|
1814
1814
|
usage = resp.usage;
|
|
1815
1815
|
}
|
|
1816
1816
|
} catch (err) {
|
|
1817
|
+
if (signal.aborted) {
|
|
1818
|
+
yield { turn: this._turn, role: "done", content: "" };
|
|
1819
|
+
return;
|
|
1820
|
+
}
|
|
1817
1821
|
yield {
|
|
1818
1822
|
turn: this._turn,
|
|
1819
1823
|
role: "error",
|
|
@@ -2215,9 +2219,11 @@ function isValidSkillName(name) {
|
|
|
2215
2219
|
var SkillStore = class {
|
|
2216
2220
|
homeDir;
|
|
2217
2221
|
projectRoot;
|
|
2222
|
+
disableBuiltins;
|
|
2218
2223
|
constructor(opts = {}) {
|
|
2219
2224
|
this.homeDir = opts.homeDir ?? homedir3();
|
|
2220
2225
|
this.projectRoot = opts.projectRoot ? resolve(opts.projectRoot) : void 0;
|
|
2226
|
+
this.disableBuiltins = opts.disableBuiltins === true;
|
|
2221
2227
|
}
|
|
2222
2228
|
/** True iff this store was configured with a project root. */
|
|
2223
2229
|
hasProjectScope() {
|
|
@@ -2241,8 +2247,8 @@ var SkillStore = class {
|
|
|
2241
2247
|
}
|
|
2242
2248
|
/**
|
|
2243
2249
|
* List every skill visible to this store. On name collisions the
|
|
2244
|
-
* higher-priority root (project over global) wins.
|
|
2245
|
-
* for stable prefix hashing.
|
|
2250
|
+
* higher-priority root (project over global over builtin) wins.
|
|
2251
|
+
* Sorted by name for stable prefix hashing.
|
|
2246
2252
|
*/
|
|
2247
2253
|
list() {
|
|
2248
2254
|
const byName = /* @__PURE__ */ new Map();
|
|
@@ -2260,6 +2266,11 @@ var SkillStore = class {
|
|
|
2260
2266
|
if (!byName.has(skill.name)) byName.set(skill.name, skill);
|
|
2261
2267
|
}
|
|
2262
2268
|
}
|
|
2269
|
+
if (!this.disableBuiltins) {
|
|
2270
|
+
for (const skill of BUILTIN_SKILLS) {
|
|
2271
|
+
if (!byName.has(skill.name)) byName.set(skill.name, skill);
|
|
2272
|
+
}
|
|
2273
|
+
}
|
|
2263
2274
|
return [...byName.values()].sort((a, b) => a.name.localeCompare(b.name));
|
|
2264
2275
|
}
|
|
2265
2276
|
/** Resolve one skill by name. Returns `null` if not found or malformed. */
|
|
@@ -2276,6 +2287,11 @@ var SkillStore = class {
|
|
|
2276
2287
|
return this.parse(flatCandidate, name, scope);
|
|
2277
2288
|
}
|
|
2278
2289
|
}
|
|
2290
|
+
if (!this.disableBuiltins) {
|
|
2291
|
+
for (const skill of BUILTIN_SKILLS) {
|
|
2292
|
+
if (skill.name === name) return skill;
|
|
2293
|
+
}
|
|
2294
|
+
}
|
|
2279
2295
|
return null;
|
|
2280
2296
|
}
|
|
2281
2297
|
readEntry(dir, scope, entry) {
|
|
@@ -2307,15 +2323,21 @@ var SkillStore = class {
|
|
|
2307
2323
|
body: body.trim(),
|
|
2308
2324
|
scope,
|
|
2309
2325
|
path,
|
|
2310
|
-
allowedTools: data["allowed-tools"]
|
|
2326
|
+
allowedTools: data["allowed-tools"],
|
|
2327
|
+
runAs: parseRunAs(data.runAs),
|
|
2328
|
+
model: data.model?.startsWith("deepseek-") ? data.model : void 0
|
|
2311
2329
|
};
|
|
2312
2330
|
}
|
|
2313
2331
|
};
|
|
2332
|
+
function parseRunAs(raw) {
|
|
2333
|
+
return raw?.trim() === "subagent" ? "subagent" : "inline";
|
|
2334
|
+
}
|
|
2314
2335
|
function skillIndexLine(s) {
|
|
2315
2336
|
const safeDesc = s.description.replace(/\n/g, " ").trim();
|
|
2316
|
-
const
|
|
2337
|
+
const marker = s.runAs === "subagent" ? "\u{1F9EC} " : "";
|
|
2338
|
+
const max = 130 - s.name.length - marker.length;
|
|
2317
2339
|
const clipped = safeDesc.length > max ? `${safeDesc.slice(0, Math.max(1, max - 1))}\u2026` : safeDesc;
|
|
2318
|
-
return clipped ? `- ${s.name} \u2014 ${clipped}` : `- ${s.name}`;
|
|
2340
|
+
return clipped ? `- ${marker}${s.name} \u2014 ${clipped}` : `- ${marker}${s.name}`;
|
|
2319
2341
|
}
|
|
2320
2342
|
function applySkillsIndex(basePrompt, opts = {}) {
|
|
2321
2343
|
const store = new SkillStore(opts);
|
|
@@ -2328,15 +2350,78 @@ function applySkillsIndex(basePrompt, opts = {}) {
|
|
|
2328
2350
|
return [
|
|
2329
2351
|
basePrompt,
|
|
2330
2352
|
"",
|
|
2331
|
-
"# Skills \u2014
|
|
2353
|
+
"# Skills \u2014 playbooks you can invoke",
|
|
2332
2354
|
"",
|
|
2333
|
-
'One-liner index. Each
|
|
2355
|
+
'One-liner index. Each entry is either a built-in or a user-authored playbook. Call `run_skill({ name: "<skill-name>", arguments: "<task>" })` to invoke one. Skills marked with \u{1F9EC} spawn an **isolated subagent** \u2014 its tool calls and reasoning never enter your context, only its final answer does. Use \u{1F9EC} skills for tasks that would otherwise flood your context (deep exploration, multi-step research, anything where you only need the conclusion). Plain skills are inlined: their body becomes a tool result you read and act on directly. The user can also invoke a skill via `/skill <name>`.',
|
|
2334
2356
|
"",
|
|
2335
2357
|
"```",
|
|
2336
2358
|
truncated,
|
|
2337
2359
|
"```"
|
|
2338
2360
|
].join("\n");
|
|
2339
2361
|
}
|
|
2362
|
+
var BUILTIN_EXPLORE_BODY = `You are running as an exploration subagent. Your job is to investigate the codebase the parent agent pointed you at, then return one focused, distilled answer.
|
|
2363
|
+
|
|
2364
|
+
How to operate:
|
|
2365
|
+
- Use read_file, search_files, search_content, directory_tree, list_directory, get_file_info as your primary tools. Stay read-only.
|
|
2366
|
+
- For "find all places that call / reference / use X" questions, use \`search_content\` (content grep) \u2014 NOT \`search_files\` (which only matches file names). This is the most common subagent mistake; using the wrong tool gives empty results and you waste your iter budget chasing a phantom.
|
|
2367
|
+
- Cast a wide net first (search_content for symbol references, directory_tree for structure) to map the territory; then read the 3-10 most relevant files in full.
|
|
2368
|
+
- Don't read every file \u2014 be selective. Aim for breadth on the first pass, depth only where the question demands it.
|
|
2369
|
+
- Stop exploring as soon as you can answer the question. The parent doesn't see your tool calls, so over-exploration is pure waste.
|
|
2370
|
+
|
|
2371
|
+
Your final answer:
|
|
2372
|
+
- One paragraph (or a few short bullets). Lead with the conclusion.
|
|
2373
|
+
- Cite specific file paths + line ranges when they support the answer.
|
|
2374
|
+
- If the question can't be answered from what you found, say so plainly and suggest where to look next.
|
|
2375
|
+
- No follow-up offers, no "let me know if you need more." The parent will ask again if they need more.
|
|
2376
|
+
|
|
2377
|
+
Formatting (rendered in a TUI):
|
|
2378
|
+
- Tabular data \u2192 GitHub-Flavored Markdown tables with ASCII pipes (\`| col | col |\` + \`| --- | --- |\`). Never use Unicode box-drawing characters (\u2502 \u2500 \u253C) \u2014 they break word-wrap.
|
|
2379
|
+
- Keep table cells short; if a cell needs a paragraph, use bullets below the table instead.
|
|
2380
|
+
- Code, file paths with line ranges, and shell commands \u2192 fenced code blocks (\`\`\`).
|
|
2381
|
+
- NEVER draw decorative frames around code or text with \`\u250C\u2500\u2500\u2510 \u2502 \u2514\u2500\u2500\u2518\` box-drawing characters. Use plain code blocks; the renderer adds its own border.
|
|
2382
|
+
- For flow charts: use a bullet list with \`\u2192\` or \`\u2193\` between steps, not ASCII boxes-and-arrows.
|
|
2383
|
+
|
|
2384
|
+
The 'task' the parent gave you is the question you must answer. Treat any other reading of it as scope creep.`;
|
|
2385
|
+
var BUILTIN_RESEARCH_BODY = `You are running as a research subagent. Your job is to gather information from code AND the web, synthesize it, and return one focused conclusion.
|
|
2386
|
+
|
|
2387
|
+
How to operate:
|
|
2388
|
+
- Combine code reading (read_file, search_files) with web tools (web_search, web_fetch) as appropriate to the question.
|
|
2389
|
+
- For "how does X work" / "is Y supported" questions: web first to find the canonical reference, then verify against the local code.
|
|
2390
|
+
- For "what's our policy on Z" / "where do we use Q": local code first, web only if you need to compare against external standards.
|
|
2391
|
+
- Cap yourself at ~10 tool calls. If you can't converge in 10, return what you have plus a note about what's missing.
|
|
2392
|
+
|
|
2393
|
+
Your final answer:
|
|
2394
|
+
- One paragraph (or short bullets). Lead with the conclusion.
|
|
2395
|
+
- Cite both code (file:line) AND web sources (URL) when they back the answer.
|
|
2396
|
+
- Distinguish "I verified this in code" from "I read this on a docs page" \u2014 the parent will trust the former more.
|
|
2397
|
+
- If the answer is uncertain, say so. Don't invent confidence.
|
|
2398
|
+
|
|
2399
|
+
Formatting (rendered in a TUI):
|
|
2400
|
+
- Tabular data \u2192 GitHub-Flavored Markdown tables with ASCII pipes (\`| col | col |\` + \`| --- | --- |\`). Never use Unicode box-drawing characters (\u2502 \u2500 \u253C) \u2014 they break word-wrap.
|
|
2401
|
+
- Keep table cells short; if a cell needs a paragraph, use bullets below the table instead.
|
|
2402
|
+
- Code, file paths with line ranges, and shell commands \u2192 fenced code blocks (\`\`\`).
|
|
2403
|
+
- NEVER draw decorative frames around code or text with \`\u250C\u2500\u2500\u2510 \u2502 \u2514\u2500\u2500\u2518\` box-drawing characters. Use plain code blocks; the renderer adds its own border.
|
|
2404
|
+
- For flow charts: use a bullet list with \`\u2192\` or \`\u2193\` between steps, not ASCII boxes-and-arrows.
|
|
2405
|
+
|
|
2406
|
+
The 'task' the parent gave you is the research question. Stay on it.`;
|
|
2407
|
+
var BUILTIN_SKILLS = Object.freeze([
|
|
2408
|
+
Object.freeze({
|
|
2409
|
+
name: "explore",
|
|
2410
|
+
description: "Explore the codebase in an isolated subagent \u2014 wide-net read-only investigation that returns one distilled answer. Best for: 'find all places that...', 'how does X work across the project', 'survey the code for Y'.",
|
|
2411
|
+
body: BUILTIN_EXPLORE_BODY,
|
|
2412
|
+
scope: "builtin",
|
|
2413
|
+
path: "(builtin)",
|
|
2414
|
+
runAs: "subagent"
|
|
2415
|
+
}),
|
|
2416
|
+
Object.freeze({
|
|
2417
|
+
name: "research",
|
|
2418
|
+
description: "Research a question by combining web search + code reading in an isolated subagent. Best for: 'is X feature supported by lib Y', 'what's the canonical way to do Z', 'compare our impl against the spec'.",
|
|
2419
|
+
body: BUILTIN_RESEARCH_BODY,
|
|
2420
|
+
scope: "builtin",
|
|
2421
|
+
path: "(builtin)",
|
|
2422
|
+
runAs: "subagent"
|
|
2423
|
+
})
|
|
2424
|
+
]);
|
|
2340
2425
|
|
|
2341
2426
|
// src/user-memory.ts
|
|
2342
2427
|
var USER_MEMORY_DIR = "memory";
|
|
@@ -2618,6 +2703,74 @@ import { promises as fs } from "fs";
|
|
|
2618
2703
|
import * as pathMod from "path";
|
|
2619
2704
|
var DEFAULT_MAX_READ_BYTES = 2 * 1024 * 1024;
|
|
2620
2705
|
var DEFAULT_MAX_LIST_BYTES = 256 * 1024;
|
|
2706
|
+
var SKIP_DIR_NAMES = /* @__PURE__ */ new Set([
|
|
2707
|
+
"node_modules",
|
|
2708
|
+
".git",
|
|
2709
|
+
".hg",
|
|
2710
|
+
".svn",
|
|
2711
|
+
"dist",
|
|
2712
|
+
"build",
|
|
2713
|
+
"out",
|
|
2714
|
+
".next",
|
|
2715
|
+
".nuxt",
|
|
2716
|
+
"target",
|
|
2717
|
+
// Rust / Java
|
|
2718
|
+
".venv",
|
|
2719
|
+
"venv",
|
|
2720
|
+
"__pycache__",
|
|
2721
|
+
".pytest_cache",
|
|
2722
|
+
".mypy_cache",
|
|
2723
|
+
".cache",
|
|
2724
|
+
"coverage"
|
|
2725
|
+
]);
|
|
2726
|
+
var BINARY_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
2727
|
+
".png",
|
|
2728
|
+
".jpg",
|
|
2729
|
+
".jpeg",
|
|
2730
|
+
".gif",
|
|
2731
|
+
".bmp",
|
|
2732
|
+
".ico",
|
|
2733
|
+
".webp",
|
|
2734
|
+
".tiff",
|
|
2735
|
+
".pdf",
|
|
2736
|
+
".zip",
|
|
2737
|
+
".tar",
|
|
2738
|
+
".gz",
|
|
2739
|
+
".bz2",
|
|
2740
|
+
".xz",
|
|
2741
|
+
".7z",
|
|
2742
|
+
".rar",
|
|
2743
|
+
".exe",
|
|
2744
|
+
".dll",
|
|
2745
|
+
".so",
|
|
2746
|
+
".dylib",
|
|
2747
|
+
".bin",
|
|
2748
|
+
".class",
|
|
2749
|
+
".jar",
|
|
2750
|
+
".war",
|
|
2751
|
+
".o",
|
|
2752
|
+
".obj",
|
|
2753
|
+
".lib",
|
|
2754
|
+
".a",
|
|
2755
|
+
".woff",
|
|
2756
|
+
".woff2",
|
|
2757
|
+
".ttf",
|
|
2758
|
+
".otf",
|
|
2759
|
+
".eot",
|
|
2760
|
+
".mp3",
|
|
2761
|
+
".mp4",
|
|
2762
|
+
".mov",
|
|
2763
|
+
".avi",
|
|
2764
|
+
".webm",
|
|
2765
|
+
".wasm",
|
|
2766
|
+
".pyc",
|
|
2767
|
+
".pyo"
|
|
2768
|
+
]);
|
|
2769
|
+
function isLikelyBinaryByName(name) {
|
|
2770
|
+
const dot = name.lastIndexOf(".");
|
|
2771
|
+
if (dot < 0) return false;
|
|
2772
|
+
return BINARY_EXTENSIONS.has(name.slice(dot).toLowerCase());
|
|
2773
|
+
}
|
|
2621
2774
|
function registerFilesystemTools(registry, opts) {
|
|
2622
2775
|
const rootDir = pathMod.resolve(opts.rootDir);
|
|
2623
2776
|
const allowWriting = opts.allowWriting !== false;
|
|
@@ -2627,7 +2780,12 @@ function registerFilesystemTools(registry, opts) {
|
|
|
2627
2780
|
if (typeof raw !== "string" || raw.length === 0) {
|
|
2628
2781
|
throw new Error("path must be a non-empty string");
|
|
2629
2782
|
}
|
|
2630
|
-
|
|
2783
|
+
let normalized = raw;
|
|
2784
|
+
while (normalized.startsWith("/") || normalized.startsWith("\\")) {
|
|
2785
|
+
normalized = normalized.slice(1);
|
|
2786
|
+
}
|
|
2787
|
+
if (normalized.length === 0) normalized = ".";
|
|
2788
|
+
const resolved = pathMod.resolve(rootDir, normalized);
|
|
2631
2789
|
const normRoot = pathMod.resolve(rootDir);
|
|
2632
2790
|
const rel = pathMod.relative(normRoot, resolved);
|
|
2633
2791
|
if (rel.startsWith("..") || pathMod.isAbsolute(rel)) {
|
|
@@ -2793,6 +2951,114 @@ function registerFilesystemTools(registry, opts) {
|
|
|
2793
2951
|
return matches.length === 0 ? "(no matches)" : matches.join("\n");
|
|
2794
2952
|
}
|
|
2795
2953
|
});
|
|
2954
|
+
registry.register({
|
|
2955
|
+
name: "search_content",
|
|
2956
|
+
description: "Recursively grep file CONTENTS for a substring or regex. This is the right tool for 'find all places that call X', 'where is Y referenced', 'what files contain Z'. Different from search_files (which matches FILE NAMES). Returns one match per line in 'path:line: text' format. Skips dependency / VCS / build directories (node_modules, .git, dist, build, .next, target, .venv) and binary files by default.",
|
|
2957
|
+
readOnly: true,
|
|
2958
|
+
parameters: {
|
|
2959
|
+
type: "object",
|
|
2960
|
+
properties: {
|
|
2961
|
+
pattern: {
|
|
2962
|
+
type: "string",
|
|
2963
|
+
description: "Substring (or regex) to search file contents for."
|
|
2964
|
+
},
|
|
2965
|
+
path: {
|
|
2966
|
+
type: "string",
|
|
2967
|
+
description: "Directory to start the search at (default: sandbox root)."
|
|
2968
|
+
},
|
|
2969
|
+
glob: {
|
|
2970
|
+
type: "string",
|
|
2971
|
+
description: "Optional file-name suffix or substring filter. Examples: '.ts' (only TypeScript), 'test' (any file with 'test' in the name). Reduces noise when you know the file shape."
|
|
2972
|
+
},
|
|
2973
|
+
case_sensitive: {
|
|
2974
|
+
type: "boolean",
|
|
2975
|
+
description: "When true, match case exactly. Default false (case-insensitive)."
|
|
2976
|
+
},
|
|
2977
|
+
include_deps: {
|
|
2978
|
+
type: "boolean",
|
|
2979
|
+
description: "When true, also search inside node_modules / .git / dist / build / etc. Off by default \u2014 most exploration questions are about the user's own code."
|
|
2980
|
+
}
|
|
2981
|
+
},
|
|
2982
|
+
required: ["pattern"]
|
|
2983
|
+
},
|
|
2984
|
+
fn: async (args) => {
|
|
2985
|
+
const startAbs = safePath(args.path ?? ".");
|
|
2986
|
+
const caseSensitive = args.case_sensitive === true;
|
|
2987
|
+
const includeDeps = args.include_deps === true;
|
|
2988
|
+
const nameFilter = typeof args.glob === "string" ? args.glob.toLowerCase() : null;
|
|
2989
|
+
let re = null;
|
|
2990
|
+
try {
|
|
2991
|
+
re = new RegExp(args.pattern, caseSensitive ? "" : "i");
|
|
2992
|
+
} catch {
|
|
2993
|
+
re = null;
|
|
2994
|
+
}
|
|
2995
|
+
const needle = caseSensitive ? args.pattern : args.pattern.toLowerCase();
|
|
2996
|
+
const matches = [];
|
|
2997
|
+
let totalBytes = 0;
|
|
2998
|
+
let scanned = 0;
|
|
2999
|
+
let truncated = false;
|
|
3000
|
+
const walk2 = async (dir) => {
|
|
3001
|
+
if (truncated) return;
|
|
3002
|
+
let entries;
|
|
3003
|
+
try {
|
|
3004
|
+
entries = await fs.readdir(dir, { withFileTypes: true });
|
|
3005
|
+
} catch {
|
|
3006
|
+
return;
|
|
3007
|
+
}
|
|
3008
|
+
for (const e of entries) {
|
|
3009
|
+
if (truncated) return;
|
|
3010
|
+
if (e.isDirectory()) {
|
|
3011
|
+
if (!includeDeps && SKIP_DIR_NAMES.has(e.name)) continue;
|
|
3012
|
+
await walk2(pathMod.join(dir, e.name));
|
|
3013
|
+
continue;
|
|
3014
|
+
}
|
|
3015
|
+
if (!e.isFile()) continue;
|
|
3016
|
+
if (nameFilter && !e.name.toLowerCase().includes(nameFilter)) continue;
|
|
3017
|
+
if (isLikelyBinaryByName(e.name)) continue;
|
|
3018
|
+
const full = pathMod.join(dir, e.name);
|
|
3019
|
+
let stat;
|
|
3020
|
+
try {
|
|
3021
|
+
stat = await fs.stat(full);
|
|
3022
|
+
} catch {
|
|
3023
|
+
continue;
|
|
3024
|
+
}
|
|
3025
|
+
if (stat.size > 2 * 1024 * 1024) continue;
|
|
3026
|
+
let raw;
|
|
3027
|
+
try {
|
|
3028
|
+
raw = await fs.readFile(full);
|
|
3029
|
+
} catch {
|
|
3030
|
+
continue;
|
|
3031
|
+
}
|
|
3032
|
+
const firstNul = raw.indexOf(0);
|
|
3033
|
+
if (firstNul !== -1 && firstNul < 8 * 1024) continue;
|
|
3034
|
+
const text = raw.toString("utf8");
|
|
3035
|
+
const rel = pathMod.relative(rootDir, full);
|
|
3036
|
+
const lines = text.split(/\r?\n/);
|
|
3037
|
+
for (let li = 0; li < lines.length; li++) {
|
|
3038
|
+
const line = lines[li];
|
|
3039
|
+
const lineForCheck = caseSensitive ? line : line.toLowerCase();
|
|
3040
|
+
const hit = re ? re.test(line) : lineForCheck.includes(needle);
|
|
3041
|
+
if (!hit) continue;
|
|
3042
|
+
const display = line.length > 200 ? `${line.slice(0, 200)}\u2026` : line;
|
|
3043
|
+
const out = `${rel}:${li + 1}: ${display}`;
|
|
3044
|
+
if (totalBytes + out.length + 1 > maxListBytes) {
|
|
3045
|
+
matches.push(`[\u2026 truncated at ${maxListBytes} bytes \u2014 refine pattern or path \u2026]`);
|
|
3046
|
+
truncated = true;
|
|
3047
|
+
return;
|
|
3048
|
+
}
|
|
3049
|
+
matches.push(out);
|
|
3050
|
+
totalBytes += out.length + 1;
|
|
3051
|
+
}
|
|
3052
|
+
scanned++;
|
|
3053
|
+
}
|
|
3054
|
+
};
|
|
3055
|
+
await walk2(startAbs);
|
|
3056
|
+
if (matches.length === 0) {
|
|
3057
|
+
return scanned === 0 ? "(no files scanned \u2014 path empty or all files filtered out)" : `(no matches across ${scanned} file${scanned === 1 ? "" : "s"})`;
|
|
3058
|
+
}
|
|
3059
|
+
return matches.join("\n");
|
|
3060
|
+
}
|
|
3061
|
+
});
|
|
2796
3062
|
registry.register({
|
|
2797
3063
|
name: "get_file_info",
|
|
2798
3064
|
description: "Stat a path under the sandbox root. Returns type (file|directory|symlink), size in bytes, mtime in ISO-8601.",
|
|
@@ -3119,6 +3385,195 @@ function registerPlanTool(registry, opts = {}) {
|
|
|
3119
3385
|
return registry;
|
|
3120
3386
|
}
|
|
3121
3387
|
|
|
3388
|
+
// src/tools/subagent.ts
|
|
3389
|
+
var DEFAULT_SUBAGENT_SYSTEM = `You are a Reasonix subagent. The parent agent spawned you to handle one focused subtask, then return.
|
|
3390
|
+
|
|
3391
|
+
Rules:
|
|
3392
|
+
- Stay on the task you were given. Do not expand scope.
|
|
3393
|
+
- Use tools as needed. You share the parent's sandbox + safety rules.
|
|
3394
|
+
- When you're done, your final assistant message is the only thing the parent will see \u2014 make it complete and self-contained. No follow-up offers, no questions, no "let me know if you need more."
|
|
3395
|
+
- Prefer one clear, distilled answer over a long log of what you tried.
|
|
3396
|
+
|
|
3397
|
+
Formatting rules (the parent renders your reply in a TUI with a real markdown renderer):
|
|
3398
|
+
- For tabular data use GitHub-Flavored Markdown tables with ASCII pipes: \`| col | col |\` headers, \`| --- | --- |\` separator. NEVER draw tables with Unicode box-drawing characters (\u2502 \u2500 \u253C \u250C \u2510 \u2514 \u2518 \u251C \u2524). They look intentional but break terminal word-wrap and produce garbled output.
|
|
3399
|
+
- Keep table cells short \u2014 one short phrase per cell, not multi-line paragraphs. If a description doesn't fit in ~40 chars, use bullets below the table instead.
|
|
3400
|
+
- Use fenced code blocks (\`\`\`) for any code, file paths with line ranges, or shell commands.
|
|
3401
|
+
- NEVER draw decorative frames around content with \`\u250C\u2500\u2500\u2510 \u2502 \u2514\u2500\u2500\u2518\` box-drawing characters. The renderer handles code blocks and headings on its own \u2014 extra ASCII art adds noise without value and breaks at narrow terminal widths.
|
|
3402
|
+
- For flow charts and diagrams: use a markdown bullet list with \`\u2192\` or \`\u2193\` between steps. Don't try to draw boxes-and-arrows in ASCII; it never survives word-wrap.`;
|
|
3403
|
+
var DEFAULT_MAX_RESULT_CHARS2 = 8e3;
|
|
3404
|
+
var DEFAULT_MAX_ITERS = 16;
|
|
3405
|
+
var DEFAULT_SUBAGENT_MODEL = "deepseek-chat";
|
|
3406
|
+
var SUBAGENT_TOOL_NAME = "spawn_subagent";
|
|
3407
|
+
var NEVER_INHERITED_TOOLS = /* @__PURE__ */ new Set([SUBAGENT_TOOL_NAME, "submit_plan"]);
|
|
3408
|
+
async function spawnSubagent(opts) {
|
|
3409
|
+
const model = opts.model ?? DEFAULT_SUBAGENT_MODEL;
|
|
3410
|
+
const maxToolIters = opts.maxToolIters ?? DEFAULT_MAX_ITERS;
|
|
3411
|
+
const maxResultChars = opts.maxResultChars ?? DEFAULT_MAX_RESULT_CHARS2;
|
|
3412
|
+
const sink = opts.sink;
|
|
3413
|
+
const startedAt = Date.now();
|
|
3414
|
+
const taskPreview = opts.task.length > 30 ? `${opts.task.slice(0, 30)}\u2026` : opts.task;
|
|
3415
|
+
sink?.current?.({
|
|
3416
|
+
kind: "start",
|
|
3417
|
+
task: taskPreview,
|
|
3418
|
+
iter: 0,
|
|
3419
|
+
elapsedMs: 0
|
|
3420
|
+
});
|
|
3421
|
+
const childTools = forkRegistryExcluding(opts.parentRegistry, NEVER_INHERITED_TOOLS);
|
|
3422
|
+
const childPrefix = new ImmutablePrefix({
|
|
3423
|
+
system: opts.system,
|
|
3424
|
+
toolSpecs: childTools.specs()
|
|
3425
|
+
});
|
|
3426
|
+
const childLoop = new CacheFirstLoop({
|
|
3427
|
+
client: opts.client,
|
|
3428
|
+
prefix: childPrefix,
|
|
3429
|
+
tools: childTools,
|
|
3430
|
+
model,
|
|
3431
|
+
maxToolIters,
|
|
3432
|
+
hooks: [],
|
|
3433
|
+
stream: false
|
|
3434
|
+
});
|
|
3435
|
+
const onParentAbort = () => childLoop.abort();
|
|
3436
|
+
opts.parentSignal?.addEventListener("abort", onParentAbort, { once: true });
|
|
3437
|
+
let final = "";
|
|
3438
|
+
let errorMessage;
|
|
3439
|
+
let toolIter = 0;
|
|
3440
|
+
try {
|
|
3441
|
+
for await (const ev of childLoop.step(opts.task)) {
|
|
3442
|
+
if (ev.role === "tool") {
|
|
3443
|
+
toolIter++;
|
|
3444
|
+
sink?.current?.({
|
|
3445
|
+
kind: "progress",
|
|
3446
|
+
task: taskPreview,
|
|
3447
|
+
iter: toolIter,
|
|
3448
|
+
elapsedMs: Date.now() - startedAt
|
|
3449
|
+
});
|
|
3450
|
+
}
|
|
3451
|
+
if (ev.role === "assistant_final") {
|
|
3452
|
+
final = ev.content ?? "";
|
|
3453
|
+
}
|
|
3454
|
+
if (ev.role === "error") {
|
|
3455
|
+
errorMessage = ev.error ?? "subagent error";
|
|
3456
|
+
}
|
|
3457
|
+
}
|
|
3458
|
+
} catch (err) {
|
|
3459
|
+
errorMessage = err.message;
|
|
3460
|
+
} finally {
|
|
3461
|
+
opts.parentSignal?.removeEventListener("abort", onParentAbort);
|
|
3462
|
+
}
|
|
3463
|
+
if (!errorMessage && !final) {
|
|
3464
|
+
errorMessage = opts.parentSignal?.aborted ? "subagent aborted before producing an answer" : "subagent ended without producing an answer";
|
|
3465
|
+
}
|
|
3466
|
+
const elapsedMs = Date.now() - startedAt;
|
|
3467
|
+
const turns = childLoop.stats.turns.length;
|
|
3468
|
+
const costUsd2 = childLoop.stats.totalCost;
|
|
3469
|
+
const truncated = final.length > maxResultChars ? `${final.slice(0, maxResultChars)}
|
|
3470
|
+
|
|
3471
|
+
[\u2026truncated ${final.length - maxResultChars} chars; ask the subagent for a tighter summary if you need more.]` : final;
|
|
3472
|
+
sink?.current?.({
|
|
3473
|
+
kind: "end",
|
|
3474
|
+
task: taskPreview,
|
|
3475
|
+
iter: toolIter,
|
|
3476
|
+
elapsedMs,
|
|
3477
|
+
summary: errorMessage ? void 0 : truncated.slice(0, 120),
|
|
3478
|
+
error: errorMessage,
|
|
3479
|
+
turns
|
|
3480
|
+
});
|
|
3481
|
+
return {
|
|
3482
|
+
success: !errorMessage,
|
|
3483
|
+
output: errorMessage ? "" : truncated,
|
|
3484
|
+
error: errorMessage,
|
|
3485
|
+
turns,
|
|
3486
|
+
toolIters: toolIter,
|
|
3487
|
+
elapsedMs,
|
|
3488
|
+
costUsd: costUsd2
|
|
3489
|
+
};
|
|
3490
|
+
}
|
|
3491
|
+
function formatSubagentResult(r) {
|
|
3492
|
+
if (!r.success) {
|
|
3493
|
+
return JSON.stringify({
|
|
3494
|
+
success: false,
|
|
3495
|
+
error: r.error ?? "unknown subagent error",
|
|
3496
|
+
turns: r.turns,
|
|
3497
|
+
tool_iters: r.toolIters,
|
|
3498
|
+
elapsed_ms: r.elapsedMs
|
|
3499
|
+
});
|
|
3500
|
+
}
|
|
3501
|
+
return JSON.stringify({
|
|
3502
|
+
success: true,
|
|
3503
|
+
output: r.output,
|
|
3504
|
+
turns: r.turns,
|
|
3505
|
+
tool_iters: r.toolIters,
|
|
3506
|
+
elapsed_ms: r.elapsedMs,
|
|
3507
|
+
cost_usd: r.costUsd
|
|
3508
|
+
});
|
|
3509
|
+
}
|
|
3510
|
+
function registerSubagentTool(parentRegistry, opts) {
|
|
3511
|
+
const baseSystem = opts.defaultSystem ?? DEFAULT_SUBAGENT_SYSTEM;
|
|
3512
|
+
const defaultSystem = opts.projectRoot ? applyProjectMemory(baseSystem, opts.projectRoot) : baseSystem;
|
|
3513
|
+
const defaultModel = opts.defaultModel ?? DEFAULT_SUBAGENT_MODEL;
|
|
3514
|
+
const maxToolIters = opts.maxToolIters ?? DEFAULT_MAX_ITERS;
|
|
3515
|
+
const maxResultChars = opts.maxResultChars ?? DEFAULT_MAX_RESULT_CHARS2;
|
|
3516
|
+
const sink = opts.sink;
|
|
3517
|
+
parentRegistry.register({
|
|
3518
|
+
name: SUBAGENT_TOOL_NAME,
|
|
3519
|
+
description: "Spawn an isolated subagent to handle a self-contained subtask in a fresh context, returning only its final answer. Use for: deep codebase exploration that would flood the main context, multi-step research where you only need the conclusion, or any focused subtask whose intermediate reasoning the user does not need to see. The subagent inherits all your tools (filesystem, shell, web, MCP, etc.) but runs in its own isolated message log \u2014 its tool calls and reasoning never enter your context. Only the final assistant message comes back as this tool's result. Keep tasks focused; the subagent has a stricter iter budget than you do.",
|
|
3520
|
+
parameters: {
|
|
3521
|
+
type: "object",
|
|
3522
|
+
properties: {
|
|
3523
|
+
task: {
|
|
3524
|
+
type: "string",
|
|
3525
|
+
description: "The subtask the subagent should perform. Be specific and self-contained \u2014 the subagent has none of your conversation context, only what you write here."
|
|
3526
|
+
},
|
|
3527
|
+
system: {
|
|
3528
|
+
type: "string",
|
|
3529
|
+
description: "Optional override for the subagent's system prompt. The default tells it to stay focused and return a concise answer; override only when the subtask needs a specialized persona."
|
|
3530
|
+
},
|
|
3531
|
+
model: {
|
|
3532
|
+
type: "string",
|
|
3533
|
+
enum: ["deepseek-chat", "deepseek-reasoner"],
|
|
3534
|
+
description: "Which DeepSeek model the subagent runs on. 'deepseek-chat' (V3) is the default \u2014 fast and cheap. Use 'deepseek-reasoner' (R1) only when the subtask genuinely needs planning or multi-step reasoning; it is roughly 5-10x more expensive."
|
|
3535
|
+
}
|
|
3536
|
+
},
|
|
3537
|
+
required: ["task"]
|
|
3538
|
+
},
|
|
3539
|
+
fn: async (args, ctx) => {
|
|
3540
|
+
const task = typeof args.task === "string" ? args.task.trim() : "";
|
|
3541
|
+
if (!task) {
|
|
3542
|
+
return JSON.stringify({
|
|
3543
|
+
error: "spawn_subagent requires a non-empty 'task' argument."
|
|
3544
|
+
});
|
|
3545
|
+
}
|
|
3546
|
+
const system = typeof args.system === "string" && args.system.trim().length > 0 ? args.system.trim() : defaultSystem;
|
|
3547
|
+
const model = typeof args.model === "string" && args.model.startsWith("deepseek-") ? args.model : defaultModel;
|
|
3548
|
+
const result = await spawnSubagent({
|
|
3549
|
+
client: opts.client,
|
|
3550
|
+
parentRegistry,
|
|
3551
|
+
system,
|
|
3552
|
+
task,
|
|
3553
|
+
model,
|
|
3554
|
+
maxToolIters,
|
|
3555
|
+
maxResultChars,
|
|
3556
|
+
sink,
|
|
3557
|
+
parentSignal: ctx?.signal
|
|
3558
|
+
});
|
|
3559
|
+
return formatSubagentResult(result);
|
|
3560
|
+
}
|
|
3561
|
+
});
|
|
3562
|
+
return parentRegistry;
|
|
3563
|
+
}
|
|
3564
|
+
function forkRegistryExcluding(parent, exclude) {
|
|
3565
|
+
const child = new ToolRegistry();
|
|
3566
|
+
for (const spec of parent.specs()) {
|
|
3567
|
+
const name = spec.function.name;
|
|
3568
|
+
if (exclude.has(name)) continue;
|
|
3569
|
+
const def = parent.get(name);
|
|
3570
|
+
if (!def) continue;
|
|
3571
|
+
child.register(def);
|
|
3572
|
+
}
|
|
3573
|
+
if (parent.planMode) child.setPlanMode(true);
|
|
3574
|
+
return child;
|
|
3575
|
+
}
|
|
3576
|
+
|
|
3122
3577
|
// src/tools/shell.ts
|
|
3123
3578
|
import { spawn as spawn2 } from "child_process";
|
|
3124
3579
|
import { existsSync as existsSync6, statSync as statSync3 } from "fs";
|
|
@@ -3316,7 +3771,7 @@ function prepareSpawn(argv, opts = {}) {
|
|
|
3316
3771
|
const cmdline = [resolved, ...tail].map(quoteForCmdExe).join(" ");
|
|
3317
3772
|
return {
|
|
3318
3773
|
bin: "cmd.exe",
|
|
3319
|
-
args: ["/d", "/s", "/c", cmdline],
|
|
3774
|
+
args: ["/d", "/s", "/c", withUtf8Codepage(cmdline)],
|
|
3320
3775
|
// windowsVerbatimArguments prevents Node from re-quoting the /c
|
|
3321
3776
|
// payload — we've already composed an exact cmd.exe command
|
|
3322
3777
|
// line. Without this Node wraps our already-quoted string in
|
|
@@ -3328,12 +3783,36 @@ function prepareSpawn(argv, opts = {}) {
|
|
|
3328
3783
|
const cmdline = [head, ...tail].map(quoteForCmdExe).join(" ");
|
|
3329
3784
|
return {
|
|
3330
3785
|
bin: "cmd.exe",
|
|
3331
|
-
args: ["/d", "/s", "/c", cmdline],
|
|
3786
|
+
args: ["/d", "/s", "/c", withUtf8Codepage(cmdline)],
|
|
3332
3787
|
spawnOverrides: { windowsVerbatimArguments: true }
|
|
3333
3788
|
};
|
|
3334
3789
|
}
|
|
3790
|
+
if (isPowerShellExe(resolved)) {
|
|
3791
|
+
const patched = injectPowerShellUtf8(tail);
|
|
3792
|
+
if (patched) {
|
|
3793
|
+
return { bin: resolved, args: patched, spawnOverrides: {} };
|
|
3794
|
+
}
|
|
3795
|
+
}
|
|
3335
3796
|
return { bin: resolved, args: [...tail], spawnOverrides: {} };
|
|
3336
3797
|
}
|
|
3798
|
+
function isPowerShellExe(resolved) {
|
|
3799
|
+
return /(?:^|[\\/])(?:powershell|pwsh)(?:\.exe)?$/i.test(resolved);
|
|
3800
|
+
}
|
|
3801
|
+
function injectPowerShellUtf8(args) {
|
|
3802
|
+
const prelude = "[Console]::OutputEncoding=[System.Text.Encoding]::UTF8;$OutputEncoding=[System.Text.Encoding]::UTF8;";
|
|
3803
|
+
for (let i = 0; i < args.length; i++) {
|
|
3804
|
+
const a = args[i] ?? "";
|
|
3805
|
+
if (/^-(?:Command|c)$/i.test(a) && i + 1 < args.length) {
|
|
3806
|
+
const out = [...args];
|
|
3807
|
+
out[i + 1] = `${prelude}${args[i + 1] ?? ""}`;
|
|
3808
|
+
return out;
|
|
3809
|
+
}
|
|
3810
|
+
}
|
|
3811
|
+
return null;
|
|
3812
|
+
}
|
|
3813
|
+
function withUtf8Codepage(cmdline) {
|
|
3814
|
+
return `chcp 65001 >nul & ${cmdline}`;
|
|
3815
|
+
}
|
|
3337
3816
|
function isBareWindowsName(s) {
|
|
3338
3817
|
if (!s) return false;
|
|
3339
3818
|
if (s.includes("/") || s.includes("\\")) return false;
|
|
@@ -4861,6 +5340,24 @@ import { existsSync as existsSync8, readFileSync as readFileSync9 } from "fs";
|
|
|
4861
5340
|
import { join as join7 } from "path";
|
|
4862
5341
|
var CODE_SYSTEM_PROMPT = `You are Reasonix Code, a coding assistant. You have filesystem tools (read_file, write_file, list_directory, search_files, etc.) rooted at the user's working directory.
|
|
4863
5342
|
|
|
5343
|
+
# Cite or shut up \u2014 non-negotiable
|
|
5344
|
+
|
|
5345
|
+
Every factual claim you make about THIS codebase must be backed by evidence. Reasonix VALIDATES the citations you write \u2014 broken paths or out-of-range lines render in **red strikethrough with \u274C** in front of the user.
|
|
5346
|
+
|
|
5347
|
+
**Positive claims** (a file exists, a function does X, a feature IS implemented) \u2014 append a markdown link to the source:
|
|
5348
|
+
|
|
5349
|
+
- \u2705 Correct: \`The MCP client supports listResources [listResources](src/mcp/client.ts:142).\`
|
|
5350
|
+
- \u274C Wrong: \`The MCP client supports listResources.\` \u2190 no citation, looks authoritative but unverifiable.
|
|
5351
|
+
|
|
5352
|
+
**Negative claims** (X is missing, Y is not implemented, lacks Z, doesn't have W) are the **most common hallucination shape**. They feel safe to write because no citation seems possible \u2014 but that's exactly why you must NOT write them on instinct.
|
|
5353
|
+
|
|
5354
|
+
If you are about to write "X is missing" or "Y is not implemented" \u2014 **STOP**. Call \`search_content\` for the relevant symbol or term FIRST. Only then:
|
|
5355
|
+
|
|
5356
|
+
- If the search returns matches \u2192 you were wrong; correct yourself and cite the matches.
|
|
5357
|
+
- If the search returns nothing \u2192 state the absence with the search query as your evidence: \`No callers of \\\`foo()\\\` found (search_content "foo").\`
|
|
5358
|
+
|
|
5359
|
+
Asserting absence without a search is the #1 way evaluative answers go wrong. Treat the urge to write "missing" as a red flag in your own reasoning.
|
|
5360
|
+
|
|
4864
5361
|
# When to propose a plan (submit_plan)
|
|
4865
5362
|
|
|
4866
5363
|
You have a \`submit_plan\` tool that shows the user a markdown plan and lets them Approve / Refine / Cancel before you execute. Use it proactively when the task is large enough to deserve a review gate:
|
|
@@ -4882,6 +5379,26 @@ The user can ALSO enter "plan mode" via /plan, which is a stronger, explicit con
|
|
|
4882
5379
|
- You MUST call submit_plan before anything will execute. Approve exits plan mode; Refine stays in; Cancel exits without implementing.
|
|
4883
5380
|
|
|
4884
5381
|
|
|
5382
|
+
# Delegating to subagents via Skills (\u{1F9EC})
|
|
5383
|
+
|
|
5384
|
+
The pinned Skills index below lists playbooks you can invoke with \`run_skill\`. Skills marked with **\u{1F9EC}** spawn an **isolated subagent** \u2014 a fresh child loop that runs the playbook in its own context and returns only the final answer. The subagent's tool calls and reasoning never enter your context, so \u{1F9EC} skills are how you keep the main session lean.
|
|
5385
|
+
|
|
5386
|
+
Two built-ins ship by default:
|
|
5387
|
+
- **\u{1F9EC} explore** \u2014 read-only investigation across the codebase. Use when the user says things like "find all places that...", "how does X work across the project", "survey the code for Y". Pass \`arguments\` describing the concrete question.
|
|
5388
|
+
- **\u{1F9EC} research** \u2014 combines web search + code reading. Use for "is X supported by lib Y", "what's the canonical way to Z", "compare our impl to the spec".
|
|
5389
|
+
|
|
5390
|
+
When to delegate (call \`run_skill\` with a \u{1F9EC} skill):
|
|
5391
|
+
- The task would otherwise need >5 file reads or searches.
|
|
5392
|
+
- You only need the conclusion, not the exploration trail.
|
|
5393
|
+
- The work is self-contained (you can describe it in one paragraph).
|
|
5394
|
+
|
|
5395
|
+
When NOT to delegate:
|
|
5396
|
+
- Direct, narrow questions answerable in 1-2 tool calls \u2014 just do them.
|
|
5397
|
+
- Anything where you need to track intermediate results yourself (planning, multi-step edits).
|
|
5398
|
+
- Anything that requires user interaction (subagents can't submit plans or ask you for clarification).
|
|
5399
|
+
|
|
5400
|
+
Always pass a clear, self-contained \`arguments\` \u2014 that text is the **only** context the subagent gets.
|
|
5401
|
+
|
|
4885
5402
|
# When to edit vs. when to explore
|
|
4886
5403
|
|
|
4887
5404
|
Only propose edits when the user explicitly asks you to change, fix, add, remove, refactor, or write something. Do NOT propose edits when the user asks you to:
|
|
@@ -4923,13 +5440,21 @@ Before exploring the filesystem to answer a factual question, check whether the
|
|
|
4923
5440
|
# Exploration
|
|
4924
5441
|
|
|
4925
5442
|
- Skip dependency, build, and VCS directories unless the user explicitly asks. The pinned .gitignore block (if any, below) is your authoritative denylist.
|
|
4926
|
-
- Prefer search_files
|
|
5443
|
+
- Prefer \`search_files\` over \`list_directory\` when you know roughly what you're looking for \u2014 it saves context and avoids enumerating huge trees. Note: \`search_files\` matches file NAMES; for searching file CONTENTS use \`search_content\`.
|
|
5444
|
+
- Available exploration tools: \`read_file\`, \`list_directory\`, \`directory_tree\`, \`search_files\` (filename match), \`search_content\` (content grep \u2014 use for "where is X called", "find all references to Y"), \`get_file_info\`. Don't call \`grep\` or other tools that aren't in this list \u2014 they don't exist as functions.
|
|
5445
|
+
|
|
5446
|
+
# Path conventions
|
|
5447
|
+
|
|
5448
|
+
Two different rules depending on which tool:
|
|
5449
|
+
|
|
5450
|
+
- **Filesystem tools** (\`read_file\`, \`list_directory\`, \`search_files\`, \`edit_file\`, etc.): paths are sandbox-relative. \`/\` means the project root, \`/src/foo.ts\` means \`<project>/src/foo.ts\`. Both relative (\`src/foo.ts\`) and POSIX-absolute (\`/src/foo.ts\`) forms work.
|
|
5451
|
+
- **\`run_command\`**: the command runs in a real OS shell with cwd pinned to the project root. Paths inside the shell command are interpreted by THAT shell, not by us. **Never use leading \`/\` in run_command arguments** \u2014 Windows treats \`/tests\` as drive-root \`F:\\tests\` (non-existent), POSIX shells treat it as filesystem root. Use plain relative paths (\`tests\`, \`./tests\`, \`src/loop.ts\`) instead.
|
|
4927
5452
|
|
|
4928
5453
|
# Style
|
|
4929
5454
|
|
|
4930
5455
|
- Show edits; don't narrate them in prose. "Here's the fix:" is enough.
|
|
4931
5456
|
- One short paragraph explaining *why*, then the blocks.
|
|
4932
|
-
- If you need to explore first (list /
|
|
5457
|
+
- If you need to explore first (list / read / search), do it with tool calls before writing any prose \u2014 silence while exploring is fine.
|
|
4933
5458
|
`;
|
|
4934
5459
|
function codeSystemPrompt(rootDir) {
|
|
4935
5460
|
const withMemory = applyMemoryStack(CODE_SYSTEM_PROMPT, rootDir);
|
|
@@ -5284,6 +5809,7 @@ export {
|
|
|
5284
5809
|
fetchWithRetry,
|
|
5285
5810
|
flattenMcpResult,
|
|
5286
5811
|
flattenSchema,
|
|
5812
|
+
forkRegistryExcluding,
|
|
5287
5813
|
formatCommandResult,
|
|
5288
5814
|
formatHookOutcomeMessage,
|
|
5289
5815
|
formatLogSize,
|
|
@@ -5294,6 +5820,7 @@ export {
|
|
|
5294
5820
|
harvest,
|
|
5295
5821
|
healLoadedMessages,
|
|
5296
5822
|
htmlToText,
|
|
5823
|
+
injectPowerShellUtf8,
|
|
5297
5824
|
inputCostUsd,
|
|
5298
5825
|
inspectMcpServer,
|
|
5299
5826
|
isAllowed,
|
|
@@ -5329,6 +5856,7 @@ export {
|
|
|
5329
5856
|
registerMemoryTools,
|
|
5330
5857
|
registerPlanTool,
|
|
5331
5858
|
registerShellTools,
|
|
5859
|
+
registerSubagentTool,
|
|
5332
5860
|
registerWebTools,
|
|
5333
5861
|
renderMarkdown as renderDiffMarkdown,
|
|
5334
5862
|
renderSummaryTable as renderDiffSummary,
|
|
@@ -5352,6 +5880,7 @@ export {
|
|
|
5352
5880
|
truncateForModel,
|
|
5353
5881
|
webFetch,
|
|
5354
5882
|
webSearch,
|
|
5883
|
+
withUtf8Codepage,
|
|
5355
5884
|
writeConfig,
|
|
5356
5885
|
writeMeta,
|
|
5357
5886
|
writeRecord
|