reasonix 0.4.16 → 0.4.17

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 CHANGED
@@ -143,11 +143,33 @@ worse than visible rejections.
143
143
  inline clip isn't enough).
144
144
  - `/think` — see the model's full R1 reasoning for the last turn
145
145
  (reasoner preset only).
146
+ - `/memory` — show the project's `REASONIX.md` (see below).
146
147
  - `/undo` — roll back the last applied edit batch.
147
148
  - `/new` — start fresh in the same directory without losing the
148
149
  session file.
149
150
  - Drop `--no-session` for an ephemeral session that doesn't persist.
150
151
 
152
+ ### Project memory — `REASONIX.md`
153
+
154
+ Drop a `REASONIX.md` in the project root and its contents are pinned
155
+ into the immutable-prefix system prompt every time you launch
156
+ `reasonix` in that directory. Good for house conventions, domain
157
+ glossary, or things the model keeps forgetting:
158
+
159
+ ```bash
160
+ cat > REASONIX.md <<'EOF'
161
+ # Notes for Reasonix
162
+ - Use snake_case for new Python modules; legacy camelCase modules keep their style.
163
+ - `cargo check` is in the auto-run allowlist; full `cargo test` needs confirmation.
164
+ - The `api/` dir mirrors `backend/` — keep schemas in sync.
165
+ EOF
166
+ ```
167
+
168
+ Re-launch (or `/new`) to pick it up; the prefix is hashed once per
169
+ session to keep the DeepSeek cache warm. `/memory` prints what's
170
+ currently pinned. `REASONIX_MEMORY=off` disables the lookup for CI /
171
+ offline repro.
172
+
151
173
  ```bash
152
174
  npx reasonix code src/ # narrower sandbox (only src/ is writable)
153
175
  npx reasonix code --no-session # ephemeral — nothing saved to disk
@@ -1,8 +1,53 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/code/prompt.ts
4
+ import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
5
+ import { join as join2 } from "path";
6
+
7
+ // src/project-memory.ts
4
8
  import { existsSync, readFileSync } from "fs";
5
9
  import { join } from "path";
10
+ var PROJECT_MEMORY_FILE = "REASONIX.md";
11
+ var PROJECT_MEMORY_MAX_CHARS = 8e3;
12
+ function readProjectMemory(rootDir) {
13
+ const path = join(rootDir, PROJECT_MEMORY_FILE);
14
+ if (!existsSync(path)) return null;
15
+ let raw;
16
+ try {
17
+ raw = readFileSync(path, "utf8");
18
+ } catch {
19
+ return null;
20
+ }
21
+ const trimmed = raw.trim();
22
+ if (!trimmed) return null;
23
+ const originalChars = trimmed.length;
24
+ const truncated = originalChars > PROJECT_MEMORY_MAX_CHARS;
25
+ const content = truncated ? `${trimmed.slice(0, PROJECT_MEMORY_MAX_CHARS)}
26
+ \u2026 (truncated ${originalChars - PROJECT_MEMORY_MAX_CHARS} chars)` : trimmed;
27
+ return { path, content, originalChars, truncated };
28
+ }
29
+ function memoryEnabled() {
30
+ const env = process.env.REASONIX_MEMORY;
31
+ if (env === "off" || env === "false" || env === "0") return false;
32
+ return true;
33
+ }
34
+ function applyProjectMemory(basePrompt, rootDir) {
35
+ if (!memoryEnabled()) return basePrompt;
36
+ const mem = readProjectMemory(rootDir);
37
+ if (!mem) return basePrompt;
38
+ return `${basePrompt}
39
+
40
+ # Project memory (REASONIX.md)
41
+
42
+ The user pinned these notes about this project \u2014 treat them as authoritative context for every turn:
43
+
44
+ \`\`\`
45
+ ${mem.content}
46
+ \`\`\`
47
+ `;
48
+ }
49
+
50
+ // src/code/prompt.ts
6
51
  var CODE_SYSTEM_PROMPT = `You are Reasonix Code, a coding assistant. You have filesystem tools (read_file, write_file, list_directory, search_files, etc.) rooted at the user's working directory.
7
52
 
8
53
  # When to edit vs. when to explore
@@ -51,18 +96,19 @@ Rules:
51
96
  - If you need to explore first (list / grep / read), do it with tool calls before writing any prose \u2014 silence while exploring is fine.
52
97
  `;
