@cortexkit/opencode-magic-context 0.33.1 → 0.34.1
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 +20 -3
- package/dist/config/agent-disable.d.ts +22 -0
- package/dist/config/agent-disable.d.ts.map +1 -1
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/migrate-experimental.d.ts +2 -1
- package/dist/config/migrate-experimental.d.ts.map +1 -1
- package/dist/config/project-security.d.ts +9 -0
- package/dist/config/project-security.d.ts.map +1 -1
- package/dist/config/schema/magic-context.d.ts +90 -17
- package/dist/config/schema/magic-context.d.ts.map +1 -1
- package/dist/config/transform-mode.d.ts +2 -0
- package/dist/config/transform-mode.d.ts.map +1 -1
- package/dist/{conflict-warning-hook-n48c783v.js → conflict-warning-hook-fvpfvt0w.js} +1 -1
- package/dist/{derive-budgets-e9j09sjr.js → derive-budgets-9crp0cvd.js} +1 -1
- package/dist/features/builtin-commands/commands.d.ts +1 -1
- package/dist/features/builtin-commands/commands.d.ts.map +1 -1
- package/dist/features/magic-context/compaction-marker.d.ts +57 -0
- package/dist/features/magic-context/compaction-marker.d.ts.map +1 -1
- package/dist/features/magic-context/compartment-chunk-embedding.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/classify.d.ts +1 -0
- package/dist/features/magic-context/dreamer/classify.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/evaluate-smart-notes.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/map-memories.d.ts +1 -0
- package/dist/features/magic-context/dreamer/map-memories.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/refresh-primers.d.ts +1 -0
- package/dist/features/magic-context/dreamer/refresh-primers.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/storage-dream-runs.d.ts +2 -0
- package/dist/features/magic-context/dreamer/storage-dream-runs.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/task-executor.d.ts +4 -1
- package/dist/features/magic-context/dreamer/task-executor.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/task-gates.d.ts +19 -1
- package/dist/features/magic-context/dreamer/task-gates.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/task-registry.d.ts +28 -0
- package/dist/features/magic-context/dreamer/task-registry.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/task-scheduler.d.ts +7 -1
- package/dist/features/magic-context/dreamer/task-scheduler.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/verify.d.ts +1 -0
- package/dist/features/magic-context/dreamer/verify.d.ts.map +1 -1
- package/dist/features/magic-context/fail-closed-block.d.ts +10 -0
- package/dist/features/magic-context/fail-closed-block.d.ts.map +1 -1
- package/dist/features/magic-context/git-commits/storage-git-commit-embeddings.d.ts.map +1 -1
- package/dist/features/magic-context/git-commits/storage-git-commits.d.ts.map +1 -1
- package/dist/features/magic-context/memory/embedding-identity.d.ts.map +1 -1
- package/dist/features/magic-context/memory/embedding-local.d.ts +7 -1
- package/dist/features/magic-context/memory/embedding-local.d.ts.map +1 -1
- package/dist/features/magic-context/memory/embedding.d.ts.map +1 -1
- package/dist/features/magic-context/memory/memory-migration.d.ts.map +1 -1
- package/dist/features/magic-context/memory/project-identity.d.ts +2 -6
- package/dist/features/magic-context/memory/project-identity.d.ts.map +1 -1
- package/dist/features/magic-context/memory/storage-memory-embeddings.d.ts.map +1 -1
- package/dist/features/magic-context/migrations.d.ts +23 -1
- package/dist/features/magic-context/migrations.d.ts.map +1 -1
- package/dist/features/magic-context/mural/compress-cues.d.ts +1 -0
- package/dist/features/magic-context/mural/compress-cues.d.ts.map +1 -1
- package/dist/features/magic-context/mural/render-mural.d.ts.map +1 -1
- package/dist/features/magic-context/overflow-detection.d.ts +8 -1
- package/dist/features/magic-context/overflow-detection.d.ts.map +1 -1
- package/dist/features/magic-context/project-embedding-registry.d.ts.map +1 -1
- package/dist/features/magic-context/scheduler.d.ts.map +1 -1
- package/dist/features/magic-context/schema-fence-probe.d.ts +28 -0
- package/dist/features/magic-context/schema-fence-probe.d.ts.map +1 -0
- package/dist/features/magic-context/sidekick/agent.d.ts.map +1 -1
- package/dist/features/magic-context/smart-notes/compiler.d.ts.map +1 -1
- package/dist/features/magic-context/storage-db.d.ts +37 -2
- package/dist/features/magic-context/storage-db.d.ts.map +1 -1
- package/dist/features/magic-context/storage-meta-persisted.d.ts +38 -4
- package/dist/features/magic-context/storage-meta-persisted.d.ts.map +1 -1
- package/dist/features/magic-context/storage-meta.d.ts +1 -1
- package/dist/features/magic-context/storage-meta.d.ts.map +1 -1
- package/dist/features/magic-context/storage.d.ts +2 -2
- package/dist/features/magic-context/storage.d.ts.map +1 -1
- package/dist/features/magic-context/user-memory/review-user-memories.d.ts.map +1 -1
- package/dist/hooks/magic-context/boundary-execution.d.ts +1 -1
- package/dist/hooks/magic-context/boundary-execution.d.ts.map +1 -1
- package/dist/hooks/magic-context/cache-busting-signals.d.ts +1 -0
- package/dist/hooks/magic-context/cache-busting-signals.d.ts.map +1 -1
- package/dist/hooks/magic-context/child-session-spawn.d.ts +26 -0
- package/dist/hooks/magic-context/child-session-spawn.d.ts.map +1 -0
- package/dist/hooks/magic-context/command-handler.d.ts +3 -0
- package/dist/hooks/magic-context/command-handler.d.ts.map +1 -1
- package/dist/hooks/magic-context/compaction-off-transition.d.ts +96 -0
- package/dist/hooks/magic-context/compaction-off-transition.d.ts.map +1 -0
- package/dist/hooks/magic-context/compartment-runner-historian.d.ts +2 -0
- package/dist/hooks/magic-context/compartment-runner-historian.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-runner-incremental.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-runner-mapping.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-runner-partial-recomp.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-runner-recomp.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-trigger.d.ts +12 -5
- package/dist/hooks/magic-context/compartment-trigger.d.ts.map +1 -1
- package/dist/hooks/magic-context/ctx-reduce-availability.d.ts +47 -1
- package/dist/hooks/magic-context/ctx-reduce-availability.d.ts.map +1 -1
- package/dist/hooks/magic-context/emergency-drop.d.ts +2 -2
- package/dist/hooks/magic-context/event-handler.d.ts +7 -0
- package/dist/hooks/magic-context/event-handler.d.ts.map +1 -1
- package/dist/hooks/magic-context/event-resolvers.d.ts +13 -21
- package/dist/hooks/magic-context/event-resolvers.d.ts.map +1 -1
- package/dist/hooks/magic-context/execute-status.d.ts +5 -1
- package/dist/hooks/magic-context/execute-status.d.ts.map +1 -1
- package/dist/hooks/magic-context/final-wire-token-estimate.d.ts +2 -0
- package/dist/hooks/magic-context/final-wire-token-estimate.d.ts.map +1 -1
- package/dist/hooks/magic-context/heuristic-cleanup.d.ts +1 -1
- package/dist/hooks/magic-context/hook-handlers.d.ts +1 -0
- package/dist/hooks/magic-context/hook-handlers.d.ts.map +1 -1
- package/dist/hooks/magic-context/hook.d.ts +14 -6
- package/dist/hooks/magic-context/hook.d.ts.map +1 -1
- package/dist/hooks/magic-context/inject-compartments.d.ts +10 -1
- package/dist/hooks/magic-context/inject-compartments.d.ts.map +1 -1
- package/dist/hooks/magic-context/live-session-state.d.ts +3 -0
- package/dist/hooks/magic-context/live-session-state.d.ts.map +1 -1
- package/dist/hooks/magic-context/protected-tail-boundary.d.ts.map +1 -1
- package/dist/hooks/magic-context/read-session-db.d.ts.map +1 -1
- package/dist/hooks/magic-context/read-session-true-raw-tokens.d.ts.map +1 -1
- package/dist/hooks/magic-context/rust-mode-transform.d.ts +3 -0
- package/dist/hooks/magic-context/rust-mode-transform.d.ts.map +1 -1
- package/dist/hooks/magic-context/send-session-notification.d.ts +20 -1
- package/dist/hooks/magic-context/send-session-notification.d.ts.map +1 -1
- package/dist/hooks/magic-context/transform-compartment-phase.d.ts +3 -0
- package/dist/hooks/magic-context/transform-compartment-phase.d.ts.map +1 -1
- package/dist/hooks/magic-context/transform-postprocess-phase.d.ts +31 -1
- package/dist/hooks/magic-context/transform-postprocess-phase.d.ts.map +1 -1
- package/dist/hooks/magic-context/transform.d.ts +29 -2
- package/dist/hooks/magic-context/transform.d.ts.map +1 -1
- package/dist/{index-56yjaeb2.js → index-2y0ybv5x.js} +11 -1
- package/dist/{index-ey1bc14z.js → index-b563sga5.js} +7 -264
- package/dist/{index-y1c9tcm3.js → index-cn960ffd.js} +101 -20
- package/dist/{index-xyaekybw.js → index-dtas91s5.js} +6 -4
- package/dist/index-fcyvznch.js +279 -0
- package/dist/{index-63dq2g4d.js → index-ry8rh8cv.js} +138 -24
- package/dist/{index-c2gahrks.js → index-x8spt5wy.js} +1682 -893
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1677 -723
- package/dist/plugin/dream-timer.d.ts +4 -2
- package/dist/plugin/dream-timer.d.ts.map +1 -1
- package/dist/plugin/embedding-bootstrap.d.ts.map +1 -1
- package/dist/plugin/hooks/create-session-hooks.d.ts +12 -1
- package/dist/plugin/hooks/create-session-hooks.d.ts.map +1 -1
- package/dist/plugin/messages-transform.d.ts +10 -0
- package/dist/plugin/messages-transform.d.ts.map +1 -1
- package/dist/plugin/rpc-handlers.d.ts +3 -7
- package/dist/plugin/rpc-handlers.d.ts.map +1 -1
- package/dist/plugin/tool-registry.d.ts +8 -0
- package/dist/plugin/tool-registry.d.ts.map +1 -1
- package/dist/{recomp-orchestrator-2yb2hfh2.js → recomp-orchestrator-k72ejkkh.js} +6 -4
- package/dist/send-session-notification-6njqewzk.js +20 -0
- package/dist/shared/conflict-detector.d.ts +38 -1
- package/dist/shared/conflict-detector.d.ts.map +1 -1
- package/dist/shared/conflict-fixer.d.ts +18 -1
- package/dist/shared/conflict-fixer.d.ts.map +1 -1
- package/dist/shared/context-limit-provenance.d.ts +3 -0
- package/dist/shared/context-limit-provenance.d.ts.map +1 -0
- package/dist/shared/escalation-bands.d.ts +9 -0
- package/dist/shared/escalation-bands.d.ts.map +1 -0
- package/dist/shared/format-threshold.d.ts +1 -1
- package/dist/shared/models-dev-cache.d.ts +29 -3
- package/dist/shared/models-dev-cache.d.ts.map +1 -1
- package/dist/shared/rpc-server.d.ts.map +1 -1
- package/dist/shared/rpc-types.d.ts +51 -1
- package/dist/shared/rpc-types.d.ts.map +1 -1
- package/dist/shared/rpc-utils.d.ts +19 -0
- package/dist/shared/rpc-utils.d.ts.map +1 -1
- package/dist/shared/storage-permissions.d.ts +5 -0
- package/dist/shared/storage-permissions.d.ts.map +1 -0
- package/dist/shared/tui-runtime-specifiers.d.ts +20 -0
- package/dist/shared/tui-runtime-specifiers.d.ts.map +1 -0
- package/dist/{storage-meta-session-td6bwtky.js → storage-meta-session-xt41zbw4.js} +2 -1
- package/dist/{task-config-5nx3qfz8.js → task-config-gbp7r23g.js} +1 -1
- package/dist/tui/compaction-off.d.ts +13 -0
- package/dist/tui/compaction-off.d.ts.map +1 -0
- package/dist/tui/data/context-db.d.ts.map +1 -1
- package/dist/tui-compiled/compaction-off.d.ts +13 -0
- package/dist/tui-compiled/compaction-off.d.ts.map +1 -0
- package/dist/tui-compiled/data/context-db.d.ts.map +1 -1
- package/package.json +6 -1
- package/src/agents/dreamer.ts +68 -0
- package/src/agents/hidden-agent-registrations.ts +331 -0
- package/src/agents/historian.ts +3 -0
- package/src/agents/language-directive.test.ts +138 -0
- package/src/agents/language-directive.ts +121 -0
- package/src/agents/magic-context-prompt.language.test.ts +35 -0
- package/src/agents/magic-context-prompt.test.ts +292 -0
- package/src/agents/magic-context-prompt.ts +182 -0
- package/src/agents/permissions.test.ts +252 -0
- package/src/agents/permissions.ts +186 -0
- package/src/agents/sidekick.ts +1 -0
- package/src/agents/smart-note-compiler.ts +1 -0
- package/src/config/agent-disable.ts +106 -0
- package/src/config/compaction-accessor-guard.test.ts +82 -0
- package/src/config/compaction-backcompat.test.ts +52 -0
- package/src/config/index.test.ts +936 -0
- package/src/config/index.ts +601 -0
- package/src/config/migrate-config-location.test.ts +332 -0
- package/src/config/migrate-config-location.ts +635 -0
- package/src/config/migrate-dreamer-v2.test.ts +287 -0
- package/src/config/migrate-dreamer-v2.ts +336 -0
- package/src/config/migrate-experimental.test.ts +41 -0
- package/src/config/migrate-experimental.ts +185 -0
- package/src/config/project-security.test.ts +388 -0
- package/src/config/project-security.ts +611 -0
- package/src/config/prune-config-leaf.test.ts +78 -0
- package/src/config/prune-config-leaf.ts +68 -0
- package/src/config/removed-config-keys.test.ts +116 -0
- package/src/config/schema/agent-overrides.ts +45 -0
- package/src/config/schema/magic-context.test.ts +359 -0
- package/src/config/schema/magic-context.ts +991 -0
- package/src/config/transform-mode.test.ts +62 -0
- package/src/config/transform-mode.ts +34 -0
- package/src/config/variable.test.ts +323 -0
- package/src/config/variable.ts +192 -0
- package/src/features/magic-context/defaults.ts +1 -0
- package/src/features/magic-context/dreamer/cron.ts +243 -0
- package/src/features/magic-context/dreamer/task-registry.ts +151 -0
- package/src/shared/conflict-detector.test.ts +121 -0
- package/src/shared/conflict-detector.ts +50 -3
- package/src/shared/conflict-fixer.test.ts +309 -0
- package/src/shared/conflict-fixer.ts +82 -5
- package/src/shared/context-limit-provenance.ts +6 -0
- package/src/shared/escalation-bands.test.ts +23 -0
- package/src/shared/escalation-bands.ts +18 -0
- package/src/shared/format-threshold.ts +1 -1
- package/src/shared/models-dev-cache.test.ts +171 -0
- package/src/shared/models-dev-cache.ts +195 -38
- package/src/shared/rpc-server.ts +27 -18
- package/src/shared/rpc-types.ts +55 -1
- package/src/shared/rpc-utils.test.ts +157 -0
- package/src/shared/rpc-utils.ts +137 -0
- package/src/shared/storage-permissions.ts +20 -0
- package/src/shared/tui-runtime-specifiers.ts +32 -0
- package/src/tui/compaction-off.test.ts +56 -0
- package/src/tui/compaction-off.ts +29 -0
- package/src/tui/data/context-db.ts +6 -0
- package/src/tui/index.tsx +113 -65
- package/src/tui/slots/sidebar-content.tsx +189 -91
- package/src/tui/tui-compiled-runtime-imports.test.ts +151 -0
- package/src/tui-compiled/compaction-off.ts +29 -0
- package/src/tui-compiled/data/context-db.ts +6 -0
- package/src/tui-compiled/index.tsx +558 -455
- package/src/tui-compiled/slots/sidebar-content.tsx +334 -169
- package/dist/send-session-notification-tghq9gnh.js +0 -11
- /package/dist/{index-v7fmc1s2.js → index-ygcng9t8.js} +0 -0
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
import { describe, expect, it } from "bun:test";
|
|
2
|
+
import {
|
|
3
|
+
existsSync,
|
|
4
|
+
mkdirSync,
|
|
5
|
+
mkdtempSync,
|
|
6
|
+
readFileSync,
|
|
7
|
+
rmSync,
|
|
8
|
+
utimesSync,
|
|
9
|
+
writeFileSync,
|
|
10
|
+
} from "node:fs";
|
|
11
|
+
import { tmpdir } from "node:os";
|
|
12
|
+
import { join } from "node:path";
|
|
13
|
+
import {
|
|
14
|
+
type LegacyConfigSource,
|
|
15
|
+
migrateConfigFile,
|
|
16
|
+
resolveLegacyConfigSources,
|
|
17
|
+
} from "./migrate-config-location";
|
|
18
|
+
|
|
19
|
+
function tmp(): string {
|
|
20
|
+
return mkdtempSync(join(tmpdir(), "mc-cfgloc-"));
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function src(path: string, label = "legacy"): LegacyConfigSource {
|
|
24
|
+
return { path, label };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
describe("migrateConfigFile (location migration)", () => {
|
|
28
|
+
it("no-ops when no legacy source exists", () => {
|
|
29
|
+
const dir = tmp();
|
|
30
|
+
try {
|
|
31
|
+
const target = join(dir, ".cortexkit", "magic-context.jsonc");
|
|
32
|
+
const r = migrateConfigFile({
|
|
33
|
+
scope: "project",
|
|
34
|
+
targetPath: target,
|
|
35
|
+
legacySources: [src(join(dir, "magic-context.jsonc"))],
|
|
36
|
+
});
|
|
37
|
+
expect(r.migrated).toBe(false);
|
|
38
|
+
expect(r.conflict).toBe(false);
|
|
39
|
+
expect(existsSync(target)).toBe(false);
|
|
40
|
+
} finally {
|
|
41
|
+
rmSync(dir, { recursive: true, force: true });
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it("reclaims a stale (crashed-holder) lock in one shot without blocking init", () => {
|
|
46
|
+
const dir = tmp();
|
|
47
|
+
try {
|
|
48
|
+
const target = join(dir, ".cortexkit", "magic-context.jsonc");
|
|
49
|
+
const legacy = join(dir, "magic-context.jsonc");
|
|
50
|
+
writeFileSync(legacy, '{"enabled":false}');
|
|
51
|
+
// A leftover lock dir from a crashed holder, aged past the stale
|
|
52
|
+
// threshold. The non-blocking lock removes it and re-attempts mkdir
|
|
53
|
+
// once (no busy-wait, no synchronous sleep), so migration proceeds
|
|
54
|
+
// and init is never frozen.
|
|
55
|
+
const lockDir = `${target}.lock`;
|
|
56
|
+
mkdirSync(lockDir, { recursive: true });
|
|
57
|
+
// Backdate the lock dir's mtime well past CONFIG_LOCK_STALE_MS (4s).
|
|
58
|
+
const old = Date.now() - 60_000;
|
|
59
|
+
utimesSync(lockDir, new Date(old), new Date(old));
|
|
60
|
+
const start = Date.now();
|
|
61
|
+
const r = migrateConfigFile({
|
|
62
|
+
scope: "project",
|
|
63
|
+
targetPath: target,
|
|
64
|
+
legacySources: [src(legacy)],
|
|
65
|
+
});
|
|
66
|
+
const elapsed = Date.now() - start;
|
|
67
|
+
// Effectively instant — no timeout budget is ever waited.
|
|
68
|
+
expect(elapsed).toBeLessThan(1_000);
|
|
69
|
+
expect(r.migrated).toBe(true);
|
|
70
|
+
expect(existsSync(target)).toBe(true);
|
|
71
|
+
} finally {
|
|
72
|
+
rmSync(dir, { recursive: true, force: true });
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it("skips migration this run (no block) when a LIVE holder owns the lock", () => {
|
|
77
|
+
const dir = tmp();
|
|
78
|
+
try {
|
|
79
|
+
const target = join(dir, ".cortexkit", "magic-context.jsonc");
|
|
80
|
+
const legacy = join(dir, "magic-context.jsonc");
|
|
81
|
+
writeFileSync(legacy, '{"enabled":false}');
|
|
82
|
+
// A FRESH lock dir (live holder, not stale): we must not block waiting
|
|
83
|
+
// for it — return immediately with migrated:false and leave the
|
|
84
|
+
// target absent (the loader read-legacy-on-conflict covers this run;
|
|
85
|
+
// the next init retries once the holder releases).
|
|
86
|
+
mkdirSync(`${target}.lock`, { recursive: true });
|
|
87
|
+
const start = Date.now();
|
|
88
|
+
const r = migrateConfigFile({
|
|
89
|
+
scope: "project",
|
|
90
|
+
targetPath: target,
|
|
91
|
+
legacySources: [src(legacy)],
|
|
92
|
+
});
|
|
93
|
+
const elapsed = Date.now() - start;
|
|
94
|
+
expect(elapsed).toBeLessThan(1_000);
|
|
95
|
+
expect(r.migrated).toBe(false);
|
|
96
|
+
expect(r.conflict).toBe(false);
|
|
97
|
+
expect(existsSync(target)).toBe(false);
|
|
98
|
+
} finally {
|
|
99
|
+
rmSync(dir, { recursive: true, force: true });
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it("moves a single legacy source to the target and leaves a .MOVED_READPLEASE marker", () => {
|
|
104
|
+
const dir = tmp();
|
|
105
|
+
try {
|
|
106
|
+
const legacy = join(dir, ".opencode", "magic-context.jsonc");
|
|
107
|
+
mkdirSync(join(dir, ".opencode"), { recursive: true });
|
|
108
|
+
writeFileSync(legacy, '{ "enabled": true }');
|
|
109
|
+
const target = join(dir, ".cortexkit", "magic-context.jsonc");
|
|
110
|
+
|
|
111
|
+
const r = migrateConfigFile({
|
|
112
|
+
scope: "project",
|
|
113
|
+
targetPath: target,
|
|
114
|
+
legacySources: [src(legacy)],
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
expect(r.migrated).toBe(true);
|
|
118
|
+
expect(r.conflict).toBe(false);
|
|
119
|
+
// Content is at the target...
|
|
120
|
+
expect(readFileSync(target, "utf8")).toContain("enabled");
|
|
121
|
+
// ...the legacy file is gone (idempotency comes from this)...
|
|
122
|
+
expect(existsSync(legacy)).toBe(false);
|
|
123
|
+
// ...and a human breadcrumb preserves the original below a header.
|
|
124
|
+
const marker = `${legacy}.MOVED_READPLEASE`;
|
|
125
|
+
expect(existsSync(marker)).toBe(true);
|
|
126
|
+
const markerText = readFileSync(marker, "utf8");
|
|
127
|
+
expect(markerText).toContain("configuration moved");
|
|
128
|
+
expect(markerText).toContain(target);
|
|
129
|
+
expect(markerText).toContain("enabled");
|
|
130
|
+
} finally {
|
|
131
|
+
rmSync(dir, { recursive: true, force: true });
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it("is idempotent: a second run finds no legacy source and no-ops", () => {
|
|
136
|
+
const dir = tmp();
|
|
137
|
+
try {
|
|
138
|
+
const legacy = join(dir, "magic-context.jsonc");
|
|
139
|
+
writeFileSync(legacy, '{ "protected_tags": 5 }');
|
|
140
|
+
const target = join(dir, ".cortexkit", "magic-context.jsonc");
|
|
141
|
+
const opts = {
|
|
142
|
+
scope: "project" as const,
|
|
143
|
+
targetPath: target,
|
|
144
|
+
legacySources: [src(legacy)],
|
|
145
|
+
};
|
|
146
|
+
const first = migrateConfigFile(opts);
|
|
147
|
+
expect(first.migrated).toBe(true);
|
|
148
|
+
const second = migrateConfigFile(opts);
|
|
149
|
+
expect(second.migrated).toBe(false);
|
|
150
|
+
expect(second.conflict).toBe(false);
|
|
151
|
+
} finally {
|
|
152
|
+
rmSync(dir, { recursive: true, force: true });
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it("moves legacy aside (no overwrite) when target already exists and matches semantically", () => {
|
|
157
|
+
const dir = tmp();
|
|
158
|
+
try {
|
|
159
|
+
const target = join(dir, ".cortexkit", "magic-context.jsonc");
|
|
160
|
+
mkdirSync(join(dir, ".cortexkit"), { recursive: true });
|
|
161
|
+
// Target = pretty-printed; legacy = compact + comment + reordered keys.
|
|
162
|
+
writeFileSync(target, '{\n "protected_tags": 5,\n "cache_ttl": "1h"\n}\n');
|
|
163
|
+
const legacy = join(dir, ".opencode", "magic-context.jsonc");
|
|
164
|
+
mkdirSync(join(dir, ".opencode"), { recursive: true });
|
|
165
|
+
writeFileSync(legacy, '// mine\n{ "cache_ttl": "1h", "protected_tags": 5, }');
|
|
166
|
+
|
|
167
|
+
const r = migrateConfigFile({
|
|
168
|
+
scope: "project",
|
|
169
|
+
targetPath: target,
|
|
170
|
+
legacySources: [src(legacy)],
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
expect(r.migrated).toBe(false);
|
|
174
|
+
expect(r.conflict).toBe(false);
|
|
175
|
+
// Target untouched (not overwritten), legacy moved aside.
|
|
176
|
+
expect(readFileSync(target, "utf8")).toContain('"protected_tags": 5');
|
|
177
|
+
expect(existsSync(legacy)).toBe(false);
|
|
178
|
+
expect(existsSync(`${legacy}.MOVED_READPLEASE`)).toBe(true);
|
|
179
|
+
} finally {
|
|
180
|
+
rmSync(dir, { recursive: true, force: true });
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
it("REFUSES (conflict) when target exists with different settings — leaves both untouched", () => {
|
|
185
|
+
const dir = tmp();
|
|
186
|
+
try {
|
|
187
|
+
const target = join(dir, ".cortexkit", "magic-context.jsonc");
|
|
188
|
+
mkdirSync(join(dir, ".cortexkit"), { recursive: true });
|
|
189
|
+
writeFileSync(target, '{ "protected_tags": 5 }');
|
|
190
|
+
const legacy = join(dir, ".opencode", "magic-context.jsonc");
|
|
191
|
+
mkdirSync(join(dir, ".opencode"), { recursive: true });
|
|
192
|
+
writeFileSync(legacy, '{ "protected_tags": 9 }');
|
|
193
|
+
|
|
194
|
+
const r = migrateConfigFile({
|
|
195
|
+
scope: "project",
|
|
196
|
+
targetPath: target,
|
|
197
|
+
legacySources: [src(legacy)],
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
expect(r.conflict).toBe(true);
|
|
201
|
+
expect(r.migrated).toBe(false);
|
|
202
|
+
expect(r.warnings.join("\n")).toContain("already exists with different settings");
|
|
203
|
+
// BOTH left as-is for manual reconciliation — never auto-clobbered.
|
|
204
|
+
expect(readFileSync(target, "utf8")).toContain('"protected_tags": 5');
|
|
205
|
+
expect(readFileSync(legacy, "utf8")).toContain('"protected_tags": 9');
|
|
206
|
+
expect(existsSync(`${legacy}.MOVED_READPLEASE`)).toBe(false);
|
|
207
|
+
} finally {
|
|
208
|
+
rmSync(dir, { recursive: true, force: true });
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
it("REFUSES (conflict) when multiple legacy sources disagree and no target exists", () => {
|
|
213
|
+
const dir = tmp();
|
|
214
|
+
try {
|
|
215
|
+
const a = join(dir, ".opencode", "magic-context.jsonc");
|
|
216
|
+
const b = join(dir, ".pi", "magic-context.jsonc");
|
|
217
|
+
mkdirSync(join(dir, ".opencode"), { recursive: true });
|
|
218
|
+
mkdirSync(join(dir, ".pi"), { recursive: true });
|
|
219
|
+
writeFileSync(a, '{ "protected_tags": 5 }');
|
|
220
|
+
writeFileSync(b, '{ "protected_tags": 9 }');
|
|
221
|
+
const target = join(dir, ".cortexkit", "magic-context.jsonc");
|
|
222
|
+
|
|
223
|
+
const r = migrateConfigFile({
|
|
224
|
+
scope: "project",
|
|
225
|
+
targetPath: target,
|
|
226
|
+
legacySources: [src(a), src(b)],
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
expect(r.conflict).toBe(true);
|
|
230
|
+
expect(r.migrated).toBe(false);
|
|
231
|
+
expect(existsSync(target)).toBe(false);
|
|
232
|
+
// Neither moved aside — user reconciles by hand.
|
|
233
|
+
expect(existsSync(a)).toBe(true);
|
|
234
|
+
expect(existsSync(b)).toBe(true);
|
|
235
|
+
} finally {
|
|
236
|
+
rmSync(dir, { recursive: true, force: true });
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
it("migrates once and moves ALL matching legacy sources aside when they agree", () => {
|
|
241
|
+
const dir = tmp();
|
|
242
|
+
try {
|
|
243
|
+
const a = join(dir, ".opencode", "magic-context.jsonc");
|
|
244
|
+
const b = join(dir, ".pi", "magic-context.jsonc");
|
|
245
|
+
mkdirSync(join(dir, ".opencode"), { recursive: true });
|
|
246
|
+
mkdirSync(join(dir, ".pi"), { recursive: true });
|
|
247
|
+
writeFileSync(a, '{ "protected_tags": 5 }');
|
|
248
|
+
writeFileSync(b, '{ "protected_tags": 5 }'); // same
|
|
249
|
+
const target = join(dir, ".cortexkit", "magic-context.jsonc");
|
|
250
|
+
|
|
251
|
+
const r = migrateConfigFile({
|
|
252
|
+
scope: "project",
|
|
253
|
+
targetPath: target,
|
|
254
|
+
legacySources: [src(a), src(b)],
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
expect(r.migrated).toBe(true);
|
|
258
|
+
expect(existsSync(target)).toBe(true);
|
|
259
|
+
expect(existsSync(`${a}.MOVED_READPLEASE`)).toBe(true);
|
|
260
|
+
expect(existsSync(`${b}.MOVED_READPLEASE`)).toBe(true);
|
|
261
|
+
} finally {
|
|
262
|
+
rmSync(dir, { recursive: true, force: true });
|
|
263
|
+
}
|
|
264
|
+
});
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
describe("resolveLegacyConfigSources", () => {
|
|
268
|
+
it("includes the bare-root project source unique to Magic Context", () => {
|
|
269
|
+
const sources = resolveLegacyConfigSources("/proj");
|
|
270
|
+
const projectPaths = sources.project.map((s) => s.path);
|
|
271
|
+
// bare root + .opencode + .pi, each in {.jsonc,.json}
|
|
272
|
+
expect(projectPaths).toContain("/proj/magic-context.jsonc");
|
|
273
|
+
expect(projectPaths).toContain("/proj/magic-context.json");
|
|
274
|
+
expect(projectPaths).toContain("/proj/.opencode/magic-context.jsonc");
|
|
275
|
+
expect(projectPaths).toContain("/proj/.pi/magic-context.jsonc");
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
it("includes both OpenCode and Pi user sources", () => {
|
|
279
|
+
const sources = resolveLegacyConfigSources("/proj");
|
|
280
|
+
const userPaths = sources.user.map((s) => s.path);
|
|
281
|
+
expect(userPaths.some((p) => p.includes(join("opencode", "magic-context.jsonc")))).toBe(
|
|
282
|
+
true,
|
|
283
|
+
);
|
|
284
|
+
expect(userPaths.some((p) => p.includes(join(".pi", "agent", "magic-context.jsonc")))).toBe(
|
|
285
|
+
true,
|
|
286
|
+
);
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
it("never lists a user-scope config as a project source when the project dir is the CortexKit config home", () => {
|
|
290
|
+
// Regression: opencode opened with cwd = ~/.config/cortexkit made the
|
|
291
|
+
// bare-root project source `<root>/magic-context.jsonc` collide with the
|
|
292
|
+
// USER config, so the project migration ate the user config into
|
|
293
|
+
// `<root>/.cortexkit/` and left the user on schema defaults.
|
|
294
|
+
const prev = process.env.XDG_CONFIG_HOME;
|
|
295
|
+
const home = tmp();
|
|
296
|
+
try {
|
|
297
|
+
process.env.XDG_CONFIG_HOME = home;
|
|
298
|
+
const cortexkitHome = join(home, "cortexkit");
|
|
299
|
+
const sources = resolveLegacyConfigSources(cortexkitHome);
|
|
300
|
+
const projectPaths = sources.project.map((s) => s.path);
|
|
301
|
+
// The user config path must NOT be a project migration source.
|
|
302
|
+
expect(projectPaths).not.toContain(join(cortexkitHome, "magic-context.jsonc"));
|
|
303
|
+
expect(projectPaths).not.toContain(join(cortexkitHome, "magic-context.json"));
|
|
304
|
+
// The genuine project subdir sources are still present.
|
|
305
|
+
expect(projectPaths).toContain(join(cortexkitHome, ".opencode", "magic-context.jsonc"));
|
|
306
|
+
expect(projectPaths).toContain(join(cortexkitHome, ".pi", "magic-context.jsonc"));
|
|
307
|
+
} finally {
|
|
308
|
+
if (prev === undefined) delete process.env.XDG_CONFIG_HOME;
|
|
309
|
+
else process.env.XDG_CONFIG_HOME = prev;
|
|
310
|
+
rmSync(home, { recursive: true, force: true });
|
|
311
|
+
}
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
it("never lists the OpenCode/Pi user legacy paths as project sources when the project dir is the user config dir", () => {
|
|
315
|
+
// Same collision class for a project opened directly in ~/.config/opencode
|
|
316
|
+
// (bare-root would resolve to the OpenCode user legacy config).
|
|
317
|
+
const prev = process.env.XDG_CONFIG_HOME;
|
|
318
|
+
const home = tmp();
|
|
319
|
+
try {
|
|
320
|
+
process.env.XDG_CONFIG_HOME = home;
|
|
321
|
+
const opencodeHome = join(home, "opencode");
|
|
322
|
+
const sources = resolveLegacyConfigSources(opencodeHome);
|
|
323
|
+
const projectPaths = sources.project.map((s) => s.path);
|
|
324
|
+
expect(projectPaths).not.toContain(join(opencodeHome, "magic-context.jsonc"));
|
|
325
|
+
expect(projectPaths).not.toContain(join(opencodeHome, "magic-context.json"));
|
|
326
|
+
} finally {
|
|
327
|
+
if (prev === undefined) delete process.env.XDG_CONFIG_HOME;
|
|
328
|
+
else process.env.XDG_CONFIG_HOME = prev;
|
|
329
|
+
rmSync(home, { recursive: true, force: true });
|
|
330
|
+
}
|
|
331
|
+
});
|
|
332
|
+
});
|