Skip to content

Commit 31501a1

Browse files
committed
Override the default log file path by environment variable MAGIC_CONTEXT_LOG_PATH
1 parent cfefdde commit 31501a1

11 files changed

Lines changed: 114 additions & 13 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` (fallback: `${TMPDIR}/opencode/magic-context/magic-context.log`, `pi/` for Pi) | Diagnostic log | 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` (fallback: `${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: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,14 @@ 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+
if let Some(env_path) = std::env::var("MAGIC_CONTEXT_LOG_PATH")
37+
.ok()
38+
.map(|value| value.trim().to_string())
39+
.filter(|value| !value.is_empty())
40+
{
41+
return PathBuf::from(env_path);
42+
}
43+
3644
std::env::temp_dir()
3745
.join(harness.as_str())
3846
.join("magic-context")
@@ -436,3 +444,76 @@ pub fn read_log_tail(path: &PathBuf, max_lines: usize) -> Vec<LogEntry> {
436444
.filter_map(|line| parse_log_line(line))
437445
.collect()
438446
}
447+
448+
#[cfg(test)]
449+
mod tests {
450+
use super::{resolve_log_path_for, Harness};
451+
use std::path::PathBuf;
452+
use std::sync::{Mutex, OnceLock};
453+
454+
static ENV_LOCK: OnceLock<Mutex<()>> = OnceLock::new();
455+
456+
fn env_lock() -> std::sync::MutexGuard<'static, ()> {
457+
ENV_LOCK
458+
.get_or_init(|| Mutex::new(()))
459+
.lock()
460+
.unwrap_or_else(|e| e.into_inner())
461+
}
462+
463+
#[test]
464+
fn resolve_log_path_for_uses_harness_fallback_when_env_unset() {
465+
let _guard = env_lock();
466+
std::env::remove_var("MAGIC_CONTEXT_LOG_PATH");
467+
468+
assert_eq!(
469+
resolve_log_path_for(Harness::Opencode),
470+
std::env::temp_dir()
471+
.join("opencode")
472+
.join("magic-context")
473+
.join("magic-context.log")
474+
);
475+
assert_eq!(
476+
resolve_log_path_for(Harness::Pi),
477+
std::env::temp_dir()
478+
.join("pi")
479+
.join("magic-context")
480+
.join("magic-context.log")
481+
);
482+
}
483+
484+
#[test]
485+
fn resolve_log_path_for_honors_magic_context_log_path_override() {
486+
let _guard = env_lock();
487+
let custom = std::env::temp_dir()
488+
.join("custom")
489+
.join("magic-context.log");
490+
std::env::set_var(
491+
"MAGIC_CONTEXT_LOG_PATH",
492+
custom.to_string_lossy().to_string(),
493+
);
494+
495+
assert_eq!(
496+
resolve_log_path_for(Harness::Opencode),
497+
PathBuf::from(&custom)
498+
);
499+
assert_eq!(resolve_log_path_for(Harness::Pi), PathBuf::from(&custom));
500+
501+
std::env::remove_var("MAGIC_CONTEXT_LOG_PATH");
502+
}
503+
504+
#[test]
505+
fn resolve_log_path_for_ignores_blank_magic_context_log_path() {
506+
let _guard = env_lock();
507+
std::env::set_var("MAGIC_CONTEXT_LOG_PATH", " ");
508+
509+
assert_eq!(
510+
resolve_log_path_for(Harness::Pi),
511+
std::env::temp_dir()
512+
.join("pi")
513+
.join("magic-context")
514+
.join("magic-context.log")
515+
);
516+
517+
std::env::remove_var("MAGIC_CONTEXT_LOG_PATH");
518+
}
519+
}

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

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

112112
## Logs
113113

114-
Optional **log tail** for `magic-context.log` with filtering, useful alongside Cache when correlating busts with plugin log lines.
114+
Optional **log tail** for `MAGIC_CONTEXT_LOG_PATH` (fallback: `${TMPDIR}/opencode/magic-context/magic-context.log`, `pi/` for Pi) with filteringuseful alongside Cache when correlating busts with plugin log lines. Open from the sidebar **Logs** item.

packages/plugin/src/features/magic-context/dreamer/retrospective-raw-provider.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -524,4 +524,3 @@ function parseJsonRecord(value: string): Record<string, unknown> | null {
524524
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) return null;
525525
return parsed as Record<string, unknown>;
526526
}
527-

packages/plugin/src/features/magic-context/dreamer/task-executor.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,6 @@ export interface DreamTaskExecutorDeps {
9191
) => Promise<RawMessageProvider | null> | RawMessageProvider | null;
9292
language?: string;
9393
}
94-
9594
/** A failed task either hot-retries (transient: provider/network/rate-limit/
9695
* timeout/abort/lease/busy) or advances to the next cron slot (permanent:
9796
* model-not-found, validation, parse). Classify off the error shape. */
@@ -973,4 +972,3 @@ async function runAgenticTask(
973972
}
974973
}
975974
}
976-

packages/plugin/src/features/magic-context/dreamer/task-registry.test.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
11
/// <reference types="bun-types" />
22

33
import { describe, expect, test } from "bun:test";
4-
import {
5-
CANONICAL_DREAM_TASKS,
6-
isCanonicalDreamTask,
7-
MEMORY_DOMAIN_TASKS,
8-
} from "./task-registry";
4+
import { CANONICAL_DREAM_TASKS, isCanonicalDreamTask, MEMORY_DOMAIN_TASKS } from "./task-registry";
95

106
describe("dreamer task registry", () => {
117
test("classify-memories is canonical, memory-domain, and ordered after curate", () => {

packages/plugin/src/features/magic-context/storage-schema-helpers.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Database } from "../../shared/sqlite";
1+
import type { Database } from "../../shared/sqlite";
22

33
/**
44
* Schema-mutation helpers shared by storage-db (fresh-DB init) and migrations

packages/plugin/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ import { registerRpcHandlers } from "./plugin/rpc-handlers";
3939
import { createToolRegistry } from "./plugin/tool-registry";
4040
import { type ConflictResult, detectConflicts } from "./shared/conflict-detector";
4141
import { getMagicContextStorageDir } from "./shared/data-path";
42-
import { registerExitAbort, unregisterExitAbort } from './shared/exit-abort-registry';
42+
import { registerExitAbort, unregisterExitAbort } from "./shared/exit-abort-registry";
4343
import { setKeepSubagents } from "./shared/keep-subagents";
4444
import { log } from "./shared/logger";
4545
import { refreshModelLimitsFromApi } from "./shared/models-dev-cache";

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

Lines changed: 25 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,24 @@ describe("data-path", () => {
158165
path.join("/some/project/", ".cortexkit", "magic-context"),
159166
);
160167
});
168+
169+
test("getMagicContextLogPath falls back to harness temp dir when env unset", () => {
170+
expect(getMagicContextLogPath("opencode")).toBe(
171+
path.join(os.tmpdir(), "opencode", "magic-context", "magic-context.log"),
172+
);
173+
});
174+
175+
test("getMagicContextLogPath honors MAGIC_CONTEXT_LOG_PATH", () => {
176+
process.env.MAGIC_CONTEXT_LOG_PATH = "/tmp/custom/magic-context.log";
177+
expect(getMagicContextLogPath("pi")).toBe("/tmp/custom/magic-context.log");
178+
});
179+
180+
test("getMagicContextLogPath ignores blank MAGIC_CONTEXT_LOG_PATH", () => {
181+
process.env.MAGIC_CONTEXT_LOG_PATH = " ";
182+
expect(getMagicContextLogPath("pi")).toBe(
183+
path.join(os.tmpdir(), "pi", "magic-context", "magic-context.log"),
184+
);
185+
});
161186
});
162187

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

0 commit comments

Comments
 (0)