@cortexkit/opencode-magic-context 0.17.2 → 0.19.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/README.md +1 -1
- package/dist/config/index.d.ts.map +1 -1
- package/dist/features/magic-context/compaction-marker.d.ts +17 -0
- package/dist/features/magic-context/compaction-marker.d.ts.map +1 -1
- package/dist/features/magic-context/compartment-storage.d.ts +11 -0
- package/dist/features/magic-context/compartment-storage.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/queue.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/runner.d.ts +15 -0
- package/dist/features/magic-context/dreamer/runner.d.ts.map +1 -1
- package/dist/features/magic-context/memory/embedding-identity.d.ts +11 -0
- package/dist/features/magic-context/memory/embedding-identity.d.ts.map +1 -0
- package/dist/features/magic-context/memory/embedding-local.d.ts.map +1 -1
- package/dist/features/magic-context/memory/embedding-openai.d.ts.map +1 -1
- package/dist/features/magic-context/memory/embedding.d.ts.map +1 -1
- package/dist/features/magic-context/migrations.d.ts.map +1 -1
- package/dist/features/magic-context/sidekick/agent.d.ts.map +1 -1
- package/dist/features/magic-context/storage-db.d.ts.map +1 -1
- package/dist/features/magic-context/storage-meta-persisted.d.ts +70 -0
- package/dist/features/magic-context/storage-meta-persisted.d.ts.map +1 -1
- package/dist/features/magic-context/storage-meta-shared.d.ts +1 -0
- package/dist/features/magic-context/storage-meta-shared.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 +1 -1
- package/dist/features/magic-context/storage.d.ts.map +1 -1
- package/dist/features/magic-context/tool-definition-tokens.d.ts.map +1 -1
- package/dist/features/magic-context/types.d.ts +1 -0
- package/dist/features/magic-context/types.d.ts.map +1 -1
- package/dist/features/magic-context/user-memory/review-user-memories.d.ts +2 -0
- package/dist/features/magic-context/user-memory/review-user-memories.d.ts.map +1 -1
- package/dist/hooks/magic-context/cache-busting-signals.d.ts +10 -0
- package/dist/hooks/magic-context/cache-busting-signals.d.ts.map +1 -0
- package/dist/hooks/magic-context/command-handler.d.ts +2 -0
- package/dist/hooks/magic-context/command-handler.d.ts.map +1 -1
- package/dist/hooks/magic-context/compaction-marker-manager.d.ts +50 -0
- package/dist/hooks/magic-context/compaction-marker-manager.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-runner-compressor.d.ts +1 -0
- package/dist/hooks/magic-context/compartment-runner-compressor.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-runner-historian.d.ts +7 -0
- package/dist/hooks/magic-context/compartment-runner-historian.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-runner-incremental.d.ts +1 -1
- package/dist/hooks/magic-context/compartment-runner-incremental.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-runner-types.d.ts +18 -7
- package/dist/hooks/magic-context/compartment-runner-types.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-runner.d.ts +7 -2
- package/dist/hooks/magic-context/compartment-runner.d.ts.map +1 -1
- package/dist/hooks/magic-context/event-handler.d.ts.map +1 -1
- package/dist/hooks/magic-context/historian-state-file.d.ts +25 -11
- package/dist/hooks/magic-context/historian-state-file.d.ts.map +1 -1
- package/dist/hooks/magic-context/hook-handlers.d.ts +11 -4
- package/dist/hooks/magic-context/hook-handlers.d.ts.map +1 -1
- package/dist/hooks/magic-context/hook.d.ts.map +1 -1
- package/dist/hooks/magic-context/inject-compartments.d.ts +2 -1
- package/dist/hooks/magic-context/inject-compartments.d.ts.map +1 -1
- package/dist/hooks/magic-context/live-session-state.d.ts +3 -1
- package/dist/hooks/magic-context/live-session-state.d.ts.map +1 -1
- package/dist/hooks/magic-context/system-prompt-hash.d.ts.map +1 -1
- package/dist/hooks/magic-context/todo-view.d.ts +102 -0
- package/dist/hooks/magic-context/todo-view.d.ts.map +1 -0
- package/dist/hooks/magic-context/transform-compartment-phase.d.ts +11 -4
- package/dist/hooks/magic-context/transform-compartment-phase.d.ts.map +1 -1
- package/dist/hooks/magic-context/transform-message-helpers.d.ts +22 -0
- package/dist/hooks/magic-context/transform-message-helpers.d.ts.map +1 -1
- package/dist/hooks/magic-context/transform-postprocess-phase.d.ts +15 -1
- package/dist/hooks/magic-context/transform-postprocess-phase.d.ts.map +1 -1
- package/dist/hooks/magic-context/transform.d.ts +4 -0
- package/dist/hooks/magic-context/transform.d.ts.map +1 -1
- package/dist/index.js +1789 -711
- package/dist/plugin/dream-timer.d.ts.map +1 -1
- package/dist/plugin/hooks/create-session-hooks.d.ts.map +1 -1
- package/dist/plugin/rpc-handlers.d.ts +2 -1
- package/dist/plugin/rpc-handlers.d.ts.map +1 -1
- package/dist/plugin/sidebar-snapshot-cache.d.ts.map +1 -1
- package/dist/shared/conflict-detector.d.ts +49 -0
- package/dist/shared/conflict-detector.d.ts.map +1 -1
- package/dist/shared/conflict-fixer.d.ts +1 -1
- package/dist/shared/conflict-fixer.d.ts.map +1 -1
- package/dist/shared/data-path.d.ts +84 -0
- package/dist/shared/data-path.d.ts.map +1 -1
- package/dist/shared/index.d.ts +1 -0
- package/dist/shared/index.d.ts.map +1 -1
- package/dist/shared/logger.d.ts +6 -0
- package/dist/shared/logger.d.ts.map +1 -1
- package/dist/shared/model-suggestion-retry.d.ts +37 -0
- package/dist/shared/model-suggestion-retry.d.ts.map +1 -1
- package/dist/shared/models-dev-cache.d.ts.map +1 -1
- package/dist/shared/resolve-fallbacks.d.ts +32 -0
- package/dist/shared/resolve-fallbacks.d.ts.map +1 -0
- package/dist/shared/rpc-client.d.ts +2 -1
- package/dist/shared/rpc-client.d.ts.map +1 -1
- package/dist/shared/rpc-notifications.d.ts +3 -2
- package/dist/shared/rpc-notifications.d.ts.map +1 -1
- package/dist/shared/rpc-server.d.ts +3 -0
- package/dist/shared/rpc-server.d.ts.map +1 -1
- package/dist/shared/rpc-types.d.ts +1 -0
- package/dist/shared/rpc-types.d.ts.map +1 -1
- package/dist/shared/rpc-utils.d.ts +13 -2
- package/dist/shared/rpc-utils.d.ts.map +1 -1
- package/dist/shared/stable-json.d.ts +21 -0
- package/dist/shared/stable-json.d.ts.map +1 -0
- package/dist/shared/tag-transcript.d.ts.map +1 -1
- package/dist/tools/ctx-memory/tools.d.ts.map +1 -1
- package/dist/tui/data/context-db.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/shared/conflict-detector.ts +4 -4
- package/src/shared/conflict-fixer.test.ts +124 -0
- package/src/shared/conflict-fixer.ts +34 -28
- package/src/shared/data-path.test.ts +38 -0
- package/src/shared/data-path.ts +99 -0
- package/src/shared/index.ts +1 -0
- package/src/shared/logger.ts +29 -3
- package/src/shared/model-suggestion-retry.test.ts +251 -0
- package/src/shared/model-suggestion-retry.ts +194 -6
- package/src/shared/models-dev-cache.ts +7 -7
- package/src/shared/resolve-fallbacks.test.ts +136 -0
- package/src/shared/resolve-fallbacks.ts +76 -0
- package/src/shared/rpc-client.test.ts +161 -0
- package/src/shared/rpc-client.ts +82 -22
- package/src/shared/rpc-notifications.test.ts +20 -0
- package/src/shared/rpc-notifications.ts +9 -6
- package/src/shared/rpc-server.ts +42 -4
- package/src/shared/rpc-types.ts +1 -0
- package/src/shared/rpc-utils.ts +59 -3
- package/src/shared/stable-json.test.ts +87 -0
- package/src/shared/stable-json.ts +37 -0
- package/src/shared/tag-transcript.ts +3 -2
- package/src/tui/data/context-db.ts +20 -1
- package/src/tui/index.tsx +114 -18
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import { stableStringify } from "./stable-json";
|
|
3
|
+
|
|
4
|
+
describe("stableStringify", () => {
|
|
5
|
+
test("primitive values match JSON.stringify", () => {
|
|
6
|
+
expect(stableStringify("hello")).toBe('"hello"');
|
|
7
|
+
expect(stableStringify(42)).toBe("42");
|
|
8
|
+
expect(stableStringify(true)).toBe("true");
|
|
9
|
+
expect(stableStringify(false)).toBe("false");
|
|
10
|
+
expect(stableStringify(null)).toBe("null");
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
test("undefined renders as literal string", () => {
|
|
14
|
+
expect(stableStringify(undefined)).toBe("undefined");
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test("object keys sort by code-point order, not locale", () => {
|
|
18
|
+
// 'Z' (0x5a) sorts before 'a' (0x61) by code-point.
|
|
19
|
+
// localeCompare would sort 'a' before 'Z' in many locales.
|
|
20
|
+
// We want code-point semantics.
|
|
21
|
+
const input = { Z: 1, a: 2 };
|
|
22
|
+
expect(stableStringify(input)).toBe('{"Z":1,"a":2}');
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test("nested objects sort recursively", () => {
|
|
26
|
+
const input = { b: { y: 1, x: 2 }, a: { z: 3, w: 4 } };
|
|
27
|
+
expect(stableStringify(input)).toBe('{"a":{"w":4,"z":3},"b":{"x":2,"y":1}}');
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test("arrays preserve order", () => {
|
|
31
|
+
const input = [3, 1, 2];
|
|
32
|
+
expect(stableStringify(input)).toBe("[3,1,2]");
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test("arrays of objects sort keys per element", () => {
|
|
36
|
+
const input = [
|
|
37
|
+
{ b: 1, a: 2 },
|
|
38
|
+
{ d: 3, c: 4 },
|
|
39
|
+
];
|
|
40
|
+
expect(stableStringify(input)).toBe('[{"a":2,"b":1},{"c":4,"d":3}]');
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test("identical objects with different key insertion order produce same string", () => {
|
|
44
|
+
const a = { foo: 1, bar: 2 };
|
|
45
|
+
const b = { bar: 2, foo: 1 };
|
|
46
|
+
expect(stableStringify(a)).toBe(stableStringify(b));
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test("circular references render as marker, do not throw", () => {
|
|
50
|
+
const a: Record<string, unknown> = { x: 1 };
|
|
51
|
+
a.self = a;
|
|
52
|
+
expect(stableStringify(a)).toBe('{"self":"[Circular]","x":1}');
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test("mixed cycle through array does not crash", () => {
|
|
56
|
+
const arr: unknown[] = [];
|
|
57
|
+
arr.push(arr);
|
|
58
|
+
expect(stableStringify(arr)).toBe('["[Circular]"]');
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test("empty object and array", () => {
|
|
62
|
+
expect(stableStringify({})).toBe("{}");
|
|
63
|
+
expect(stableStringify([])).toBe("[]");
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test("special string characters JSON-escaped in keys", () => {
|
|
67
|
+
const input = { 'with "quotes"': 1 };
|
|
68
|
+
expect(stableStringify(input)).toBe('{"with \\"quotes\\"":1}');
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test("Unicode key sort by code-point, not by collation", () => {
|
|
72
|
+
// 'ä' (U+00E4) sorts AFTER 'z' (U+007A) by code-point.
|
|
73
|
+
// localeCompare in many locales would put 'ä' near 'a'.
|
|
74
|
+
const input = { z: 1, ä: 2 };
|
|
75
|
+
const result = stableStringify(input);
|
|
76
|
+
expect(result).toBe('{"z":1,"ä":2}');
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test("deterministic across multiple calls", () => {
|
|
80
|
+
const input = { c: 3, a: 1, b: 2 };
|
|
81
|
+
const first = stableStringify(input);
|
|
82
|
+
const second = stableStringify(input);
|
|
83
|
+
const third = stableStringify(input);
|
|
84
|
+
expect(first).toBe(second);
|
|
85
|
+
expect(second).toBe(third);
|
|
86
|
+
});
|
|
87
|
+
});
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Process-local deterministic JSON serialization for JSON-like plain
|
|
3
|
+
* objects. Keys are sorted by code-point order (NOT locale-sensitive).
|
|
4
|
+
*
|
|
5
|
+
* Contract:
|
|
6
|
+
* - Stable for plain objects, arrays, primitives, and `null`.
|
|
7
|
+
* - `undefined` serialized as the string "undefined".
|
|
8
|
+
* - Circular references serialized as the string `"[Circular]"`.
|
|
9
|
+
* - **NOT** a canonical cross-runtime / cross-locale JSON serializer.
|
|
10
|
+
* Two different runtimes that disagree on `JSON.stringify` of primitives
|
|
11
|
+
* (none known today) would produce different output.
|
|
12
|
+
*
|
|
13
|
+
* Used for:
|
|
14
|
+
* - `tool_definition_measurements` fingerprint hashing
|
|
15
|
+
* - `pending_compaction_marker_state` CAS comparison
|
|
16
|
+
*
|
|
17
|
+
* If a future use case needs true canonical JSON (e.g. cross-process
|
|
18
|
+
* signing), build a separate utility — do NOT widen this contract.
|
|
19
|
+
*/
|
|
20
|
+
export function stableStringify(value: unknown, seen = new WeakSet<object>()): string {
|
|
21
|
+
if (value === undefined) return "undefined";
|
|
22
|
+
if (value === null || typeof value !== "object") return JSON.stringify(value) ?? String(value);
|
|
23
|
+
if (seen.has(value)) return '"[Circular]"';
|
|
24
|
+
seen.add(value);
|
|
25
|
+
if (Array.isArray(value)) {
|
|
26
|
+
return `[${value.map((item) => stableStringify(item, seen)).join(",")}]`;
|
|
27
|
+
}
|
|
28
|
+
// Code-point sort (NOT localeCompare). Stable across runtimes/locales.
|
|
29
|
+
const entries = Object.entries(value as Record<string, unknown>).sort(([a], [b]) => {
|
|
30
|
+
if (a < b) return -1;
|
|
31
|
+
if (a > b) return 1;
|
|
32
|
+
return 0;
|
|
33
|
+
});
|
|
34
|
+
return `{${entries
|
|
35
|
+
.map(([key, child]) => `${JSON.stringify(key)}:${stableStringify(child, seen)}`)
|
|
36
|
+
.join(",")}}`;
|
|
37
|
+
}
|
|
@@ -159,9 +159,10 @@ export function tagTranscript(
|
|
|
159
159
|
const messageId = message.info.id;
|
|
160
160
|
|
|
161
161
|
let textOrdinal = 0;
|
|
162
|
+
const parts = message.parts;
|
|
162
163
|
|
|
163
|
-
for (let partIndex = 0; partIndex <
|
|
164
|
-
const part =
|
|
164
|
+
for (let partIndex = 0; partIndex < parts.length; partIndex += 1) {
|
|
165
|
+
const part = parts[partIndex];
|
|
165
166
|
if (part === undefined) continue;
|
|
166
167
|
|
|
167
168
|
if (part.kind === "text") {
|
|
@@ -10,6 +10,7 @@ import type { RpcNotificationMessage, SidebarSnapshot, StatusDetail } from "../.
|
|
|
10
10
|
export type { SidebarSnapshot, StatusDetail };
|
|
11
11
|
|
|
12
12
|
let rpcClient: MagicContextRpcClient | null = null;
|
|
13
|
+
let lastReceivedNotificationId = 0;
|
|
13
14
|
|
|
14
15
|
function getStorageDir(): string {
|
|
15
16
|
// Plugin v0.16+ uses the shared cortexkit/magic-context path so OpenCode
|
|
@@ -30,6 +31,7 @@ export function initRpcClient(directory: string): void {
|
|
|
30
31
|
export function closeRpc(): void {
|
|
31
32
|
rpcClient?.reset();
|
|
32
33
|
rpcClient = null;
|
|
34
|
+
lastReceivedNotificationId = 0;
|
|
33
35
|
}
|
|
34
36
|
|
|
35
37
|
const EMPTY_SNAPSHOT: SidebarSnapshot = {
|
|
@@ -103,9 +105,19 @@ function recallSidebarSnapshot(sessionId: string, fallback: SidebarSnapshot): Si
|
|
|
103
105
|
stickySidebarCache.delete(sessionId);
|
|
104
106
|
return fallback;
|
|
105
107
|
}
|
|
108
|
+
if (!hasInFlightEvidence(fallback)) {
|
|
109
|
+
stickySidebarCache.delete(sessionId);
|
|
110
|
+
return fallback;
|
|
111
|
+
}
|
|
106
112
|
return cached.snapshot;
|
|
107
113
|
}
|
|
108
114
|
|
|
115
|
+
function hasInFlightEvidence(snapshot: SidebarSnapshot): boolean {
|
|
116
|
+
return (
|
|
117
|
+
snapshot.compartmentInProgress || snapshot.historianRunning || snapshot.pendingOpsCount > 0
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
|
|
109
121
|
/** Fetch sidebar snapshot from the server via RPC. */
|
|
110
122
|
export async function loadSidebarSnapshot(
|
|
111
123
|
sessionId: string,
|
|
@@ -225,8 +237,15 @@ export async function consumeTuiMessages(): Promise<TuiMessage[]> {
|
|
|
225
237
|
try {
|
|
226
238
|
const result = await rpcClient.call<{ messages: RpcNotificationMessage[] }>(
|
|
227
239
|
"pending-notifications",
|
|
240
|
+
{ lastReceivedId: lastReceivedNotificationId },
|
|
228
241
|
);
|
|
229
|
-
|
|
242
|
+
const messages = result.messages ?? [];
|
|
243
|
+
for (const message of messages) {
|
|
244
|
+
if (message.id > lastReceivedNotificationId) {
|
|
245
|
+
lastReceivedNotificationId = message.id;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
return messages.map((m) => ({
|
|
230
249
|
type: m.type,
|
|
231
250
|
payload: m.payload,
|
|
232
251
|
sessionId: m.sessionId,
|
package/src/tui/index.tsx
CHANGED
|
@@ -453,6 +453,111 @@ function showStatusDialog(api: TuiPluginApi) {
|
|
|
453
453
|
})
|
|
454
454
|
}
|
|
455
455
|
|
|
456
|
+
/**
|
|
457
|
+
* Register Magic Context command palette entries, preferring the v1.14.42+
|
|
458
|
+
* `keymap.registerLayer` API and falling back to the legacy
|
|
459
|
+
* `api.command.register` for older hosts.
|
|
460
|
+
*
|
|
461
|
+
* The `keymap.registerLayer` shape uses `name`/`title`/`run`/`namespace`
|
|
462
|
+
* (see `@opencode-ai/plugin/tui` types) and is what the host's own legacy
|
|
463
|
+
* command-shim translates into. Calling it directly skips the deprecation
|
|
464
|
+
* warning and works without depending on the (now-deprecated) `api.command`
|
|
465
|
+
* namespace existing at all.
|
|
466
|
+
*
|
|
467
|
+
* Version coverage:
|
|
468
|
+
* 1.14.0–1.14.41 — `api.command.register` only
|
|
469
|
+
* 1.14.42–1.14.43 — both surfaces broken (api.command removed, keymap landed
|
|
470
|
+
* but with bugs); plugins crash on init either way
|
|
471
|
+
* 1.14.44+ — `api.keymap.registerLayer` canonical, `api.command` shim
|
|
472
|
+
*/
|
|
473
|
+
function registerCommandPaletteEntries(api: TuiPluginApi): void {
|
|
474
|
+
type ApiAny = {
|
|
475
|
+
keymap?: {
|
|
476
|
+
registerLayer?: (layer: {
|
|
477
|
+
commands: Array<Record<string, unknown>>
|
|
478
|
+
bindings: Array<Record<string, unknown>>
|
|
479
|
+
}) => unknown
|
|
480
|
+
}
|
|
481
|
+
command?: {
|
|
482
|
+
register?: (cb: () => Array<Record<string, unknown>>) => unknown
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
const apiAny = api as unknown as ApiAny
|
|
486
|
+
|
|
487
|
+
if (typeof apiAny.keymap?.registerLayer === "function") {
|
|
488
|
+
// Audit Finding #2 hardening: even when registerLayer exists as a
|
|
489
|
+
// function, the underlying keymap implementation in OpenCode TUI
|
|
490
|
+
// 1.14.42-1.14.43 can throw at call time. Without the try-catch the
|
|
491
|
+
// `return` below would propagate the throw and the legacy
|
|
492
|
+
// `command.register` fallback path (~20 lines down) would be
|
|
493
|
+
// unreachable. The cost is one debug log on the rare broken-TUI
|
|
494
|
+
// build; the benefit is that older command.register-only TUIs
|
|
495
|
+
// running alongside a partially-broken keymap surface still get
|
|
496
|
+
// their command palette entries.
|
|
497
|
+
try {
|
|
498
|
+
apiAny.keymap.registerLayer({
|
|
499
|
+
commands: [
|
|
500
|
+
{
|
|
501
|
+
namespace: "palette",
|
|
502
|
+
name: "magic-context.status",
|
|
503
|
+
title: "Magic Context: Status",
|
|
504
|
+
category: "Magic Context",
|
|
505
|
+
run() {
|
|
506
|
+
showStatusDialog(api)
|
|
507
|
+
},
|
|
508
|
+
},
|
|
509
|
+
{
|
|
510
|
+
namespace: "palette",
|
|
511
|
+
name: "magic-context.recomp",
|
|
512
|
+
title: "Magic Context: Recomp",
|
|
513
|
+
category: "Magic Context",
|
|
514
|
+
run() {
|
|
515
|
+
showRecompDialog(api)
|
|
516
|
+
},
|
|
517
|
+
},
|
|
518
|
+
],
|
|
519
|
+
bindings: [],
|
|
520
|
+
})
|
|
521
|
+
return
|
|
522
|
+
} catch (err) {
|
|
523
|
+
console.debug(
|
|
524
|
+
"[magic-context-tui] keymap.registerLayer threw; falling back to command.register",
|
|
525
|
+
err,
|
|
526
|
+
)
|
|
527
|
+
// Fall through to legacy registration.
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
if (typeof apiAny.command?.register === "function") {
|
|
532
|
+
apiAny.command.register(() => [
|
|
533
|
+
{
|
|
534
|
+
title: "Magic Context: Status",
|
|
535
|
+
value: "magic-context.status",
|
|
536
|
+
category: "Magic Context",
|
|
537
|
+
onSelect() {
|
|
538
|
+
showStatusDialog(api)
|
|
539
|
+
},
|
|
540
|
+
},
|
|
541
|
+
{
|
|
542
|
+
title: "Magic Context: Recomp",
|
|
543
|
+
value: "magic-context.recomp",
|
|
544
|
+
category: "Magic Context",
|
|
545
|
+
onSelect() {
|
|
546
|
+
showRecompDialog(api)
|
|
547
|
+
},
|
|
548
|
+
},
|
|
549
|
+
])
|
|
550
|
+
return
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
// Neither API surface is present. The TUI host can still load — we only
|
|
554
|
+
// lose the command palette entry points. The sidebar (registered above
|
|
555
|
+
// via api.slots.register) remains visible. Status/Recomp are still
|
|
556
|
+
// reachable through the server-side `/ctx-status` and `/ctx-recomp`
|
|
557
|
+
// slash commands, which the server handler bridges to the TUI dialogs
|
|
558
|
+
// via RPC.
|
|
559
|
+
}
|
|
560
|
+
|
|
456
561
|
const tui: TuiPlugin = async (api, _options, meta) => {
|
|
457
562
|
// Initialize RPC client for server communication
|
|
458
563
|
const directory = api.state.path.directory ?? ""
|
|
@@ -465,24 +570,15 @@ const tui: TuiPlugin = async (api, _options, meta) => {
|
|
|
465
570
|
// are registered server-side so there's only one /ctx-* registration).
|
|
466
571
|
// The server detects TUI mode and sends dialog requests via RPC instead
|
|
467
572
|
// of sendIgnoredMessage.
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
{
|
|
478
|
-
title: "Magic Context: Recomp",
|
|
479
|
-
value: "magic-context.recomp",
|
|
480
|
-
category: "Magic Context",
|
|
481
|
-
onSelect() {
|
|
482
|
-
showRecompDialog(api)
|
|
483
|
-
},
|
|
484
|
-
},
|
|
485
|
-
])
|
|
573
|
+
//
|
|
574
|
+
// OpenCode 1.14.42 removed `api.command.register` entirely
|
|
575
|
+
// (anomalyco/opencode#26053). A later patch (1.14.44+) reinstated it as
|
|
576
|
+
// a deprecated shim that translates to `api.keymap.registerLayer`. To
|
|
577
|
+
// work across all hosts (1.14.0–1.14.41 with command-only, the broken
|
|
578
|
+
// 1.14.42–1.14.43, and 1.14.44+ where both exist), we prefer
|
|
579
|
+
// `api.keymap.registerLayer` and fall back to `api.command.register`
|
|
580
|
+
// only when keymap is missing.
|
|
581
|
+
registerCommandPaletteEntries(api)
|
|
486
582
|
|
|
487
583
|
// Poll for server→TUI messages: toasts and dialog requests.
|
|
488
584
|
// Single poller because consumeTuiMessages() is destructive (deletes consumed rows).
|