Skip to content
This repository was archived by the owner on Jul 13, 2026. It is now read-only.

Commit 4bdcdaa

Browse files
KorenKritakecsap
andcommitted
feat: MAGIC_CONTEXT_LOG_PATH env override for the diagnostic log (cortexkit#183)
Let users redirect the diagnostic log away from the harness temp-dir default, which matters in sandboxed/ephemeral setups (Docker, CI) where TMPDIR is disposable or the plugin and dashboard need to agree on a shared path. The override is read at the single chokepoint getMagicContextLogPath (TS) and its Rust mirror resolve_log_path_for, so both the plugin logger and the dashboard log tail honor it; blank/whitespace is treated as unset. Docs updated in three places. Reimplemented from @kecsap's PR cortexkit#183 onto current master (the PR branch predated the v0.30.2 WebSocket migration and carried a large stale revert). Co-authored-by: kecsap <kecsap@users.noreply.github.com>
1 parent 940adab commit 4bdcdaa

6 files changed

Lines changed: 121 additions & 3 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,7 @@ Magic Context also writes to a few other locations:
280280
|---|---|---|
281281
| `~/.local/share/cortexkit/magic-context/context.db` | SQLite database — tags, compartments, memories, all durable state (XDG-equivalent on Windows) | **Must persist.** Losing it loses your memory/history. |
282282
| `~/.local/share/cortexkit/magic-context/models/` | Local embedding model cache (~90 MB `Xenova/all-MiniLM-L6-v2` ONNX), downloaded on first use when local embeddings are enabled | Should persist, else re-downloaded each run. Not used when `memory.enabled: false` or an `openai_compatible`/`ollama` embedding backend is configured. |
283-
| `${TMPDIR}/opencode/magic-context/magic-context.log` (`pi/` for Pi) | Diagnostic log | Disposable. |
283+
| `$MAGIC_CONTEXT_LOG_PATH` (default: `${TMPDIR}/opencode/magic-context/magic-context.log`, `pi/` for Pi) | Diagnostic log. Set `MAGIC_CONTEXT_LOG_PATH` to redirect it (e.g. to a persistent path in a container). | Disposable. |
284284

285285
**Sandboxed / ephemeral environments (Docker, CI, disposable containers):** mount the `~/.local/share/cortexkit/magic-context/` directory on a persistent volume so the database and model cache survive between runs. If only the model cache is ephemeral, the model is simply re-downloaded; if the database is ephemeral, memory and history don't accumulate. To avoid the ~90 MB model download entirely, set `memory.enabled: false` or point `embedding` at a remote `openai_compatible`/`ollama` backend.
286286

packages/dashboard/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ packages/dashboard/
7171
The dashboard reads from the same SQLite database the plugin writes to:
7272
- **Database**: `~/.local/share/cortexkit/magic-context/context.db`
7373
- **Config**: `~/.config/cortexkit/magic-context.jsonc` (user) · `<project>/.cortexkit/magic-context.jsonc` (project)
74-
- **Logs**: `${TMPDIR}/opencode/magic-context/magic-context.log` (`pi/` for Pi)
74+
- **Logs**: `$MAGIC_CONTEXT_LOG_PATH` (default: `${TMPDIR}/opencode/magic-context/magic-context.log`, `pi/` for Pi)
7575

7676
Database access uses WAL mode for safe concurrent reads while the plugin writes. Write operations (memory edits, dream queue entries) use `busy_timeout` to handle contention.
7777

packages/dashboard/src-tauri/src/log_parser.rs

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,18 @@ impl Harness {
3333
/// in sync manually because the dashboard doesn't import any TypeScript
3434
/// source.
3535
pub fn resolve_log_path_for(harness: Harness) -> PathBuf {
36+
// Mirror the plugin's getMagicContextLogPath: an explicit override wins over
37+
// the harness temp-dir default so the dashboard reads the same file the
38+
// plugin writes when the user relocates it. Blank/whitespace is treated as
39+
// unset.
40+
if let Some(env_path) = std::env::var("MAGIC_CONTEXT_LOG_PATH")
41+
.ok()
42+
.map(|value| value.trim().to_string())
43+
.filter(|value| !value.is_empty())
44+
{
45+
return PathBuf::from(env_path);
46+
}
47+
3648
std::env::temp_dir()
3749
.join(harness.as_str())
3850
.join("magic-context")
@@ -436,3 +448,76 @@ pub fn read_log_tail(path: &PathBuf, max_lines: usize) -> Vec<LogEntry> {
436448
.filter_map(|line| parse_log_line(line))
437449
.collect()
438450
}
451+
452+
#[cfg(test)]
453+
mod tests {
454+
use super::{resolve_log_path_for, Harness};
455+
use std::path::PathBuf;
456+
use std::sync::{Mutex, OnceLock};
457+
458+
// The env var is process-global; serialize the tests that mutate it.
459+
fn env_lock() -> std::sync::MutexGuard<'static, ()> {
460+
static ENV_LOCK: OnceLock<Mutex<()>> = OnceLock::new();
461+
ENV_LOCK
462+
.get_or_init(|| Mutex::new(()))
463+
.lock()
464+
.unwrap_or_else(|e| e.into_inner())
465+
}
466+
467+
#[test]
468+
fn resolve_log_path_for_uses_harness_fallback_when_env_unset() {
469+
let _guard = env_lock();
470+
std::env::remove_var("MAGIC_CONTEXT_LOG_PATH");
471+
472+
assert_eq!(
473+
resolve_log_path_for(Harness::Opencode),
474+
std::env::temp_dir()
475+
.join("opencode")
476+
.join("magic-context")
477+
.join("magic-context.log")
478+
);
479+
assert_eq!(
480+
resolve_log_path_for(Harness::Pi),
481+
std::env::temp_dir()
482+
.join("pi")
483+
.join("magic-context")
484+
.join("magic-context.log")
485+
);
486+
}
487+
488+
#[test]
489+
fn resolve_log_path_for_honors_magic_context_log_path_override() {
490+
let _guard = env_lock();
491+
let custom = std::env::temp_dir()
492+
.join("custom")
493+
.join("magic-context.log");
494+
std::env::set_var(
495+
"MAGIC_CONTEXT_LOG_PATH",
496+
custom.to_string_lossy().to_string(),
497+
);
498+
499+
assert_eq!(
500+
resolve_log_path_for(Harness::Opencode),
501+
PathBuf::from(&custom)
502+
);
503+
assert_eq!(resolve_log_path_for(Harness::Pi), PathBuf::from(&custom));
504+
505+
std::env::remove_var("MAGIC_CONTEXT_LOG_PATH");
506+
}
507+
508+
#[test]
509+
fn resolve_log_path_for_ignores_blank_magic_context_log_path() {
510+
let _guard = env_lock();
511+
std::env::set_var("MAGIC_CONTEXT_LOG_PATH", " ");
512+
513+
assert_eq!(
514+
resolve_log_path_for(Harness::Pi),
515+
std::env::temp_dir()
516+
.join("pi")
517+
.join("magic-context")
518+
.join("magic-context.log")
519+
);
520+
521+
std::env::remove_var("MAGIC_CONTEXT_LOG_PATH");
522+
}
523+
}

packages/docs/src/content/docs/reference/dashboard.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,4 +148,4 @@ Use [Configuration](/reference/configuration/) for the full generated key refere
148148

149149
## Logs
150150

151-
Optional **log tail** for `magic-context.log` with filtering, useful alongside Cache when correlating busts with plugin log lines.
151+
Optional **log tail** for `magic-context.log` with filtering, useful alongside Cache when correlating busts with plugin log lines. The log path defaults to `${TMPDIR}/opencode/magic-context/magic-context.log` (`pi/` for Pi); set the `MAGIC_CONTEXT_LOG_PATH` environment variable to redirect it.

packages/plugin/src/shared/data-path.test.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
getCacheDir,
88
getDataDir,
99
getLegacyOpenCodeMagicContextStorageDir,
10+
getMagicContextLogPath,
1011
getMagicContextStorageDir,
1112
getOpenCodeCacheDir,
1213
getOpenCodeStorageDir,
@@ -18,17 +19,20 @@ const savedEnv = {
1819
XDG_CACHE_HOME: process.env.XDG_CACHE_HOME,
1920
XDG_DATA_HOME: process.env.XDG_DATA_HOME,
2021
LOCALAPPDATA: process.env.LOCALAPPDATA,
22+
MAGIC_CONTEXT_LOG_PATH: process.env.MAGIC_CONTEXT_LOG_PATH,
2123
};
2224

2325
describe("data-path", () => {
2426
beforeEach(() => {
2527
process.env.XDG_CACHE_HOME = undefined;
2628
process.env.XDG_DATA_HOME = undefined;
2729
process.env.LOCALAPPDATA = undefined;
30+
process.env.MAGIC_CONTEXT_LOG_PATH = undefined;
2831
// Bun's env handling: explicit delete for unset
2932
delete process.env.XDG_CACHE_HOME;
3033
delete process.env.XDG_DATA_HOME;
3134
delete process.env.LOCALAPPDATA;
35+
delete process.env.MAGIC_CONTEXT_LOG_PATH;
3236
});
3337

3438
afterEach(() => {
@@ -37,6 +41,9 @@ describe("data-path", () => {
3741
if (savedEnv.XDG_DATA_HOME !== undefined)
3842
process.env.XDG_DATA_HOME = savedEnv.XDG_DATA_HOME;
3943
if (savedEnv.LOCALAPPDATA !== undefined) process.env.LOCALAPPDATA = savedEnv.LOCALAPPDATA;
44+
if (savedEnv.MAGIC_CONTEXT_LOG_PATH !== undefined)
45+
process.env.MAGIC_CONTEXT_LOG_PATH = savedEnv.MAGIC_CONTEXT_LOG_PATH;
46+
else delete process.env.MAGIC_CONTEXT_LOG_PATH;
4047
});
4148

4249
test("getCacheDir falls back to <homedir>/.cache when XDG_CACHE_HOME is unset (all platforms)", () => {
@@ -158,6 +165,27 @@ describe("data-path", () => {
158165
path.join("/some/project/", ".cortexkit", "magic-context"),
159166
);
160167
});
168+
169+
test("getMagicContextLogPath falls back to the harness temp dir when the env override is unset", () => {
170+
expect(getMagicContextLogPath("opencode")).toBe(
171+
path.join(os.tmpdir(), "opencode", "magic-context", "magic-context.log"),
172+
);
173+
expect(getMagicContextLogPath("pi")).toBe(
174+
path.join(os.tmpdir(), "pi", "magic-context", "magic-context.log"),
175+
);
176+
});
177+
178+
test("getMagicContextLogPath honors MAGIC_CONTEXT_LOG_PATH", () => {
179+
process.env.MAGIC_CONTEXT_LOG_PATH = "/tmp/custom/magic-context.log";
180+
expect(getMagicContextLogPath("pi")).toBe("/tmp/custom/magic-context.log");
181+
});
182+
183+
test("getMagicContextLogPath ignores a blank MAGIC_CONTEXT_LOG_PATH", () => {
184+
process.env.MAGIC_CONTEXT_LOG_PATH = " ";
185+
expect(getMagicContextLogPath("pi")).toBe(
186+
path.join(os.tmpdir(), "pi", "magic-context", "magic-context.log"),
187+
);
188+
});
161189
});
162190

163191
describe("ensureCortexKitArtifactGitignore", () => {

packages/plugin/src/shared/data-path.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@ export function getMagicContextTempDir(harness: HarnessId = getHarness()): strin
4646
* reflected in the next flush.
4747
*/
4848
export function getMagicContextLogPath(harness: HarnessId = getHarness()): string {
49+
// An explicit override wins over the harness temp-dir default, so users on
50+
// sandboxed/ephemeral setups (Docker, CI) can point the diagnostic log at a
51+
// persistent or shared path. Blank/whitespace is treated as unset.
52+
const envPath = process.env.MAGIC_CONTEXT_LOG_PATH?.trim();
53+
if (envPath) return envPath;
4954
return path.join(getMagicContextTempDir(harness), "magic-context.log");
5055
}
5156

0 commit comments

Comments
 (0)