reasonix 0.21.0 → 0.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +1478 -912
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +37 -2
- package/dist/index.js +197 -139
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -477,10 +477,10 @@ function globalSettingsPath(homeDirOverride) {
|
|
|
477
477
|
function projectSettingsPath(projectRoot) {
|
|
478
478
|
return join(projectRoot, HOOK_SETTINGS_DIRNAME, HOOK_SETTINGS_FILENAME);
|
|
479
479
|
}
|
|
480
|
-
function readSettingsFile(
|
|
481
|
-
if (!existsSync(
|
|
480
|
+
function readSettingsFile(path2) {
|
|
481
|
+
if (!existsSync(path2)) return null;
|
|
482
482
|
try {
|
|
483
|
-
const raw = readFileSync(
|
|
483
|
+
const raw = readFileSync(path2, "utf8");
|
|
484
484
|
const parsed = JSON.parse(raw);
|
|
485
485
|
if (parsed && typeof parsed === "object") return parsed;
|
|
486
486
|
} catch {
|
|
@@ -890,14 +890,14 @@ function collect(prefix, schema, out, required, isRootRequired) {
|
|
|
890
890
|
out[prefix] = schema;
|
|
891
891
|
if (isRootRequired) required.push(prefix);
|
|
892
892
|
}
|
|
893
|
-
function setByPath(target,
|
|
893
|
+
function setByPath(target, path2, value) {
|
|
894
894
|
let cur = target;
|
|
895
|
-
for (let i = 0; i <
|
|
896
|
-
const key =
|
|
895
|
+
for (let i = 0; i < path2.length - 1; i++) {
|
|
896
|
+
const key = path2[i];
|
|
897
897
|
if (typeof cur[key] !== "object" || cur[key] === null) cur[key] = {};
|
|
898
898
|
cur = cur[key];
|
|
899
899
|
}
|
|
900
|
-
cur[
|
|
900
|
+
cur[path2[path2.length - 1]] = value;
|
|
901
901
|
}
|
|
902
902
|
|
|
903
903
|
// src/tools.ts
|
|
@@ -1065,6 +1065,26 @@ function computeP95(samples) {
|
|
|
1065
1065
|
// src/mcp/registry.ts
|
|
1066
1066
|
var DEFAULT_MAX_RESULT_CHARS = 32e3;
|
|
1067
1067
|
var DEFAULT_MAX_RESULT_TOKENS = 8e3;
|
|
1068
|
+
function registerSingleMcpTool(mcpTool, env) {
|
|
1069
|
+
if (!mcpTool.name) return "";
|
|
1070
|
+
const registeredName = `${env.prefix}${mcpTool.name}`;
|
|
1071
|
+
env.registry.register({
|
|
1072
|
+
name: registeredName,
|
|
1073
|
+
description: mcpTool.description ?? "",
|
|
1074
|
+
parameters: mcpTool.inputSchema,
|
|
1075
|
+
fn: async (args, ctx) => {
|
|
1076
|
+
const t0 = env.tracker ? Date.now() : 0;
|
|
1077
|
+
const live = env.host.client;
|
|
1078
|
+
const toolResult = await live.callTool(mcpTool.name, args, {
|
|
1079
|
+
onProgress: env.onProgress ? (info) => env.onProgress({ toolName: registeredName, ...info }) : void 0,
|
|
1080
|
+
signal: ctx?.signal
|
|
1081
|
+
});
|
|
1082
|
+
if (env.tracker) env.tracker.record(Date.now() - t0);
|
|
1083
|
+
return flattenMcpResult(toolResult, { maxChars: env.maxResultChars });
|
|
1084
|
+
}
|
|
1085
|
+
});
|
|
1086
|
+
return registeredName;
|
|
1087
|
+
}
|
|
1068
1088
|
async function bridgeMcpTools(client, opts = {}) {
|
|
1069
1089
|
const registry = opts.registry ?? new ToolRegistry({ autoFlatten: opts.autoFlatten });
|
|
1070
1090
|
const prefix = opts.namePrefix ?? "";
|
|
@@ -1072,39 +1092,25 @@ async function bridgeMcpTools(client, opts = {}) {
|
|
|
1072
1092
|
const result = { registry, registeredNames: [], skipped: [] };
|
|
1073
1093
|
const serverName = opts.serverName ?? prefix.replace(/_$/, "") ?? "anon";
|
|
1074
1094
|
const tracker = opts.onSlow ? new LatencyTracker(serverName, { thresholdMs: opts.slowThresholdMs, onSlow: opts.onSlow }) : null;
|
|
1095
|
+
const host = opts.host ?? { client };
|
|
1096
|
+
const env = {
|
|
1097
|
+
registry,
|
|
1098
|
+
host,
|
|
1099
|
+
prefix,
|
|
1100
|
+
maxResultChars,
|
|
1101
|
+
tracker,
|
|
1102
|
+
onProgress: opts.onProgress
|
|
1103
|
+
};
|
|
1075
1104
|
const listed = await client.listTools();
|
|
1076
1105
|
for (const mcpTool of listed.tools) {
|
|
1077
1106
|
if (!mcpTool.name) {
|
|
1078
1107
|
result.skipped.push({ name: "?", reason: "empty tool name" });
|
|
1079
1108
|
continue;
|
|
1080
1109
|
}
|
|
1081
|
-
const registeredName =
|
|
1082
|
-
|
|
1083
|
-
name: registeredName,
|
|
1084
|
-
description: mcpTool.description ?? "",
|
|
1085
|
-
parameters: mcpTool.inputSchema,
|
|
1086
|
-
fn: async (args, ctx) => {
|
|
1087
|
-
const t0 = tracker ? Date.now() : 0;
|
|
1088
|
-
const toolResult = await client.callTool(mcpTool.name, args, {
|
|
1089
|
-
// Forward server-side progress frames to the bridge caller,
|
|
1090
|
-
// tagged with the registered name so multi-server UIs can
|
|
1091
|
-
// disambiguate. No-op when `onProgress` isn't configured —
|
|
1092
|
-
// the client then also omits the _meta.progressToken and
|
|
1093
|
-
// the server won't emit progress.
|
|
1094
|
-
onProgress: opts.onProgress ? (info) => opts.onProgress({ toolName: registeredName, ...info }) : void 0,
|
|
1095
|
-
// Thread the tool-dispatch AbortSignal all the way down to
|
|
1096
|
-
// the MCP request so Esc truly cancels in flight — the
|
|
1097
|
-
// client will emit notifications/cancelled AND reject the
|
|
1098
|
-
// pending promise immediately, no "wait for subprocess".
|
|
1099
|
-
signal: ctx?.signal
|
|
1100
|
-
});
|
|
1101
|
-
if (tracker) tracker.record(Date.now() - t0);
|
|
1102
|
-
return flattenMcpResult(toolResult, { maxChars: maxResultChars });
|
|
1103
|
-
}
|
|
1104
|
-
});
|
|
1105
|
-
result.registeredNames.push(registeredName);
|
|
1110
|
+
const registeredName = registerSingleMcpTool(mcpTool, env);
|
|
1111
|
+
if (registeredName) result.registeredNames.push(registeredName);
|
|
1106
1112
|
}
|
|
1107
|
-
return result;
|
|
1113
|
+
return { ...result, env };
|
|
1108
1114
|
}
|
|
1109
1115
|
function flattenMcpResult(result, opts = {}) {
|
|
1110
1116
|
const parts = result.content.map(blockToString);
|
|
@@ -1305,10 +1311,10 @@ function sanitizeName(name) {
|
|
|
1305
1311
|
return cleaned || "default";
|
|
1306
1312
|
}
|
|
1307
1313
|
function loadSessionMessages(name) {
|
|
1308
|
-
const
|
|
1309
|
-
if (!existsSync3(
|
|
1314
|
+
const path2 = sessionPath(name);
|
|
1315
|
+
if (!existsSync3(path2)) return [];
|
|
1310
1316
|
try {
|
|
1311
|
-
const raw = readFileSync3(
|
|
1317
|
+
const raw = readFileSync3(path2, "utf8");
|
|
1312
1318
|
const out = [];
|
|
1313
1319
|
for (const line of raw.split(/\r?\n/)) {
|
|
1314
1320
|
const trimmed = line.trim();
|
|
@@ -1325,12 +1331,12 @@ function loadSessionMessages(name) {
|
|
|
1325
1331
|
}
|
|
1326
1332
|
}
|
|
1327
1333
|
function appendSessionMessage(name, message) {
|
|
1328
|
-
const
|
|
1329
|
-
mkdirSync(dirname2(
|
|
1330
|
-
appendFileSync(
|
|
1334
|
+
const path2 = sessionPath(name);
|
|
1335
|
+
mkdirSync(dirname2(path2), { recursive: true });
|
|
1336
|
+
appendFileSync(path2, `${JSON.stringify(message)}
|
|
1331
1337
|
`, "utf8");
|
|
1332
1338
|
try {
|
|
1333
|
-
chmodSync(
|
|
1339
|
+
chmodSync(path2, 384);
|
|
1334
1340
|
} catch {
|
|
1335
1341
|
}
|
|
1336
1342
|
}
|
|
@@ -1342,13 +1348,13 @@ function listSessions() {
|
|
|
1342
1348
|
(f) => f.endsWith(".jsonl") && !f.endsWith(".events.jsonl")
|
|
1343
1349
|
);
|
|
1344
1350
|
return files.map((file) => {
|
|
1345
|
-
const
|
|
1346
|
-
const stat2 = statSync(
|
|
1351
|
+
const path2 = join3(dir, file);
|
|
1352
|
+
const stat2 = statSync(path2);
|
|
1347
1353
|
const name = file.replace(/\.jsonl$/, "");
|
|
1348
|
-
const messageCount = countLines(
|
|
1354
|
+
const messageCount = countLines(path2);
|
|
1349
1355
|
return {
|
|
1350
1356
|
name,
|
|
1351
|
-
path,
|
|
1357
|
+
path: path2,
|
|
1352
1358
|
size: stat2.size,
|
|
1353
1359
|
messageCount,
|
|
1354
1360
|
mtime: stat2.mtime,
|
|
@@ -1373,11 +1379,11 @@ function loadSessionMeta(name) {
|
|
|
1373
1379
|
}
|
|
1374
1380
|
}
|
|
1375
1381
|
function deleteSession(name) {
|
|
1376
|
-
const
|
|
1382
|
+
const path2 = sessionPath(name);
|
|
1377
1383
|
try {
|
|
1378
|
-
unlinkSync(
|
|
1384
|
+
unlinkSync(path2);
|
|
1379
1385
|
for (const ext of [".events.jsonl", ".pending.json", ".meta.json", ".plan.json"]) {
|
|
1380
|
-
const sidecar =
|
|
1386
|
+
const sidecar = path2.replace(/\.jsonl$/, ext);
|
|
1381
1387
|
try {
|
|
1382
1388
|
unlinkSync(sidecar);
|
|
1383
1389
|
} catch {
|
|
@@ -1389,19 +1395,19 @@ function deleteSession(name) {
|
|
|
1389
1395
|
}
|
|
1390
1396
|
}
|
|
1391
1397
|
function rewriteSession(name, messages) {
|
|
1392
|
-
const
|
|
1393
|
-
mkdirSync(dirname2(
|
|
1398
|
+
const path2 = sessionPath(name);
|
|
1399
|
+
mkdirSync(dirname2(path2), { recursive: true });
|
|
1394
1400
|
const body = messages.map((m) => JSON.stringify(m)).join("\n");
|
|
1395
|
-
writeFileSync(
|
|
1401
|
+
writeFileSync(path2, body ? `${body}
|
|
1396
1402
|
` : "", "utf8");
|
|
1397
1403
|
try {
|
|
1398
|
-
chmodSync(
|
|
1404
|
+
chmodSync(path2, 384);
|
|
1399
1405
|
} catch {
|
|
1400
1406
|
}
|
|
1401
1407
|
}
|
|
1402
|
-
function countLines(
|
|
1408
|
+
function countLines(path2) {
|
|
1403
1409
|
try {
|
|
1404
|
-
const raw = readFileSync3(
|
|
1410
|
+
const raw = readFileSync3(path2, "utf8");
|
|
1405
1411
|
return raw.split(/\r?\n/).filter((l) => l.trim()).length;
|
|
1406
1412
|
} catch {
|
|
1407
1413
|
return 0;
|
|
@@ -2976,9 +2982,39 @@ function extractDeepSeekErrorMessage(body) {
|
|
|
2976
2982
|
}
|
|
2977
2983
|
|
|
2978
2984
|
// src/at-mentions.ts
|
|
2979
|
-
import { existsSync as existsSync4, readFileSync as
|
|
2985
|
+
import { existsSync as existsSync4, readFileSync as readFileSync5, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
|
|
2980
2986
|
import { readdir, stat } from "fs/promises";
|
|
2981
2987
|
import { isAbsolute, join as join4, relative, resolve } from "path";
|
|
2988
|
+
|
|
2989
|
+
// src/gitignore.ts
|
|
2990
|
+
import { readFileSync as readFileSync4 } from "fs";
|
|
2991
|
+
import { readFile } from "fs/promises";
|
|
2992
|
+
import path from "path";
|
|
2993
|
+
import ignore from "ignore";
|
|
2994
|
+
async function loadGitignoreAt(dirAbs) {
|
|
2995
|
+
try {
|
|
2996
|
+
return ignore().add(await readFile(path.join(dirAbs, ".gitignore"), "utf8"));
|
|
2997
|
+
} catch {
|
|
2998
|
+
return null;
|
|
2999
|
+
}
|
|
3000
|
+
}
|
|
3001
|
+
function loadGitignoreAtSync(dirAbs) {
|
|
3002
|
+
try {
|
|
3003
|
+
return ignore().add(readFileSync4(path.join(dirAbs, ".gitignore"), "utf8"));
|
|
3004
|
+
} catch {
|
|
3005
|
+
return null;
|
|
3006
|
+
}
|
|
3007
|
+
}
|
|
3008
|
+
function ignoredByLayers(layers, abs, isDir) {
|
|
3009
|
+
for (const layer of layers) {
|
|
3010
|
+
const rel = path.relative(layer.dirAbs, abs).split(path.sep).join("/");
|
|
3011
|
+
if (!rel || rel.startsWith("..")) continue;
|
|
3012
|
+
if (layer.ig.ignores(isDir ? `${rel}/` : rel)) return true;
|
|
3013
|
+
}
|
|
3014
|
+
return false;
|
|
3015
|
+
}
|
|
3016
|
+
|
|
3017
|
+
// src/at-mentions.ts
|
|
2982
3018
|
var DEFAULT_AT_MENTION_MAX_BYTES = 64 * 1024;
|
|
2983
3019
|
var DEFAULT_PICKER_IGNORE_DIRS = [
|
|
2984
3020
|
"node_modules",
|
|
@@ -3000,12 +3036,18 @@ function listFilesSync(root, opts = {}) {
|
|
|
3000
3036
|
return listFilesWithStatsSync(root, opts).map((e) => e.path);
|
|
3001
3037
|
}
|
|
3002
3038
|
function listFilesWithStatsSync(root, opts = {}) {
|
|
3003
|
-
const maxResults = Math.max(1, opts.maxResults ??
|
|
3004
|
-
const
|
|
3039
|
+
const maxResults = Math.max(1, opts.maxResults ?? 2e3);
|
|
3040
|
+
const ignoreDirs = new Set(opts.ignoreDirs ?? DEFAULT_PICKER_IGNORE_DIRS);
|
|
3005
3041
|
const rootAbs = resolve(root);
|
|
3042
|
+
const respectGi = opts.respectGitignore !== false;
|
|
3006
3043
|
const out = [];
|
|
3007
|
-
const walk2 = (dirAbs, dirRel) => {
|
|
3044
|
+
const walk2 = (dirAbs, dirRel, layers) => {
|
|
3008
3045
|
if (out.length >= maxResults) return;
|
|
3046
|
+
let effectiveLayers = layers;
|
|
3047
|
+
if (respectGi) {
|
|
3048
|
+
const ig = loadGitignoreAtSync(dirAbs);
|
|
3049
|
+
if (ig) effectiveLayers = [...layers, { dirAbs, ig }];
|
|
3050
|
+
}
|
|
3009
3051
|
let entries;
|
|
3010
3052
|
try {
|
|
3011
3053
|
entries = readdirSync2(dirAbs, { withFileTypes: true });
|
|
@@ -3016,29 +3058,38 @@ function listFilesWithStatsSync(root, opts = {}) {
|
|
|
3016
3058
|
for (const ent of entries) {
|
|
3017
3059
|
if (out.length >= maxResults) return;
|
|
3018
3060
|
const relPath = dirRel ? `${dirRel}/${ent.name}` : ent.name;
|
|
3061
|
+
const absPath = join4(dirAbs, ent.name);
|
|
3019
3062
|
if (ent.isDirectory()) {
|
|
3020
|
-
if (ent.name.startsWith(".") ||
|
|
3021
|
-
|
|
3063
|
+
if (ent.name.startsWith(".") || ignoreDirs.has(ent.name)) continue;
|
|
3064
|
+
if (ignoredByLayers(effectiveLayers, absPath, true)) continue;
|
|
3065
|
+
walk2(absPath, relPath, effectiveLayers);
|
|
3022
3066
|
} else if (ent.isFile()) {
|
|
3067
|
+
if (ignoredByLayers(effectiveLayers, absPath, false)) continue;
|
|
3023
3068
|
let mtimeMs = 0;
|
|
3024
3069
|
try {
|
|
3025
|
-
mtimeMs = statSync2(
|
|
3070
|
+
mtimeMs = statSync2(absPath).mtimeMs;
|
|
3026
3071
|
} catch {
|
|
3027
3072
|
}
|
|
3028
3073
|
out.push({ path: relPath, mtimeMs });
|
|
3029
3074
|
}
|
|
3030
3075
|
}
|
|
3031
3076
|
};
|
|
3032
|
-
walk2(rootAbs, "");
|
|
3077
|
+
walk2(rootAbs, "", []);
|
|
3033
3078
|
return out;
|
|
3034
3079
|
}
|
|
3035
3080
|
async function listFilesWithStatsAsync(root, opts = {}) {
|
|
3036
|
-
const maxResults = Math.max(1, opts.maxResults ??
|
|
3037
|
-
const
|
|
3081
|
+
const maxResults = Math.max(1, opts.maxResults ?? 2e3);
|
|
3082
|
+
const ignoreDirs = new Set(opts.ignoreDirs ?? DEFAULT_PICKER_IGNORE_DIRS);
|
|
3038
3083
|
const rootAbs = resolve(root);
|
|
3084
|
+
const respectGi = opts.respectGitignore !== false;
|
|
3039
3085
|
const out = [];
|
|
3040
|
-
const walk2 = async (dirAbs, dirRel) => {
|
|
3086
|
+
const walk2 = async (dirAbs, dirRel, layers) => {
|
|
3041
3087
|
if (out.length >= maxResults) return;
|
|
3088
|
+
let effectiveLayers = layers;
|
|
3089
|
+
if (respectGi) {
|
|
3090
|
+
const ig = await loadGitignoreAt(dirAbs);
|
|
3091
|
+
if (ig) effectiveLayers = [...layers, { dirAbs, ig }];
|
|
3092
|
+
}
|
|
3042
3093
|
let entries;
|
|
3043
3094
|
try {
|
|
3044
3095
|
entries = await readdir(dirAbs, { withFileTypes: true });
|
|
@@ -3049,35 +3100,42 @@ async function listFilesWithStatsAsync(root, opts = {}) {
|
|
|
3049
3100
|
const fileEnts = [];
|
|
3050
3101
|
for (const ent of entries) {
|
|
3051
3102
|
if (out.length >= maxResults) break;
|
|
3103
|
+
const relPath = dirRel ? `${dirRel}/${ent.name}` : ent.name;
|
|
3104
|
+
const absPath = join4(dirAbs, ent.name);
|
|
3052
3105
|
if (ent.isDirectory()) {
|
|
3053
|
-
if (ent.name.startsWith(".") ||
|
|
3106
|
+
if (ent.name.startsWith(".") || ignoreDirs.has(ent.name)) continue;
|
|
3107
|
+
if (ignoredByLayers(effectiveLayers, absPath, true)) continue;
|
|
3054
3108
|
if (fileEnts.length > 0) {
|
|
3055
|
-
await statBatch(fileEnts, dirAbs, dirRel, out, maxResults);
|
|
3109
|
+
await statBatch(fileEnts, dirAbs, dirRel, out, maxResults, effectiveLayers);
|
|
3056
3110
|
fileEnts.length = 0;
|
|
3057
3111
|
if (out.length >= maxResults) return;
|
|
3058
3112
|
}
|
|
3059
|
-
await walk2(
|
|
3113
|
+
await walk2(absPath, relPath, effectiveLayers);
|
|
3060
3114
|
} else if (ent.isFile()) {
|
|
3061
3115
|
fileEnts.push(ent);
|
|
3062
3116
|
}
|
|
3063
3117
|
}
|
|
3064
3118
|
if (fileEnts.length > 0 && out.length < maxResults) {
|
|
3065
|
-
await statBatch(fileEnts, dirAbs, dirRel, out, maxResults);
|
|
3119
|
+
await statBatch(fileEnts, dirAbs, dirRel, out, maxResults, effectiveLayers);
|
|
3066
3120
|
}
|
|
3067
3121
|
};
|
|
3068
|
-
await walk2(rootAbs, "");
|
|
3122
|
+
await walk2(rootAbs, "", []);
|
|
3069
3123
|
return out;
|
|
3070
3124
|
}
|
|
3071
|
-
async function statBatch(ents, dirAbs, dirRel, out, maxResults) {
|
|
3072
|
-
const
|
|
3073
|
-
const
|
|
3125
|
+
async function statBatch(ents, dirAbs, dirRel, out, maxResults, layers) {
|
|
3126
|
+
const accepted = [];
|
|
3127
|
+
for (const e of ents) {
|
|
3128
|
+
if (out.length + accepted.length >= maxResults) break;
|
|
3129
|
+
if (ignoredByLayers(layers, join4(dirAbs, e.name), false)) continue;
|
|
3130
|
+
accepted.push(e);
|
|
3131
|
+
}
|
|
3074
3132
|
const stats = await Promise.all(
|
|
3075
|
-
|
|
3133
|
+
accepted.map(
|
|
3076
3134
|
(e) => stat(join4(dirAbs, e.name)).then((s) => s.mtimeMs).catch(() => 0)
|
|
3077
3135
|
)
|
|
3078
3136
|
);
|
|
3079
|
-
for (let i = 0; i <
|
|
3080
|
-
const ent =
|
|
3137
|
+
for (let i = 0; i < accepted.length; i++) {
|
|
3138
|
+
const ent = accepted[i];
|
|
3081
3139
|
out.push({
|
|
3082
3140
|
path: dirRel ? `${dirRel}/${ent.name}` : ent.name,
|
|
3083
3141
|
mtimeMs: stats[i] ?? 0
|
|
@@ -3218,20 +3276,20 @@ var defaultFs = {
|
|
|
3218
3276
|
return 0;
|
|
3219
3277
|
}
|
|
3220
3278
|
},
|
|
3221
|
-
read: (p) =>
|
|
3279
|
+
read: (p) => readFileSync5(p, "utf8")
|
|
3222
3280
|
};
|
|
3223
3281
|
|
|
3224
3282
|
// src/memory/project.ts
|
|
3225
|
-
import { existsSync as existsSync5, readFileSync as
|
|
3283
|
+
import { existsSync as existsSync5, readFileSync as readFileSync6 } from "fs";
|
|
3226
3284
|
import { join as join5 } from "path";
|
|
3227
3285
|
var PROJECT_MEMORY_FILE = "REASONIX.md";
|
|
3228
3286
|
var PROJECT_MEMORY_MAX_CHARS = 8e3;
|
|
3229
3287
|
function readProjectMemory(rootDir) {
|
|
3230
|
-
const
|
|
3231
|
-
if (!existsSync5(
|
|
3288
|
+
const path2 = join5(rootDir, PROJECT_MEMORY_FILE);
|
|
3289
|
+
if (!existsSync5(path2)) return null;
|
|
3232
3290
|
let raw;
|
|
3233
3291
|
try {
|
|
3234
|
-
raw =
|
|
3292
|
+
raw = readFileSync6(path2, "utf8");
|
|
3235
3293
|
} catch {
|
|
3236
3294
|
return null;
|
|
3237
3295
|
}
|
|
@@ -3241,7 +3299,7 @@ function readProjectMemory(rootDir) {
|
|
|
3241
3299
|
const truncated = originalChars > PROJECT_MEMORY_MAX_CHARS;
|
|
3242
3300
|
const content = truncated ? `${trimmed.slice(0, PROJECT_MEMORY_MAX_CHARS)}
|
|
3243
3301
|
\u2026 (truncated ${originalChars - PROJECT_MEMORY_MAX_CHARS} chars)` : trimmed;
|
|
3244
|
-
return { path, content, originalChars, truncated };
|
|
3302
|
+
return { path: path2, content, originalChars, truncated };
|
|
3245
3303
|
}
|
|
3246
3304
|
function memoryEnabled() {
|
|
3247
3305
|
const env = process.env.REASONIX_MEMORY;
|
|
@@ -3269,7 +3327,7 @@ import { createHash as createHash2 } from "crypto";
|
|
|
3269
3327
|
import {
|
|
3270
3328
|
existsSync as existsSync7,
|
|
3271
3329
|
mkdirSync as mkdirSync2,
|
|
3272
|
-
readFileSync as
|
|
3330
|
+
readFileSync as readFileSync8,
|
|
3273
3331
|
readdirSync as readdirSync4,
|
|
3274
3332
|
unlinkSync as unlinkSync2,
|
|
3275
3333
|
writeFileSync as writeFileSync2
|
|
@@ -3278,7 +3336,7 @@ import { homedir as homedir4 } from "os";
|
|
|
3278
3336
|
import { join as join7, resolve as resolve3 } from "path";
|
|
3279
3337
|
|
|
3280
3338
|
// src/skills.ts
|
|
3281
|
-
import { existsSync as existsSync6, readFileSync as
|
|
3339
|
+
import { existsSync as existsSync6, readFileSync as readFileSync7, readdirSync as readdirSync3, statSync as statSync3 } from "fs";
|
|
3282
3340
|
import { homedir as homedir3 } from "os";
|
|
3283
3341
|
import { join as join6, resolve as resolve2 } from "path";
|
|
3284
3342
|
|
|
@@ -3415,10 +3473,10 @@ var SkillStore = class {
|
|
|
3415
3473
|
}
|
|
3416
3474
|
return null;
|
|
3417
3475
|
}
|
|
3418
|
-
parse(
|
|
3476
|
+
parse(path2, stem, scope) {
|
|
3419
3477
|
let raw;
|
|
3420
3478
|
try {
|
|
3421
|
-
raw =
|
|
3479
|
+
raw = readFileSync7(path2, "utf8");
|
|
3422
3480
|
} catch {
|
|
3423
3481
|
return null;
|
|
3424
3482
|
}
|
|
@@ -3429,7 +3487,7 @@ var SkillStore = class {
|
|
|
3429
3487
|
description: (data.description ?? "").trim(),
|
|
3430
3488
|
body: body.trim(),
|
|
3431
3489
|
scope,
|
|
3432
|
-
path,
|
|
3490
|
+
path: path2,
|
|
3433
3491
|
allowedTools: data["allowed-tools"],
|
|
3434
3492
|
runAs: parseRunAs(data.runAs),
|
|
3435
3493
|
model: data.model?.startsWith("deepseek-") ? data.model : void 0
|
|
@@ -3752,7 +3810,7 @@ var MemoryStore = class {
|
|
|
3752
3810
|
if (!existsSync7(file)) return null;
|
|
3753
3811
|
let raw;
|
|
3754
3812
|
try {
|
|
3755
|
-
raw =
|
|
3813
|
+
raw = readFileSync8(file, "utf8");
|
|
3756
3814
|
} catch {
|
|
3757
3815
|
return null;
|
|
3758
3816
|
}
|
|
@@ -3770,7 +3828,7 @@ var MemoryStore = class {
|
|
|
3770
3828
|
if (!existsSync7(file)) {
|
|
3771
3829
|
throw new Error(`memory not found: scope=${scope} name=${name}`);
|
|
3772
3830
|
}
|
|
3773
|
-
const raw =
|
|
3831
|
+
const raw = readFileSync8(file, "utf8");
|
|
3774
3832
|
const { data, body } = parseFrontmatter2(raw);
|
|
3775
3833
|
return {
|
|
3776
3834
|
name: data.name ?? name,
|
|
@@ -3872,11 +3930,11 @@ var MemoryStore = class {
|
|
|
3872
3930
|
}
|
|
3873
3931
|
};
|
|
3874
3932
|
function readGlobalReasonixMemory(homeDir = join7(homedir4(), ".reasonix")) {
|
|
3875
|
-
const
|
|
3876
|
-
if (!existsSync7(
|
|
3933
|
+
const path2 = join7(homeDir, "REASONIX.md");
|
|
3934
|
+
if (!existsSync7(path2)) return null;
|
|
3877
3935
|
let raw;
|
|
3878
3936
|
try {
|
|
3879
|
-
raw =
|
|
3937
|
+
raw = readFileSync8(path2, "utf8");
|
|
3880
3938
|
} catch {
|
|
3881
3939
|
return null;
|
|
3882
3940
|
}
|
|
@@ -3886,7 +3944,7 @@ function readGlobalReasonixMemory(homeDir = join7(homedir4(), ".reasonix")) {
|
|
|
3886
3944
|
const truncated = originalChars > 8e3;
|
|
3887
3945
|
const content = truncated ? `${trimmed.slice(0, 8e3)}
|
|
3888
3946
|
\u2026 (truncated ${originalChars - 8e3} chars)` : trimmed;
|
|
3889
|
-
return { path, content, originalChars, truncated };
|
|
3947
|
+
return { path: path2, content, originalChars, truncated };
|
|
3890
3948
|
}
|
|
3891
3949
|
function applyGlobalReasonixMemory(basePrompt, homeDir) {
|
|
3892
3950
|
if (!memoryEnabled()) return basePrompt;
|
|
@@ -4607,7 +4665,7 @@ function registerMemoryTools(registry, opts = {}) {
|
|
|
4607
4665
|
});
|
|
4608
4666
|
}
|
|
4609
4667
|
try {
|
|
4610
|
-
const
|
|
4668
|
+
const path2 = store.write({
|
|
4611
4669
|
name: args.name,
|
|
4612
4670
|
type: args.type,
|
|
4613
4671
|
scope: args.scope,
|
|
@@ -4620,7 +4678,7 @@ function registerMemoryTools(registry, opts = {}) {
|
|
|
4620
4678
|
"",
|
|
4621
4679
|
"TREAT THIS AS ESTABLISHED FACT for the rest of this session.",
|
|
4622
4680
|
"The user just told you \u2014 don't re-explore the filesystem to re-derive it.",
|
|
4623
|
-
`(Saved to ${
|
|
4681
|
+
`(Saved to ${path2}; pins into the system prompt on next /new or launch.)`
|
|
4624
4682
|
].join("\n");
|
|
4625
4683
|
} catch (err) {
|
|
4626
4684
|
return JSON.stringify({ error: `remember failed: ${err.message}` });
|
|
@@ -6349,12 +6407,12 @@ ${i + 1}. ${r.title}`);
|
|
|
6349
6407
|
}
|
|
6350
6408
|
|
|
6351
6409
|
// src/env.ts
|
|
6352
|
-
import { readFileSync as
|
|
6410
|
+
import { readFileSync as readFileSync9 } from "fs";
|
|
6353
6411
|
import { resolve as resolve7 } from "path";
|
|
6354
|
-
function loadDotenv(
|
|
6412
|
+
function loadDotenv(path2 = ".env") {
|
|
6355
6413
|
let raw;
|
|
6356
6414
|
try {
|
|
6357
|
-
raw =
|
|
6415
|
+
raw = readFileSync9(resolve7(process.cwd(), path2), "utf8");
|
|
6358
6416
|
} catch {
|
|
6359
6417
|
return;
|
|
6360
6418
|
}
|
|
@@ -6373,7 +6431,7 @@ function loadDotenv(path = ".env") {
|
|
|
6373
6431
|
}
|
|
6374
6432
|
|
|
6375
6433
|
// src/transcript/log.ts
|
|
6376
|
-
import { createWriteStream, readFileSync as
|
|
6434
|
+
import { createWriteStream, readFileSync as readFileSync10 } from "fs";
|
|
6377
6435
|
function recordFromLoopEvent(ev, extra) {
|
|
6378
6436
|
const rec = {
|
|
6379
6437
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -6418,13 +6476,13 @@ function writeMeta(stream, meta) {
|
|
|
6418
6476
|
stream.write(`${JSON.stringify(line)}
|
|
6419
6477
|
`);
|
|
6420
6478
|
}
|
|
6421
|
-
function openTranscriptFile(
|
|
6422
|
-
const stream = createWriteStream(
|
|
6479
|
+
function openTranscriptFile(path2, meta) {
|
|
6480
|
+
const stream = createWriteStream(path2, { flags: "a" });
|
|
6423
6481
|
writeMeta(stream, meta);
|
|
6424
6482
|
return stream;
|
|
6425
6483
|
}
|
|
6426
|
-
function readTranscript(
|
|
6427
|
-
const raw =
|
|
6484
|
+
function readTranscript(path2) {
|
|
6485
|
+
const raw = readFileSync10(path2, "utf8");
|
|
6428
6486
|
return parseTranscript(raw);
|
|
6429
6487
|
}
|
|
6430
6488
|
function isPlanStateEmptyShape(s) {
|
|
@@ -6455,8 +6513,8 @@ function parseTranscript(raw) {
|
|
|
6455
6513
|
}
|
|
6456
6514
|
|
|
6457
6515
|
// src/transcript/replay.ts
|
|
6458
|
-
function replayFromFile(
|
|
6459
|
-
const parsed = readTranscript(
|
|
6516
|
+
function replayFromFile(path2) {
|
|
6517
|
+
const parsed = readTranscript(path2);
|
|
6460
6518
|
return { parsed, stats: computeReplayStats(parsed.records) };
|
|
6461
6519
|
}
|
|
6462
6520
|
function computeReplayStats(records) {
|
|
@@ -6871,7 +6929,7 @@ function truncate(s, n) {
|
|
|
6871
6929
|
}
|
|
6872
6930
|
|
|
6873
6931
|
// src/version.ts
|
|
6874
|
-
import { existsSync as existsSync9, mkdirSync as mkdirSync3, readFileSync as
|
|
6932
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync3, readFileSync as readFileSync11, writeFileSync as writeFileSync3 } from "fs";
|
|
6875
6933
|
import { homedir as homedir5 } from "os";
|
|
6876
6934
|
import { dirname as dirname4, join as join9 } from "path";
|
|
6877
6935
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
@@ -6884,7 +6942,7 @@ function readPackageVersion() {
|
|
|
6884
6942
|
for (let i = 0; i < 6; i++) {
|
|
6885
6943
|
const p = join9(dir, "package.json");
|
|
6886
6944
|
if (existsSync9(p)) {
|
|
6887
|
-
const pkg = JSON.parse(
|
|
6945
|
+
const pkg = JSON.parse(readFileSync11(p, "utf8"));
|
|
6888
6946
|
if (pkg?.name === "reasonix" && typeof pkg.version === "string") {
|
|
6889
6947
|
return pkg.version;
|
|
6890
6948
|
}
|
|
@@ -6903,7 +6961,7 @@ function cachePath(homeDirOverride) {
|
|
|
6903
6961
|
}
|
|
6904
6962
|
function readCache(homeDirOverride) {
|
|
6905
6963
|
try {
|
|
6906
|
-
const raw =
|
|
6964
|
+
const raw = readFileSync11(cachePath(homeDirOverride), "utf8");
|
|
6907
6965
|
const parsed = JSON.parse(raw);
|
|
6908
6966
|
if (parsed && typeof parsed.version === "string" && typeof parsed.checkedAt === "number") {
|
|
6909
6967
|
return parsed;
|
|
@@ -7709,7 +7767,7 @@ async function trySection(load) {
|
|
|
7709
7767
|
}
|
|
7710
7768
|
|
|
7711
7769
|
// src/code/edit-blocks.ts
|
|
7712
|
-
import { existsSync as existsSync10, mkdirSync as mkdirSync4, readFileSync as
|
|
7770
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync4, readFileSync as readFileSync12, unlinkSync as unlinkSync3, writeFileSync as writeFileSync4 } from "fs";
|
|
7713
7771
|
import { dirname as dirname5, resolve as resolve8 } from "path";
|
|
7714
7772
|
var BLOCK_RE = /^(\S[^\n]*)\n<{7} SEARCH\n([\s\S]*?)\n?={7}\n([\s\S]*?)\n?>{7} REPLACE/gm;
|
|
7715
7773
|
function parseEditBlocks(text) {
|
|
@@ -7752,7 +7810,7 @@ function applyEditBlock(block, rootDir) {
|
|
|
7752
7810
|
writeFileSync4(absTarget, block.replace, "utf8");
|
|
7753
7811
|
return { path: block.path, status: "created" };
|
|
7754
7812
|
}
|
|
7755
|
-
const content =
|
|
7813
|
+
const content = readFileSync12(absTarget, "utf8");
|
|
7756
7814
|
if (searchEmpty) {
|
|
7757
7815
|
return {
|
|
7758
7816
|
path: block.path,
|
|
@@ -7791,7 +7849,7 @@ function snapshotBeforeEdits(blocks, rootDir) {
|
|
|
7791
7849
|
continue;
|
|
7792
7850
|
}
|
|
7793
7851
|
try {
|
|
7794
|
-
snapshots.push({ path: b.path, prevContent:
|
|
7852
|
+
snapshots.push({ path: b.path, prevContent: readFileSync12(abs, "utf8") });
|
|
7795
7853
|
} catch {
|
|
7796
7854
|
snapshots.push({ path: b.path, prevContent: null });
|
|
7797
7855
|
}
|
|
@@ -7834,7 +7892,7 @@ function sep() {
|
|
|
7834
7892
|
}
|
|
7835
7893
|
|
|
7836
7894
|
// src/code/prompt.ts
|
|
7837
|
-
import { existsSync as existsSync11, readFileSync as
|
|
7895
|
+
import { existsSync as existsSync11, readFileSync as readFileSync13 } from "fs";
|
|
7838
7896
|
import { join as join10 } from "path";
|
|
7839
7897
|
var CODE_SYSTEM_PROMPT = `You are Reasonix Code, a coding assistant. You have filesystem tools (read_file, write_file, edit_file, list_directory, directory_tree, search_files, search_content, get_file_info) rooted at the user's working directory, plus run_command / run_background for shell.
|
|
7840
7898
|
|
|
@@ -8042,7 +8100,7 @@ function codeSystemPrompt(rootDir, opts = {}) {
|
|
|
8042
8100
|
if (existsSync11(gitignorePath)) {
|
|
8043
8101
|
let content;
|
|
8044
8102
|
try {
|
|
8045
|
-
content =
|
|
8103
|
+
content = readFileSync13(gitignorePath, "utf8");
|
|
8046
8104
|
} catch {
|
|
8047
8105
|
}
|
|
8048
8106
|
if (content !== void 0) {
|
|
@@ -8073,37 +8131,37 @@ ${appendParts.join("\n\n")}`;
|
|
|
8073
8131
|
}
|
|
8074
8132
|
|
|
8075
8133
|
// src/config.ts
|
|
8076
|
-
import { chmodSync as chmodSync2, mkdirSync as mkdirSync5, readFileSync as
|
|
8134
|
+
import { chmodSync as chmodSync2, mkdirSync as mkdirSync5, readFileSync as readFileSync14, writeFileSync as writeFileSync5 } from "fs";
|
|
8077
8135
|
import { homedir as homedir6 } from "os";
|
|
8078
8136
|
import { dirname as dirname6, join as join11 } from "path";
|
|
8079
8137
|
function defaultConfigPath() {
|
|
8080
8138
|
return join11(homedir6(), ".reasonix", "config.json");
|
|
8081
8139
|
}
|
|
8082
|
-
function readConfig(
|
|
8140
|
+
function readConfig(path2 = defaultConfigPath()) {
|
|
8083
8141
|
try {
|
|
8084
|
-
const raw =
|
|
8142
|
+
const raw = readFileSync14(path2, "utf8");
|
|
8085
8143
|
const parsed = JSON.parse(raw);
|
|
8086
8144
|
if (parsed && typeof parsed === "object") return parsed;
|
|
8087
8145
|
} catch {
|
|
8088
8146
|
}
|
|
8089
8147
|
return {};
|
|
8090
8148
|
}
|
|
8091
|
-
function writeConfig(cfg,
|
|
8092
|
-
mkdirSync5(dirname6(
|
|
8093
|
-
writeFileSync5(
|
|
8149
|
+
function writeConfig(cfg, path2 = defaultConfigPath()) {
|
|
8150
|
+
mkdirSync5(dirname6(path2), { recursive: true });
|
|
8151
|
+
writeFileSync5(path2, JSON.stringify(cfg, null, 2), "utf8");
|
|
8094
8152
|
try {
|
|
8095
|
-
chmodSync2(
|
|
8153
|
+
chmodSync2(path2, 384);
|
|
8096
8154
|
} catch {
|
|
8097
8155
|
}
|
|
8098
8156
|
}
|
|
8099
|
-
function loadApiKey(
|
|
8157
|
+
function loadApiKey(path2 = defaultConfigPath()) {
|
|
8100
8158
|
if (process.env.DEEPSEEK_API_KEY) return process.env.DEEPSEEK_API_KEY;
|
|
8101
|
-
return readConfig(
|
|
8159
|
+
return readConfig(path2).apiKey;
|
|
8102
8160
|
}
|
|
8103
|
-
function saveApiKey(key,
|
|
8104
|
-
const cfg = readConfig(
|
|
8161
|
+
function saveApiKey(key, path2 = defaultConfigPath()) {
|
|
8162
|
+
const cfg = readConfig(path2);
|
|
8105
8163
|
cfg.apiKey = key.trim();
|
|
8106
|
-
writeConfig(cfg,
|
|
8164
|
+
writeConfig(cfg, path2);
|
|
8107
8165
|
}
|
|
8108
8166
|
function isPlausibleKey(key) {
|
|
8109
8167
|
const trimmed = key.trim();
|
|
@@ -8120,7 +8178,7 @@ import {
|
|
|
8120
8178
|
appendFileSync as appendFileSync2,
|
|
8121
8179
|
existsSync as existsSync12,
|
|
8122
8180
|
mkdirSync as mkdirSync6,
|
|
8123
|
-
readFileSync as
|
|
8181
|
+
readFileSync as readFileSync15,
|
|
8124
8182
|
statSync as statSync5,
|
|
8125
8183
|
writeFileSync as writeFileSync6
|
|
8126
8184
|
} from "fs";
|
|
@@ -8131,10 +8189,10 @@ function defaultUsageLogPath(homeDirOverride) {
|
|
|
8131
8189
|
}
|
|
8132
8190
|
var USAGE_COMPACTION_THRESHOLD_BYTES = 5 * 1024 * 1024;
|
|
8133
8191
|
var USAGE_RETENTION_DAYS = 365;
|
|
8134
|
-
function compactUsageLogIfLarge(
|
|
8192
|
+
function compactUsageLogIfLarge(path2, now) {
|
|
8135
8193
|
let size;
|
|
8136
8194
|
try {
|
|
8137
|
-
size = statSync5(
|
|
8195
|
+
size = statSync5(path2).size;
|
|
8138
8196
|
} catch {
|
|
8139
8197
|
return;
|
|
8140
8198
|
}
|
|
@@ -8142,7 +8200,7 @@ function compactUsageLogIfLarge(path, now) {
|
|
|
8142
8200
|
const cutoff = now - USAGE_RETENTION_DAYS * 24 * 60 * 60 * 1e3;
|
|
8143
8201
|
let raw;
|
|
8144
8202
|
try {
|
|
8145
|
-
raw =
|
|
8203
|
+
raw = readFileSync15(path2, "utf8");
|
|
8146
8204
|
} catch {
|
|
8147
8205
|
return;
|
|
8148
8206
|
}
|
|
@@ -8158,7 +8216,7 @@ function compactUsageLogIfLarge(path, now) {
|
|
|
8158
8216
|
}
|
|
8159
8217
|
if (kept.length === lines.filter((l) => l.trim()).length) return;
|
|
8160
8218
|
try {
|
|
8161
|
-
writeFileSync6(
|
|
8219
|
+
writeFileSync6(path2, kept.length > 0 ? `${kept.join("\n")}
|
|
8162
8220
|
` : "", "utf8");
|
|
8163
8221
|
} catch {
|
|
8164
8222
|
}
|
|
@@ -8177,21 +8235,21 @@ function appendUsage(input) {
|
|
|
8177
8235
|
};
|
|
8178
8236
|
if (input.kind === "subagent") record.kind = "subagent";
|
|
8179
8237
|
if (input.subagent) record.subagent = input.subagent;
|
|
8180
|
-
const
|
|
8238
|
+
const path2 = input.path ?? defaultUsageLogPath();
|
|
8181
8239
|
try {
|
|
8182
|
-
mkdirSync6(dirname7(
|
|
8183
|
-
appendFileSync2(
|
|
8240
|
+
mkdirSync6(dirname7(path2), { recursive: true });
|
|
8241
|
+
appendFileSync2(path2, `${JSON.stringify(record)}
|
|
8184
8242
|
`, "utf8");
|
|
8185
|
-
compactUsageLogIfLarge(
|
|
8243
|
+
compactUsageLogIfLarge(path2, record.ts);
|
|
8186
8244
|
} catch {
|
|
8187
8245
|
}
|
|
8188
8246
|
return record;
|
|
8189
8247
|
}
|
|
8190
|
-
function readUsageLog(
|
|
8191
|
-
if (!existsSync12(
|
|
8248
|
+
function readUsageLog(path2 = defaultUsageLogPath()) {
|
|
8249
|
+
if (!existsSync12(path2)) return [];
|
|
8192
8250
|
let raw;
|
|
8193
8251
|
try {
|
|
8194
|
-
raw =
|
|
8252
|
+
raw = readFileSync15(path2, "utf8");
|
|
8195
8253
|
} catch {
|
|
8196
8254
|
return [];
|
|
8197
8255
|
}
|
|
@@ -8297,10 +8355,10 @@ function aggregateUsage(records, opts = {}) {
|
|
|
8297
8355
|
subagents
|
|
8298
8356
|
};
|
|
8299
8357
|
}
|
|
8300
|
-
function formatLogSize(
|
|
8301
|
-
if (!existsSync12(
|
|
8358
|
+
function formatLogSize(path2 = defaultUsageLogPath()) {
|
|
8359
|
+
if (!existsSync12(path2)) return "";
|
|
8302
8360
|
try {
|
|
8303
|
-
const s = statSync5(
|
|
8361
|
+
const s = statSync5(path2);
|
|
8304
8362
|
const bytes = s.size;
|
|
8305
8363
|
if (bytes < 1024) return `${bytes} B`;
|
|
8306
8364
|
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|