53
98
  function codeSystemPrompt(rootDir) {
54
- const gitignorePath = join(rootDir, ".gitignore");
55
- if (!existsSync(gitignorePath)) return CODE_SYSTEM_PROMPT;
99
+ const withMemory = applyProjectMemory(CODE_SYSTEM_PROMPT, rootDir);
100
+ const gitignorePath = join2(rootDir, ".gitignore");
101
+ if (!existsSync2(gitignorePath)) return withMemory;
56
102
  let content;
57
103
  try {
58
- content = readFileSync(gitignorePath, "utf8");
104
+ content = readFileSync2(gitignorePath, "utf8");
59
105
  } catch {
60
- return CODE_SYSTEM_PROMPT;
106
+ return withMemory;
61
107
  }
62
108
  const MAX = 2e3;
63
109
  const truncated = content.length > MAX ? `${content.slice(0, MAX)}
64
110
  \u2026 (truncated ${content.length - MAX} chars)` : content;
65
- return `${CODE_SYSTEM_PROMPT}
111
+ return `${withMemory}
66
112
 
67
113
  # Project .gitignore
68
114
 
@@ -75,7 +121,11 @@ ${truncated}
75
121
  }
76
122
 
77
123
  export {
124
+ PROJECT_MEMORY_FILE,
125
+ readProjectMemory,
126
+ memoryEnabled,
127
+ applyProjectMemory,
78
128
  CODE_SYSTEM_PROMPT,
79
129
  codeSystemPrompt
80
130
  };
