reasonix 0.4.20 → 0.4.22
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/README.md +250 -294
- package/dist/cli/{chunk-DDIKQZVD.js → chunk-K6MR4SWS.js} +190 -34
- package/dist/cli/chunk-K6MR4SWS.js.map +1 -0
- package/dist/cli/index.js +369 -18
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/{prompt-YEJEJ3IZ.js → prompt-VDN5U3YE.js} +2 -2
- package/dist/index.d.ts +94 -8
- package/dist/index.js +355 -77
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/cli/chunk-DDIKQZVD.js.map +0 -1
- /package/dist/cli/{prompt-YEJEJ3IZ.js.map → prompt-VDN5U3YE.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -47,8 +47,8 @@ function computeWait(attempt, initial, cap, retryAfter) {
|
|
|
47
47
|
}
|
|
48
48
|
function sleep(ms, signal) {
|
|
49
49
|
if (ms <= 0) return Promise.resolve();
|
|
50
|
-
return new Promise((
|
|
51
|
-
const timer = setTimeout(
|
|
50
|
+
return new Promise((resolve7, reject) => {
|
|
51
|
+
const timer = setTimeout(resolve7, ms);
|
|
52
52
|
if (signal) {
|
|
53
53
|
const onAbort = () => {
|
|
54
54
|
clearTimeout(timer);
|
|
@@ -1537,8 +1537,8 @@ var CacheFirstLoop = class {
|
|
|
1537
1537
|
}
|
|
1538
1538
|
);
|
|
1539
1539
|
for (let k = 0; k < budget; k++) {
|
|
1540
|
-
const sample = queue.shift() ?? await new Promise((
|
|
1541
|
-
waiter =
|
|
1540
|
+
const sample = queue.shift() ?? await new Promise((resolve7) => {
|
|
1541
|
+
waiter = resolve7;
|
|
1542
1542
|
});
|
|
1543
1543
|
yield {
|
|
1544
1544
|
turn: this._turn,
|
|
@@ -1953,15 +1953,171 @@ ${mem.content}
|
|
|
1953
1953
|
// src/user-memory.ts
|
|
1954
1954
|
import { createHash as createHash2 } from "crypto";
|
|
1955
1955
|
import {
|
|
1956
|
-
existsSync as
|
|
1956
|
+
existsSync as existsSync4,
|
|
1957
1957
|
mkdirSync as mkdirSync2,
|
|
1958
|
-
readFileSync as
|
|
1959
|
-
readdirSync as
|
|
1958
|
+
readFileSync as readFileSync4,
|
|
1959
|
+
readdirSync as readdirSync3,
|
|
1960
1960
|
unlinkSync as unlinkSync2,
|
|
1961
1961
|
writeFileSync as writeFileSync2
|
|
1962
1962
|
} from "fs";
|
|
1963
|
+
import { homedir as homedir3 } from "os";
|
|
1964
|
+
import { join as join4, resolve as resolve2 } from "path";
|
|
1965
|
+
|
|
1966
|
+
// src/skills.ts
|
|
1967
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
|
|
1963
1968
|
import { homedir as homedir2 } from "os";
|
|
1964
1969
|
import { join as join3, resolve } from "path";
|
|
1970
|
+
var SKILLS_DIRNAME = "skills";
|
|
1971
|
+
var SKILL_FILE = "SKILL.md";
|
|
1972
|
+
var SKILLS_INDEX_MAX_CHARS = 4e3;
|
|
1973
|
+
var VALID_SKILL_NAME = /^[a-zA-Z0-9][a-zA-Z0-9._-]{0,63}$/;
|
|
1974
|
+
function parseFrontmatter(raw) {
|
|
1975
|
+
const lines = raw.split(/\r?\n/);
|
|
1976
|
+
if (lines[0] !== "---") return { data: {}, body: raw };
|
|
1977
|
+
const end = lines.indexOf("---", 1);
|
|
1978
|
+
if (end < 0) return { data: {}, body: raw };
|
|
1979
|
+
const data = {};
|
|
1980
|
+
for (let i = 1; i < end; i++) {
|
|
1981
|
+
const line = lines[i];
|
|
1982
|
+
if (!line) continue;
|
|
1983
|
+
const m = line.match(/^([a-zA-Z_][a-zA-Z0-9_-]*):\s*(.*)$/);
|
|
1984
|
+
if (m?.[1]) data[m[1]] = (m[2] ?? "").trim();
|
|
1985
|
+
}
|
|
1986
|
+
return {
|
|
1987
|
+
data,
|
|
1988
|
+
body: lines.slice(end + 1).join("\n").replace(/^\n+/, "")
|
|
1989
|
+
};
|
|
1990
|
+
}
|
|
1991
|
+
function isValidSkillName(name) {
|
|
1992
|
+
return VALID_SKILL_NAME.test(name);
|
|
1993
|
+
}
|
|
1994
|
+
var SkillStore = class {
|
|
1995
|
+
homeDir;
|
|
1996
|
+
projectRoot;
|
|
1997
|
+
constructor(opts = {}) {
|
|
1998
|
+
this.homeDir = opts.homeDir ?? homedir2();
|
|
1999
|
+
this.projectRoot = opts.projectRoot ? resolve(opts.projectRoot) : void 0;
|
|
2000
|
+
}
|
|
2001
|
+
/** True iff this store was configured with a project root. */
|
|
2002
|
+
hasProjectScope() {
|
|
2003
|
+
return this.projectRoot !== void 0;
|
|
2004
|
+
}
|
|
2005
|
+
/**
|
|
2006
|
+
* Root directories scanned, in priority order. Project scope first
|
|
2007
|
+
* so a per-repo skill overrides a global one with the same name —
|
|
2008
|
+
* users expect the local copy to win when both exist.
|
|
2009
|
+
*/
|
|
2010
|
+
roots() {
|
|
2011
|
+
const out = [];
|
|
2012
|
+
if (this.projectRoot) {
|
|
2013
|
+
out.push({
|
|
2014
|
+
dir: join3(this.projectRoot, ".reasonix", SKILLS_DIRNAME),
|
|
2015
|
+
scope: "project"
|
|
2016
|
+
});
|
|
2017
|
+
}
|
|
2018
|
+
out.push({ dir: join3(this.homeDir, ".reasonix", SKILLS_DIRNAME), scope: "global" });
|
|
2019
|
+
return out;
|
|
2020
|
+
}
|
|
2021
|
+
/**
|
|
2022
|
+
* List every skill visible to this store. On name collisions the
|
|
2023
|
+
* higher-priority root (project over global) wins. Sorted by name
|
|
2024
|
+
* for stable prefix hashing.
|
|
2025
|
+
*/
|
|
2026
|
+
list() {
|
|
2027
|
+
const byName = /* @__PURE__ */ new Map();
|
|
2028
|
+
for (const { dir, scope } of this.roots()) {
|
|
2029
|
+
if (!existsSync3(dir)) continue;
|
|
2030
|
+
let entries;
|
|
2031
|
+
try {
|
|
2032
|
+
entries = readdirSync2(dir, { withFileTypes: true });
|
|
2033
|
+
} catch {
|
|
2034
|
+
continue;
|
|
2035
|
+
}
|
|
2036
|
+
for (const entry of entries) {
|
|
2037
|
+
const skill = this.readEntry(dir, scope, entry);
|
|
2038
|
+
if (!skill) continue;
|
|
2039
|
+
if (!byName.has(skill.name)) byName.set(skill.name, skill);
|
|
2040
|
+
}
|
|
2041
|
+
}
|
|
2042
|
+
return [...byName.values()].sort((a, b) => a.name.localeCompare(b.name));
|
|
2043
|
+
}
|
|
2044
|
+
/** Resolve one skill by name. Returns `null` if not found or malformed. */
|
|
2045
|
+
read(name) {
|
|
2046
|
+
if (!isValidSkillName(name)) return null;
|
|
2047
|
+
for (const { dir, scope } of this.roots()) {
|
|
2048
|
+
if (!existsSync3(dir)) continue;
|
|
2049
|
+
const dirCandidate = join3(dir, name, SKILL_FILE);
|
|
2050
|
+
if (existsSync3(dirCandidate) && statSync2(dirCandidate).isFile()) {
|
|
2051
|
+
return this.parse(dirCandidate, name, scope);
|
|
2052
|
+
}
|
|
2053
|
+
const flatCandidate = join3(dir, `${name}.md`);
|
|
2054
|
+
if (existsSync3(flatCandidate) && statSync2(flatCandidate).isFile()) {
|
|
2055
|
+
return this.parse(flatCandidate, name, scope);
|
|
2056
|
+
}
|
|
2057
|
+
}
|
|
2058
|
+
return null;
|
|
2059
|
+
}
|
|
2060
|
+
readEntry(dir, scope, entry) {
|
|
2061
|
+
if (entry.isDirectory()) {
|
|
2062
|
+
if (!isValidSkillName(entry.name)) return null;
|
|
2063
|
+
const file = join3(dir, entry.name, SKILL_FILE);
|
|
2064
|
+
if (!existsSync3(file)) return null;
|
|
2065
|
+
return this.parse(file, entry.name, scope);
|
|
2066
|
+
}
|
|
2067
|
+
if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
2068
|
+
const stem = entry.name.slice(0, -3);
|
|
2069
|
+
if (!isValidSkillName(stem)) return null;
|
|
2070
|
+
return this.parse(join3(dir, entry.name), stem, scope);
|
|
2071
|
+
}
|
|
2072
|
+
return null;
|
|
2073
|
+
}
|
|
2074
|
+
parse(path, stem, scope) {
|
|
2075
|
+
let raw;
|
|
2076
|
+
try {
|
|
2077
|
+
raw = readFileSync3(path, "utf8");
|
|
2078
|
+
} catch {
|
|
2079
|
+
return null;
|
|
2080
|
+
}
|
|
2081
|
+
const { data, body } = parseFrontmatter(raw);
|
|
2082
|
+
const name = data.name && isValidSkillName(data.name) ? data.name : stem;
|
|
2083
|
+
return {
|
|
2084
|
+
name,
|
|
2085
|
+
description: (data.description ?? "").trim(),
|
|
2086
|
+
body: body.trim(),
|
|
2087
|
+
scope,
|
|
2088
|
+
path,
|
|
2089
|
+
allowedTools: data["allowed-tools"]
|
|
2090
|
+
};
|
|
2091
|
+
}
|
|
2092
|
+
};
|
|
2093
|
+
function skillIndexLine(s) {
|
|
2094
|
+
const safeDesc = s.description.replace(/\n/g, " ").trim();
|
|
2095
|
+
const max = 130 - s.name.length;
|
|
2096
|
+
const clipped = safeDesc.length > max ? `${safeDesc.slice(0, Math.max(1, max - 1))}\u2026` : safeDesc;
|
|
2097
|
+
return clipped ? `- ${s.name} \u2014 ${clipped}` : `- ${s.name}`;
|
|
2098
|
+
}
|
|
2099
|
+
function applySkillsIndex(basePrompt, opts = {}) {
|
|
2100
|
+
const store = new SkillStore(opts);
|
|
2101
|
+
const skills = store.list().filter((s) => s.description);
|
|
2102
|
+
if (skills.length === 0) return basePrompt;
|
|
2103
|
+
const lines = skills.map(skillIndexLine);
|
|
2104
|
+
const joined = lines.join("\n");
|
|
2105
|
+
const truncated = joined.length > SKILLS_INDEX_MAX_CHARS ? `${joined.slice(0, SKILLS_INDEX_MAX_CHARS)}
|
|
2106
|
+
\u2026 (truncated ${joined.length - SKILLS_INDEX_MAX_CHARS} chars)` : joined;
|
|
2107
|
+
return [
|
|
2108
|
+
basePrompt,
|
|
2109
|
+
"",
|
|
2110
|
+
"# Skills \u2014 user-defined prompt packs",
|
|
2111
|
+
"",
|
|
2112
|
+
'One-liner index. Each skill is a self-contained instruction block (plus optional tool hints) the user or an earlier session saved. To load the full body, call `run_skill({ name: "<skill-name>" })` \u2014 the body is NOT in this prompt, only the name and description are. The user can also invoke a skill directly as `/skill <name>`.',
|
|
2113
|
+
"",
|
|
2114
|
+
"```",
|
|
2115
|
+
truncated,
|
|
2116
|
+
"```"
|
|
2117
|
+
].join("\n");
|
|
2118
|
+
}
|
|
2119
|
+
|
|
2120
|
+
// src/user-memory.ts
|
|
1965
2121
|
var USER_MEMORY_DIR = "memory";
|
|
1966
2122
|
var MEMORY_INDEX_FILE = "MEMORY.md";
|
|
1967
2123
|
var MEMORY_INDEX_MAX_CHARS = 4e3;
|
|
@@ -1976,22 +2132,22 @@ function sanitizeMemoryName(raw) {
|
|
|
1976
2132
|
return trimmed;
|
|
1977
2133
|
}
|
|
1978
2134
|
function projectHash(rootDir) {
|
|
1979
|
-
const abs =
|
|
2135
|
+
const abs = resolve2(rootDir);
|
|
1980
2136
|
return createHash2("sha1").update(abs).digest("hex").slice(0, 16);
|
|
1981
2137
|
}
|
|
1982
2138
|
function scopeDir(opts) {
|
|
1983
2139
|
if (opts.scope === "global") {
|
|
1984
|
-
return
|
|
2140
|
+
return join4(opts.homeDir, USER_MEMORY_DIR, "global");
|
|
1985
2141
|
}
|
|
1986
2142
|
if (!opts.projectRoot) {
|
|
1987
2143
|
throw new Error("scope=project requires a projectRoot on MemoryStore");
|
|
1988
2144
|
}
|
|
1989
|
-
return
|
|
2145
|
+
return join4(opts.homeDir, USER_MEMORY_DIR, projectHash(opts.projectRoot));
|
|
1990
2146
|
}
|
|
1991
2147
|
function ensureDir(p) {
|
|
1992
|
-
if (!
|
|
2148
|
+
if (!existsSync4(p)) mkdirSync2(p, { recursive: true });
|
|
1993
2149
|
}
|
|
1994
|
-
function
|
|
2150
|
+
function parseFrontmatter2(raw) {
|
|
1995
2151
|
const lines = raw.split(/\r?\n/);
|
|
1996
2152
|
if (lines[0] !== "---") return { data: {}, body: raw };
|
|
1997
2153
|
const end = lines.indexOf("---", 1);
|
|
@@ -2034,8 +2190,8 @@ var MemoryStore = class {
|
|
|
2034
2190
|
homeDir;
|
|
2035
2191
|
projectRoot;
|
|
2036
2192
|
constructor(opts = {}) {
|
|
2037
|
-
this.homeDir = opts.homeDir ??
|
|
2038
|
-
this.projectRoot = opts.projectRoot ?
|
|
2193
|
+
this.homeDir = opts.homeDir ?? join4(homedir3(), ".reasonix");
|
|
2194
|
+
this.projectRoot = opts.projectRoot ? resolve2(opts.projectRoot) : void 0;
|
|
2039
2195
|
}
|
|
2040
2196
|
/** Directory this store writes `scope` files into, creating it if needed. */
|
|
2041
2197
|
dir(scope) {
|
|
@@ -2045,7 +2201,7 @@ var MemoryStore = class {
|
|
|
2045
2201
|
}
|
|
2046
2202
|
/** Absolute path to a memory file (no existence check). */
|
|
2047
2203
|
pathFor(scope, name) {
|
|
2048
|
-
return
|
|
2204
|
+
return join4(this.dir(scope), `${sanitizeMemoryName(name)}.md`);
|
|
2049
2205
|
}
|
|
2050
2206
|
/** True iff this store is configured with a project scope available. */
|
|
2051
2207
|
hasProjectScope() {
|
|
@@ -2057,14 +2213,14 @@ var MemoryStore = class {
|
|
|
2057
2213
|
*/
|
|
2058
2214
|
loadIndex(scope) {
|
|
2059
2215
|
if (scope === "project" && !this.projectRoot) return null;
|
|
2060
|
-
const file =
|
|
2216
|
+
const file = join4(
|
|
2061
2217
|
scopeDir({ homeDir: this.homeDir, scope, projectRoot: this.projectRoot }),
|
|
2062
2218
|
MEMORY_INDEX_FILE
|
|
2063
2219
|
);
|
|
2064
|
-
if (!
|
|
2220
|
+
if (!existsSync4(file)) return null;
|
|
2065
2221
|
let raw;
|
|
2066
2222
|
try {
|
|
2067
|
-
raw =
|
|
2223
|
+
raw = readFileSync4(file, "utf8");
|
|
2068
2224
|
} catch {
|
|
2069
2225
|
return null;
|
|
2070
2226
|
}
|
|
@@ -2079,11 +2235,11 @@ var MemoryStore = class {
|
|
|
2079
2235
|
/** Read one memory file's body (frontmatter stripped). Throws if missing. */
|
|
2080
2236
|
read(scope, name) {
|
|
2081
2237
|
const file = this.pathFor(scope, name);
|
|
2082
|
-
if (!
|
|
2238
|
+
if (!existsSync4(file)) {
|
|
2083
2239
|
throw new Error(`memory not found: scope=${scope} name=${name}`);
|
|
2084
2240
|
}
|
|
2085
|
-
const raw =
|
|
2086
|
-
const { data, body } =
|
|
2241
|
+
const raw = readFileSync4(file, "utf8");
|
|
2242
|
+
const { data, body } = parseFrontmatter2(raw);
|
|
2087
2243
|
return {
|
|
2088
2244
|
name: data.name ?? name,
|
|
2089
2245
|
type: data.type ?? "project",
|
|
@@ -2103,10 +2259,10 @@ var MemoryStore = class {
|
|
|
2103
2259
|
const scopes = this.projectRoot ? ["global", "project"] : ["global"];
|
|
2104
2260
|
for (const scope of scopes) {
|
|
2105
2261
|
const dir = scopeDir({ homeDir: this.homeDir, scope, projectRoot: this.projectRoot });
|
|
2106
|
-
if (!
|
|
2262
|
+
if (!existsSync4(dir)) continue;
|
|
2107
2263
|
let entries;
|
|
2108
2264
|
try {
|
|
2109
|
-
entries =
|
|
2265
|
+
entries = readdirSync3(dir);
|
|
2110
2266
|
} catch {
|
|
2111
2267
|
continue;
|
|
2112
2268
|
}
|
|
@@ -2144,7 +2300,7 @@ var MemoryStore = class {
|
|
|
2144
2300
|
createdAt: todayIso()
|
|
2145
2301
|
};
|
|
2146
2302
|
const dir = this.dir(input.scope);
|
|
2147
|
-
const file =
|
|
2303
|
+
const file = join4(dir, `${name}.md`);
|
|
2148
2304
|
const content = `${formatFrontmatter(entry)}${body}
|
|
2149
2305
|
`;
|
|
2150
2306
|
writeFileSync2(file, content, "utf8");
|
|
@@ -2157,7 +2313,7 @@ var MemoryStore = class {
|
|
|
2157
2313
|
throw new Error("cannot delete project-scoped memory: no projectRoot configured");
|
|
2158
2314
|
}
|
|
2159
2315
|
const file = this.pathFor(scope, rawName);
|
|
2160
|
-
if (!
|
|
2316
|
+
if (!existsSync4(file)) return false;
|
|
2161
2317
|
unlinkSync2(file);
|
|
2162
2318
|
this.regenerateIndex(scope);
|
|
2163
2319
|
return true;
|
|
@@ -2170,17 +2326,17 @@ var MemoryStore = class {
|
|
|
2170
2326
|
*/
|
|
2171
2327
|
regenerateIndex(scope) {
|
|
2172
2328
|
const dir = scopeDir({ homeDir: this.homeDir, scope, projectRoot: this.projectRoot });
|
|
2173
|
-
if (!
|
|
2329
|
+
if (!existsSync4(dir)) return;
|
|
2174
2330
|
let files;
|
|
2175
2331
|
try {
|
|
2176
|
-
files =
|
|
2332
|
+
files = readdirSync3(dir);
|
|
2177
2333
|
} catch {
|
|
2178
2334
|
return;
|
|
2179
2335
|
}
|
|
2180
2336
|
const mdFiles = files.filter((f) => f !== MEMORY_INDEX_FILE && f.endsWith(".md")).sort((a, b) => a.localeCompare(b));
|
|
2181
|
-
const indexPath =
|
|
2337
|
+
const indexPath = join4(dir, MEMORY_INDEX_FILE);
|
|
2182
2338
|
if (mdFiles.length === 0) {
|
|
2183
|
-
if (
|
|
2339
|
+
if (existsSync4(indexPath)) unlinkSync2(indexPath);
|
|
2184
2340
|
return;
|
|
2185
2341
|
}
|
|
2186
2342
|
const lines = [];
|
|
@@ -2232,7 +2388,8 @@ function applyUserMemory(basePrompt, opts = {}) {
|
|
|
2232
2388
|
}
|
|
2233
2389
|
function applyMemoryStack(basePrompt, rootDir) {
|
|
2234
2390
|
const withProject = applyProjectMemory(basePrompt, rootDir);
|
|
2235
|
-
|
|
2391
|
+
const withMemory = applyUserMemory(withProject, { projectRoot: rootDir });
|
|
2392
|
+
return applySkillsIndex(withMemory, { projectRoot: rootDir });
|
|
2236
2393
|
}
|
|
2237
2394
|
|
|
2238
2395
|
// src/tools/filesystem.ts
|
|
@@ -2743,7 +2900,7 @@ function registerPlanTool(registry, opts = {}) {
|
|
|
2743
2900
|
|
|
2744
2901
|
// src/tools/shell.ts
|
|
2745
2902
|
import { spawn } from "child_process";
|
|
2746
|
-
import { existsSync as
|
|
2903
|
+
import { existsSync as existsSync5, statSync as statSync3 } from "fs";
|
|
2747
2904
|
import * as pathMod2 from "path";
|
|
2748
2905
|
var DEFAULT_TIMEOUT_SEC = 60;
|
|
2749
2906
|
var DEFAULT_MAX_OUTPUT_CHARS = 32e3;
|
|
@@ -2863,7 +3020,7 @@ async function runCommand(cmd, opts) {
|
|
|
2863
3020
|
};
|
|
2864
3021
|
const { bin, args, spawnOverrides } = prepareSpawn(argv);
|
|
2865
3022
|
const effectiveSpawnOpts = { ...spawnOpts, ...spawnOverrides };
|
|
2866
|
-
return await new Promise((
|
|
3023
|
+
return await new Promise((resolve7, reject) => {
|
|
2867
3024
|
let child;
|
|
2868
3025
|
try {
|
|
2869
3026
|
child = spawn(bin, args, effectiveSpawnOpts);
|
|
@@ -2896,7 +3053,7 @@ async function runCommand(cmd, opts) {
|
|
|
2896
3053
|
const output = buf.length > maxChars ? `${buf.slice(0, maxChars)}
|
|
2897
3054
|
|
|
2898
3055
|
[\u2026 truncated ${buf.length - maxChars} chars \u2026]` : buf;
|
|
2899
|
-
|
|
3056
|
+
resolve7({ exitCode: code, output, timedOut });
|
|
2900
3057
|
});
|
|
2901
3058
|
});
|
|
2902
3059
|
}
|
|
@@ -2921,7 +3078,7 @@ function resolveExecutable(cmd, opts = {}) {
|
|
|
2921
3078
|
}
|
|
2922
3079
|
function defaultIsFile(full) {
|
|
2923
3080
|
try {
|
|
2924
|
-
return
|
|
3081
|
+
return existsSync5(full) && statSync3(full).isFile();
|
|
2925
3082
|
} catch {
|
|
2926
3083
|
return false;
|
|
2927
3084
|
}
|
|
@@ -2946,8 +3103,23 @@ function prepareSpawn(argv, opts = {}) {
|
|
|
2946
3103
|
spawnOverrides: { windowsVerbatimArguments: true }
|
|
2947
3104
|
};
|
|
2948
3105
|
}
|
|
3106
|
+
if (isBareWindowsName(resolved) && resolved === head) {
|
|
3107
|
+
const cmdline = [head, ...tail].map(quoteForCmdExe).join(" ");
|
|
3108
|
+
return {
|
|
3109
|
+
bin: "cmd.exe",
|
|
3110
|
+
args: ["/d", "/s", "/c", cmdline],
|
|
3111
|
+
spawnOverrides: { windowsVerbatimArguments: true }
|
|
3112
|
+
};
|
|
3113
|
+
}
|
|
2949
3114
|
return { bin: resolved, args: [...tail], spawnOverrides: {} };
|
|
2950
3115
|
}
|
|
3116
|
+
function isBareWindowsName(s) {
|
|
3117
|
+
if (!s) return false;
|
|
3118
|
+
if (s.includes("/") || s.includes("\\")) return false;
|
|
3119
|
+
if (pathMod2.isAbsolute(s)) return false;
|
|
3120
|
+
if (pathMod2.extname(s)) return false;
|
|
3121
|
+
return true;
|
|
3122
|
+
}
|
|
2951
3123
|
function quoteForCmdExe(arg) {
|
|
2952
3124
|
if (arg === "") return '""';
|
|
2953
3125
|
if (!/[\s"&|<>^%(),;!]/.test(arg)) return arg;
|
|
@@ -2967,7 +3139,10 @@ function registerShellTools(registry, opts) {
|
|
|
2967
3139
|
const rootDir = pathMod2.resolve(opts.rootDir);
|
|
2968
3140
|
const timeoutSec = opts.timeoutSec ?? DEFAULT_TIMEOUT_SEC;
|
|
2969
3141
|
const maxOutputChars = opts.maxOutputChars ?? DEFAULT_MAX_OUTPUT_CHARS;
|
|
2970
|
-
const
|
|
3142
|
+
const getExtraAllowed = typeof opts.extraAllowed === "function" ? opts.extraAllowed : (() => {
|
|
3143
|
+
const snapshot = opts.extraAllowed ?? [];
|
|
3144
|
+
return () => snapshot;
|
|
3145
|
+
})();
|
|
2971
3146
|
const allowAll = opts.allowAll ?? false;
|
|
2972
3147
|
registry.register({
|
|
2973
3148
|
name: "run_command",
|
|
@@ -2980,7 +3155,7 @@ function registerShellTools(registry, opts) {
|
|
|
2980
3155
|
if (allowAll) return true;
|
|
2981
3156
|
const cmd = typeof args?.command === "string" ? args.command.trim() : "";
|
|
2982
3157
|
if (!cmd) return false;
|
|
2983
|
-
return isAllowed(cmd,
|
|
3158
|
+
return isAllowed(cmd, getExtraAllowed());
|
|
2984
3159
|
},
|
|
2985
3160
|
parameters: {
|
|
2986
3161
|
type: "object",
|
|
@@ -2999,7 +3174,7 @@ function registerShellTools(registry, opts) {
|
|
|
2999
3174
|
fn: async (args, ctx) => {
|
|
3000
3175
|
const cmd = args.command.trim();
|
|
3001
3176
|
if (!cmd) throw new Error("run_command: empty command");
|
|
3002
|
-
if (!allowAll && !isAllowed(cmd,
|
|
3177
|
+
if (!allowAll && !isAllowed(cmd, getExtraAllowed())) {
|
|
3003
3178
|
throw new NeedsConfirmationError(cmd);
|
|
3004
3179
|
}
|
|
3005
3180
|
const effectiveTimeout = Math.max(1, Math.min(600, args.timeoutSec ?? timeoutSec));
|
|
@@ -3206,12 +3381,12 @@ ${i + 1}. ${r.title}`);
|
|
|
3206
3381
|
}
|
|
3207
3382
|
|
|
3208
3383
|
// src/env.ts
|
|
3209
|
-
import { readFileSync as
|
|
3210
|
-
import { resolve as
|
|
3384
|
+
import { readFileSync as readFileSync5 } from "fs";
|
|
3385
|
+
import { resolve as resolve5 } from "path";
|
|
3211
3386
|
function loadDotenv(path = ".env") {
|
|
3212
3387
|
let raw;
|
|
3213
3388
|
try {
|
|
3214
|
-
raw =
|
|
3389
|
+
raw = readFileSync5(resolve5(process.cwd(), path), "utf8");
|
|
3215
3390
|
} catch {
|
|
3216
3391
|
return;
|
|
3217
3392
|
}
|
|
@@ -3230,7 +3405,7 @@ function loadDotenv(path = ".env") {
|
|
|
3230
3405
|
}
|
|
3231
3406
|
|
|
3232
3407
|
// src/transcript.ts
|
|
3233
|
-
import { createWriteStream, readFileSync as
|
|
3408
|
+
import { createWriteStream, readFileSync as readFileSync6 } from "fs";
|
|
3234
3409
|
function recordFromLoopEvent(ev, extra) {
|
|
3235
3410
|
const rec = {
|
|
3236
3411
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -3281,7 +3456,7 @@ function openTranscriptFile(path, meta) {
|
|
|
3281
3456
|
return stream;
|
|
3282
3457
|
}
|
|
3283
3458
|
function readTranscript(path) {
|
|
3284
|
-
const raw =
|
|
3459
|
+
const raw = readFileSync6(path, "utf8");
|
|
3285
3460
|
return parseTranscript(raw);
|
|
3286
3461
|
}
|
|
3287
3462
|
function isPlanStateEmptyShape(s) {
|
|
@@ -3893,7 +4068,7 @@ var McpClient = class {
|
|
|
3893
4068
|
const id = this.nextId++;
|
|
3894
4069
|
const frame = { jsonrpc: "2.0", id, method, params };
|
|
3895
4070
|
let abortHandler = null;
|
|
3896
|
-
const promise = new Promise((
|
|
4071
|
+
const promise = new Promise((resolve7, reject) => {
|
|
3897
4072
|
const timeout = setTimeout(() => {
|
|
3898
4073
|
this.pending.delete(id);
|
|
3899
4074
|
if (abortHandler && signal) signal.removeEventListener("abort", abortHandler);
|
|
@@ -3902,7 +4077,7 @@ var McpClient = class {
|
|
|
3902
4077
|
);
|
|
3903
4078
|
}, this.requestTimeoutMs);
|
|
3904
4079
|
this.pending.set(id, {
|
|
3905
|
-
resolve:
|
|
4080
|
+
resolve: resolve7,
|
|
3906
4081
|
reject,
|
|
3907
4082
|
timeout
|
|
3908
4083
|
});
|
|
@@ -4025,12 +4200,12 @@ var StdioTransport = class {
|
|
|
4025
4200
|
}
|
|
4026
4201
|
async send(message) {
|
|
4027
4202
|
if (this.closed) throw new Error("MCP transport is closed");
|
|
4028
|
-
return new Promise((
|
|
4203
|
+
return new Promise((resolve7, reject) => {
|
|
4029
4204
|
const line = `${JSON.stringify(message)}
|
|
4030
4205
|
`;
|
|
4031
4206
|
this.child.stdin.write(line, "utf8", (err) => {
|
|
4032
4207
|
if (err) reject(err);
|
|
4033
|
-
else
|
|
4208
|
+
else resolve7();
|
|
4034
4209
|
});
|
|
4035
4210
|
});
|
|
4036
4211
|
}
|
|
@@ -4041,8 +4216,8 @@ var StdioTransport = class {
|
|
|
4041
4216
|
continue;
|
|
4042
4217
|
}
|
|
4043
4218
|
if (this.closed) return;
|
|
4044
|
-
const next = await new Promise((
|
|
4045
|
-
this.waiters.push(
|
|
4219
|
+
const next = await new Promise((resolve7) => {
|
|
4220
|
+
this.waiters.push(resolve7);
|
|
4046
4221
|
});
|
|
4047
4222
|
if (next === null) return;
|
|
4048
4223
|
yield next;
|
|
@@ -4108,8 +4283,8 @@ var SseTransport = class {
|
|
|
4108
4283
|
constructor(opts) {
|
|
4109
4284
|
this.url = opts.url;
|
|
4110
4285
|
this.headers = opts.headers ?? {};
|
|
4111
|
-
this.endpointReady = new Promise((
|
|
4112
|
-
this.resolveEndpoint =
|
|
4286
|
+
this.endpointReady = new Promise((resolve7, reject) => {
|
|
4287
|
+
this.resolveEndpoint = resolve7;
|
|
4113
4288
|
this.rejectEndpoint = reject;
|
|
4114
4289
|
});
|
|
4115
4290
|
this.endpointReady.catch(() => void 0);
|
|
@@ -4136,8 +4311,8 @@ var SseTransport = class {
|
|
|
4136
4311
|
continue;
|
|
4137
4312
|
}
|
|
4138
4313
|
if (this.closed) return;
|
|
4139
|
-
const next = await new Promise((
|
|
4140
|
-
this.waiters.push(
|
|
4314
|
+
const next = await new Promise((resolve7) => {
|
|
4315
|
+
this.waiters.push(resolve7);
|
|
4141
4316
|
});
|
|
4142
4317
|
if (next === null) return;
|
|
4143
4318
|
yield next;
|
|
@@ -4336,8 +4511,8 @@ async function trySection(load) {
|
|
|
4336
4511
|
}
|
|
4337
4512
|
|
|
4338
4513
|
// src/code/edit-blocks.ts
|
|
4339
|
-
import { existsSync as
|
|
4340
|
-
import { dirname as dirname3, resolve as
|
|
4514
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync3, readFileSync as readFileSync7, unlinkSync as unlinkSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
4515
|
+
import { dirname as dirname3, resolve as resolve6 } from "path";
|
|
4341
4516
|
var BLOCK_RE = /^(\S[^\n]*)\n<{7} SEARCH\n([\s\S]*?)\n?={7}\n([\s\S]*?)\n?>{7} REPLACE/gm;
|
|
4342
4517
|
function parseEditBlocks(text) {
|
|
4343
4518
|
const out = [];
|
|
@@ -4355,8 +4530,8 @@ function parseEditBlocks(text) {
|
|
|
4355
4530
|
return out;
|
|
4356
4531
|
}
|
|
4357
4532
|
function applyEditBlock(block, rootDir) {
|
|
4358
|
-
const absRoot =
|
|
4359
|
-
const absTarget =
|
|
4533
|
+
const absRoot = resolve6(rootDir);
|
|
4534
|
+
const absTarget = resolve6(absRoot, block.path);
|
|
4360
4535
|
if (absTarget !== absRoot && !absTarget.startsWith(`${absRoot}${sep()}`)) {
|
|
4361
4536
|
return {
|
|
4362
4537
|
path: block.path,
|
|
@@ -4365,7 +4540,7 @@ function applyEditBlock(block, rootDir) {
|
|
|
4365
4540
|
};
|
|
4366
4541
|
}
|
|
4367
4542
|
const searchEmpty = block.search.length === 0;
|
|
4368
|
-
const exists =
|
|
4543
|
+
const exists = existsSync6(absTarget);
|
|
4369
4544
|
try {
|
|
4370
4545
|
if (!exists) {
|
|
4371
4546
|
if (!searchEmpty) {
|
|
@@ -4379,7 +4554,7 @@ function applyEditBlock(block, rootDir) {
|
|
|
4379
4554
|
writeFileSync3(absTarget, block.replace, "utf8");
|
|
4380
4555
|
return { path: block.path, status: "created" };
|
|
4381
4556
|
}
|
|
4382
|
-
const content =
|
|
4557
|
+
const content = readFileSync7(absTarget, "utf8");
|
|
4383
4558
|
if (searchEmpty) {
|
|
4384
4559
|
return {
|
|
4385
4560
|
path: block.path,
|
|
@@ -4406,19 +4581,19 @@ function applyEditBlocks(blocks, rootDir) {
|
|
|
4406
4581
|
return blocks.map((b) => applyEditBlock(b, rootDir));
|
|
4407
4582
|
}
|
|
4408
4583
|
function snapshotBeforeEdits(blocks, rootDir) {
|
|
4409
|
-
const absRoot =
|
|
4584
|
+
const absRoot = resolve6(rootDir);
|
|
4410
4585
|
const seen = /* @__PURE__ */ new Set();
|
|
4411
4586
|
const snapshots = [];
|
|
4412
4587
|
for (const b of blocks) {
|
|
4413
4588
|
if (seen.has(b.path)) continue;
|
|
4414
4589
|
seen.add(b.path);
|
|
4415
|
-
const abs =
|
|
4416
|
-
if (!
|
|
4590
|
+
const abs = resolve6(absRoot, b.path);
|
|
4591
|
+
if (!existsSync6(abs)) {
|
|
4417
4592
|
snapshots.push({ path: b.path, prevContent: null });
|
|
4418
4593
|
continue;
|
|
4419
4594
|
}
|
|
4420
4595
|
try {
|
|
4421
|
-
snapshots.push({ path: b.path, prevContent:
|
|
4596
|
+
snapshots.push({ path: b.path, prevContent: readFileSync7(abs, "utf8") });
|
|
4422
4597
|
} catch {
|
|
4423
4598
|
snapshots.push({ path: b.path, prevContent: null });
|
|
4424
4599
|
}
|
|
@@ -4426,9 +4601,9 @@ function snapshotBeforeEdits(blocks, rootDir) {
|
|
|
4426
4601
|
return snapshots;
|
|
4427
4602
|
}
|
|
4428
4603
|
function restoreSnapshots(snapshots, rootDir) {
|
|
4429
|
-
const absRoot =
|
|
4604
|
+
const absRoot = resolve6(rootDir);
|
|
4430
4605
|
return snapshots.map((snap) => {
|
|
4431
|
-
const abs =
|
|
4606
|
+
const abs = resolve6(absRoot, snap.path);
|
|
4432
4607
|
if (abs !== absRoot && !abs.startsWith(`${absRoot}${sep()}`)) {
|
|
4433
4608
|
return {
|
|
4434
4609
|
path: snap.path,
|
|
@@ -4438,7 +4613,7 @@ function restoreSnapshots(snapshots, rootDir) {
|
|
|
4438
4613
|
}
|
|
4439
4614
|
try {
|
|
4440
4615
|
if (snap.prevContent === null) {
|
|
4441
|
-
if (
|
|
4616
|
+
if (existsSync6(abs)) unlinkSync3(abs);
|
|
4442
4617
|
return {
|
|
4443
4618
|
path: snap.path,
|
|
4444
4619
|
status: "applied",
|
|
@@ -4461,8 +4636,8 @@ function sep() {
|
|
|
4461
4636
|
}
|
|
4462
4637
|
|
|
4463
4638
|
// src/code/prompt.ts
|
|
4464
|
-
import { existsSync as
|
|
4465
|
-
import { join as
|
|
4639
|
+
import { existsSync as existsSync7, readFileSync as readFileSync8 } from "fs";
|
|
4640
|
+
import { join as join6 } from "path";
|
|
4466
4641
|
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.
|
|
4467
4642
|
|
|
4468
4643
|
# When to propose a plan (submit_plan)
|
|
@@ -4537,11 +4712,11 @@ Before exploring the filesystem to answer a factual question, check whether the
|
|
|
4537
4712
|
`;
|
|
4538
4713
|
function codeSystemPrompt(rootDir) {
|
|
4539
4714
|
const withMemory = applyMemoryStack(CODE_SYSTEM_PROMPT, rootDir);
|
|
4540
|
-
const gitignorePath =
|
|
4541
|
-
if (!
|
|
4715
|
+
const gitignorePath = join6(rootDir, ".gitignore");
|
|
4716
|
+
if (!existsSync7(gitignorePath)) return withMemory;
|
|
4542
4717
|
let content;
|
|
4543
4718
|
try {
|
|
4544
|
-
content =
|
|
4719
|
+
content = readFileSync8(gitignorePath, "utf8");
|
|
4545
4720
|
} catch {
|
|
4546
4721
|
return withMemory;
|
|
4547
4722
|
}
|
|
@@ -4561,15 +4736,15 @@ ${truncated}
|
|
|
4561
4736
|
}
|
|
4562
4737
|
|
|
4563
4738
|
// src/config.ts
|
|
4564
|
-
import { chmodSync as chmodSync2, mkdirSync as mkdirSync4, readFileSync as
|
|
4565
|
-
import { homedir as
|
|
4566
|
-
import { dirname as dirname4, join as
|
|
4739
|
+
import { chmodSync as chmodSync2, mkdirSync as mkdirSync4, readFileSync as readFileSync9, writeFileSync as writeFileSync4 } from "fs";
|
|
4740
|
+
import { homedir as homedir4 } from "os";
|
|
4741
|
+
import { dirname as dirname4, join as join7 } from "path";
|
|
4567
4742
|
function defaultConfigPath() {
|
|
4568
|
-
return
|
|
4743
|
+
return join7(homedir4(), ".reasonix", "config.json");
|
|
4569
4744
|
}
|
|
4570
4745
|
function readConfig(path = defaultConfigPath()) {
|
|
4571
4746
|
try {
|
|
4572
|
-
const raw =
|
|
4747
|
+
const raw = readFileSync9(path, "utf8");
|
|
4573
4748
|
const parsed = JSON.parse(raw);
|
|
4574
4749
|
if (parsed && typeof parsed === "object") return parsed;
|
|
4575
4750
|
} catch {
|
|
@@ -4603,8 +4778,106 @@ function redactKey(key) {
|
|
|
4603
4778
|
return `${key.slice(0, 6)}\u2026${key.slice(-4)}`;
|
|
4604
4779
|
}
|
|
4605
4780
|
|
|
4606
|
-
// src/
|
|
4607
|
-
|
|
4781
|
+
// src/version.ts
|
|
4782
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync5, readFileSync as readFileSync10, writeFileSync as writeFileSync5 } from "fs";
|
|
4783
|
+
import { homedir as homedir5 } from "os";
|
|
4784
|
+
import { dirname as dirname5, join as join8 } from "path";
|
|
4785
|
+
import { fileURLToPath } from "url";
|
|
4786
|
+
var REGISTRY_URL = "https://registry.npmjs.org/reasonix/latest";
|
|
4787
|
+
var LATEST_CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
4788
|
+
var LATEST_FETCH_TIMEOUT_MS = 2e3;
|
|
4789
|
+
function readPackageVersion() {
|
|
4790
|
+
try {
|
|
4791
|
+
let dir = dirname5(fileURLToPath(import.meta.url));
|
|
4792
|
+
for (let i = 0; i < 6; i++) {
|
|
4793
|
+
const p = join8(dir, "package.json");
|
|
4794
|
+
if (existsSync8(p)) {
|
|
4795
|
+
const pkg = JSON.parse(readFileSync10(p, "utf8"));
|
|
4796
|
+
if (pkg?.name === "reasonix" && typeof pkg.version === "string") {
|
|
4797
|
+
return pkg.version;
|
|
4798
|
+
}
|
|
4799
|
+
}
|
|
4800
|
+
const parent = dirname5(dir);
|
|
4801
|
+
if (parent === dir) break;
|
|
4802
|
+
dir = parent;
|
|
4803
|
+
}
|
|
4804
|
+
} catch {
|
|
4805
|
+
}
|
|
4806
|
+
return "0.0.0-dev";
|
|
4807
|
+
}
|
|
4808
|
+
var VERSION = readPackageVersion();
|
|
4809
|
+
function cachePath(homeDirOverride) {
|
|
4810
|
+
return join8(homeDirOverride ?? homedir5(), ".reasonix", "version-cache.json");
|
|
4811
|
+
}
|
|
4812
|
+
function readCache(homeDirOverride) {
|
|
4813
|
+
try {
|
|
4814
|
+
const raw = readFileSync10(cachePath(homeDirOverride), "utf8");
|
|
4815
|
+
const parsed = JSON.parse(raw);
|
|
4816
|
+
if (parsed && typeof parsed.version === "string" && typeof parsed.checkedAt === "number") {
|
|
4817
|
+
return parsed;
|
|
4818
|
+
}
|
|
4819
|
+
} catch {
|
|
4820
|
+
}
|
|
4821
|
+
return null;
|
|
4822
|
+
}
|
|
4823
|
+
function writeCache(entry, homeDirOverride) {
|
|
4824
|
+
try {
|
|
4825
|
+
const p = cachePath(homeDirOverride);
|
|
4826
|
+
mkdirSync5(dirname5(p), { recursive: true });
|
|
4827
|
+
writeFileSync5(p, JSON.stringify(entry), "utf8");
|
|
4828
|
+
} catch {
|
|
4829
|
+
}
|
|
4830
|
+
}
|
|
4831
|
+
async function getLatestVersion(opts = {}) {
|
|
4832
|
+
const ttl = opts.ttlMs ?? LATEST_CACHE_TTL_MS;
|
|
4833
|
+
if (!opts.force) {
|
|
4834
|
+
const cached = readCache(opts.homeDir);
|
|
4835
|
+
if (cached && Date.now() - cached.checkedAt < ttl) return cached.version;
|
|
4836
|
+
}
|
|
4837
|
+
const fetchImpl = opts.fetchImpl ?? globalThis.fetch;
|
|
4838
|
+
if (!fetchImpl) return null;
|
|
4839
|
+
const url = opts.registryUrl ?? REGISTRY_URL;
|
|
4840
|
+
const timeout = opts.timeoutMs ?? LATEST_FETCH_TIMEOUT_MS;
|
|
4841
|
+
const controller = new AbortController();
|
|
4842
|
+
const timer = setTimeout(() => controller.abort(), timeout);
|
|
4843
|
+
try {
|
|
4844
|
+
const res = await fetchImpl(url, {
|
|
4845
|
+
signal: controller.signal,
|
|
4846
|
+
headers: { accept: "application/json" }
|
|
4847
|
+
});
|
|
4848
|
+
if (!res.ok) return null;
|
|
4849
|
+
const body = await res.json();
|
|
4850
|
+
if (typeof body.version !== "string") return null;
|
|
4851
|
+
writeCache({ version: body.version, checkedAt: Date.now() }, opts.homeDir);
|
|
4852
|
+
return body.version;
|
|
4853
|
+
} catch {
|
|
4854
|
+
return null;
|
|
4855
|
+
} finally {
|
|
4856
|
+
clearTimeout(timer);
|
|
4857
|
+
}
|
|
4858
|
+
}
|
|
4859
|
+
function compareVersions(a, b) {
|
|
4860
|
+
const [aCore = "0", aPre = ""] = a.split("-", 2);
|
|
4861
|
+
const [bCore = "0", bPre = ""] = b.split("-", 2);
|
|
4862
|
+
const aParts = aCore.split(".").map((p) => Number.parseInt(p, 10) || 0);
|
|
4863
|
+
const bParts = bCore.split(".").map((p) => Number.parseInt(p, 10) || 0);
|
|
4864
|
+
for (let i = 0; i < 3; i++) {
|
|
4865
|
+
const diff = (aParts[i] ?? 0) - (bParts[i] ?? 0);
|
|
4866
|
+
if (diff !== 0) return diff;
|
|
4867
|
+
}
|
|
4868
|
+
if (!aPre && !bPre) return 0;
|
|
4869
|
+
if (!aPre) return 1;
|
|
4870
|
+
if (!bPre) return -1;
|
|
4871
|
+
return aPre < bPre ? -1 : aPre > bPre ? 1 : 0;
|
|
4872
|
+
}
|
|
4873
|
+
function isNpxInstall() {
|
|
4874
|
+
const bin = process.argv[1] ?? "";
|
|
4875
|
+
if (/[/\\]_npx[/\\]/.test(bin)) return true;
|
|
4876
|
+
if (/[/\\]\.pnpm[/\\]/.test(bin) && /dlx/i.test(bin)) return true;
|
|
4877
|
+
const ua = process.env.npm_config_user_agent ?? "";
|
|
4878
|
+
if (ua.includes("npx/")) return true;
|
|
4879
|
+
return false;
|
|
4880
|
+
}
|
|
4608
4881
|
export {
|
|
4609
4882
|
AppendOnlyLog,
|
|
4610
4883
|
CODE_SYSTEM_PROMPT,
|
|
@@ -4612,6 +4885,8 @@ export {
|
|
|
4612
4885
|
DEFAULT_MAX_RESULT_CHARS,
|
|
4613
4886
|
DeepSeekClient,
|
|
4614
4887
|
ImmutablePrefix,
|
|
4888
|
+
LATEST_CACHE_TTL_MS,
|
|
4889
|
+
LATEST_FETCH_TIMEOUT_MS,
|
|
4615
4890
|
MCP_PROTOCOL_VERSION,
|
|
4616
4891
|
MEMORY_INDEX_FILE,
|
|
4617
4892
|
MEMORY_INDEX_MAX_CHARS,
|
|
@@ -4642,6 +4917,7 @@ export {
|
|
|
4642
4917
|
bridgeMcpTools,
|
|
4643
4918
|
claudeEquivalentCost,
|
|
4644
4919
|
codeSystemPrompt,
|
|
4920
|
+
compareVersions,
|
|
4645
4921
|
computeReplayStats,
|
|
4646
4922
|
costUsd,
|
|
4647
4923
|
defaultConfigPath,
|
|
@@ -4655,6 +4931,7 @@ export {
|
|
|
4655
4931
|
formatCommandResult,
|
|
4656
4932
|
formatLoopError,
|
|
4657
4933
|
formatSearchResults,
|
|
4934
|
+
getLatestVersion,
|
|
4658
4935
|
harvest,
|
|
4659
4936
|
healLoadedMessages,
|
|
4660
4937
|
htmlToText,
|
|
@@ -4662,6 +4939,7 @@ export {
|
|
|
4662
4939
|
inspectMcpServer,
|
|
4663
4940
|
isAllowed,
|
|
4664
4941
|
isJsonRpcError,
|
|
4942
|
+
isNpxInstall,
|
|
4665
4943
|
isPlanStateEmpty,
|
|
4666
4944
|
isPlausibleKey,
|
|
4667
4945
|
listSessions,
|