@cortexkit/opencode-magic-context 0.8.9 → 0.8.11
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/cli/doctor.d.ts.map +1 -1
- package/dist/cli.js +22 -8
- package/dist/index.js +61 -18
- package/dist/shared/models-dev-cache.d.ts.map +1 -1
- package/dist/shared/tui-config.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/shared/models-dev-cache.ts +86 -16
- package/src/shared/tui-config.ts +12 -5
package/README.md
CHANGED
|
@@ -333,7 +333,7 @@ On startup, Magic Context checks for common configuration problems — OpenCode'
|
|
|
333
333
|
A companion desktop app for browsing and managing Magic Context state outside of OpenCode.
|
|
334
334
|
|
|
335
335
|
<p align="center">
|
|
336
|
-
<a href="https://github.com/cortexkit/opencode-magic-context/releases/tag/dashboard-v0.2.
|
|
336
|
+
<a href="https://github.com/cortexkit/opencode-magic-context/releases/tag/dashboard-v0.2.6"><strong>⬇️ Download for macOS · Windows · Linux</strong></a></p>
|
|
337
337
|
|
|
338
338
|
**Features:**
|
|
339
339
|
- **Memory Browser** — search, filter, and edit project memories with category and project filtering
|
package/dist/cli/doctor.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/cli/doctor.ts"],"names":[],"mappings":"AA4MA,wBAAsB,SAAS,CAC3B,OAAO,GAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAO,GACnD,OAAO,CAAC,MAAM,CAAC,
|
|
1
|
+
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/cli/doctor.ts"],"names":[],"mappings":"AA4MA,wBAAsB,SAAS,CAC3B,OAAO,GAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAO,GACnD,OAAO,CAAC,MAAM,CAAC,CAuNjB"}
|
package/dist/cli.js
CHANGED
|
@@ -8393,15 +8393,20 @@ function ensureTuiPluginEntry() {
|
|
|
8393
8393
|
config = import_comment_json.parse(raw) ?? {};
|
|
8394
8394
|
}
|
|
8395
8395
|
const plugins = Array.isArray(config.plugin) ? config.plugin.filter((p) => typeof p === "string") : [];
|
|
8396
|
-
|
|
8397
|
-
|
|
8396
|
+
const existingIdx = plugins.findIndex((p) => p === PLUGIN_NAME || p.startsWith(`${PLUGIN_NAME}@`));
|
|
8397
|
+
if (existingIdx >= 0) {
|
|
8398
|
+
if (plugins[existingIdx] === PLUGIN_ENTRY) {
|
|
8399
|
+
return false;
|
|
8400
|
+
}
|
|
8401
|
+
plugins[existingIdx] = PLUGIN_ENTRY;
|
|
8402
|
+
} else {
|
|
8403
|
+
plugins.push(PLUGIN_ENTRY);
|
|
8398
8404
|
}
|
|
8399
|
-
plugins.push(PLUGIN_ENTRY);
|
|
8400
8405
|
config.plugin = plugins;
|
|
8401
8406
|
mkdirSync2(dirname2(configPath), { recursive: true });
|
|
8402
8407
|
writeFileSync2(configPath, `${import_comment_json.stringify(config, null, 2)}
|
|
8403
8408
|
`);
|
|
8404
|
-
log(`[magic-context]
|
|
8409
|
+
log(`[magic-context] updated TUI plugin entry in ${configPath}`);
|
|
8405
8410
|
return true;
|
|
8406
8411
|
} catch (error) {
|
|
8407
8412
|
log(`[magic-context] failed to update tui.json: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -10158,13 +10163,22 @@ async function runDoctor(options = {}) {
|
|
|
10158
10163
|
const raw = readFileSync5(paths.opencodeConfig, "utf-8");
|
|
10159
10164
|
const config = import_comment_json3.parse(raw);
|
|
10160
10165
|
const plugins = Array.isArray(config?.plugin) ? config.plugin : [];
|
|
10161
|
-
const
|
|
10166
|
+
const pluginList = plugins.filter((p) => typeof p === "string");
|
|
10167
|
+
const existingIdx = pluginList.findIndex((p) => p === PLUGIN_NAME3 || p.startsWith(`${PLUGIN_NAME3}@`) || p.includes("opencode-magic-context"));
|
|
10162
10168
|
const configName = paths.opencodeConfigFormat === "jsonc" ? "opencode.jsonc" : "opencode.json";
|
|
10163
|
-
if (
|
|
10169
|
+
if (existingIdx >= 0 && pluginList[existingIdx] === PLUGIN_ENTRY_WITH_VERSION2) {
|
|
10164
10170
|
R2.success(`Plugin registered in ${configName}`);
|
|
10171
|
+
} else if (existingIdx >= 0) {
|
|
10172
|
+
const oldEntry = pluginList[existingIdx];
|
|
10173
|
+
pluginList[existingIdx] = PLUGIN_ENTRY_WITH_VERSION2;
|
|
10174
|
+
config.plugin = pluginList;
|
|
10175
|
+
writeFileSync4(paths.opencodeConfig, `${import_comment_json3.stringify(config, null, 2)}
|
|
10176
|
+
`);
|
|
10177
|
+
R2.success(`Upgraded plugin entry in ${configName}: ${oldEntry} → ${PLUGIN_ENTRY_WITH_VERSION2}`);
|
|
10178
|
+
fixed++;
|
|
10165
10179
|
} else {
|
|
10166
|
-
|
|
10167
|
-
config.plugin =
|
|
10180
|
+
pluginList.push(PLUGIN_ENTRY_WITH_VERSION2);
|
|
10181
|
+
config.plugin = pluginList;
|
|
10168
10182
|
writeFileSync4(paths.opencodeConfig, `${import_comment_json3.stringify(config, null, 2)}
|
|
10169
10183
|
`);
|
|
10170
10184
|
R2.success(`Added plugin to ${configName}`);
|
package/dist/index.js
CHANGED
|
@@ -16916,7 +16916,7 @@ function isThinkingPart(part) {
|
|
|
16916
16916
|
var encoder, TAG_PREFIX_REGEX;
|
|
16917
16917
|
var init_tag_content_primitives = __esm(() => {
|
|
16918
16918
|
encoder = new TextEncoder;
|
|
16919
|
-
TAG_PREFIX_REGEX =
|
|
16919
|
+
TAG_PREFIX_REGEX = /^(?:\u00A7\d+\u00A7\s*)+/;
|
|
16920
16920
|
});
|
|
16921
16921
|
|
|
16922
16922
|
// src/hooks/magic-context/tag-part-guards.ts
|
|
@@ -28223,15 +28223,20 @@ function ensureTuiPluginEntry() {
|
|
|
28223
28223
|
config2 = import_comment_json.parse(raw) ?? {};
|
|
28224
28224
|
}
|
|
28225
28225
|
const plugins = Array.isArray(config2.plugin) ? config2.plugin.filter((p) => typeof p === "string") : [];
|
|
28226
|
-
|
|
28227
|
-
|
|
28226
|
+
const existingIdx = plugins.findIndex((p) => p === PLUGIN_NAME || p.startsWith(`${PLUGIN_NAME}@`));
|
|
28227
|
+
if (existingIdx >= 0) {
|
|
28228
|
+
if (plugins[existingIdx] === PLUGIN_ENTRY) {
|
|
28229
|
+
return false;
|
|
28230
|
+
}
|
|
28231
|
+
plugins[existingIdx] = PLUGIN_ENTRY;
|
|
28232
|
+
} else {
|
|
28233
|
+
plugins.push(PLUGIN_ENTRY);
|
|
28228
28234
|
}
|
|
28229
|
-
plugins.push(PLUGIN_ENTRY);
|
|
28230
28235
|
config2.plugin = plugins;
|
|
28231
28236
|
mkdirSync5(dirname2(configPath), { recursive: true });
|
|
28232
28237
|
writeFileSync3(configPath, `${import_comment_json.stringify(config2, null, 2)}
|
|
28233
28238
|
`);
|
|
28234
|
-
log(`[magic-context]
|
|
28239
|
+
log(`[magic-context] updated TUI plugin entry in ${configPath}`);
|
|
28235
28240
|
return true;
|
|
28236
28241
|
} catch (error48) {
|
|
28237
28242
|
log(`[magic-context] failed to update tui.json: ${error48 instanceof Error ? error48.message : String(error48)}`);
|
|
@@ -30221,28 +30226,66 @@ function getModelsJsonPath() {
|
|
|
30221
30226
|
}
|
|
30222
30227
|
return join11(cacheBase, "opencode", "models.json");
|
|
30223
30228
|
}
|
|
30229
|
+
function getOpencodeConfigPath() {
|
|
30230
|
+
const envDir = process.env.OPENCODE_CONFIG_DIR?.trim();
|
|
30231
|
+
const configDir = envDir ? envDir : platform2() === "win32" ? join11(homedir5(), ".config", "opencode") : join11(process.env.XDG_CONFIG_HOME || join11(homedir5(), ".config"), "opencode");
|
|
30232
|
+
const jsonc = join11(configDir, "opencode.jsonc");
|
|
30233
|
+
if (existsSync5(jsonc))
|
|
30234
|
+
return jsonc;
|
|
30235
|
+
const json2 = join11(configDir, "opencode.json");
|
|
30236
|
+
if (existsSync5(json2))
|
|
30237
|
+
return json2;
|
|
30238
|
+
return null;
|
|
30239
|
+
}
|
|
30224
30240
|
function loadModelsDevLimits() {
|
|
30225
30241
|
const limits = new Map;
|
|
30226
|
-
const
|
|
30242
|
+
const modelsJsonPath = getModelsJsonPath();
|
|
30227
30243
|
try {
|
|
30228
|
-
if (
|
|
30229
|
-
|
|
30230
|
-
|
|
30231
|
-
|
|
30232
|
-
|
|
30233
|
-
|
|
30234
|
-
|
|
30235
|
-
|
|
30236
|
-
|
|
30237
|
-
|
|
30238
|
-
|
|
30239
|
-
|
|
30244
|
+
if (existsSync5(modelsJsonPath)) {
|
|
30245
|
+
const raw = readFileSync4(modelsJsonPath, "utf-8");
|
|
30246
|
+
const data = JSON.parse(raw);
|
|
30247
|
+
for (const [providerId, provider2] of Object.entries(data)) {
|
|
30248
|
+
if (!provider2?.models || typeof provider2.models !== "object")
|
|
30249
|
+
continue;
|
|
30250
|
+
for (const [modelId, model] of Object.entries(provider2.models)) {
|
|
30251
|
+
const context = model?.limit?.context;
|
|
30252
|
+
if (typeof context === "number" && context > 0) {
|
|
30253
|
+
limits.set(`${providerId}/${modelId}`, context);
|
|
30254
|
+
const modes = model?.experimental?.modes;
|
|
30255
|
+
if (modes && typeof modes === "object") {
|
|
30256
|
+
for (const mode of Object.keys(modes)) {
|
|
30257
|
+
limits.set(`${providerId}/${modelId}-${mode}`, context);
|
|
30258
|
+
}
|
|
30259
|
+
}
|
|
30260
|
+
}
|
|
30240
30261
|
}
|
|
30241
30262
|
}
|
|
30242
30263
|
}
|
|
30243
30264
|
} catch (error48) {
|
|
30244
30265
|
sessionLog("global", "models-dev-cache: failed to read models.json:", error48 instanceof Error ? error48.message : String(error48));
|
|
30245
30266
|
}
|
|
30267
|
+
try {
|
|
30268
|
+
const configPath = getOpencodeConfigPath();
|
|
30269
|
+
if (configPath && existsSync5(configPath)) {
|
|
30270
|
+
let raw = readFileSync4(configPath, "utf-8");
|
|
30271
|
+
raw = raw.replace(/"(?:[^"\\]|\\.)*"|\/\/.*$/gm, (match) => match.startsWith('"') ? match : "");
|
|
30272
|
+
const config2 = JSON.parse(raw);
|
|
30273
|
+
if (config2.provider && typeof config2.provider === "object") {
|
|
30274
|
+
for (const [providerId, provider2] of Object.entries(config2.provider)) {
|
|
30275
|
+
if (!provider2?.models || typeof provider2.models !== "object")
|
|
30276
|
+
continue;
|
|
30277
|
+
for (const [modelId, model] of Object.entries(provider2.models)) {
|
|
30278
|
+
const context = model?.limit?.context;
|
|
30279
|
+
if (typeof context === "number" && context > 0) {
|
|
30280
|
+
limits.set(`${providerId}/${modelId}`, context);
|
|
30281
|
+
}
|
|
30282
|
+
}
|
|
30283
|
+
}
|
|
30284
|
+
}
|
|
30285
|
+
}
|
|
30286
|
+
} catch (error48) {
|
|
30287
|
+
sessionLog("global", "models-dev-cache: failed to read opencode config for custom models:", error48 instanceof Error ? error48.message : String(error48));
|
|
30288
|
+
}
|
|
30246
30289
|
return limits;
|
|
30247
30290
|
}
|
|
30248
30291
|
function getModelsDevContextLimit(providerID, modelID) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"models-dev-cache.d.ts","sourceRoot":"","sources":["../../src/shared/models-dev-cache.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"models-dev-cache.d.ts","sourceRoot":"","sources":["../../src/shared/models-dev-cache.ts"],"names":[],"mappings":"AA6IA;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAShG;AAED,8CAA8C;AAC9C,wBAAgB,mBAAmB,IAAI,IAAI,CAG1C"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tui-config.d.ts","sourceRoot":"","sources":["../../src/shared/tui-config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAqBH;;;GAGG;AACH,wBAAgB,oBAAoB,IAAI,OAAO,
|
|
1
|
+
{"version":3,"file":"tui-config.d.ts","sourceRoot":"","sources":["../../src/shared/tui-config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAqBH;;;GAGG;AACH,wBAAgB,oBAAoB,IAAI,OAAO,CAsC9C"}
|
package/package.json
CHANGED
|
@@ -34,27 +34,58 @@ function getModelsJsonPath(): string {
|
|
|
34
34
|
return join(cacheBase, "opencode", "models.json");
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
function getOpencodeConfigPath(): string | null {
|
|
38
|
+
const envDir = process.env.OPENCODE_CONFIG_DIR?.trim();
|
|
39
|
+
const configDir = envDir
|
|
40
|
+
? envDir
|
|
41
|
+
: platform() === "win32"
|
|
42
|
+
? join(homedir(), ".config", "opencode")
|
|
43
|
+
: join(process.env.XDG_CONFIG_HOME || join(homedir(), ".config"), "opencode");
|
|
44
|
+
|
|
45
|
+
// Check jsonc first, then json (matches OpenCode's own lookup order)
|
|
46
|
+
const jsonc = join(configDir, "opencode.jsonc");
|
|
47
|
+
if (existsSync(jsonc)) return jsonc;
|
|
48
|
+
const json = join(configDir, "opencode.json");
|
|
49
|
+
if (existsSync(json)) return json;
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
|
|
37
53
|
function loadModelsDevLimits(): Map<string, number> {
|
|
38
54
|
const limits = new Map<string, number>();
|
|
39
|
-
const filePath = getModelsJsonPath();
|
|
40
55
|
|
|
56
|
+
// 1. Load from OpenCode's models.dev cache (base layer — all known public models)
|
|
57
|
+
const modelsJsonPath = getModelsJsonPath();
|
|
41
58
|
try {
|
|
42
|
-
if (
|
|
43
|
-
|
|
44
|
-
|
|
59
|
+
if (existsSync(modelsJsonPath)) {
|
|
60
|
+
const raw = readFileSync(modelsJsonPath, "utf-8");
|
|
61
|
+
const data = JSON.parse(raw) as Record<
|
|
62
|
+
string,
|
|
63
|
+
{
|
|
64
|
+
models?: Record<
|
|
65
|
+
string,
|
|
66
|
+
{
|
|
67
|
+
limit?: { context?: number };
|
|
68
|
+
experimental?: { modes?: Record<string, unknown> };
|
|
69
|
+
}
|
|
70
|
+
>;
|
|
71
|
+
}
|
|
72
|
+
>;
|
|
45
73
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
74
|
+
for (const [providerId, provider] of Object.entries(data)) {
|
|
75
|
+
if (!provider?.models || typeof provider.models !== "object") continue;
|
|
76
|
+
for (const [modelId, model] of Object.entries(provider.models)) {
|
|
77
|
+
const context = model?.limit?.context;
|
|
78
|
+
if (typeof context === "number" && context > 0) {
|
|
79
|
+
limits.set(`${providerId}/${modelId}`, context);
|
|
80
|
+
// OpenCode creates derived model IDs from experimental.modes
|
|
81
|
+
// e.g. gpt-5.4 + modes.fast → gpt-5.4-fast (inherits parent limit)
|
|
82
|
+
const modes = model?.experimental?.modes;
|
|
83
|
+
if (modes && typeof modes === "object") {
|
|
84
|
+
for (const mode of Object.keys(modes)) {
|
|
85
|
+
limits.set(`${providerId}/${modelId}-${mode}`, context);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
58
89
|
}
|
|
59
90
|
}
|
|
60
91
|
}
|
|
@@ -66,6 +97,45 @@ function loadModelsDevLimits(): Map<string, number> {
|
|
|
66
97
|
);
|
|
67
98
|
}
|
|
68
99
|
|
|
100
|
+
// 2. Overlay custom provider models from OpenCode config (higher priority).
|
|
101
|
+
// Users define custom/proxy models via provider.<id>.models.<name>.limit.context
|
|
102
|
+
// in opencode.json(c). These override models.dev entries for the same key.
|
|
103
|
+
try {
|
|
104
|
+
const configPath = getOpencodeConfigPath();
|
|
105
|
+
if (configPath && existsSync(configPath)) {
|
|
106
|
+
let raw = readFileSync(configPath, "utf-8");
|
|
107
|
+
// Strip JSONC single-line comments while preserving // inside strings.
|
|
108
|
+
// Match strings first (to skip them), then match comments outside strings.
|
|
109
|
+
raw = raw.replace(/"(?:[^"\\]|\\.)*"|\/\/.*$/gm, (match) =>
|
|
110
|
+
match.startsWith('"') ? match : "",
|
|
111
|
+
);
|
|
112
|
+
const config = JSON.parse(raw) as {
|
|
113
|
+
provider?: Record<
|
|
114
|
+
string,
|
|
115
|
+
{ models?: Record<string, { limit?: { context?: number } }> }
|
|
116
|
+
>;
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
if (config.provider && typeof config.provider === "object") {
|
|
120
|
+
for (const [providerId, provider] of Object.entries(config.provider)) {
|
|
121
|
+
if (!provider?.models || typeof provider.models !== "object") continue;
|
|
122
|
+
for (const [modelId, model] of Object.entries(provider.models)) {
|
|
123
|
+
const context = model?.limit?.context;
|
|
124
|
+
if (typeof context === "number" && context > 0) {
|
|
125
|
+
limits.set(`${providerId}/${modelId}`, context);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
} catch (error) {
|
|
132
|
+
sessionLog(
|
|
133
|
+
"global",
|
|
134
|
+
"models-dev-cache: failed to read opencode config for custom models:",
|
|
135
|
+
error instanceof Error ? error.message : String(error),
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
|
|
69
139
|
return limits;
|
|
70
140
|
}
|
|
71
141
|
|
package/src/shared/tui-config.ts
CHANGED
|
@@ -40,16 +40,23 @@ export function ensureTuiPluginEntry(): boolean {
|
|
|
40
40
|
? config.plugin.filter((p): p is string => typeof p === "string")
|
|
41
41
|
: [];
|
|
42
42
|
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
const existingIdx = plugins.findIndex(
|
|
44
|
+
(p) => p === PLUGIN_NAME || p.startsWith(`${PLUGIN_NAME}@`),
|
|
45
|
+
);
|
|
46
|
+
if (existingIdx >= 0) {
|
|
47
|
+
if (plugins[existingIdx] === PLUGIN_ENTRY) {
|
|
48
|
+
return false; // Already @latest
|
|
49
|
+
}
|
|
50
|
+
// Upgrade pinned version to @latest
|
|
51
|
+
plugins[existingIdx] = PLUGIN_ENTRY;
|
|
52
|
+
} else {
|
|
53
|
+
plugins.push(PLUGIN_ENTRY);
|
|
45
54
|
}
|
|
46
|
-
|
|
47
|
-
plugins.push(PLUGIN_ENTRY);
|
|
48
55
|
config.plugin = plugins;
|
|
49
56
|
|
|
50
57
|
mkdirSync(dirname(configPath), { recursive: true });
|
|
51
58
|
writeFileSync(configPath, `${stringify(config, null, 2)}\n`);
|
|
52
|
-
log(`[magic-context]
|
|
59
|
+
log(`[magic-context] updated TUI plugin entry in ${configPath}`);
|
|
53
60
|
return true;
|
|
54
61
|
} catch (error) {
|
|
55
62
|
log(
|