81
- //# sourceMappingURL=chunk-2P2MZLCE.js.map
131
+ //# sourceMappingURL=chunk-3YQRWFES.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/code/prompt.ts","../../src/project-memory.ts"],"sourcesContent":["/**\n * System prompt used by `reasonix code`. Teaches the model:\n *\n * 1. It has a filesystem MCP bridge rooted at the user's CWD.\n * 2. To modify files it emits SEARCH/REPLACE blocks (not\n * `write_file` — that would whole-file rewrite and kill diff\n * reviewability).\n * 3. Read first, edit second — SEARCH must match byte-for-byte.\n * 4. Be concise. The user can read a diff faster than prose.\n *\n * Kept short on purpose. Long system prompts eat context budget that\n * the Cache-First Loop is trying to conserve. The SEARCH/REPLACE spec\n * is the one unavoidable bloat; we trim everything else.\n */\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { applyProjectMemory } from \"../project-memory.js\";\n\nexport const CODE_SYSTEM_PROMPT = `You are Reasonix Code, a coding assistant. You have filesystem tools (read_file, write_file, list_directory, search_files, etc.) rooted at the user's working directory.\n\n# When to edit vs. when to explore\n\nOnly propose edits when the user explicitly asks you to change, fix, add, remove, refactor, or write something. Do NOT propose edits when the user asks you to:\n- analyze, read, explore, describe, or summarize a project\n- explain how something works\n- answer a question about the code\n\nIn those cases, use tools to gather what you need, then reply in prose. No SEARCH/REPLACE blocks, no file changes. If you're unsure what the user wants, ask.\n\nWhen you do propose edits, the user will review them and decide whether to \\`/apply\\` or \\`/discard\\`. Don't assume they'll accept — write as if each edit will be audited, because it will.\n\n# Editing files\n\nWhen you've been asked to change a file, output 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- Always read_file first so your SEARCH matches byte-for-byte. If it doesn't match, the edit is rejected and you'll have to retry with the exact current content.\n- One edit per block. Multiple blocks in one response are fine.\n- To create a new file, leave SEARCH empty:\n path/to/new.ts\n <<<<<<< SEARCH\n =======\n (whole file content here)\n >>>>>>> REPLACE\n- Do NOT use write_file to change existing files — the user reviews your edits as SEARCH/REPLACE. write_file is only for files you explicitly want to overwrite wholesale (rare).\n- Paths are relative to the working directory. Don't use absolute paths.\n\n# Exploration\n\n- Avoid listing or reading inside these common dependency / build directories unless the user explicitly asks about them: node_modules, dist, build, out, .next, .nuxt, .svelte-kit, .git, .venv, venv, __pycache__, target, coverage, .turbo, .cache. They're expensive and usually irrelevant.\n- Prefer search_files / grep over list_directory when you know roughly what you're looking for — it saves context and avoids enumerating huge trees.\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- If you need to explore first (list / grep / read), do it with tool calls before writing any prose — silence while exploring is fine.\n`;\n\n/**\n * Inject the project's `.gitignore` content into the system prompt as a\n * \"respect this on top of the built-in denylist\" hint. We don't parse\n * the file — we hand it to the model as-is. Truncate long ones so we\n * don't eat context budget on huge generated ignore lists.\n *\n * Stacking order (stable for cache prefix):\n * base prompt → project memory (REASONIX.md) → .gitignore block\n */\nexport function codeSystemPrompt(rootDir: string): string {\n const withMemory = applyProjectMemory(CODE_SYSTEM_PROMPT, rootDir);\n const gitignorePath = join(rootDir, \".gitignore\");\n if (!existsSync(gitignorePath)) return withMemory;\n let content: string;\n try {\n content = readFileSync(gitignorePath, \"utf8\");\n } catch {\n return withMemory;\n }\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 return `${withMemory}\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 * Project memory — a user-authored `REASONIX.md` in the project root\n * that gets pinned into the immutable-prefix system prompt.\n *\n * Design notes:\n *\n * - The file lands in `ImmutablePrefix.system`, so the whole memory\n * block is hashed into the cache prefix fingerprint. Editing the\n * file invalidates the prefix; unchanged memory across sessions\n * keeps the DeepSeek prefix cache warm. That matches Pillar 1 —\n * memory is a deliberate, stable prefix, not per-turn drift.\n * - Only one source: the working-root `REASONIX.md`. No parent walk,\n * no `~/.reasonix/REASONIX.md`, no CLAUDE.md fallback. User-global\n * memory can come later; for v1 one file == one mental model.\n * - Truncated at 8 000 chars (≈ 2k tokens). `.gitignore` gets 2 000\n * because it's a constraint dump; memory gets more headroom because\n * it's deliberate instructions.\n * - Opt-out via `REASONIX_MEMORY=off|false|0`. No CLI flag — memory\n * is a file, `rm REASONIX.md` is the other opt-out.\n */\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\nexport const PROJECT_MEMORY_FILE = \"REASONIX.md\";\nexport const PROJECT_MEMORY_MAX_CHARS = 8000;\n\nexport interface ProjectMemory {\n /** Absolute path the memory was read from. */\n path: string;\n /** Post-truncation content (may include a \"… (truncated N chars)\" marker). */\n content: string;\n /** Original byte length before truncation. */\n originalChars: number;\n /** True iff `originalChars > PROJECT_MEMORY_MAX_CHARS`. */\n truncated: boolean;\n}\n\n/**\n * Read `REASONIX.md` from `rootDir`. Returns `null` when the file is\n * missing, unreadable, or empty (whitespace-only counts as empty — an\n * empty memory file shouldn't perturb the cache prefix).\n */\nexport function readProjectMemory(rootDir: string): ProjectMemory | null {\n const path = join(rootDir, PROJECT_MEMORY_FILE);\n if (!existsSync(path)) return null;\n let raw: string;\n try {\n raw = readFileSync(path, \"utf8\");\n } catch {\n return null;\n }\n const trimmed = raw.trim();\n if (!trimmed) return null;\n const originalChars = trimmed.length;\n const truncated = originalChars > PROJECT_MEMORY_MAX_CHARS;\n const content = truncated\n ? `${trimmed.slice(0, PROJECT_MEMORY_MAX_CHARS)}\\n… (truncated ${\n originalChars - PROJECT_MEMORY_MAX_CHARS\n } chars)`\n : trimmed;\n return { path, content, originalChars, truncated };\n}\n\n/**\n * Resolve whether project memory should be read. Default: on.\n * `REASONIX_MEMORY=off|false|0` turns it off (CI, reproducing issues,\n * intentional offline runs).\n */\nexport function memoryEnabled(): boolean {\n const env = process.env.REASONIX_MEMORY;\n if (env === \"off\" || env === \"false\" || env === \"0\") return false;\n return true;\n}\n\n/**\n * Return `basePrompt` with the project's `REASONIX.md` appended as a\n * \"Project memory\" section. No-op when the file is absent, empty, or\n * memory is disabled via env.\n *\n * The appended block is deterministic — identical input ⇒ identical\n * output — so every session that opens against the same memory file\n * gets the same prefix hash.\n */\nexport function applyProjectMemory(basePrompt: string, rootDir: string): string {\n if (!memoryEnabled()) return basePrompt;\n const mem = readProjectMemory(rootDir);\n if (!mem) return basePrompt;\n return `${basePrompt}\n\n# Project memory (REASONIX.md)\n\nThe user pinned these notes about this project — treat them as authoritative context for every turn:\n\n\\`\\`\\`\n${mem.content}\n\\`\\`\\`\n`;\n}\n"],"mappings":";;;AAeA,SAAS,cAAAA,aAAY,gBAAAC,qBAAoB;AACzC,SAAS,QAAAC,aAAY;;;ACKrB,SAAS,YAAY,oBAAoB;AACzC,SAAS,YAAY;AAEd,IAAM,sBAAsB;AAC5B,IAAM,2BAA2B;AAkBjC,SAAS,kBAAkB,SAAuC;AACvE,QAAM,OAAO,KAAK,SAAS,mBAAmB;AAC9C,MAAI,CAAC,WAAW,IAAI,EAAG,QAAO;AAC9B,MAAI;AACJ,MAAI;AACF,UAAM,aAAa,MAAM,MAAM;AAAA,EACjC,QAAQ;AACN,WAAO;AAAA,EACT;AACA,QAAM,UAAU,IAAI,KAAK;AACzB,MAAI,CAAC,QAAS,QAAO;AACrB,QAAM,gBAAgB,QAAQ;AAC9B,QAAM,YAAY,gBAAgB;AAClC,QAAM,UAAU,YACZ,GAAG,QAAQ,MAAM,GAAG,wBAAwB,CAAC;AAAA,oBAC3C,gBAAgB,wBAClB,YACA;AACJ,SAAO,EAAE,MAAM,SAAS,eAAe,UAAU;AACnD;AAOO,SAAS,gBAAyB;AACvC,QAAM,MAAM,QAAQ,IAAI;AACxB,MAAI,QAAQ,SAAS,QAAQ,WAAW,QAAQ,IAAK,QAAO;AAC5D,SAAO;AACT;AAWO,SAAS,mBAAmB,YAAoB,SAAyB;AAC9E,MAAI,CAAC,cAAc,EAAG,QAAO;AAC7B,QAAM,MAAM,kBAAkB,OAAO;AACrC,MAAI,CAAC,IAAK,QAAO;AACjB,SAAO,GAAG,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOpB,IAAI,OAAO;AAAA;AAAA;AAGb;;;AD/EO,IAAM,qBAAqB;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;AAyD3B,SAAS,iBAAiB,SAAyB;AACxD,QAAM,aAAa,mBAAmB,oBAAoB,OAAO;AACjE,QAAM,gBAAgBC,MAAK,SAAS,YAAY;AAChD,MAAI,CAACC,YAAW,aAAa,EAAG,QAAO;AACvC,MAAI;AACJ,MAAI;AACF,cAAUC,cAAa,eAAe,MAAM;AAAA,EAC9C,QAAQ;AACN,WAAO;AAAA,EACT;AACA,QAAM,MAAM;AACZ,QAAM,YACJ,QAAQ,SAAS,MACb,GAAG,QAAQ,MAAM,GAAG,GAAG,CAAC;AAAA,oBAAkB,QAAQ,SAAS,GAAG,YAC9D;AACN,SAAO,GAAG,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOpB,SAAS;AAAA;AAAA;AAGX;","names":["existsSync","readFileSync","join","join","existsSync","readFileSync"]}
package/dist/cli/index.js CHANGED
@@ -1,5 +1,10 @@
1
1
  #!/usr/bin/env node
