reasonix 0.50.1 → 0.51.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/dashboard/dist/app.css +1 -1
- package/dashboard/dist/app.js +24 -22
- package/dashboard/dist/app.js.map +1 -1
- package/dist/cli/{acp-6B25WIFF.js → acp-XEUHGG7X.js} +34 -31
- package/dist/cli/acp-XEUHGG7X.js.map +1 -0
- package/dist/cli/chat-NJ2Q5KHG.js +50 -0
- package/dist/cli/{chunk-OPGWCKKU.js → chunk-2HVTBFCI.js} +3 -3
- package/dist/cli/{chunk-AJIZ5KFK.js → chunk-2WUEAI2I.js} +3 -3
- package/dist/cli/{chunk-I4Q3QT4W.js → chunk-36BM7INR.js} +2 -2
- package/dist/cli/{chunk-3RNFYDDM.js → chunk-3BTK5BHI.js} +11 -7
- package/dist/cli/chunk-3BTK5BHI.js.map +1 -0
- package/dist/cli/{chunk-GMSAB2TC.js → chunk-3YRTIWFX.js} +2 -2
- package/dist/cli/{chunk-NLRC3DWQ.js → chunk-544J4PXD.js} +5 -5
- package/dist/cli/{chunk-7WITYWKN.js → chunk-5AIDYVH2.js} +2 -2
- package/dist/cli/{chunk-ALCOQP6R.js → chunk-5BBC6YMV.js} +5 -5
- package/dist/cli/{chunk-S4XVGLRW.js → chunk-6UNHNVJR.js} +72 -5
- package/dist/cli/chunk-6UNHNVJR.js.map +1 -0
- package/dist/cli/{chunk-IK6WWRIX.js → chunk-6XWXIVQ3.js} +38 -22
- package/dist/cli/chunk-6XWXIVQ3.js.map +1 -0
- package/dist/cli/{chunk-AAHB2PFX.js → chunk-7YB26OQO.js} +4 -4
- package/dist/cli/chunk-7YB26OQO.js.map +1 -0
- package/dist/cli/{chunk-MXWPAPZW.js → chunk-A5PBEIJ7.js} +53 -10
- package/dist/cli/chunk-A5PBEIJ7.js.map +1 -0
- package/dist/cli/{chunk-FQSQFCBI.js → chunk-BA5R6BAE.js} +2 -2
- package/dist/cli/{chunk-XWPZHWC2.js → chunk-BM6BBFAV.js} +2 -2
- package/dist/cli/{chunk-CAGKEGNE.js → chunk-BOWSNGQC.js} +52 -140
- package/dist/cli/chunk-BOWSNGQC.js.map +1 -0
- package/dist/cli/{chunk-EZ57UEZQ.js → chunk-C2MRSJTV.js} +2 -2
- package/dist/cli/{chunk-PYIZZAVQ.js → chunk-DVD67FXQ.js} +1716 -4
- package/dist/cli/chunk-DVD67FXQ.js.map +1 -0
- package/dist/cli/{chunk-ZAXMJANP.js → chunk-EAMXOWUW.js} +3 -3
- package/dist/cli/{chunk-TX652NBA.js → chunk-EWVFGYT6.js} +2 -2
- package/dist/cli/{chunk-IBRTU5WO.js → chunk-FP7IOWBQ.js} +18 -1182
- package/dist/cli/chunk-FP7IOWBQ.js.map +1 -0
- package/dist/cli/{chunk-I6FBSTTR.js → chunk-HGK57NBN.js} +9 -353
- package/dist/cli/chunk-HGK57NBN.js.map +1 -0
- package/dist/cli/chunk-JHWQDJZA.js +80 -0
- package/dist/cli/chunk-JHWQDJZA.js.map +1 -0
- package/dist/cli/{chunk-X2BQZQEE.js → chunk-K3QJ3GKI.js} +3 -3
- package/dist/cli/{chunk-GPUH2BNM.js → chunk-K4YQFULP.js} +612 -254
- package/dist/cli/chunk-K4YQFULP.js.map +1 -0
- package/dist/cli/chunk-L3VPEESB.js +31 -0
- package/dist/cli/chunk-L3VPEESB.js.map +1 -0
- package/dist/cli/{chunk-ENFBF6HI.js → chunk-N4SEBLU4.js} +383 -5
- package/dist/cli/chunk-N4SEBLU4.js.map +1 -0
- package/dist/cli/chunk-NRROJXXT.js +879 -0
- package/dist/cli/chunk-NRROJXXT.js.map +1 -0
- package/dist/cli/{chunk-3KRRTLC5.js → chunk-R6KIHEF3.js} +1619 -1036
- package/dist/cli/chunk-R6KIHEF3.js.map +1 -0
- package/dist/cli/{chunk-VVMY4M7J.js → chunk-SBHF5NWD.js} +27 -4
- package/dist/cli/chunk-SBHF5NWD.js.map +1 -0
- package/dist/cli/{chunk-OWA42BKS.js → chunk-SXSAWOB7.js} +14 -14
- package/dist/cli/{chunk-6IUMTRFP.js → chunk-UMZ6KHTS.js} +2 -2
- package/dist/cli/{chunk-7X4JJOO7.js → chunk-UO6E7FN3.js} +69 -5
- package/dist/cli/{chunk-7X4JJOO7.js.map → chunk-UO6E7FN3.js.map} +1 -1
- package/dist/cli/{chunk-3ZZXQ3CZ.js → chunk-UPW544V3.js} +2 -2
- package/dist/cli/{chunk-XJZWMU5P.js → chunk-WPOKBW5E.js} +2 -2
- package/dist/cli/{chunk-WSBFVOCO.js → chunk-Z3MKG7MQ.js} +2 -2
- package/dist/cli/{code-TBK2TASK.js → code-BMXLBC7D.js} +37 -36
- package/dist/cli/{code-TBK2TASK.js.map → code-BMXLBC7D.js.map} +1 -1
- package/dist/cli/{commands-NXTKSQTN.js → commands-E4RZXMF6.js} +5 -5
- package/dist/cli/{commit-IR5SPP7A.js → commit-KSRQ64IL.js} +3 -3
- package/dist/cli/{config-XK5WQGTS.js → config-QNDONOTU.js} +4 -2
- package/dist/cli/{desktop-5NTQBADL.js → desktop-H3ZHIMDA.js} +83 -37
- package/dist/cli/desktop-H3ZHIMDA.js.map +1 -0
- package/dist/cli/{diff-JNYX5BSZ.js → diff-I4PYI43W.js} +9 -9
- package/dist/cli/{doctor-IKYLUFXX.js → doctor-Y2E4MY2F.js} +12 -12
- package/dist/cli/{events-HSC57ONU.js → events-47HOT7ZA.js} +5 -5
- package/dist/cli/find-in-code-YLEIK5FK.js +145 -0
- package/dist/cli/find-in-code-YLEIK5FK.js.map +1 -0
- package/dist/cli/index.js +95 -44
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/{mcp-BDJJWOCD.js → mcp-76DK63ZB.js} +3 -3
- package/dist/cli/{mcp-browse-NJRZDI6V.js → mcp-browse-SDNUGO74.js} +3 -3
- package/dist/cli/{mcp-inspect-Y62NWZQL.js → mcp-inspect-BL5DEO5M.js} +3 -3
- package/dist/cli/{prompt-UTOIFUQC.js → prompt-JLATI3P7.js} +5 -5
- package/dist/cli/{prune-sessions-UCUD4XAP.js → prune-sessions-WHZDFUKD.js} +4 -4
- package/dist/cli/{replay-VVIN64MN.js → replay-MHXS7C7Z.js} +10 -10
- package/dist/cli/{run-76OBDZFB.js → run-SXNCPRJE.js} +22 -22
- package/dist/cli/{server-SZZDKTH2.js → server-GEHOE6CO.js} +61 -35
- package/dist/cli/server-GEHOE6CO.js.map +1 -0
- package/dist/cli/{sessions-FZTGRCM5.js → sessions-EPBFYISL.js} +18 -18
- package/dist/cli/{setup-4UNENGOE.js → setup-IW2XR5XI.js} +8 -7
- package/dist/cli/setup-IW2XR5XI.js.map +1 -0
- package/dist/cli/{stats-F4NDOD7D.js → stats-4WB4XHBP.js} +6 -6
- package/dist/cli/symbols-UQ274IOB.js +167 -0
- package/dist/cli/symbols-UQ274IOB.js.map +1 -0
- package/dist/cli/version-4SP3DLLH.js +33 -0
- package/dist/index.d.ts +25 -6
- package/dist/index.js +2700 -578
- package/dist/index.js.map +1 -1
- package/package.json +4 -2
- package/scripts/postinstall.mjs +3 -5
- package/dist/cli/acp-6B25WIFF.js.map +0 -1
- package/dist/cli/chat-7WASPB4O.js +0 -50
- package/dist/cli/chunk-3KRRTLC5.js.map +0 -1
- package/dist/cli/chunk-3RNFYDDM.js.map +0 -1
- package/dist/cli/chunk-AAHB2PFX.js.map +0 -1
- package/dist/cli/chunk-CAGKEGNE.js.map +0 -1
- package/dist/cli/chunk-ENFBF6HI.js.map +0 -1
- package/dist/cli/chunk-GPUH2BNM.js.map +0 -1
- package/dist/cli/chunk-I6FBSTTR.js.map +0 -1
- package/dist/cli/chunk-IBRTU5WO.js.map +0 -1
- package/dist/cli/chunk-IK6WWRIX.js.map +0 -1
- package/dist/cli/chunk-MXWPAPZW.js.map +0 -1
- package/dist/cli/chunk-PYIZZAVQ.js.map +0 -1
- package/dist/cli/chunk-S4XVGLRW.js.map +0 -1
- package/dist/cli/chunk-VVMY4M7J.js.map +0 -1
- package/dist/cli/desktop-5NTQBADL.js.map +0 -1
- package/dist/cli/server-SZZDKTH2.js.map +0 -1
- package/dist/cli/setup-4UNENGOE.js.map +0 -1
- package/dist/cli/version-LUVTWHLL.js +0 -33
- /package/dist/cli/{chat-7WASPB4O.js.map → chat-NJ2Q5KHG.js.map} +0 -0
- /package/dist/cli/{chunk-OPGWCKKU.js.map → chunk-2HVTBFCI.js.map} +0 -0
- /package/dist/cli/{chunk-AJIZ5KFK.js.map → chunk-2WUEAI2I.js.map} +0 -0
- /package/dist/cli/{chunk-I4Q3QT4W.js.map → chunk-36BM7INR.js.map} +0 -0
- /package/dist/cli/{chunk-GMSAB2TC.js.map → chunk-3YRTIWFX.js.map} +0 -0
- /package/dist/cli/{chunk-NLRC3DWQ.js.map → chunk-544J4PXD.js.map} +0 -0
- /package/dist/cli/{chunk-7WITYWKN.js.map → chunk-5AIDYVH2.js.map} +0 -0
- /package/dist/cli/{chunk-ALCOQP6R.js.map → chunk-5BBC6YMV.js.map} +0 -0
- /package/dist/cli/{chunk-FQSQFCBI.js.map → chunk-BA5R6BAE.js.map} +0 -0
- /package/dist/cli/{chunk-XWPZHWC2.js.map → chunk-BM6BBFAV.js.map} +0 -0
- /package/dist/cli/{chunk-EZ57UEZQ.js.map → chunk-C2MRSJTV.js.map} +0 -0
- /package/dist/cli/{chunk-ZAXMJANP.js.map → chunk-EAMXOWUW.js.map} +0 -0
- /package/dist/cli/{chunk-TX652NBA.js.map → chunk-EWVFGYT6.js.map} +0 -0
- /package/dist/cli/{chunk-X2BQZQEE.js.map → chunk-K3QJ3GKI.js.map} +0 -0
- /package/dist/cli/{chunk-OWA42BKS.js.map → chunk-SXSAWOB7.js.map} +0 -0
- /package/dist/cli/{chunk-6IUMTRFP.js.map → chunk-UMZ6KHTS.js.map} +0 -0
- /package/dist/cli/{chunk-3ZZXQ3CZ.js.map → chunk-UPW544V3.js.map} +0 -0
- /package/dist/cli/{chunk-XJZWMU5P.js.map → chunk-WPOKBW5E.js.map} +0 -0
- /package/dist/cli/{chunk-WSBFVOCO.js.map → chunk-Z3MKG7MQ.js.map} +0 -0
- /package/dist/cli/{commands-NXTKSQTN.js.map → commands-E4RZXMF6.js.map} +0 -0
- /package/dist/cli/{commit-IR5SPP7A.js.map → commit-KSRQ64IL.js.map} +0 -0
- /package/dist/cli/{config-XK5WQGTS.js.map → config-QNDONOTU.js.map} +0 -0
- /package/dist/cli/{diff-JNYX5BSZ.js.map → diff-I4PYI43W.js.map} +0 -0
- /package/dist/cli/{doctor-IKYLUFXX.js.map → doctor-Y2E4MY2F.js.map} +0 -0
- /package/dist/cli/{events-HSC57ONU.js.map → events-47HOT7ZA.js.map} +0 -0
- /package/dist/cli/{mcp-BDJJWOCD.js.map → mcp-76DK63ZB.js.map} +0 -0
- /package/dist/cli/{mcp-browse-NJRZDI6V.js.map → mcp-browse-SDNUGO74.js.map} +0 -0
- /package/dist/cli/{mcp-inspect-Y62NWZQL.js.map → mcp-inspect-BL5DEO5M.js.map} +0 -0
- /package/dist/cli/{prompt-UTOIFUQC.js.map → prompt-JLATI3P7.js.map} +0 -0
- /package/dist/cli/{prune-sessions-UCUD4XAP.js.map → prune-sessions-WHZDFUKD.js.map} +0 -0
- /package/dist/cli/{replay-VVIN64MN.js.map → replay-MHXS7C7Z.js.map} +0 -0
- /package/dist/cli/{run-76OBDZFB.js.map → run-SXNCPRJE.js.map} +0 -0
- /package/dist/cli/{sessions-FZTGRCM5.js.map → sessions-EPBFYISL.js.map} +0 -0
- /package/dist/cli/{stats-F4NDOD7D.js.map → stats-4WB4XHBP.js.map} +0 -0
- /package/dist/cli/{version-LUVTWHLL.js.map → version-4SP3DLLH.js.map} +0 -0
|
@@ -2,353 +2,13 @@
|
|
|
2
2
|
import { createRequire as __cr } from 'node:module'; if (typeof globalThis.require === 'undefined') { globalThis.require = __cr(import.meta.url); }
|
|
3
3
|
import {
|
|
4
4
|
TUI_FORMATTING_RULES,
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
memoryEnabled,
|
|
9
|
-
parseFrontmatter
|
|
10
|
-
} from "./chunk-ENFBF6HI.js";
|
|
11
|
-
import {
|
|
12
|
-
loadResolvedSkillPaths,
|
|
13
|
-
memoryTypeDefaults,
|
|
14
|
-
resolveSkillPaths
|
|
15
|
-
} from "./chunk-MXWPAPZW.js";
|
|
16
|
-
|
|
17
|
-
// src/code/prompt.ts
|
|
18
|
-
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
19
|
-
import { join as join2 } from "path";
|
|
20
|
-
|
|
21
|
-
// src/memory/user.ts
|
|
22
|
-
import { createHash } from "crypto";
|
|
23
|
-
import {
|
|
24
|
-
existsSync,
|
|
25
|
-
mkdirSync,
|
|
26
|
-
readFileSync,
|
|
27
|
-
readdirSync,
|
|
28
|
-
unlinkSync,
|
|
29
|
-
writeFileSync
|
|
30
|
-
} from "fs";
|
|
31
|
-
import { homedir } from "os";
|
|
32
|
-
import { join, resolve } from "path";
|
|
33
|
-
var USER_MEMORY_DIR = "memory";
|
|
34
|
-
var MEMORY_INDEX_FILE = "MEMORY.md";
|
|
35
|
-
var MEMORY_INDEX_MAX_CHARS = 4e3;
|
|
36
|
-
var VALID_NAME = /^[a-zA-Z0-9_-][a-zA-Z0-9_.-]{1,38}[a-zA-Z0-9]$/;
|
|
37
|
-
function sanitizeMemoryName(raw) {
|
|
38
|
-
const trimmed = String(raw ?? "").trim();
|
|
39
|
-
if (!VALID_NAME.test(trimmed)) {
|
|
40
|
-
throw new Error(
|
|
41
|
-
`invalid memory name: ${JSON.stringify(raw)} \u2014 must be 3-40 chars, alnum/_/-, no path separators`
|
|
42
|
-
);
|
|
43
|
-
}
|
|
44
|
-
return trimmed;
|
|
45
|
-
}
|
|
46
|
-
function projectHash(rootDir) {
|
|
47
|
-
const abs = resolve(rootDir);
|
|
48
|
-
return createHash("sha1").update(abs).digest("hex").slice(0, 16);
|
|
49
|
-
}
|
|
50
|
-
function scopeDir(opts) {
|
|
51
|
-
if (opts.scope === "global") {
|
|
52
|
-
return join(opts.homeDir, USER_MEMORY_DIR, "global");
|
|
53
|
-
}
|
|
54
|
-
if (!opts.projectRoot) {
|
|
55
|
-
throw new Error("scope=project requires a projectRoot on MemoryStore");
|
|
56
|
-
}
|
|
57
|
-
return join(opts.homeDir, USER_MEMORY_DIR, projectHash(opts.projectRoot));
|
|
58
|
-
}
|
|
59
|
-
function ensureDir(p) {
|
|
60
|
-
if (!existsSync(p)) mkdirSync(p, { recursive: true });
|
|
61
|
-
}
|
|
62
|
-
function formatFrontmatter(e) {
|
|
63
|
-
const lines = [
|
|
64
|
-
"---",
|
|
65
|
-
`name: ${e.name}`,
|
|
66
|
-
`description: ${e.description.replace(/\n/g, " ")}`,
|
|
67
|
-
`type: ${e.type}`,
|
|
68
|
-
`scope: ${e.scope}`,
|
|
69
|
-
`created: ${e.createdAt}`
|
|
70
|
-
];
|
|
71
|
-
if (e.priority) lines.push(`priority: ${e.priority}`);
|
|
72
|
-
if (e.expires) lines.push(`expires: ${e.expires}`);
|
|
73
|
-
lines.push("---", "");
|
|
74
|
-
return lines.join("\n");
|
|
75
|
-
}
|
|
76
|
-
function coercePriority(v) {
|
|
77
|
-
return v === "low" || v === "medium" || v === "high" ? v : void 0;
|
|
78
|
-
}
|
|
79
|
-
function coerceExpires(v) {
|
|
80
|
-
return v === "project_end" ? v : void 0;
|
|
81
|
-
}
|
|
82
|
-
function todayIso() {
|
|
83
|
-
const d = /* @__PURE__ */ new Date();
|
|
84
|
-
return d.toISOString().slice(0, 10);
|
|
85
|
-
}
|
|
86
|
-
function indexLine(e) {
|
|
87
|
-
const safeDesc = e.description.replace(/\n/g, " ").trim();
|
|
88
|
-
const max = 130 - e.name.length;
|
|
89
|
-
const clipped = safeDesc.length > max ? `${safeDesc.slice(0, Math.max(1, max - 1))}\u2026` : safeDesc;
|
|
90
|
-
return `- [${e.name}](${e.name}.md) \u2014 ${clipped}`;
|
|
91
|
-
}
|
|
92
|
-
var MemoryStore = class {
|
|
93
|
-
homeDir;
|
|
94
|
-
projectRoot;
|
|
95
|
-
constructor(opts = {}) {
|
|
96
|
-
this.homeDir = opts.homeDir ?? join(homedir(), ".reasonix");
|
|
97
|
-
this.projectRoot = opts.projectRoot ? resolve(opts.projectRoot) : void 0;
|
|
98
|
-
}
|
|
99
|
-
/** Directory this store writes `scope` files into, creating it if needed. */
|
|
100
|
-
dir(scope) {
|
|
101
|
-
const d = scopeDir({ homeDir: this.homeDir, scope, projectRoot: this.projectRoot });
|
|
102
|
-
ensureDir(d);
|
|
103
|
-
return d;
|
|
104
|
-
}
|
|
105
|
-
/** Absolute path to a memory file (no existence check). */
|
|
106
|
-
pathFor(scope, name) {
|
|
107
|
-
return join(this.dir(scope), `${sanitizeMemoryName(name)}.md`);
|
|
108
|
-
}
|
|
109
|
-
/** True iff this store is configured with a project scope available. */
|
|
110
|
-
hasProjectScope() {
|
|
111
|
-
return this.projectRoot !== void 0;
|
|
112
|
-
}
|
|
113
|
-
loadIndex(scope) {
|
|
114
|
-
if (scope === "project" && !this.projectRoot) return null;
|
|
115
|
-
const file = join(
|
|
116
|
-
scopeDir({ homeDir: this.homeDir, scope, projectRoot: this.projectRoot }),
|
|
117
|
-
MEMORY_INDEX_FILE
|
|
118
|
-
);
|
|
119
|
-
if (!existsSync(file)) return null;
|
|
120
|
-
let raw;
|
|
121
|
-
try {
|
|
122
|
-
raw = readFileSync(file, "utf8");
|
|
123
|
-
} catch {
|
|
124
|
-
return null;
|
|
125
|
-
}
|
|
126
|
-
const trimmed = raw.trim();
|
|
127
|
-
if (!trimmed) return null;
|
|
128
|
-
const originalChars = trimmed.length;
|
|
129
|
-
const truncated = originalChars > MEMORY_INDEX_MAX_CHARS;
|
|
130
|
-
const content = truncated ? `${trimmed.slice(0, MEMORY_INDEX_MAX_CHARS)}
|
|
131
|
-
\u2026 (truncated ${originalChars - MEMORY_INDEX_MAX_CHARS} chars)` : trimmed;
|
|
132
|
-
return { content, originalChars, truncated };
|
|
133
|
-
}
|
|
134
|
-
/** Read one memory file's body (frontmatter stripped). Throws if missing. */
|
|
135
|
-
read(scope, name) {
|
|
136
|
-
const file = this.pathFor(scope, name);
|
|
137
|
-
if (!existsSync(file)) {
|
|
138
|
-
throw new Error(`memory not found: scope=${scope} name=${name}`);
|
|
139
|
-
}
|
|
140
|
-
const raw = readFileSync(file, "utf8");
|
|
141
|
-
const { data, body } = parseFrontmatter(raw);
|
|
142
|
-
const entry = {
|
|
143
|
-
name: data.name ?? name,
|
|
144
|
-
type: data.type ?? "project",
|
|
145
|
-
scope: data.scope ?? scope,
|
|
146
|
-
description: data.description ?? "",
|
|
147
|
-
body: body.trim(),
|
|
148
|
-
createdAt: data.created ?? ""
|
|
149
|
-
};
|
|
150
|
-
const priority = coercePriority(data.priority);
|
|
151
|
-
if (priority) entry.priority = priority;
|
|
152
|
-
const expires = coerceExpires(data.expires);
|
|
153
|
-
if (expires) entry.expires = expires;
|
|
154
|
-
return entry;
|
|
155
|
-
}
|
|
156
|
-
/** Skips malformed files — index stays queryable even if one file is hand-edited into nonsense. */
|
|
157
|
-
list() {
|
|
158
|
-
const out = [];
|
|
159
|
-
const scopes = this.projectRoot ? ["global", "project"] : ["global"];
|
|
160
|
-
for (const scope of scopes) {
|
|
161
|
-
const dir = scopeDir({ homeDir: this.homeDir, scope, projectRoot: this.projectRoot });
|
|
162
|
-
if (!existsSync(dir)) continue;
|
|
163
|
-
let entries;
|
|
164
|
-
try {
|
|
165
|
-
entries = readdirSync(dir);
|
|
166
|
-
} catch {
|
|
167
|
-
continue;
|
|
168
|
-
}
|
|
169
|
-
for (const entry of entries) {
|
|
170
|
-
if (entry === MEMORY_INDEX_FILE) continue;
|
|
171
|
-
if (!entry.endsWith(".md")) continue;
|
|
172
|
-
const name = entry.slice(0, -3);
|
|
173
|
-
try {
|
|
174
|
-
out.push(this.read(scope, name));
|
|
175
|
-
} catch {
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
return out;
|
|
180
|
-
}
|
|
181
|
-
write(input) {
|
|
182
|
-
if (input.scope === "project" && !this.projectRoot) {
|
|
183
|
-
throw new Error("cannot write project-scoped memory: no projectRoot configured");
|
|
184
|
-
}
|
|
185
|
-
const name = sanitizeMemoryName(input.name);
|
|
186
|
-
const desc = String(input.description ?? "").trim();
|
|
187
|
-
if (!desc) throw new Error("memory description cannot be empty");
|
|
188
|
-
const body = String(input.body ?? "").trim();
|
|
189
|
-
if (!body) throw new Error("memory body cannot be empty");
|
|
190
|
-
const entry = {
|
|
191
|
-
...input,
|
|
192
|
-
name,
|
|
193
|
-
description: desc,
|
|
194
|
-
body,
|
|
195
|
-
createdAt: todayIso()
|
|
196
|
-
};
|
|
197
|
-
if (input.priority) entry.priority = input.priority;
|
|
198
|
-
if (input.expires) entry.expires = input.expires;
|
|
199
|
-
const dir = this.dir(input.scope);
|
|
200
|
-
const file = join(dir, `${name}.md`);
|
|
201
|
-
const content = `${formatFrontmatter(entry)}${body}
|
|
202
|
-
`;
|
|
203
|
-
writeFileSync(file, content, "utf8");
|
|
204
|
-
this.regenerateIndex(input.scope);
|
|
205
|
-
return file;
|
|
206
|
-
}
|
|
207
|
-
/** Delete one memory + its index line. No-op if the file is already gone. */
|
|
208
|
-
delete(scope, rawName) {
|
|
209
|
-
if (scope === "project" && !this.projectRoot) {
|
|
210
|
-
throw new Error("cannot delete project-scoped memory: no projectRoot configured");
|
|
211
|
-
}
|
|
212
|
-
const file = this.pathFor(scope, rawName);
|
|
213
|
-
if (!existsSync(file)) return false;
|
|
214
|
-
unlinkSync(file);
|
|
215
|
-
this.regenerateIndex(scope);
|
|
216
|
-
return true;
|
|
217
|
-
}
|
|
218
|
-
/** Sorted by name — same file set must produce byte-identical MEMORY.md for stable prefix hashing. */
|
|
219
|
-
regenerateIndex(scope) {
|
|
220
|
-
const dir = scopeDir({ homeDir: this.homeDir, scope, projectRoot: this.projectRoot });
|
|
221
|
-
if (!existsSync(dir)) return;
|
|
222
|
-
let files;
|
|
223
|
-
try {
|
|
224
|
-
files = readdirSync(dir);
|
|
225
|
-
} catch {
|
|
226
|
-
return;
|
|
227
|
-
}
|
|
228
|
-
const mdFiles = files.filter((f) => f !== MEMORY_INDEX_FILE && f.endsWith(".md")).sort((a, b) => a.localeCompare(b));
|
|
229
|
-
const indexPath = join(dir, MEMORY_INDEX_FILE);
|
|
230
|
-
if (mdFiles.length === 0) {
|
|
231
|
-
if (existsSync(indexPath)) unlinkSync(indexPath);
|
|
232
|
-
return;
|
|
233
|
-
}
|
|
234
|
-
const lines = [];
|
|
235
|
-
for (const f of mdFiles) {
|
|
236
|
-
const name = f.slice(0, -3);
|
|
237
|
-
try {
|
|
238
|
-
const entry = this.read(scope, name);
|
|
239
|
-
lines.push(indexLine({ name: entry.name || name, description: entry.description }));
|
|
240
|
-
} catch {
|
|
241
|
-
lines.push(`- [${name}](${name}.md) \u2014 (malformed, check frontmatter)`);
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
writeFileSync(indexPath, `${lines.join("\n")}
|
|
245
|
-
`, "utf8");
|
|
246
|
-
}
|
|
247
|
-
};
|
|
248
|
-
function readGlobalReasonixMemory(homeDir = join(homedir(), ".reasonix")) {
|
|
249
|
-
const path = join(homeDir, "REASONIX.md");
|
|
250
|
-
if (!existsSync(path)) return null;
|
|
251
|
-
let raw;
|
|
252
|
-
try {
|
|
253
|
-
raw = readFileSync(path, "utf8");
|
|
254
|
-
} catch {
|
|
255
|
-
return null;
|
|
256
|
-
}
|
|
257
|
-
const trimmed = raw.trim();
|
|
258
|
-
if (!trimmed) return null;
|
|
259
|
-
const originalChars = trimmed.length;
|
|
260
|
-
const truncated = originalChars > 8e3;
|
|
261
|
-
const content = truncated ? `${trimmed.slice(0, 8e3)}
|
|
262
|
-
\u2026 (truncated ${originalChars - 8e3} chars)` : trimmed;
|
|
263
|
-
return { path, content, originalChars, truncated };
|
|
264
|
-
}
|
|
265
|
-
function applyGlobalReasonixMemory(basePrompt, homeDir) {
|
|
266
|
-
if (!memoryEnabled()) return basePrompt;
|
|
267
|
-
const dir = homeDir ?? join(homedir(), ".reasonix");
|
|
268
|
-
const mem = readGlobalReasonixMemory(dir);
|
|
269
|
-
if (!mem) return basePrompt;
|
|
270
|
-
return [
|
|
271
|
-
basePrompt,
|
|
272
|
-
"",
|
|
273
|
-
"# Global memory (~/.reasonix/REASONIX.md)",
|
|
274
|
-
"",
|
|
275
|
-
"Cross-project notes the user pinned via the `#g` prompt prefix. Treat as authoritative \u2014 same level of trust as project memory.",
|
|
276
|
-
"",
|
|
277
|
-
"```",
|
|
278
|
-
mem.content,
|
|
279
|
-
"```"
|
|
280
|
-
].join("\n");
|
|
281
|
-
}
|
|
282
|
-
function effectivePriority(entry, cfg) {
|
|
283
|
-
if (entry.priority) return entry.priority;
|
|
284
|
-
return memoryTypeDefaults(entry.type, cfg).priority;
|
|
285
|
-
}
|
|
286
|
-
function highPriorityBlock(entries, cfg) {
|
|
287
|
-
const high = entries.filter((e) => effectivePriority(e, cfg) === "high");
|
|
288
|
-
if (high.length === 0) return null;
|
|
289
|
-
const lines = [
|
|
290
|
-
"# HIGH PRIORITY constraints (must observe)",
|
|
291
|
-
"",
|
|
292
|
-
"These memories were declared `priority: high` (via config.memory.customTypes or the memory file itself). Treat them as hard rules \u2014 violations override any other guidance below.",
|
|
293
|
-
""
|
|
294
|
-
];
|
|
295
|
-
for (const e of high) {
|
|
296
|
-
const head = `!!! [${e.scope}/${e.type}/${e.name}] ${e.description || "(no description)"}`;
|
|
297
|
-
lines.push(head);
|
|
298
|
-
if (e.body) lines.push("", e.body);
|
|
299
|
-
lines.push("");
|
|
300
|
-
}
|
|
301
|
-
return lines.join("\n").trimEnd();
|
|
302
|
-
}
|
|
303
|
-
function applyUserMemory(basePrompt, opts = {}) {
|
|
304
|
-
if (!memoryEnabled()) return basePrompt;
|
|
305
|
-
const store = new MemoryStore(opts);
|
|
306
|
-
const global = store.loadIndex("global");
|
|
307
|
-
const project = store.hasProjectScope() ? store.loadIndex("project") : null;
|
|
308
|
-
const high = highPriorityBlock(store.list(), opts.cfg);
|
|
309
|
-
if (!global && !project && !high) return basePrompt;
|
|
310
|
-
const parts = [basePrompt];
|
|
311
|
-
if (high) parts.push("", high);
|
|
312
|
-
if (global) {
|
|
313
|
-
parts.push(
|
|
314
|
-
"",
|
|
315
|
-
"# User memory \u2014 global (~/.reasonix/memory/global/MEMORY.md)",
|
|
316
|
-
"",
|
|
317
|
-
"Cross-project facts and preferences the user has told you in prior sessions. TREAT AS AUTHORITATIVE \u2014 don't re-verify via filesystem or web. One-liners index detail files; call `recall_memory` for full bodies only when the one-liner isn't enough.",
|
|
318
|
-
"",
|
|
319
|
-
"```",
|
|
320
|
-
global.content,
|
|
321
|
-
"```"
|
|
322
|
-
);
|
|
323
|
-
}
|
|
324
|
-
if (project) {
|
|
325
|
-
parts.push(
|
|
326
|
-
"",
|
|
327
|
-
"# User memory \u2014 this project",
|
|
328
|
-
"",
|
|
329
|
-
"Per-project facts the user established in prior sessions (not committed to the repo). TREAT AS AUTHORITATIVE. Same recall pattern as global memory.",
|
|
330
|
-
"",
|
|
331
|
-
"```",
|
|
332
|
-
project.content,
|
|
333
|
-
"```"
|
|
334
|
-
);
|
|
335
|
-
}
|
|
336
|
-
return parts.join("\n");
|
|
337
|
-
}
|
|
338
|
-
function applyMemoryStack(basePrompt, rootDir, opts = {}) {
|
|
339
|
-
const homeDir = opts.homeDir;
|
|
340
|
-
const cfg = opts.cfg;
|
|
341
|
-
const withProject = applyProjectMemory(basePrompt, rootDir);
|
|
342
|
-
const withGlobal = applyGlobalReasonixMemory(
|
|
343
|
-
withProject,
|
|
344
|
-
homeDir ? join(homeDir, ".reasonix") : void 0
|
|
345
|
-
);
|
|
346
|
-
const withMemory = applyUserMemory(withGlobal, { projectRoot: rootDir, homeDir, cfg });
|
|
347
|
-
const customSkillPaths = cfg?.skills?.paths ? resolveSkillPaths(cfg.skills.paths, rootDir) : loadResolvedSkillPaths(rootDir);
|
|
348
|
-
return applySkillsIndex(withMemory, { projectRoot: rootDir, homeDir, customSkillPaths });
|
|
349
|
-
}
|
|
5
|
+
applyMemoryStack,
|
|
6
|
+
escalationContract
|
|
7
|
+
} from "./chunk-N4SEBLU4.js";
|
|
350
8
|
|
|
351
9
|
// src/code/prompt.ts
|
|
10
|
+
import { existsSync, readFileSync } from "fs";
|
|
11
|
+
import { join } from "path";
|
|
352
12
|
var DEFAULT_CODE_MODEL = "deepseek-v4-flash";
|
|
353
13
|
function codeSystemBase(modelId) {
|
|
354
14
|
return CODE_SYSTEM_TEMPLATE.replace("__ESCALATION_CONTRACT__", escalationContract(modelId));
|
|
@@ -477,12 +137,12 @@ function codeSystemPrompt(rootDir, opts = {}) {
|
|
|
477
137
|
const codeBase = codeSystemBase(opts.modelId ?? DEFAULT_CODE_MODEL);
|
|
478
138
|
const base = opts.hasSemanticSearch ? `${codeBase}${SEMANTIC_SEARCH_ROUTING}` : codeBase;
|
|
479
139
|
const withMemory = applyMemoryStack(base, rootDir);
|
|
480
|
-
const gitignorePath =
|
|
140
|
+
const gitignorePath = join(rootDir, ".gitignore");
|
|
481
141
|
let result = withMemory;
|
|
482
|
-
if (
|
|
142
|
+
if (existsSync(gitignorePath)) {
|
|
483
143
|
let content;
|
|
484
144
|
try {
|
|
485
|
-
content =
|
|
145
|
+
content = readFileSync(gitignorePath, "utf8");
|
|
486
146
|
} catch {
|
|
487
147
|
}
|
|
488
148
|
if (content !== void 0) {
|
|
@@ -513,12 +173,8 @@ ${appendParts.join("\n\n")}`;
|
|
|
513
173
|
}
|
|
514
174
|
|
|
515
175
|
export {
|
|
516
|
-
sanitizeMemoryName,
|
|
517
|
-
MemoryStore,
|
|
518
|
-
effectivePriority,
|
|
519
|
-
applyMemoryStack,
|
|
520
176
|
codeSystemBase,
|
|
521
177
|
CODE_SYSTEM_PROMPT,
|
|
522
178
|
codeSystemPrompt
|
|
523
179
|
};
|
|
524
|
-
//# sourceMappingURL=chunk-
|
|
180
|
+
//# sourceMappingURL=chunk-HGK57NBN.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/code/prompt.ts"],"sourcesContent":["import { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { applyMemoryStack } from \"../memory/user.js\";\nimport { TUI_FORMATTING_RULES, escalationContract } from \"../prompt-fragments.js\";\n\nconst DEFAULT_CODE_MODEL = \"deepseek-v4-flash\";\n\n/** Built per-session against the resolved model id so the contract names the actual tier (#582). */\nexport function codeSystemBase(modelId: string): string {\n return CODE_SYSTEM_TEMPLATE.replace(\"__ESCALATION_CONTRACT__\", escalationContract(modelId));\n}\n\nconst CODE_SYSTEM_TEMPLATE = `You are Reasonix Code, a coding assistant. Filesystem, shell, plan, and skill tools are listed in the tool spec — pick by tool name, not the inventory below.\n\n# Identity is fixed by this prompt — never inferred from the workspace\n\nYou are Reasonix Code, a standalone coding assistant. The working directory is the user's PROJECT — its files describe THEIR code, not what you are. If the workspace contains another platform's config (\\`config.yaml\\` with agent/persona keys, \\`SOUL.md\\`, \\`AGENT.md\\`, \\`PERSONA.md\\`, foreign \\`skills/\\` or \\`memories/\\` tree, a \\`REASONIX.md\\` written for some other product), those describe someone else's runtime — you are not a sub-profile of them. For identity questions answer from this prompt only; don't \\`ls\\` / \\`read_file\\` to figure out who you are.\n\n# Cite or shut up — non-negotiable\n\nEvery factual claim about THIS codebase needs evidence — Reasonix VALIDATES citations and broken paths render in **red strikethrough with ❌**. **Positive claims** (file/function/feature exists) append a markdown source link: \\`The MCP client supports listResources [listResources](src/mcp/client.ts:142).\\` **Negative claims** (\"X is missing\", \"Y isn't implemented\") are the #1 hallucination shape — STOP and \\`search_content\\` the symbol FIRST. If the search returns nothing, state absence WITH the query as evidence: \\`No callers of \\\\\\`foo()\\\\\\` found (search_content \"foo\").\\`\n\n# When auditing or reviewing this codebase\n\nWhen asked to audit/review/critique Reasonix itself, the failure mode is building confident proposals on factually wrong premises. Six rails:\n\n- **Auto-preview is for locating, not auditing.** Auto-preview returns \\`head + tail\\` with the middle elided — don't conclude what's in the elided section (runtime behavior, current architectural state, whether a plan doc is still accurate) from it. Re-call \\`read_file\\` with \\`range:\"A-B\"\\` before asserting.\n- **Flag → consumer trace.** Reading a type field (\\`parallelSafe?: boolean\\`, \\`stormExempt?: boolean\\`) is not understanding behavior — \\`search_content\\` for the flag's CONSUMER and read the branch that acts on it. **For inventory claims** (\"which tools have flag F?\"), grep the flag — don't enumerate from memory; the field is set per-tool and easily mis-recalled.\n- **No fabricated percentages.** \"Saves 40-60% tokens\" is invented unless you computed it. Ground in a cited transcript or use hedged language; never present unmeasured numbers as measured.\n- **Schema cost is real.** Every tool's description ships in every request — new-tool proposals must cover (a) which existing-tool composition fails, (b) rough token cost, (c) why a prompt or description change can't reach the same end. Default to \"tighten prompt / existing tool\".\n- **MEMORY.md is part of the design space.** Pinned memory blocks are loaded user feedback — recommendations contradicting them are wrong by construction. Cross-check before proposing.\n- **User-facing ≠ model-facing ≠ library-facing.** Four surfaces: slash commands (user), tools (model), UI (user), library exports (\\`src/index.ts\\`). Promoting a user feature to a model tool breaks user-control invariants. Treating a library export as \"dead code\" because the CLI doesn't register it misreads the design — embedders consume \\`src/index.ts\\` directly.\n\n# Picking the right tool: submit_plan / ask_choice / todo_write\n\n- **submit_plan** — review-gate for multi-file refactors, architecture changes, anything expensive to undo. Markdown body + structured \\`steps\\`. After calling, STOP and wait. Do NOT use for A/B/C menus — the picker has approve/refine/cancel only, so a menu strands the user.\n- **ask_choice** — when the user is supposed to pick between alternatives, the TOOL picks; never enumerate choices as prose. Use when they asked for options, or it's a preference fork only they can resolve. Skip when one option is clearly correct (just do it). After calling, STOP.\n- **todo_write** — in-session tracker for 3+ step work. NOT a plan (no approval gate, no files touched). One \\`in_progress\\` at a time; flip to \\`completed\\` immediately. For approval gates use submit_plan; for branching use ask_choice.\n\n# Plan mode (/plan)\n\nStronger constraint than submit_plan: writes + non-allowlisted run_command are bounced at dispatch (\"unavailable in plan mode\" — don't retry). Read tools and allowlisted shell commands still work. You MUST call submit_plan before anything will execute.\n\n# Delegating to subagents via Skills\n\nThe pinned Skills index below lists every available playbook (built-ins + user-installed). Entries tagged \\`[🧬 subagent]\\` spawn an isolated child loop and return only the final answer — their tool calls never enter your context. Pass \\`name\\` as the BARE identifier (e.g. \\`\"explore\"\\`), not the \\`[🧬 subagent]\\` tag.\n\n**Default: don't delegate.** Direct tools are cheaper and keep evidence in your context. Spawn ONLY for (a) true parallelism — 2+ independent investigations in one batch — or (b) context blow-up — >10 file reads where you only need the conclusion. Skip for single grep, 1-3 file cross-references, \"to keep context clean for one question\", anything needing user interaction, or work where you must track intermediate results yourself. Always pass clear, self-contained \\`arguments\\` — the subagent gets no other context.\n\n# When to edit vs. when to explore\n\nOnly propose edits when the user explicitly says change / fix / add / remove / refactor / write. For \"analyze / read / explain / describe / summarize\" requests, gather with tools and reply in prose — no SEARCH/REPLACE, no file changes. If unclear, ask.\n\nThe **edit gate** routes \\`edit_file\\` / \\`write_file\\` based on the user's mode (\\`review\\` or \\`auto\\`) — you don't see which is active, write the same way in both. Responses:\n- \\`\"edit blocks: 1/1 applied\"\\` — proceed.\n- \\`\"User rejected this edit to <path>. Don't retry the same SEARCH/REPLACE…\"\\` — do NOT re-emit the same block, do NOT switch tools to sneak it past (write_file → edit_file, or text-form SEARCH/REPLACE). Take a clearly different approach or ask.\n- Esc mid-prompt aborts the whole turn — don't keep calling tools after.\n\n# Editing files\n\nOutput one or more SEARCH/REPLACE blocks in this exact format:\n\npath/to/file.ext\n<<<<<<< SEARCH\nexact existing lines from the file, including whitespace\n=======\nthe new lines\n>>>>>>> REPLACE\n\nRules:\n- **Read before edit (enforced).** You MUST call \\`read_file\\` on the target this session before \\`edit_file\\` / \\`multi_edit\\` will accept it — the tool refuses unread targets up front, so SEARCH text is grounded in on-disk bytes, not a guess. A fold / mechanical truncate clears the tracker, so re-read after one of those before mutating. \\`write_file\\` counts as a read for that path (the content is what you just wrote).\n- One edit per block; multiple blocks per response are fine.\n- Create a new file with empty SEARCH:\n path/to/new.ts\n <<<<<<< SEARCH\n =======\n (whole file content here)\n >>>>>>> REPLACE\n- Don't use write_file to change existing files — the user reviews edits as SEARCH/REPLACE. write_file is for wholesale overwrites only.\n- Paths are relative to the working directory.\n- For multi-site changes use \\`multi_edit\\` — validation runs before any write; validation failures leave all files untouched. Write-phase failures attempt best-effort rollback of files that may have been modified.\n\n# Trust what you already know\n\nBefore exploring to answer a factual question, check context first: the user's message, prior turns (including \\`remember\\` results), the pinned memory blocks above. User-stated facts outrank what the files say — don't re-derive what the user just told you.\n\n# Exploration\n\nSkip dependency, build, and VCS directories unless asked (the pinned .gitignore below is your denylist). \\`search_files\\` matches FILE NAMES; \\`search_content\\` matches CONTENTS — pick accordingly. Use \\`glob\\` for \"what changed lately\" / \"all *.ts under src/\", \\`search_content\\` with \\`context:N\\` for grep -C around hits.\n\n# Path conventions\n\n- **Filesystem tools** (\\`read_file\\`, \\`list_directory\\`, \\`edit_file\\`, etc.): paths resolve against the sandbox root. Relative, POSIX-absolute (\\`/\\` = project root), and OS-absolute (e.g. \\`D:\\\\\\\\path\\\\\\\\foo.cpp\\`) all work as long as they resolve INSIDE the sandbox. Don't refuse on path shape — the tool returns a clear sandbox-escape error if it's actually out of scope.\n- **\\`run_command\\`**: cwd pinned to project root. Never use a leading \\`/\\` in arguments — Windows reads it as drive root, POSIX as filesystem root. Use relative paths.\n\n# Workspace is pinned\n\nYou can't switch project / working directory mid-session — tell the user to quit and relaunch (e.g. \\`cd ../other-project && reasonix code\\`). Don't try \\`cd\\` via \\`run_command\\` either; the sandbox is pinned and \\`cd\\` doesn't carry between calls.\n\n# Foreground vs background\n\n\\`run_command\\` blocks until exit — use for tests / builds / lints / typechecks / git / one-shot scripts under a minute. \\`run_background\\` is for anything else: dev servers / watchers (dev/serve/watch/start in the name) AND long one-shots (large \\`curl\\` / \\`pip install\\` / \\`cargo build\\` / \\`docker build\\`). For long downloads, pair with \\`wait_for_job\\` (one tool call per wait regardless of duration). Don't restart a running dev server — \\`list_jobs\\` first.\n\n# Scope discipline on \"run it\" / \"start it\" requests\n\nWhen the user says run / start / launch / serve / boot up: start it, verify it came up, report what's running and STOP. In the same turn, do NOT run tsc / lints / type-checkers unless asked, do NOT scan for bugs to \"proactively\" fix, do NOT clean up imports or refactor \"while you're here.\" If you notice an issue, mention in one sentence and wait. \"It works\" is the end state — resist the urge to polish.\n\n# Style\n\n- Show edits; don't narrate them in prose. \"Here's the fix:\" is enough.\n- One short paragraph explaining *why*, then the blocks.\n- Silence during exploration is fine — tool calls first, prose after.\n\n# Task integrity — non-negotiable\n\nThe user's original objective and ALL constraints (especially \"do NOT do X\", \"avoid Y\", \"never Z\") remain in force for the entire session. You may NOT unilaterally simplify, narrow, or change the objective to save tokens, time, or steps. If you believe the objective needs adjustment, ask the user — do NOT decide on your own.\n\n__ESCALATION_CONTRACT__\n\n${TUI_FORMATTING_RULES}\n`;\n\n/** Backward-compat — public-API const, frozen at the historical flash phrasing. Internal callers use codeSystemPrompt(rootDir, { modelId }) so the contract names the real tier (#582). */\nexport const CODE_SYSTEM_PROMPT = codeSystemBase(DEFAULT_CODE_MODEL);\n\n/** Stack order (stable for cache prefix): base → REASONIX.md → global → project → .gitignore. */\nconst SEMANTIC_SEARCH_ROUTING = `\n\n# Search routing\n\nYou have BOTH \\`semantic_search\\` (vector index) and \\`search_content\\` (literal grep).\n\n- **Descriptive queries** (\"where do we handle X\", \"which file owns Y\", \"how does Z work\", \"find the logic that does …\", \"the code responsible for …\") → call \\`semantic_search\\` FIRST. It indexes the project by meaning, so it finds the right file even when your phrasing shares no tokens with the code.\n- **Exact-token queries** (a specific identifier, regex, or \"find every call to foo\") → call \\`search_content\\`.\n\nIf \\`semantic_search\\` returns nothing useful (low scores, off-topic), THEN fall back to \\`search_content\\`. Don't go the other way — grepping a paraphrased question wastes turns.`;\n\nexport interface CodeSystemPromptOptions {\n /** True when semantic_search is registered for this run. Adds an\n * explicit routing fragment so the model picks it for intent-style\n * queries instead of defaulting to grep. */\n hasSemanticSearch?: boolean;\n /** Inline string appended after the generated code system prompt.\n * Preserves the default prompt — this is append-only, not a replacement. */\n systemAppend?: string;\n /** UTF-8 file contents appended after the generated code system prompt.\n * Preserves the default prompt — this is append-only, not a replacement. */\n systemAppendFile?: string;\n /** Model the loop will run on — interpolated into the escalation contract so the model can name itself correctly when asked (#582). */\n modelId?: string;\n /** Back-compat no-op: lifecycle is runtime-only so strict/off do not change the cache prefix. */\n engineeringLifecycleMode?: \"off\" | \"strict\";\n}\n\nexport function codeSystemPrompt(rootDir: string, opts: CodeSystemPromptOptions = {}): string {\n const codeBase = codeSystemBase(opts.modelId ?? DEFAULT_CODE_MODEL);\n const base = opts.hasSemanticSearch ? `${codeBase}${SEMANTIC_SEARCH_ROUTING}` : codeBase;\n const withMemory = applyMemoryStack(base, rootDir);\n const gitignorePath = join(rootDir, \".gitignore\");\n let result = withMemory;\n if (existsSync(gitignorePath)) {\n let content: string | undefined;\n try {\n content = readFileSync(gitignorePath, \"utf8\");\n } catch {}\n if (content !== undefined) {\n const MAX = 2000;\n const truncated =\n content.length > MAX\n ? `${content.slice(0, MAX)}\\n… (truncated ${content.length - MAX} chars)`\n : content;\n result = `${result}\\n\\n# Project .gitignore\\n\\nThe user's repo ships this .gitignore — treat every pattern as \"don't traverse or edit inside these paths unless explicitly asked\":\\n\\n\\`\\`\\`\\n${truncated}\\n\\`\\`\\`\\n`;\n }\n }\n const appendParts = [opts.systemAppend, opts.systemAppendFile].filter(Boolean);\n if (appendParts.length > 0) {\n result = `${result}\\n\\n# User System Append\\n\\n${appendParts.join(\"\\n\\n\")}`;\n }\n return result;\n}\n"],"mappings":";;;;;;;;;AAAA,SAAS,YAAY,oBAAoB;AACzC,SAAS,YAAY;AAIrB,IAAM,qBAAqB;AAGpB,SAAS,eAAe,SAAyB;AACtD,SAAO,qBAAqB,QAAQ,2BAA2B,mBAAmB,OAAO,CAAC;AAC5F;AAEA,IAAM,uBAAuB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA2G3B,oBAAoB;AAAA;AAIf,IAAM,qBAAqB,eAAe,kBAAkB;AAGnE,IAAM,0BAA0B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA4BzB,SAAS,iBAAiB,SAAiB,OAAgC,CAAC,GAAW;AAC5F,QAAM,WAAW,eAAe,KAAK,WAAW,kBAAkB;AAClE,QAAM,OAAO,KAAK,oBAAoB,GAAG,QAAQ,GAAG,uBAAuB,KAAK;AAChF,QAAM,aAAa,iBAAiB,MAAM,OAAO;AACjD,QAAM,gBAAgB,KAAK,SAAS,YAAY;AAChD,MAAI,SAAS;AACb,MAAI,WAAW,aAAa,GAAG;AAC7B,QAAI;AACJ,QAAI;AACF,gBAAU,aAAa,eAAe,MAAM;AAAA,IAC9C,QAAQ;AAAA,IAAC;AACT,QAAI,YAAY,QAAW;AACzB,YAAM,MAAM;AACZ,YAAM,YACJ,QAAQ,SAAS,MACb,GAAG,QAAQ,MAAM,GAAG,GAAG,CAAC;AAAA,oBAAkB,QAAQ,SAAS,GAAG,YAC9D;AACN,eAAS,GAAG,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAA8K,SAAS;AAAA;AAAA;AAAA,IAC3M;AAAA,EACF;AACA,QAAM,cAAc,CAAC,KAAK,cAAc,KAAK,gBAAgB,EAAE,OAAO,OAAO;AAC7E,MAAI,YAAY,SAAS,GAAG;AAC1B,aAAS,GAAG,MAAM;AAAA;AAAA;AAAA;AAAA,EAA+B,YAAY,KAAK,MAAM,CAAC;AAAA,EAC3E;AACA,SAAO;AACT;","names":[]}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRequire as __cr } from 'node:module'; if (typeof globalThis.require === 'undefined') { globalThis.require = __cr(import.meta.url); }
|
|
3
|
+
import {
|
|
4
|
+
MemoryStore,
|
|
5
|
+
readGlobalReasonixMemory,
|
|
6
|
+
readProjectMemory
|
|
7
|
+
} from "./chunk-N4SEBLU4.js";
|
|
8
|
+
|
|
9
|
+
// src/desktop/memory-browser.ts
|
|
10
|
+
import { existsSync, readFileSync } from "fs";
|
|
11
|
+
import { basename, resolve } from "path";
|
|
12
|
+
function collectMemoryEntriesForWorkspace(projectRoot, opts = {}) {
|
|
13
|
+
const out = [];
|
|
14
|
+
const project = readProjectMemory(projectRoot);
|
|
15
|
+
if (project) {
|
|
16
|
+
out.push({
|
|
17
|
+
kind: "project_file",
|
|
18
|
+
scope: "project",
|
|
19
|
+
name: basename(project.path),
|
|
20
|
+
path: project.path,
|
|
21
|
+
description: "Project memory file",
|
|
22
|
+
type: "freeform"
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
const global = readGlobalReasonixMemory(opts.reasonixHome);
|
|
26
|
+
if (global) {
|
|
27
|
+
out.push({
|
|
28
|
+
kind: "global_file",
|
|
29
|
+
scope: "global",
|
|
30
|
+
name: basename(global.path),
|
|
31
|
+
path: global.path,
|
|
32
|
+
description: "Global memory file",
|
|
33
|
+
type: "freeform"
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
const store = new MemoryStore({ homeDir: opts.reasonixHome, projectRoot });
|
|
37
|
+
for (const entry of store.list()) {
|
|
38
|
+
out.push(structuredInfo(store, entry));
|
|
39
|
+
}
|
|
40
|
+
return out;
|
|
41
|
+
}
|
|
42
|
+
function readMemoryEntryDetail(request, projectRoot, opts = {}) {
|
|
43
|
+
const requested = resolve(request.path);
|
|
44
|
+
const entry = collectMemoryEntriesForWorkspace(projectRoot, opts).find(
|
|
45
|
+
(candidate) => resolve(candidate.path) === requested
|
|
46
|
+
);
|
|
47
|
+
if (!entry) throw new Error(`memory path not available: ${request.path}`);
|
|
48
|
+
if (entry.kind === "structured") {
|
|
49
|
+
const store = new MemoryStore({ homeDir: opts.reasonixHome, projectRoot });
|
|
50
|
+
const structured = store.read(entry.scope, entry.name);
|
|
51
|
+
return {
|
|
52
|
+
...entry,
|
|
53
|
+
description: structured.description,
|
|
54
|
+
type: structured.type,
|
|
55
|
+
body: structured.body,
|
|
56
|
+
createdAt: structured.createdAt
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
if (!existsSync(entry.path)) throw new Error(`memory file missing: ${entry.path}`);
|
|
60
|
+
return {
|
|
61
|
+
...entry,
|
|
62
|
+
body: readFileSync(entry.path, "utf8").trim()
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
function structuredInfo(store, entry) {
|
|
66
|
+
return {
|
|
67
|
+
kind: "structured",
|
|
68
|
+
scope: entry.scope,
|
|
69
|
+
name: entry.name,
|
|
70
|
+
path: store.pathFor(entry.scope, entry.name),
|
|
71
|
+
description: entry.description,
|
|
72
|
+
type: entry.type
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export {
|
|
77
|
+
collectMemoryEntriesForWorkspace,
|
|
78
|
+
readMemoryEntryDetail
|
|
79
|
+
};
|
|
80
|
+
//# sourceMappingURL=chunk-JHWQDJZA.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/desktop/memory-browser.ts"],"sourcesContent":["import { existsSync, readFileSync } from \"node:fs\";\nimport { basename, resolve } from \"node:path\";\nimport { readProjectMemory } from \"../memory/project.js\";\nimport {\n type MemoryEntry,\n type MemoryScope,\n MemoryStore,\n readGlobalReasonixMemory,\n} from \"../memory/user.js\";\n\nexport type MemoryEntryKind = \"project_file\" | \"global_file\" | \"structured\";\n\nexport interface MemoryEntryInfo {\n kind: MemoryEntryKind;\n scope: MemoryScope;\n name: string;\n path: string;\n description: string;\n type?: string;\n}\n\nexport interface MemoryEntryDetail extends MemoryEntryInfo {\n body: string;\n createdAt?: string;\n}\n\nexport interface MemoryBrowserOptions {\n /** Absolute ~/.reasonix directory. Tests override this; production uses homedir(). */\n reasonixHome?: string;\n}\n\nexport function collectMemoryEntriesForWorkspace(\n projectRoot: string,\n opts: MemoryBrowserOptions = {},\n): MemoryEntryInfo[] {\n const out: MemoryEntryInfo[] = [];\n const project = readProjectMemory(projectRoot);\n if (project) {\n out.push({\n kind: \"project_file\",\n scope: \"project\",\n name: basename(project.path),\n path: project.path,\n description: \"Project memory file\",\n type: \"freeform\",\n });\n }\n\n const global = readGlobalReasonixMemory(opts.reasonixHome);\n if (global) {\n out.push({\n kind: \"global_file\",\n scope: \"global\",\n name: basename(global.path),\n path: global.path,\n description: \"Global memory file\",\n type: \"freeform\",\n });\n }\n\n const store = new MemoryStore({ homeDir: opts.reasonixHome, projectRoot });\n for (const entry of store.list()) {\n out.push(structuredInfo(store, entry));\n }\n return out;\n}\n\nexport function readMemoryEntryDetail(\n request: { path: string },\n projectRoot: string,\n opts: MemoryBrowserOptions = {},\n): MemoryEntryDetail {\n const requested = resolve(request.path);\n const entry = collectMemoryEntriesForWorkspace(projectRoot, opts).find(\n (candidate) => resolve(candidate.path) === requested,\n );\n if (!entry) throw new Error(`memory path not available: ${request.path}`);\n\n if (entry.kind === \"structured\") {\n const store = new MemoryStore({ homeDir: opts.reasonixHome, projectRoot });\n const structured = store.read(entry.scope, entry.name);\n return {\n ...entry,\n description: structured.description,\n type: structured.type,\n body: structured.body,\n createdAt: structured.createdAt,\n };\n }\n\n if (!existsSync(entry.path)) throw new Error(`memory file missing: ${entry.path}`);\n return {\n ...entry,\n body: readFileSync(entry.path, \"utf8\").trim(),\n };\n}\n\nfunction structuredInfo(store: MemoryStore, entry: MemoryEntry): MemoryEntryInfo {\n return {\n kind: \"structured\",\n scope: entry.scope,\n name: entry.name,\n path: store.pathFor(entry.scope, entry.name),\n description: entry.description,\n type: entry.type,\n };\n}\n"],"mappings":";;;;;;;;;AAAA,SAAS,YAAY,oBAAoB;AACzC,SAAS,UAAU,eAAe;AA8B3B,SAAS,iCACd,aACA,OAA6B,CAAC,GACX;AACnB,QAAM,MAAyB,CAAC;AAChC,QAAM,UAAU,kBAAkB,WAAW;AAC7C,MAAI,SAAS;AACX,QAAI,KAAK;AAAA,MACP,MAAM;AAAA,MACN,OAAO;AAAA,MACP,MAAM,SAAS,QAAQ,IAAI;AAAA,MAC3B,MAAM,QAAQ;AAAA,MACd,aAAa;AAAA,MACb,MAAM;AAAA,IACR,CAAC;AAAA,EACH;AAEA,QAAM,SAAS,yBAAyB,KAAK,YAAY;AACzD,MAAI,QAAQ;AACV,QAAI,KAAK;AAAA,MACP,MAAM;AAAA,MACN,OAAO;AAAA,MACP,MAAM,SAAS,OAAO,IAAI;AAAA,MAC1B,MAAM,OAAO;AAAA,MACb,aAAa;AAAA,MACb,MAAM;AAAA,IACR,CAAC;AAAA,EACH;AAEA,QAAM,QAAQ,IAAI,YAAY,EAAE,SAAS,KAAK,cAAc,YAAY,CAAC;AACzE,aAAW,SAAS,MAAM,KAAK,GAAG;AAChC,QAAI,KAAK,eAAe,OAAO,KAAK,CAAC;AAAA,EACvC;AACA,SAAO;AACT;AAEO,SAAS,sBACd,SACA,aACA,OAA6B,CAAC,GACX;AACnB,QAAM,YAAY,QAAQ,QAAQ,IAAI;AACtC,QAAM,QAAQ,iCAAiC,aAAa,IAAI,EAAE;AAAA,IAChE,CAAC,cAAc,QAAQ,UAAU,IAAI,MAAM;AAAA,EAC7C;AACA,MAAI,CAAC,MAAO,OAAM,IAAI,MAAM,8BAA8B,QAAQ,IAAI,EAAE;AAExE,MAAI,MAAM,SAAS,cAAc;AAC/B,UAAM,QAAQ,IAAI,YAAY,EAAE,SAAS,KAAK,cAAc,YAAY,CAAC;AACzE,UAAM,aAAa,MAAM,KAAK,MAAM,OAAO,MAAM,IAAI;AACrD,WAAO;AAAA,MACL,GAAG;AAAA,MACH,aAAa,WAAW;AAAA,MACxB,MAAM,WAAW;AAAA,MACjB,MAAM,WAAW;AAAA,MACjB,WAAW,WAAW;AAAA,IACxB;AAAA,EACF;AAEA,MAAI,CAAC,WAAW,MAAM,IAAI,EAAG,OAAM,IAAI,MAAM,wBAAwB,MAAM,IAAI,EAAE;AACjF,SAAO;AAAA,IACL,GAAG;AAAA,IACH,MAAM,aAAa,MAAM,MAAM,MAAM,EAAE,KAAK;AAAA,EAC9C;AACF;AAEA,SAAS,eAAe,OAAoB,OAAqC;AAC/E,SAAO;AAAA,IACL,MAAM;AAAA,IACN,OAAO,MAAM;AAAA,IACb,MAAM,MAAM;AAAA,IACZ,MAAM,MAAM,QAAQ,MAAM,OAAO,MAAM,IAAI;AAAA,IAC3C,aAAa,MAAM;AAAA,IACnB,MAAM,MAAM;AAAA,EACd;AACF;","names":[]}
|
|
@@ -7,10 +7,10 @@ import {
|
|
|
7
7
|
defaultUsageLogPath,
|
|
8
8
|
formatLogSize,
|
|
9
9
|
readUsageLog
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-3YRTIWFX.js";
|
|
11
11
|
import {
|
|
12
12
|
t
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-DVD67FXQ.js";
|
|
14
14
|
|
|
15
15
|
// src/cli/commands/stats.ts
|
|
16
16
|
import { existsSync, readFileSync } from "fs";
|
|
@@ -150,4 +150,4 @@ export {
|
|
|
150
150
|
statsCommand,
|
|
151
151
|
renderDashboard
|
|
152
152
|
};
|
|
153
|
-
//# sourceMappingURL=chunk-
|
|
153
|
+
//# sourceMappingURL=chunk-K3QJ3GKI.js.map
|