reasonix 0.4.19 → 0.4.20
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 +47 -2
- package/dist/cli/chunk-DDIKQZVD.js +445 -0
- package/dist/cli/chunk-DDIKQZVD.js.map +1 -0
- package/dist/cli/index.js +300 -51
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/{prompt-JNNNJLYF.js → prompt-YEJEJ3IZ.js} +2 -2
- package/dist/index.d.ts +139 -4
- package/dist/index.js +477 -58
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/cli/chunk-HNEWBEWZ.js +0 -152
- package/dist/cli/chunk-HNEWBEWZ.js.map +0 -1
- /package/dist/cli/{prompt-JNNNJLYF.js.map → prompt-YEJEJ3IZ.js.map} +0 -0
package/dist/cli/index.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
+
MemoryStore,
|
|
3
4
|
PROJECT_MEMORY_FILE,
|
|
4
|
-
|
|
5
|
+
applyMemoryStack,
|
|
5
6
|
memoryEnabled,
|
|
6
|
-
readProjectMemory
|
|
7
|
-
|
|
7
|
+
readProjectMemory,
|
|
8
|
+
sanitizeMemoryName
|
|
9
|
+
} from "./chunk-DDIKQZVD.js";
|
|
8
10
|
|
|
9
11
|
// src/cli/index.ts
|
|
10
12
|
import { Command } from "commander";
|
|
@@ -2206,7 +2208,7 @@ function registerFilesystemTools(registry, opts) {
|
|
|
2206
2208
|
});
|
|
2207
2209
|
registry.register({
|
|
2208
2210
|
name: "edit_file",
|
|
2209
|
-
description: "Apply a SEARCH/REPLACE edit to an existing file. `search` must match exactly (whitespace sensitive) \u2014 no regex. The match must be unique in the file; otherwise the edit is refused to avoid surprise rewrites.
|
|
2211
|
+
description: "Apply a SEARCH/REPLACE edit to an existing file. `search` must match exactly (whitespace sensitive) \u2014 no regex. The match must be unique in the file; otherwise the edit is refused to avoid surprise rewrites.",
|
|
2210
2212
|
parameters: {
|
|
2211
2213
|
type: "object",
|
|
2212
2214
|
properties: {
|
|
@@ -2323,6 +2325,127 @@ function lineDiff(a, b) {
|
|
|
2323
2325
|
return out;
|
|
2324
2326
|
}
|
|
2325
2327
|
|
|
2328
|
+
// src/tools/memory.ts
|
|
2329
|
+
function registerMemoryTools(registry, opts = {}) {
|
|
2330
|
+
const store = new MemoryStore({ homeDir: opts.homeDir, projectRoot: opts.projectRoot });
|
|
2331
|
+
const hasProject = store.hasProjectScope();
|
|
2332
|
+
registry.register({
|
|
2333
|
+
name: "remember",
|
|
2334
|
+
description: "Save a memory for future sessions. Use when the user states a preference, corrects your approach, shares a non-obvious fact about this project, or explicitly asks you to remember something. Don't remember transient task state \u2014 only things worth recalling next session. The memory is written now but won't re-load into the system prompt until the next `/new` or launch.",
|
|
2335
|
+
parameters: {
|
|
2336
|
+
type: "object",
|
|
2337
|
+
properties: {
|
|
2338
|
+
type: {
|
|
2339
|
+
type: "string",
|
|
2340
|
+
enum: ["user", "feedback", "project", "reference"],
|
|
2341
|
+
description: "'user' = role/skills/prefs; 'feedback' = corrections or confirmed approaches; 'project' = facts/decisions about the current work; 'reference' = pointers to external systems the user uses."
|
|
2342
|
+
},
|
|
2343
|
+
scope: {
|
|
2344
|
+
type: "string",
|
|
2345
|
+
enum: ["global", "project"],
|
|
2346
|
+
description: "'global' = applies across every project (preferences, tooling); 'project' = scoped to the current sandbox (decisions, local facts). Only available in `reasonix code`."
|
|
2347
|
+
},
|
|
2348
|
+
name: {
|
|
2349
|
+
type: "string",
|
|
2350
|
+
description: "filename-safe identifier, 3-40 chars, alnum + _ - . (no path separators, no leading dot)."
|
|
2351
|
+
},
|
|
2352
|
+
description: {
|
|
2353
|
+
type: "string",
|
|
2354
|
+
description: "One-line summary shown in MEMORY.md (under ~150 chars)."
|
|
2355
|
+
},
|
|
2356
|
+
content: {
|
|
2357
|
+
type: "string",
|
|
2358
|
+
description: "Full memory body in markdown. For feedback/project types, structure as: rule/fact, then **Why:** line, then **How to apply:** line."
|
|
2359
|
+
}
|
|
2360
|
+
},
|
|
2361
|
+
required: ["type", "scope", "name", "description", "content"]
|
|
2362
|
+
},
|
|
2363
|
+
fn: async (args) => {
|
|
2364
|
+
if (args.scope === "project" && !hasProject) {
|
|
2365
|
+
return JSON.stringify({
|
|
2366
|
+
error: "scope='project' is unavailable in this session (no sandbox root). Retry with scope='global', or ask the user to switch to `reasonix code` for project-scoped memory."
|
|
2367
|
+
});
|
|
2368
|
+
}
|
|
2369
|
+
try {
|
|
2370
|
+
const path = store.write({
|
|
2371
|
+
name: args.name,
|
|
2372
|
+
type: args.type,
|
|
2373
|
+
scope: args.scope,
|
|
2374
|
+
description: args.description,
|
|
2375
|
+
body: args.content
|
|
2376
|
+
});
|
|
2377
|
+
const key = sanitizeMemoryName(args.name);
|
|
2378
|
+
return [
|
|
2379
|
+
`\u2713 REMEMBERED (${args.scope}/${key}): ${args.description}`,
|
|
2380
|
+
"",
|
|
2381
|
+
"TREAT THIS AS ESTABLISHED FACT for the rest of this session.",
|
|
2382
|
+
"The user just told you \u2014 don't re-explore the filesystem to re-derive it.",
|
|
2383
|
+
`(Saved to ${path}; pins into the system prompt on next /new or launch.)`
|
|
2384
|
+
].join("\n");
|
|
2385
|
+
} catch (err) {
|
|
2386
|
+
return JSON.stringify({ error: `remember failed: ${err.message}` });
|
|
2387
|
+
}
|
|
2388
|
+
}
|
|
2389
|
+
});
|
|
2390
|
+
registry.register({
|
|
2391
|
+
name: "forget",
|
|
2392
|
+
description: "Delete a memory file and remove it from MEMORY.md. Use when the user explicitly asks to forget something, or when a previously-remembered fact has become wrong. Irreversible \u2014 no tombstone.",
|
|
2393
|
+
parameters: {
|
|
2394
|
+
type: "object",
|
|
2395
|
+
properties: {
|
|
2396
|
+
name: { type: "string", description: "Memory name (the identifier used in `remember`)." },
|
|
2397
|
+
scope: { type: "string", enum: ["global", "project"] }
|
|
2398
|
+
},
|
|
2399
|
+
required: ["name", "scope"]
|
|
2400
|
+
},
|
|
2401
|
+
fn: async (args) => {
|
|
2402
|
+
if (args.scope === "project" && !hasProject) {
|
|
2403
|
+
return JSON.stringify({
|
|
2404
|
+
error: "scope='project' is unavailable in this session (no sandbox root)."
|
|
2405
|
+
});
|
|
2406
|
+
}
|
|
2407
|
+
try {
|
|
2408
|
+
const existed = store.delete(args.scope, args.name);
|
|
2409
|
+
return existed ? `forgot (${args.scope}/${sanitizeMemoryName(args.name)}). Re-load on next /new or launch.` : `no such memory: ${args.scope}/${args.name} (nothing to forget).`;
|
|
2410
|
+
} catch (err) {
|
|
2411
|
+
return JSON.stringify({ error: `forget failed: ${err.message}` });
|
|
2412
|
+
}
|
|
2413
|
+
}
|
|
2414
|
+
});
|
|
2415
|
+
registry.register({
|
|
2416
|
+
name: "recall_memory",
|
|
2417
|
+
description: "Read the full body of a memory file when its MEMORY.md one-liner (already in the system prompt) isn't enough detail. Most of the time the index suffices \u2014 only call this when the user's question genuinely requires the full context.",
|
|
2418
|
+
readOnly: true,
|
|
2419
|
+
parameters: {
|
|
2420
|
+
type: "object",
|
|
2421
|
+
properties: {
|
|
2422
|
+
name: { type: "string" },
|
|
2423
|
+
scope: { type: "string", enum: ["global", "project"] }
|
|
2424
|
+
},
|
|
2425
|
+
required: ["name", "scope"]
|
|
2426
|
+
},
|
|
2427
|
+
fn: async (args) => {
|
|
2428
|
+
if (args.scope === "project" && !hasProject) {
|
|
2429
|
+
return JSON.stringify({
|
|
2430
|
+
error: "scope='project' is unavailable in this session (no sandbox root)."
|
|
2431
|
+
});
|
|
2432
|
+
}
|
|
2433
|
+
try {
|
|
2434
|
+
const entry = store.read(args.scope, args.name);
|
|
2435
|
+
return [
|
|
2436
|
+
`# ${entry.name} (${entry.scope}/${entry.type}, created ${entry.createdAt || "?"})`,
|
|
2437
|
+
entry.description ? `> ${entry.description}` : "",
|
|
2438
|
+
"",
|
|
2439
|
+
entry.body
|
|
2440
|
+
].filter(Boolean).join("\n");
|
|
2441
|
+
} catch (err) {
|
|
2442
|
+
return JSON.stringify({ error: `recall failed: ${err.message}` });
|
|
2443
|
+
}
|
|
2444
|
+
}
|
|
2445
|
+
});
|
|
2446
|
+
return registry;
|
|
2447
|
+
}
|
|
2448
|
+
|
|
2326
2449
|
// src/tools/plan.ts
|
|
2327
2450
|
var PlanProposedError = class extends Error {
|
|
2328
2451
|
plan;
|
|
@@ -2540,7 +2663,7 @@ function resolveExecutable(cmd, opts = {}) {
|
|
|
2540
2663
|
const isFile = opts.isFile ?? defaultIsFile;
|
|
2541
2664
|
for (const dir of pathDirs) {
|
|
2542
2665
|
for (const ext of pathExt) {
|
|
2543
|
-
const full = pathMod2.join(dir, cmd + ext);
|
|
2666
|
+
const full = pathMod2.win32.join(dir, cmd + ext);
|
|
2544
2667
|
if (isFile(full)) return full;
|
|
2545
2668
|
}
|
|
2546
2669
|
}
|
|
@@ -2598,7 +2721,7 @@ function registerShellTools(registry, opts) {
|
|
|
2598
2721
|
const allowAll = opts.allowAll ?? false;
|
|
2599
2722
|
registry.register({
|
|
2600
2723
|
name: "run_command",
|
|
2601
|
-
description: "Run a shell command in the project root and return its combined stdout+stderr.
|
|
2724
|
+
description: "Run a shell command in the project root and return its combined stdout+stderr. Common read-only inspection and test/lint/typecheck commands run immediately; anything that could mutate state, install dependencies, or touch the network is refused until the user confirms it in the TUI. Prefer this over asking the user to run a command manually \u2014 after edits, run the project's tests to verify.",
|
|
2602
2725
|
// Plan-mode gate: allow allowlisted commands through (git status,
|
|
2603
2726
|
// cargo check, ls, grep …) so the model can actually investigate
|
|
2604
2727
|
// during planning. Anything that would otherwise trigger a
|
|
@@ -2614,7 +2737,7 @@ function registerShellTools(registry, opts) {
|
|
|
2614
2737
|
properties: {
|
|
2615
2738
|
command: {
|
|
2616
2739
|
type: "string",
|
|
2617
|
-
description: "Full command line
|
|
2740
|
+
description: "Full command line. Tokenized with POSIX-ish quoting; no shell expansion, no pipes, no redirects."
|
|
2618
2741
|
},
|
|
2619
2742
|
timeoutSec: {
|
|
2620
2743
|
type: "integer",
|
|
@@ -4119,7 +4242,7 @@ function sep() {
|
|
|
4119
4242
|
}
|
|
4120
4243
|
|
|
4121
4244
|
// src/index.ts
|
|
4122
|
-
var VERSION = "0.4.
|
|
4245
|
+
var VERSION = "0.4.20";
|
|
4123
4246
|
|
|
4124
4247
|
// src/cli/commands/chat.tsx
|
|
4125
4248
|
import { existsSync as existsSync4, statSync as statSync3 } from "fs";
|
|
@@ -5093,7 +5216,11 @@ var SLASH_COMMANDS = [
|
|
|
5093
5216
|
{ cmd: "branch", argsHint: "<N|off>", summary: "run N parallel samples per turn (N>=2)" },
|
|
5094
5217
|
{ cmd: "mcp", summary: "list MCP servers + tools attached to this session" },
|
|
5095
5218
|
{ cmd: "tool", argsHint: "[N]", summary: "dump full output of the Nth tool call (1=latest)" },
|
|
5096
|
-
{
|
|
5219
|
+
{
|
|
5220
|
+
cmd: "memory",
|
|
5221
|
+
argsHint: "[list|show <name>|forget <name>|clear <scope> confirm]",
|
|
5222
|
+
summary: "show / manage pinned memory (REASONIX.md + ~/.reasonix/memory)"
|
|
5223
|
+
},
|
|
5097
5224
|
{ cmd: "think", summary: "dump the last turn's full R1 reasoning (reasoner only)" },
|
|
5098
5225
|
{ cmd: "retry", summary: "truncate & resend your last message (fresh sample)" },
|
|
5099
5226
|
{ cmd: "compact", argsHint: "[cap]", summary: "shrink oversized tool results in the log" },
|
|
@@ -5173,7 +5300,8 @@ function handleSlash(cmd, args, loop, ctx = {}) {
|
|
|
5173
5300
|
" /compact [cap] shrink large tool results in history (default 4k/result)",
|
|
5174
5301
|
" /think dump the most recent turn's full R1 reasoning (reasoner only)",
|
|
5175
5302
|
" /tool [N] list tool calls (or dump full output of #N, 1=most recent)",
|
|
5176
|
-
" /memory
|
|
5303
|
+
" /memory [sub] show pinned memory (REASONIX.md + ~/.reasonix/memory).",
|
|
5304
|
+
" subs: list | show <name> | forget <name> | clear <scope> confirm",
|
|
5177
5305
|
" /retry truncate & resend your last message (fresh sample from the model)",
|
|
5178
5306
|
" /apply (code mode) commit the pending edit blocks to disk",
|
|
5179
5307
|
" /discard (code mode) drop pending edits without writing",
|
|
@@ -5258,43 +5386,7 @@ function handleSlash(cmd, args, loop, ctx = {}) {
|
|
|
5258
5386
|
};
|
|
5259
5387
|
}
|
|
5260
5388
|
case "memory": {
|
|
5261
|
-
|
|
5262
|
-
return {
|
|
5263
|
-
info: "project memory is disabled (REASONIX_MEMORY=off in env). Unset the var to re-enable; no REASONIX.md will be pinned in the meantime."
|
|
5264
|
-
};
|
|
5265
|
-
}
|
|
5266
|
-
if (!ctx.memoryRoot) {
|
|
5267
|
-
return {
|
|
5268
|
-
info: "no project root on this session \u2014 `/memory` needs a working directory to resolve REASONIX.md from."
|
|
5269
|
-
};
|
|
5270
|
-
}
|
|
5271
|
-
const mem = readProjectMemory(ctx.memoryRoot);
|
|
5272
|
-
if (!mem) {
|
|
5273
|
-
return {
|
|
5274
|
-
info: [
|
|
5275
|
-
`no ${PROJECT_MEMORY_FILE} in ${ctx.memoryRoot}.`,
|
|
5276
|
-
"",
|
|
5277
|
-
"Project memory is an optional file you pin notes into \u2014 project conventions,",
|
|
5278
|
-
"things the model keeps forgetting, domain glossary, setup gotchas. When present,",
|
|
5279
|
-
"its contents are appended to the system prompt (the immutable-prefix region)",
|
|
5280
|
-
"so every turn sees it without eating per-turn context, and the prefix cache stays",
|
|
5281
|
-
"warm as long as the file is stable.",
|
|
5282
|
-
"",
|
|
5283
|
-
`Create it with: echo "# Project notes for Reasonix" > ${PROJECT_MEMORY_FILE}`,
|
|
5284
|
-
"Re-launch (or `/new`) to pick up changes \u2014 the prefix is hashed at session start."
|
|
5285
|
-
].join("\n")
|
|
5286
|
-
};
|
|
5287
|
-
}
|
|
5288
|
-
const header = mem.truncated ? `\u25B8 project memory: ${mem.path} (${mem.originalChars.toLocaleString()} chars, truncated for the prefix)` : `\u25B8 project memory: ${mem.path} (${mem.originalChars.toLocaleString()} chars)`;
|
|
5289
|
-
return {
|
|
5290
|
-
info: [
|
|
5291
|
-
header,
|
|
5292
|
-
"",
|
|
5293
|
-
mem.content,
|
|
5294
|
-
"",
|
|
5295
|
-
"Changes take effect on the next launch or `/new` \u2014 the system prompt is hashed once per session to keep the prefix cache warm."
|
|
5296
|
-
].join("\n")
|
|
5297
|
-
};
|
|
5389
|
+
return handleMemorySlash(args, ctx);
|
|
5298
5390
|
}
|
|
5299
5391
|
case "think":
|
|
5300
5392
|
case "reasoning": {
|
|
@@ -5532,6 +5624,158 @@ ${entry.text}`
|
|
|
5532
5624
|
return { unknown: true, info: `unknown command: /${cmd} (try /help)` };
|
|
5533
5625
|
}
|
|
5534
5626
|
}
|
|
5627
|
+
function handleMemorySlash(args, ctx) {
|
|
5628
|
+
if (!memoryEnabled()) {
|
|
5629
|
+
return {
|
|
5630
|
+
info: "memory is disabled (REASONIX_MEMORY=off in env). Unset the var to re-enable \u2014 no REASONIX.md or ~/.reasonix/memory content will be pinned in the meantime."
|
|
5631
|
+
};
|
|
5632
|
+
}
|
|
5633
|
+
if (!ctx.memoryRoot) {
|
|
5634
|
+
return {
|
|
5635
|
+
info: "no working directory on this session \u2014 `/memory` needs a root to resolve REASONIX.md from. (Running in a test harness?)"
|
|
5636
|
+
};
|
|
5637
|
+
}
|
|
5638
|
+
const store = new MemoryStore({ projectRoot: ctx.codeRoot });
|
|
5639
|
+
const sub = (args[0] ?? "").toLowerCase();
|
|
5640
|
+
if (sub === "list" || sub === "ls") {
|
|
5641
|
+
const entries = store.list();
|
|
5642
|
+
if (entries.length === 0) {
|
|
5643
|
+
return {
|
|
5644
|
+
info: "no user memories yet. The model can call `remember` to save one, or you can create files by hand in ~/.reasonix/memory/global/ or the per-project subdir."
|
|
5645
|
+
};
|
|
5646
|
+
}
|
|
5647
|
+
const lines = [`User memories (${entries.length}):`];
|
|
5648
|
+
for (const e of entries) {
|
|
5649
|
+
const tag = `${e.scope}/${e.type}`.padEnd(18);
|
|
5650
|
+
const name = e.name.padEnd(28);
|
|
5651
|
+
const desc = e.description.length > 70 ? `${e.description.slice(0, 69)}\u2026` : e.description;
|
|
5652
|
+
lines.push(` ${tag} ${name} ${desc}`);
|
|
5653
|
+
}
|
|
5654
|
+
lines.push("");
|
|
5655
|
+
lines.push("View body: /memory show <name> Delete: /memory forget <name>");
|
|
5656
|
+
return { info: lines.join("\n") };
|
|
5657
|
+
}
|
|
5658
|
+
if (sub === "show" || sub === "cat") {
|
|
5659
|
+
const target = args[1];
|
|
5660
|
+
if (!target) return { info: "usage: /memory show <name> or /memory show <scope>/<name>" };
|
|
5661
|
+
const resolved = resolveMemoryTarget(store, target);
|
|
5662
|
+
if (!resolved) return { info: `no memory found: ${target}` };
|
|
5663
|
+
try {
|
|
5664
|
+
const entry = store.read(resolved.scope, resolved.name);
|
|
5665
|
+
return {
|
|
5666
|
+
info: [
|
|
5667
|
+
`\u25B8 ${entry.scope}/${entry.name} (${entry.type}, created ${entry.createdAt || "?"})`,
|
|
5668
|
+
entry.description ? ` ${entry.description}` : "",
|
|
5669
|
+
"",
|
|
5670
|
+
entry.body
|
|
5671
|
+
].filter((l) => l !== "").concat("").join("\n")
|
|
5672
|
+
};
|
|
5673
|
+
} catch (err) {
|
|
5674
|
+
return { info: `show failed: ${err.message}` };
|
|
5675
|
+
}
|
|
5676
|
+
}
|
|
5677
|
+
if (sub === "forget" || sub === "rm" || sub === "delete") {
|
|
5678
|
+
const target = args[1];
|
|
5679
|
+
if (!target) return { info: "usage: /memory forget <name> or /memory forget <scope>/<name>" };
|
|
5680
|
+
const resolved = resolveMemoryTarget(store, target);
|
|
5681
|
+
if (!resolved) return { info: `no memory found: ${target}` };
|
|
5682
|
+
try {
|
|
5683
|
+
const ok = store.delete(resolved.scope, resolved.name);
|
|
5684
|
+
return {
|
|
5685
|
+
info: ok ? `\u25B8 forgot ${resolved.scope}/${resolved.name}. Next /new or launch won't see it.` : `could not forget ${resolved.scope}/${resolved.name} (already gone?)`
|
|
5686
|
+
};
|
|
5687
|
+
} catch (err) {
|
|
5688
|
+
return { info: `forget failed: ${err.message}` };
|
|
5689
|
+
}
|
|
5690
|
+
}
|
|
5691
|
+
if (sub === "clear") {
|
|
5692
|
+
const rawScope = (args[1] ?? "").toLowerCase();
|
|
5693
|
+
if (rawScope !== "global" && rawScope !== "project") {
|
|
5694
|
+
return { info: "usage: /memory clear <global|project> confirm" };
|
|
5695
|
+
}
|
|
5696
|
+
if ((args[2] ?? "").toLowerCase() !== "confirm") {
|
|
5697
|
+
return {
|
|
5698
|
+
info: `about to delete every memory in scope=${rawScope}. Re-run with the word 'confirm' to proceed: /memory clear ${rawScope} confirm`
|
|
5699
|
+
};
|
|
5700
|
+
}
|
|
5701
|
+
const scope = rawScope;
|
|
5702
|
+
const entries = store.list().filter((e) => e.scope === scope);
|
|
5703
|
+
let deleted = 0;
|
|
5704
|
+
for (const e of entries) {
|
|
5705
|
+
try {
|
|
5706
|
+
if (store.delete(scope, e.name)) deleted++;
|
|
5707
|
+
} catch {
|
|
5708
|
+
}
|
|
5709
|
+
}
|
|
5710
|
+
return { info: `\u25B8 cleared scope=${scope} \u2014 deleted ${deleted} memory file(s).` };
|
|
5711
|
+
}
|
|
5712
|
+
const parts = [];
|
|
5713
|
+
const projMem = readProjectMemory(ctx.memoryRoot);
|
|
5714
|
+
if (projMem) {
|
|
5715
|
+
const hdr = projMem.truncated ? `\u25B8 ${PROJECT_MEMORY_FILE}: ${projMem.path} (${projMem.originalChars.toLocaleString()} chars, truncated)` : `\u25B8 ${PROJECT_MEMORY_FILE}: ${projMem.path} (${projMem.originalChars.toLocaleString()} chars)`;
|
|
5716
|
+
parts.push(hdr, "", projMem.content);
|
|
5717
|
+
}
|
|
5718
|
+
const globalIdx = store.loadIndex("global");
|
|
5719
|
+
if (globalIdx) {
|
|
5720
|
+
parts.push(
|
|
5721
|
+
"",
|
|
5722
|
+
`\u25B8 global memory (${globalIdx.originalChars.toLocaleString()} chars${globalIdx.truncated ? ", truncated" : ""})`,
|
|
5723
|
+
"",
|
|
5724
|
+
globalIdx.content
|
|
5725
|
+
);
|
|
5726
|
+
}
|
|
5727
|
+
const projectIdx = store.loadIndex("project");
|
|
5728
|
+
if (projectIdx) {
|
|
5729
|
+
parts.push(
|
|
5730
|
+
"",
|
|
5731
|
+
`\u25B8 project memory (${projectIdx.originalChars.toLocaleString()} chars${projectIdx.truncated ? ", truncated" : ""})`,
|
|
5732
|
+
"",
|
|
5733
|
+
projectIdx.content
|
|
5734
|
+
);
|
|
5735
|
+
}
|
|
5736
|
+
if (parts.length === 0) {
|
|
5737
|
+
return {
|
|
5738
|
+
info: [
|
|
5739
|
+
`no memory pinned in ${ctx.memoryRoot}.`,
|
|
5740
|
+
"",
|
|
5741
|
+
"Three layers are available:",
|
|
5742
|
+
` 1. ${PROJECT_MEMORY_FILE} \u2014 committable team memory (in the repo).`,
|
|
5743
|
+
" 2. ~/.reasonix/memory/global/ \u2014 your cross-project private memory.",
|
|
5744
|
+
` 3. ~/.reasonix/memory/<project-hash>/ \u2014 this project's private memory.`,
|
|
5745
|
+
"",
|
|
5746
|
+
"Ask the model to `remember` something, or hand-edit files directly.",
|
|
5747
|
+
"Changes take effect on next /new or launch \u2014 the system prompt is hashed once per session to keep the prefix cache warm.",
|
|
5748
|
+
"",
|
|
5749
|
+
"Subcommands: /memory list | /memory show <name> | /memory forget <name> | /memory clear <scope> confirm"
|
|
5750
|
+
].join("\n")
|
|
5751
|
+
};
|
|
5752
|
+
}
|
|
5753
|
+
parts.push(
|
|
5754
|
+
"",
|
|
5755
|
+
"Changes take effect on next /new or launch. Subcommands: /memory list | show | forget | clear"
|
|
5756
|
+
);
|
|
5757
|
+
return { info: parts.join("\n") };
|
|
5758
|
+
}
|
|
5759
|
+
function resolveMemoryTarget(store, raw) {
|
|
5760
|
+
const slash = raw.indexOf("/");
|
|
5761
|
+
if (slash > 0) {
|
|
5762
|
+
const scopeRaw = raw.slice(0, slash).toLowerCase();
|
|
5763
|
+
const name = raw.slice(slash + 1);
|
|
5764
|
+
if (scopeRaw !== "global" && scopeRaw !== "project") return null;
|
|
5765
|
+
const scope = scopeRaw;
|
|
5766
|
+
if (scope === "project" && !store.hasProjectScope()) return null;
|
|
5767
|
+
return { scope, name };
|
|
5768
|
+
}
|
|
5769
|
+
for (const scope of ["project", "global"]) {
|
|
5770
|
+
if (scope === "project" && !store.hasProjectScope()) continue;
|
|
5771
|
+
try {
|
|
5772
|
+
store.read(scope, raw);
|
|
5773
|
+
return { scope, name: raw };
|
|
5774
|
+
} catch {
|
|
5775
|
+
}
|
|
5776
|
+
}
|
|
5777
|
+
return null;
|
|
5778
|
+
}
|
|
5535
5779
|
function appendSection(lines, label, section) {
|
|
5536
5780
|
if (!section || !section.supported) {
|
|
5537
5781
|
lines.push(
|
|
@@ -6611,6 +6855,10 @@ async function chatCommand(opts) {
|
|
|
6611
6855
|
if (!tools) tools = new ToolRegistry();
|
|
6612
6856
|
registerWebTools(tools);
|
|
6613
6857
|
}
|
|
6858
|
+
if (!opts.seedTools) {
|
|
6859
|
+
if (!tools) tools = new ToolRegistry();
|
|
6860
|
+
registerMemoryTools(tools, {});
|
|
6861
|
+
}
|
|
6614
6862
|
let sessionPreview;
|
|
6615
6863
|
if (opts.session && !opts.forceResume && !opts.forceNew) {
|
|
6616
6864
|
const prior = loadSessionMessages(opts.session);
|
|
@@ -6649,7 +6897,7 @@ async function chatCommand(opts) {
|
|
|
6649
6897
|
// src/cli/commands/code.tsx
|
|
6650
6898
|
import { basename, resolve as resolve5 } from "path";
|
|
6651
6899
|
async function codeCommand(opts = {}) {
|
|
6652
|
-
const { codeSystemPrompt: codeSystemPrompt2 } = await import("./prompt-
|
|
6900
|
+
const { codeSystemPrompt: codeSystemPrompt2 } = await import("./prompt-YEJEJ3IZ.js");
|
|
6653
6901
|
const rootDir = resolve5(opts.dir ?? process.cwd());
|
|
6654
6902
|
const session = opts.noSession ? void 0 : `code-${sanitizeName(basename(rootDir))}`;
|
|
6655
6903
|
const tools = new ToolRegistry();
|
|
@@ -6661,6 +6909,7 @@ async function codeCommand(opts = {}) {
|
|
|
6661
6909
|
extraAllowed: loadProjectShellAllowed(rootDir)
|
|
6662
6910
|
});
|
|
6663
6911
|
registerPlanTool(tools);
|
|
6912
|
+
registerMemoryTools(tools, { projectRoot: rootDir });
|
|
6664
6913
|
process.stderr.write(
|
|
6665
6914
|
`\u25B8 reasonix code: rooted at ${rootDir}, session "${session ?? "(ephemeral)"}" \xB7 ${tools.size} native tool(s)
|
|
6666
6915
|
`
|
|
@@ -7710,7 +7959,7 @@ program.action(async () => {
|
|
|
7710
7959
|
const defaults = resolveDefaults({});
|
|
7711
7960
|
await chatCommand({
|
|
7712
7961
|
model: defaults.model,
|
|
7713
|
-
system:
|
|
7962
|
+
system: applyMemoryStack(DEFAULT_SYSTEM, process.cwd()),
|
|
7714
7963
|
harvest: defaults.harvest,
|
|
7715
7964
|
branch: defaults.branch,
|
|
7716
7965
|
session: defaults.session,
|
|
@@ -7762,7 +8011,7 @@ program.command("chat").description("Interactive Ink TUI with live cache/cost pa
|
|
|
7762
8011
|
});
|
|
7763
8012
|
await chatCommand({
|
|
7764
8013
|
model: defaults.model,
|
|
7765
|
-
system:
|
|
8014
|
+
system: applyMemoryStack(opts.system, process.cwd()),
|
|
7766
8015
|
transcript: opts.transcript,
|
|
7767
8016
|
harvest: defaults.harvest,
|
|
7768
8017
|
branch: defaults.branch,
|
|
@@ -7797,7 +8046,7 @@ program.command("run <task>").description("Run a single task non-interactively,
|
|
|
7797
8046
|
await runCommand2({
|
|
7798
8047
|
task,
|
|
7799
8048
|
model: defaults.model,
|
|
7800
|
-
system:
|
|
8049
|
+
system: applyMemoryStack(opts.system, process.cwd()),
|
|
7801
8050
|
harvest: defaults.harvest,
|
|
7802
8051
|
branch: defaults.branch,
|
|
7803
8052
|
transcript: opts.transcript,
|