2
- import "./chunk-2P2MZLCE.js";
2
+ import {
3
+ PROJECT_MEMORY_FILE,
4
+ applyProjectMemory,
5
+ memoryEnabled,
6
+ readProjectMemory
7
+ } from "./chunk-3YQRWFES.js";
3
8
 
4
9
  // src/cli/index.ts
5
10
  import { Command } from "commander";
@@ -3937,7 +3942,7 @@ function sep() {
3937
3942
  }
3938
3943
 
3939
3944
  // src/index.ts
3940
- var VERSION = "0.4.16";
3945
+ var VERSION = "0.4.17";
3941
3946
 
3942
3947
  // src/cli/commands/chat.tsx
3943
3948
  import { existsSync as existsSync3, statSync as statSync2 } from "fs";
@@ -4845,6 +4850,7 @@ var SLASH_COMMANDS = [
4845
4850
  { cmd: "branch", argsHint: "<N|off>", summary: "run N parallel samples per turn (N>=2)" },
4846
4851
  { cmd: "mcp", summary: "list MCP servers + tools attached to this session" },
4847
4852
  { cmd: "tool", argsHint: "[N]", summary: "dump full output of the Nth tool call (1=latest)" },
4853
+ { cmd: "memory", summary: "show the project's REASONIX.md (pinned into the system prompt)" },
4848
4854
  { cmd: "think", summary: "dump the last turn's full R1 reasoning (reasoner only)" },
4849
4855
  { cmd: "retry", summary: "truncate & resend your last message (fresh sample)" },
4850
4856
  { cmd: "compact", argsHint: "[cap]", summary: "shrink oversized tool results in the log" },
@@ -4913,6 +4919,7 @@ function handleSlash(cmd, args, loop, ctx = {}) {
4913
4919
  " /compact [cap] shrink large tool results in history (default 4k/result)",
4914
4920
  " /think dump the most recent turn's full R1 reasoning (reasoner only)",
4915
4921
  " /tool [N] list tool calls (or dump full output of #N, 1=most recent)",
4922
+ " /memory show the project's REASONIX.md (pinned into the system prompt)",
4916
4923
  " /retry truncate & resend your last message (fresh sample from the model)",
4917
4924
  " /apply (code mode) commit the pending edit blocks to disk",
4918
4925
  " /discard (code mode) drop pending edits without writing",
@@ -4994,6 +5001,45 @@ function handleSlash(cmd, args, loop, ctx = {}) {
4994
5001
  resubmit: prev
4995
5002
  };
4996
5003
  }
5004
+ case "memory": {
5005
+ if (!memoryEnabled()) {
5006
+ return {
5007
+ 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."
5008
+ };
5009
+ }
5010
+ if (!ctx.memoryRoot) {
5011
+ return {
5012
+ info: "no project root on this session \u2014 `/memory` needs a working directory to resolve REASONIX.md from."
5013
+ };
5014
+ }
5015
+ const mem = readProjectMemory(ctx.memoryRoot);
5016
+ if (!mem) {
5017
+ return {
5018
+ info: [
5019
+ `no ${PROJECT_MEMORY_FILE} in ${ctx.memoryRoot}.`,
5020
+ "",
5021
+ "Project memory is an optional file you pin notes into \u2014 project conventions,",
5022
+ "things the model keeps forgetting, domain glossary, setup gotchas. When present,",
5023
+ "its contents are appended to the system prompt (the immutable-prefix region)",
5024
+ "so every turn sees it without eating per-turn context, and the prefix cache stays",
5025
+ "warm as long as the file is stable.",
5026
+ "",
5027
+ `Create it with: echo "# Project notes for Reasonix" > ${PROJECT_MEMORY_FILE}`,
5028
+ "Re-launch (or `/new`) to pick up changes \u2014 the prefix is hashed at session start."
5029
+ ].join("\n")
5030
+ };
5031
+ }
5032
+ 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)`;
5033
+ return {
5034
+ info: [
5035
+ header,
5036
+ "",
5037
+ mem.content,
5038
+ "",
5039
+ "Changes take effect on the next launch or `/new` \u2014 the system prompt is hashed once per session to keep the prefix cache warm."
5040
+ ].join("\n")
5041
+ };
5042
+ }
4997
5043
  case "think":
4998
5044
  case "reasoning": {
4999
5045
  const raw = loop.scratch.reasoning;
@@ -5520,7 +5566,8 @@ function App({
5520
5566
  codeDiscard: codeMode ? codeDiscard : void 0,
5521
5567
  codeRoot: codeMode?.rootDir,
5522
5568
  pendingEditCount: codeMode ? pendingEdits.current.length : void 0,
5523
- toolHistory: () => toolHistoryRef.current
5569
+ toolHistory: () => toolHistoryRef.current,
5570
+ memoryRoot: codeMode?.rootDir ?? process.cwd()
5524
5571
  });
5525
5572
  if (result.exit) {
5526
5573
  transcriptRef.current?.end();
@@ -6187,7 +6234,7 @@ async function chatCommand(opts) {
6187
6234
  // src/cli/commands/code.tsx
6188
6235
  import { basename, resolve as resolve5 } from "path";
6189
6236
  async function codeCommand(opts = {}) {
6190
- const { codeSystemPrompt: codeSystemPrompt2 } = await import("./prompt-MMANQ36Z.js");
6237
+ const { codeSystemPrompt: codeSystemPrompt2 } = await import("./prompt-HK5XLH55.js");
6191
6238
  const rootDir = resolve5(opts.dir ?? process.cwd());
6192
6239
  const session = opts.noSession ? void 0 : `code-${sanitizeName(basename(rootDir))}`;
6193
6240
  const tools = new ToolRegistry();
@@ -7247,7 +7294,7 @@ program.action(async () => {
7247
7294
  const defaults = resolveDefaults({});
7248
7295
  await chatCommand({
7249
7296
  model: defaults.model,
7250
- system: DEFAULT_SYSTEM,
7297
+ system: applyProjectMemory(DEFAULT_SYSTEM, process.cwd()),
7251
7298
  harvest: defaults.harvest,
7252
7299
  branch: defaults.branch,
7253
7300
  session: defaults.session,
@@ -7299,7 +7346,7 @@ program.command("chat").description("Interactive Ink TUI with live cache/cost pa
7299
7346
  });
7300
7347
  await chatCommand({
7301
7348
  model: defaults.model,
7302
- system: opts.system,
7349
+ system: applyProjectMemory(opts.system, process.cwd()),
7303
7350
  transcript: opts.transcript,
7304
7351
  harvest: defaults.harvest,
7305
7352
  branch: defaults.branch,
@@ -7334,7 +7381,7 @@ program.command("run <task>").description("Run a single task non-interactively,
7334
7381
  await runCommand2({
7335
7382
  task,
7336
7383
  model: defaults.model,
7337
- system: opts.system,
7384
+ system: applyProjectMemory(opts.system, process.cwd()),
7338
7385
  harvest: defaults.harvest,
7339
7386
  branch: defaults.branch,
7340
7387
  transcript: opts.transcript,