oh-my-opencode-slim 1.0.6 → 1.1.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 +30 -17
- package/dist/cli/config-io.d.ts +1 -0
- package/dist/cli/divoom.d.ts +23 -0
- package/dist/cli/doctor.d.ts +38 -0
- package/dist/cli/index.js +469 -58
- package/dist/cli/providers.d.ts +3 -0
- package/dist/config/council-schema.d.ts +2 -2
- package/dist/config/index.d.ts +1 -1
- package/dist/config/loader.d.ts +46 -1
- package/dist/config/schema.d.ts +23 -0
- package/dist/divoom/council.gif +0 -0
- package/dist/divoom/designer.gif +0 -0
- package/dist/divoom/explorer.gif +0 -0
- package/dist/divoom/fixer.gif +0 -0
- package/dist/divoom/input.gif +0 -0
- package/dist/divoom/intro.gif +0 -0
- package/dist/divoom/librarian.gif +0 -0
- package/dist/divoom/manager.d.ts +57 -0
- package/dist/divoom/oracle.gif +0 -0
- package/dist/divoom/orchestrator.gif +0 -0
- package/dist/index.js +1304 -291
- package/dist/integrations/divoom/index.d.ts +3 -0
- package/dist/integrations/divoom/status-manager.d.ts +31 -0
- package/dist/integrations/divoom/swift-helper-source.d.ts +1 -0
- package/dist/integrations/divoom/swift-transport.d.ts +26 -0
- package/dist/integrations/divoom/types.d.ts +41 -0
- package/dist/multiplexer/tmux/index.d.ts +5 -0
- package/dist/tools/council.d.ts +2 -2
- package/dist/tools/fork/command.d.ts +28 -0
- package/dist/tools/fork/files.d.ts +33 -0
- package/dist/tools/fork/index.d.ts +10 -0
- package/dist/tools/fork/state.d.ts +7 -0
- package/dist/tools/fork/tools.d.ts +23 -0
- package/dist/tools/fork/vendor.d.ts +28 -0
- package/dist/tools/handoff/command.d.ts +29 -0
- package/dist/tools/handoff/files.d.ts +33 -0
- package/dist/tools/handoff/index.d.ts +10 -0
- package/dist/tools/handoff/state.d.ts +7 -0
- package/dist/tools/handoff/tools.d.ts +23 -0
- package/dist/tools/handoff/vendor.d.ts +28 -0
- package/dist/tools/index.d.ts +2 -0
- package/dist/tools/subtask/command.d.ts +30 -0
- package/dist/tools/subtask/files.d.ts +34 -0
- package/dist/tools/subtask/index.d.ts +11 -0
- package/dist/tools/subtask/state.d.ts +7 -0
- package/dist/tools/subtask/tools.d.ts +23 -0
- package/dist/tools/subtask/vendor.d.ts +27 -0
- package/dist/tui.d.ts +1 -0
- package/dist/tui.js +679 -11
- package/dist/utils/session.d.ts +11 -4
- package/oh-my-opencode-slim.schema.json +59 -0
- package/package.json +3 -2
- package/src/skills/clonedeps/README.md +23 -0
- package/src/skills/clonedeps/SKILL.md +237 -0
- package/src/skills/clonedeps/codemap.md +41 -0
- package/src/skills/codemap.md +8 -5
package/dist/tui.js
CHANGED
|
@@ -75,17 +75,648 @@ var TMUX_SPAWN_DELAY_MS = 500;
|
|
|
75
75
|
var COUNCILLOR_STAGGER_MS = 250;
|
|
76
76
|
var DEFAULT_DISABLED_AGENTS = ["observer"];
|
|
77
77
|
|
|
78
|
-
// src/
|
|
78
|
+
// src/config/loader.ts
|
|
79
79
|
import * as fs from "node:fs";
|
|
80
|
-
import * as os from "node:os";
|
|
81
80
|
import * as path from "node:path";
|
|
81
|
+
|
|
82
|
+
// src/utils/compat.ts
|
|
83
|
+
import { spawn as nodeSpawn } from "node:child_process";
|
|
84
|
+
import { writeFile as fsWriteFile } from "node:fs/promises";
|
|
85
|
+
var isBun = typeof globalThis.Bun !== "undefined";
|
|
86
|
+
function collectStream(stream) {
|
|
87
|
+
if (!stream)
|
|
88
|
+
return () => Promise.resolve("");
|
|
89
|
+
const chunks = [];
|
|
90
|
+
stream.on("data", (chunk) => chunks.push(chunk));
|
|
91
|
+
return () => new Promise((resolve, reject) => {
|
|
92
|
+
if (!stream.readable) {
|
|
93
|
+
resolve(Buffer.concat(chunks).toString("utf-8"));
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
stream.on("end", () => resolve(Buffer.concat(chunks).toString("utf-8")));
|
|
97
|
+
stream.on("error", reject);
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
function crossSpawn(command, options) {
|
|
101
|
+
const [cmd, ...args] = command;
|
|
102
|
+
const proc = nodeSpawn(cmd, args, {
|
|
103
|
+
stdio: [
|
|
104
|
+
options?.stdin ?? "ignore",
|
|
105
|
+
options?.stdout ?? "pipe",
|
|
106
|
+
options?.stderr ?? "pipe"
|
|
107
|
+
],
|
|
108
|
+
cwd: options?.cwd,
|
|
109
|
+
env: options?.env
|
|
110
|
+
});
|
|
111
|
+
const stdoutCollector = collectStream(proc.stdout);
|
|
112
|
+
const stderrCollector = collectStream(proc.stderr);
|
|
113
|
+
const exited = new Promise((resolve, reject) => {
|
|
114
|
+
proc.on("error", reject);
|
|
115
|
+
proc.on("close", (code) => resolve(code ?? 1));
|
|
116
|
+
});
|
|
117
|
+
return {
|
|
118
|
+
proc,
|
|
119
|
+
stdout: stdoutCollector,
|
|
120
|
+
stderr: stderrCollector,
|
|
121
|
+
exited,
|
|
122
|
+
kill: (signal) => proc.kill(signal),
|
|
123
|
+
get exitCode() {
|
|
124
|
+
return proc.exitCode;
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
async function crossWrite(path, data) {
|
|
129
|
+
await fsWriteFile(path, Buffer.from(data));
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// src/cli/paths.ts
|
|
133
|
+
import { homedir } from "node:os";
|
|
134
|
+
import { dirname, join } from "node:path";
|
|
135
|
+
function getDefaultOpenCodeConfigDir() {
|
|
136
|
+
const userConfigDir = process.env.XDG_CONFIG_HOME ? process.env.XDG_CONFIG_HOME : join(homedir(), ".config");
|
|
137
|
+
return join(userConfigDir, "opencode");
|
|
138
|
+
}
|
|
139
|
+
function getCustomOpenCodeConfigDir() {
|
|
140
|
+
const configDir = process.env.OPENCODE_CONFIG_DIR?.trim();
|
|
141
|
+
return configDir || undefined;
|
|
142
|
+
}
|
|
143
|
+
function getConfigSearchDirs() {
|
|
144
|
+
const dirs = [getCustomOpenCodeConfigDir(), getDefaultOpenCodeConfigDir()];
|
|
145
|
+
return dirs.filter((dir, index) => {
|
|
146
|
+
return Boolean(dir) && dirs.indexOf(dir) === index;
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
function getOpenCodeConfigPaths() {
|
|
150
|
+
const configDir = getDefaultOpenCodeConfigDir();
|
|
151
|
+
return [join(configDir, "opencode.json"), join(configDir, "opencode.jsonc")];
|
|
152
|
+
}
|
|
153
|
+
// src/config/council-schema.ts
|
|
154
|
+
import { z } from "zod";
|
|
155
|
+
var ModelIdSchema = z.string().regex(/^[^/\s]+\/[^\s]+$/, 'Expected provider/model format (e.g. "openai/gpt-5.4-mini")');
|
|
156
|
+
var CouncillorConfigSchema = z.object({
|
|
157
|
+
model: ModelIdSchema.describe('Model ID in provider/model format (e.g. "openai/gpt-5.4-mini")'),
|
|
158
|
+
variant: z.string().optional(),
|
|
159
|
+
prompt: z.string().optional().describe("Optional role/guidance injected into the councillor user prompt")
|
|
160
|
+
});
|
|
161
|
+
var CouncilPresetSchema = z.record(z.string(), z.record(z.string(), z.unknown())).transform((entries, ctx) => {
|
|
162
|
+
const councillors = {};
|
|
163
|
+
for (const [key, raw] of Object.entries(entries)) {
|
|
164
|
+
if (key === "master")
|
|
165
|
+
continue;
|
|
166
|
+
if (key === "councillors" && typeof raw === "object" && raw !== null) {
|
|
167
|
+
for (const [innerKey, innerRaw] of Object.entries(raw)) {
|
|
168
|
+
const innerParsed = CouncillorConfigSchema.safeParse(innerRaw);
|
|
169
|
+
if (!innerParsed.success) {
|
|
170
|
+
ctx.addIssue({
|
|
171
|
+
code: z.ZodIssueCode.custom,
|
|
172
|
+
message: `Invalid councillor "${innerKey}" (nested under legacy "councillors" key): ${innerParsed.error.issues.map((i) => i.message).join(", ")}`
|
|
173
|
+
});
|
|
174
|
+
return z.NEVER;
|
|
175
|
+
}
|
|
176
|
+
councillors[innerKey] = innerParsed.data;
|
|
177
|
+
}
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
const parsed = CouncillorConfigSchema.safeParse(raw);
|
|
181
|
+
if (!parsed.success) {
|
|
182
|
+
ctx.addIssue({
|
|
183
|
+
code: z.ZodIssueCode.custom,
|
|
184
|
+
message: `Invalid councillor "${key}": ${parsed.error.issues.map((i) => i.message).join(", ")}`
|
|
185
|
+
});
|
|
186
|
+
return z.NEVER;
|
|
187
|
+
}
|
|
188
|
+
councillors[key] = parsed.data;
|
|
189
|
+
}
|
|
190
|
+
return councillors;
|
|
191
|
+
});
|
|
192
|
+
var CouncillorExecutionModeSchema = z.enum(["parallel", "serial"]).default("parallel").describe('Execution mode for councillors. Use "serial" for single-model systems to avoid conflicts. ' + 'Use "parallel" for multi-model systems for faster execution.');
|
|
193
|
+
var CouncilConfigSchema = z.object({
|
|
194
|
+
presets: z.record(z.string(), CouncilPresetSchema),
|
|
195
|
+
timeout: z.number().min(0).default(180000),
|
|
196
|
+
default_preset: z.string().default("default"),
|
|
197
|
+
councillor_execution_mode: CouncillorExecutionModeSchema.describe('Execution mode for councillors. "serial" runs them one at a time (required for single-model systems). "parallel" runs them concurrently (default, faster for multi-model systems).'),
|
|
198
|
+
councillor_retries: z.number().int().min(0).max(5).default(3).describe("Number of retry attempts for councillors that return empty responses " + "(e.g. due to provider rate limiting). Default: 3 retries."),
|
|
199
|
+
master: z.unknown().optional().describe("DEPRECATED — ignored. Council agent synthesizes directly."),
|
|
200
|
+
master_timeout: z.unknown().optional().describe('DEPRECATED — ignored. Use "timeout" instead.'),
|
|
201
|
+
master_fallback: z.unknown().optional().describe("DEPRECATED — ignored. No separate master session.")
|
|
202
|
+
}).transform((data) => {
|
|
203
|
+
const deprecated = [];
|
|
204
|
+
if (data.master !== undefined)
|
|
205
|
+
deprecated.push("master");
|
|
206
|
+
if (data.master_timeout !== undefined)
|
|
207
|
+
deprecated.push("master_timeout");
|
|
208
|
+
if (data.master_fallback !== undefined)
|
|
209
|
+
deprecated.push("master_fallback");
|
|
210
|
+
const legacyMasterModel = typeof data.master === "object" && data.master !== null && "model" in data.master && typeof data.master.model === "string" ? data.master.model : undefined;
|
|
211
|
+
return {
|
|
212
|
+
presets: data.presets,
|
|
213
|
+
timeout: data.timeout,
|
|
214
|
+
default_preset: data.default_preset,
|
|
215
|
+
councillor_execution_mode: data.councillor_execution_mode,
|
|
216
|
+
councillor_retries: data.councillor_retries,
|
|
217
|
+
_deprecated: deprecated.length > 0 ? deprecated : undefined,
|
|
218
|
+
_legacyMasterModel: legacyMasterModel
|
|
219
|
+
};
|
|
220
|
+
});
|
|
221
|
+
// src/config/schema.ts
|
|
222
|
+
import { z as z2 } from "zod";
|
|
223
|
+
var ProviderModelIdSchema = z2.string().regex(/^[^/\s]+\/[^\s]+$/, "Expected provider/model format (provider/.../model)");
|
|
224
|
+
var ManualAgentPlanSchema = z2.object({
|
|
225
|
+
primary: ProviderModelIdSchema,
|
|
226
|
+
fallback1: ProviderModelIdSchema,
|
|
227
|
+
fallback2: ProviderModelIdSchema,
|
|
228
|
+
fallback3: ProviderModelIdSchema
|
|
229
|
+
}).superRefine((value, ctx) => {
|
|
230
|
+
const unique = new Set([
|
|
231
|
+
value.primary,
|
|
232
|
+
value.fallback1,
|
|
233
|
+
value.fallback2,
|
|
234
|
+
value.fallback3
|
|
235
|
+
]);
|
|
236
|
+
if (unique.size !== 4) {
|
|
237
|
+
ctx.addIssue({
|
|
238
|
+
code: z2.ZodIssueCode.custom,
|
|
239
|
+
message: "primary and fallbacks must be unique per agent"
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
var ManualPlanSchema = z2.object({
|
|
244
|
+
orchestrator: ManualAgentPlanSchema,
|
|
245
|
+
oracle: ManualAgentPlanSchema,
|
|
246
|
+
designer: ManualAgentPlanSchema,
|
|
247
|
+
explorer: ManualAgentPlanSchema,
|
|
248
|
+
librarian: ManualAgentPlanSchema,
|
|
249
|
+
fixer: ManualAgentPlanSchema
|
|
250
|
+
}).strict();
|
|
251
|
+
var AgentModelChainSchema = z2.array(z2.string()).min(1);
|
|
252
|
+
var FallbackChainsSchema = z2.object({
|
|
253
|
+
orchestrator: AgentModelChainSchema.optional(),
|
|
254
|
+
oracle: AgentModelChainSchema.optional(),
|
|
255
|
+
designer: AgentModelChainSchema.optional(),
|
|
256
|
+
explorer: AgentModelChainSchema.optional(),
|
|
257
|
+
librarian: AgentModelChainSchema.optional(),
|
|
258
|
+
fixer: AgentModelChainSchema.optional()
|
|
259
|
+
}).catchall(AgentModelChainSchema);
|
|
260
|
+
var AgentOverrideConfigSchema = z2.object({
|
|
261
|
+
model: z2.union([
|
|
262
|
+
z2.string(),
|
|
263
|
+
z2.array(z2.union([
|
|
264
|
+
z2.string(),
|
|
265
|
+
z2.object({
|
|
266
|
+
id: z2.string(),
|
|
267
|
+
variant: z2.string().optional()
|
|
268
|
+
})
|
|
269
|
+
])).min(1)
|
|
270
|
+
]).optional(),
|
|
271
|
+
temperature: z2.number().min(0).max(2).optional(),
|
|
272
|
+
variant: z2.string().optional().catch(undefined),
|
|
273
|
+
skills: z2.array(z2.string()).optional(),
|
|
274
|
+
mcps: z2.array(z2.string()).optional(),
|
|
275
|
+
prompt: z2.string().min(1).optional(),
|
|
276
|
+
orchestratorPrompt: z2.string().min(1).optional(),
|
|
277
|
+
options: z2.record(z2.string(), z2.unknown()).optional(),
|
|
278
|
+
displayName: z2.string().min(1).optional()
|
|
279
|
+
}).strict();
|
|
280
|
+
var MultiplexerTypeSchema = z2.enum(["auto", "tmux", "zellij", "none"]);
|
|
281
|
+
var MultiplexerLayoutSchema = z2.enum([
|
|
282
|
+
"main-horizontal",
|
|
283
|
+
"main-vertical",
|
|
284
|
+
"tiled",
|
|
285
|
+
"even-horizontal",
|
|
286
|
+
"even-vertical"
|
|
287
|
+
]);
|
|
288
|
+
var TmuxLayoutSchema = MultiplexerLayoutSchema;
|
|
289
|
+
var MultiplexerConfigSchema = z2.object({
|
|
290
|
+
type: MultiplexerTypeSchema.default("none"),
|
|
291
|
+
layout: MultiplexerLayoutSchema.default("main-vertical"),
|
|
292
|
+
main_pane_size: z2.number().min(20).max(80).default(60)
|
|
293
|
+
});
|
|
294
|
+
var TmuxConfigSchema = z2.object({
|
|
295
|
+
enabled: z2.boolean().default(false),
|
|
296
|
+
layout: TmuxLayoutSchema.default("main-vertical"),
|
|
297
|
+
main_pane_size: z2.number().min(20).max(80).default(60)
|
|
298
|
+
});
|
|
299
|
+
var PresetSchema = z2.record(z2.string(), AgentOverrideConfigSchema);
|
|
300
|
+
var WebsearchConfigSchema = z2.object({
|
|
301
|
+
provider: z2.enum(["exa", "tavily"]).default("exa")
|
|
302
|
+
});
|
|
303
|
+
var McpNameSchema = z2.enum(["websearch", "context7", "grep_app"]);
|
|
304
|
+
var InterviewConfigSchema = z2.object({
|
|
305
|
+
maxQuestions: z2.number().int().min(1).max(10).default(2),
|
|
306
|
+
outputFolder: z2.string().min(1).default("interview"),
|
|
307
|
+
autoOpenBrowser: z2.boolean().default(true).describe("Automatically open the interview UI in your default browser during interactive runs. Disabled automatically in tests and CI."),
|
|
308
|
+
port: z2.number().int().min(0).max(65535).default(0),
|
|
309
|
+
dashboard: z2.boolean().default(false)
|
|
310
|
+
});
|
|
311
|
+
var SessionManagerConfigSchema = z2.object({
|
|
312
|
+
maxSessionsPerAgent: z2.number().int().min(1).max(10).default(2),
|
|
313
|
+
readContextMinLines: z2.number().int().min(0).max(1000).default(10),
|
|
314
|
+
readContextMaxFiles: z2.number().int().min(0).max(50).default(8)
|
|
315
|
+
});
|
|
316
|
+
var DivoomConfigSchema = z2.object({
|
|
317
|
+
enabled: z2.boolean().default(false),
|
|
318
|
+
python: z2.string().min(1).default("/Applications/Divoom MiniToo.app/Contents/Resources/.venv/bin/python"),
|
|
319
|
+
script: z2.string().min(1).default("/Applications/Divoom MiniToo.app/Contents/Resources/tools/divoom_send.py"),
|
|
320
|
+
size: z2.number().int().min(1).max(1024).default(128),
|
|
321
|
+
fps: z2.number().int().min(1).max(60).default(8),
|
|
322
|
+
speed: z2.number().int().min(1).max(1e4).default(125),
|
|
323
|
+
maxFrames: z2.number().int().min(1).max(500).default(24),
|
|
324
|
+
posterizeBits: z2.number().int().min(1).max(8).default(3),
|
|
325
|
+
gifs: z2.record(z2.string(), z2.string().min(1)).optional()
|
|
326
|
+
});
|
|
327
|
+
var TodoContinuationConfigSchema = z2.object({
|
|
328
|
+
maxContinuations: z2.number().int().min(1).max(50).default(5).describe("Maximum consecutive auto-continuations before stopping to ask user"),
|
|
329
|
+
cooldownMs: z2.number().int().min(0).max(30000).default(3000).describe("Delay in ms before auto-continuing (gives user time to abort)"),
|
|
330
|
+
autoEnable: z2.boolean().default(false).describe("Automatically enable auto-continue when the orchestrator session has enough todos"),
|
|
331
|
+
autoEnableThreshold: z2.number().int().min(1).max(50).default(4).describe("Number of todos that triggers auto-enable (only used when autoEnable is true)")
|
|
332
|
+
});
|
|
333
|
+
var FailoverConfigSchema = z2.object({
|
|
334
|
+
enabled: z2.boolean().default(true),
|
|
335
|
+
timeoutMs: z2.number().min(0).default(15000),
|
|
336
|
+
retryDelayMs: z2.number().min(0).default(500),
|
|
337
|
+
chains: FallbackChainsSchema.default({}),
|
|
338
|
+
retry_on_empty: z2.boolean().default(true).describe("When true (default), empty provider responses are treated as failures, " + "triggering fallback/retry. Set to false to treat them as successes.")
|
|
339
|
+
});
|
|
340
|
+
function validateCustomOnlyPromptFields(overrides, ctx, pathPrefix) {
|
|
341
|
+
for (const [name, override] of Object.entries(overrides)) {
|
|
342
|
+
const isBuiltInOrAlias = ALL_AGENT_NAMES.includes(name) || AGENT_ALIASES[name] !== undefined;
|
|
343
|
+
if (!isBuiltInOrAlias) {
|
|
344
|
+
continue;
|
|
345
|
+
}
|
|
346
|
+
if (override.prompt !== undefined) {
|
|
347
|
+
ctx.addIssue({
|
|
348
|
+
code: z2.ZodIssueCode.custom,
|
|
349
|
+
path: [...pathPrefix, name, "prompt"],
|
|
350
|
+
message: "prompt is only supported for custom agents"
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
if (override.orchestratorPrompt !== undefined) {
|
|
354
|
+
ctx.addIssue({
|
|
355
|
+
code: z2.ZodIssueCode.custom,
|
|
356
|
+
path: [...pathPrefix, name, "orchestratorPrompt"],
|
|
357
|
+
message: "orchestratorPrompt is only supported for custom agents"
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
var PluginConfigSchema = z2.object({
|
|
363
|
+
preset: z2.string().optional(),
|
|
364
|
+
setDefaultAgent: z2.boolean().optional(),
|
|
365
|
+
scoringEngineVersion: z2.enum(["v1", "v2-shadow", "v2"]).optional(),
|
|
366
|
+
balanceProviderUsage: z2.boolean().optional(),
|
|
367
|
+
autoUpdate: z2.boolean().optional().describe("Disable automatic installation of plugin updates when false. Defaults to true."),
|
|
368
|
+
manualPlan: ManualPlanSchema.optional(),
|
|
369
|
+
presets: z2.record(z2.string(), PresetSchema).optional(),
|
|
370
|
+
agents: z2.record(z2.string(), AgentOverrideConfigSchema).optional(),
|
|
371
|
+
disabled_agents: z2.array(z2.string()).optional().describe("Agent names to disable completely. " + "Disabled agents are not instantiated and cannot be delegated to. " + "Orchestrator and council internal agents (councillor) cannot be disabled. " + "By default, 'observer' is disabled. Remove it from this list and configure a vision-capable model to enable."),
|
|
372
|
+
disabled_mcps: z2.array(z2.string()).optional(),
|
|
373
|
+
multiplexer: MultiplexerConfigSchema.optional(),
|
|
374
|
+
tmux: TmuxConfigSchema.optional(),
|
|
375
|
+
websearch: WebsearchConfigSchema.optional(),
|
|
376
|
+
interview: InterviewConfigSchema.optional(),
|
|
377
|
+
sessionManager: SessionManagerConfigSchema.optional(),
|
|
378
|
+
divoom: DivoomConfigSchema.optional(),
|
|
379
|
+
todoContinuation: TodoContinuationConfigSchema.optional(),
|
|
380
|
+
fallback: FailoverConfigSchema.optional(),
|
|
381
|
+
council: CouncilConfigSchema.optional()
|
|
382
|
+
}).superRefine((value, ctx) => {
|
|
383
|
+
if (value.agents) {
|
|
384
|
+
validateCustomOnlyPromptFields(value.agents, ctx, ["agents"]);
|
|
385
|
+
}
|
|
386
|
+
if (value.presets) {
|
|
387
|
+
for (const [presetName, preset] of Object.entries(value.presets)) {
|
|
388
|
+
validateCustomOnlyPromptFields(preset, ctx, ["presets", presetName]);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
});
|
|
392
|
+
// src/config/utils.ts
|
|
393
|
+
function getAgentOverride(config, name) {
|
|
394
|
+
const overrides = config?.agents ?? {};
|
|
395
|
+
return overrides[name] ?? overrides[Object.keys(AGENT_ALIASES).find((k) => AGENT_ALIASES[k] === name) ?? ""];
|
|
396
|
+
}
|
|
397
|
+
function getCustomAgentNames(config) {
|
|
398
|
+
const overrides = config?.agents ?? {};
|
|
399
|
+
return Object.keys(overrides).filter((name) => {
|
|
400
|
+
if (AGENT_ALIASES[name] !== undefined) {
|
|
401
|
+
return false;
|
|
402
|
+
}
|
|
403
|
+
return !ALL_AGENT_NAMES.includes(name);
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
// src/config/agent-mcps.ts
|
|
407
|
+
var DEFAULT_AGENT_MCPS = {
|
|
408
|
+
orchestrator: ["*", "!context7"],
|
|
409
|
+
designer: [],
|
|
410
|
+
oracle: [],
|
|
411
|
+
librarian: ["websearch", "context7", "grep_app"],
|
|
412
|
+
explorer: [],
|
|
413
|
+
fixer: [],
|
|
414
|
+
observer: [],
|
|
415
|
+
council: [],
|
|
416
|
+
councillor: []
|
|
417
|
+
};
|
|
418
|
+
function parseList(items, allAvailable) {
|
|
419
|
+
if (!items || items.length === 0) {
|
|
420
|
+
return [];
|
|
421
|
+
}
|
|
422
|
+
const allow = items.filter((i) => !i.startsWith("!"));
|
|
423
|
+
const deny = items.filter((i) => i.startsWith("!")).map((i) => i.slice(1));
|
|
424
|
+
if (deny.includes("*")) {
|
|
425
|
+
return [];
|
|
426
|
+
}
|
|
427
|
+
if (allow.includes("*")) {
|
|
428
|
+
return allAvailable.filter((item) => !deny.includes(item));
|
|
429
|
+
}
|
|
430
|
+
return allow.filter((item) => !deny.includes(item) && allAvailable.includes(item));
|
|
431
|
+
}
|
|
432
|
+
function getAgentMcpList(agentName, config) {
|
|
433
|
+
const agentConfig = getAgentOverride(config, agentName);
|
|
434
|
+
if (agentConfig?.mcps !== undefined) {
|
|
435
|
+
return agentConfig.mcps;
|
|
436
|
+
}
|
|
437
|
+
const defaultMcps = DEFAULT_AGENT_MCPS[agentName];
|
|
438
|
+
return defaultMcps ?? [];
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
// src/cli/custom-skills.ts
|
|
442
|
+
var CUSTOM_SKILLS = [
|
|
443
|
+
{
|
|
444
|
+
name: "simplify",
|
|
445
|
+
description: "Code simplification and readability-focused refactoring",
|
|
446
|
+
allowedAgents: ["oracle"],
|
|
447
|
+
sourcePath: "src/skills/simplify"
|
|
448
|
+
},
|
|
449
|
+
{
|
|
450
|
+
name: "codemap",
|
|
451
|
+
description: "Repository understanding and hierarchical codemap generation",
|
|
452
|
+
allowedAgents: ["orchestrator"],
|
|
453
|
+
sourcePath: "src/skills/codemap"
|
|
454
|
+
},
|
|
455
|
+
{
|
|
456
|
+
name: "clonedeps",
|
|
457
|
+
description: "Clone important dependency source for local inspection",
|
|
458
|
+
allowedAgents: ["orchestrator"],
|
|
459
|
+
sourcePath: "src/skills/clonedeps"
|
|
460
|
+
}
|
|
461
|
+
];
|
|
462
|
+
|
|
463
|
+
// src/cli/skills.ts
|
|
464
|
+
var RECOMMENDED_SKILLS = [
|
|
465
|
+
{
|
|
466
|
+
name: "agent-browser",
|
|
467
|
+
repo: "https://github.com/vercel-labs/agent-browser",
|
|
468
|
+
skillName: "agent-browser",
|
|
469
|
+
allowedAgents: ["designer"],
|
|
470
|
+
description: "High-performance browser automation",
|
|
471
|
+
postInstallCommands: [
|
|
472
|
+
"npm install -g agent-browser",
|
|
473
|
+
"agent-browser install"
|
|
474
|
+
]
|
|
475
|
+
}
|
|
476
|
+
];
|
|
477
|
+
var PERMISSION_ONLY_SKILLS = [
|
|
478
|
+
{
|
|
479
|
+
name: "requesting-code-review",
|
|
480
|
+
allowedAgents: ["oracle"],
|
|
481
|
+
description: "Code review template for reviewer subagents in multi-step workflows"
|
|
482
|
+
}
|
|
483
|
+
];
|
|
484
|
+
function getSkillPermissionsForAgent(agentName, skillList) {
|
|
485
|
+
const permissions = {
|
|
486
|
+
"*": agentName === "orchestrator" ? "allow" : "deny"
|
|
487
|
+
};
|
|
488
|
+
if (skillList) {
|
|
489
|
+
permissions["*"] = "deny";
|
|
490
|
+
for (const name of skillList) {
|
|
491
|
+
if (name === "*") {
|
|
492
|
+
permissions["*"] = "allow";
|
|
493
|
+
} else if (name.startsWith("!")) {
|
|
494
|
+
permissions[name.slice(1)] = "deny";
|
|
495
|
+
} else {
|
|
496
|
+
permissions[name] = "allow";
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
return permissions;
|
|
500
|
+
}
|
|
501
|
+
for (const skill of RECOMMENDED_SKILLS) {
|
|
502
|
+
const isAllowed = skill.allowedAgents.includes("*") || skill.allowedAgents.includes(agentName);
|
|
503
|
+
if (isAllowed) {
|
|
504
|
+
permissions[skill.skillName] = "allow";
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
for (const skill of CUSTOM_SKILLS) {
|
|
508
|
+
const isAllowed = skill.allowedAgents.includes("*") || skill.allowedAgents.includes(agentName);
|
|
509
|
+
if (isAllowed) {
|
|
510
|
+
permissions[skill.name] = "allow";
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
for (const skill of PERMISSION_ONLY_SKILLS) {
|
|
514
|
+
const isAllowed = skill.allowedAgents.includes("*") || skill.allowedAgents.includes(agentName);
|
|
515
|
+
if (isAllowed) {
|
|
516
|
+
permissions[skill.name] = "allow";
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
return permissions;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
// src/cli/config-io.ts
|
|
523
|
+
function stripJsonComments(json) {
|
|
524
|
+
const commentPattern = /\\"|"(?:\\"|[^"])*"|(\/\/.*|\/\*[\s\S]*?\*\/)/g;
|
|
525
|
+
const trailingCommaPattern = /\\"|"(?:\\"|[^"])*"|(,)(\s*[}\]])/g;
|
|
526
|
+
return json.replace(commentPattern, (match, commentGroup) => commentGroup ? "" : match).replace(trailingCommaPattern, (match, comma, closing) => comma ? closing : match);
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
// src/config/loader.ts
|
|
530
|
+
var PROMPTS_DIR_NAME = "oh-my-opencode-slim";
|
|
531
|
+
function loadConfigFromPath(configPath, options) {
|
|
532
|
+
try {
|
|
533
|
+
const content = fs.readFileSync(configPath, "utf-8");
|
|
534
|
+
let rawConfig;
|
|
535
|
+
try {
|
|
536
|
+
rawConfig = JSON.parse(stripJsonComments(content));
|
|
537
|
+
} catch (error) {
|
|
538
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
539
|
+
options?.onWarning?.({
|
|
540
|
+
path: configPath,
|
|
541
|
+
kind: "invalid-json",
|
|
542
|
+
message
|
|
543
|
+
});
|
|
544
|
+
if (!options?.silent) {
|
|
545
|
+
console.warn(`[oh-my-opencode-slim] Invalid JSON in ${configPath}:`, message);
|
|
546
|
+
}
|
|
547
|
+
return null;
|
|
548
|
+
}
|
|
549
|
+
const result = PluginConfigSchema.safeParse(rawConfig);
|
|
550
|
+
if (!result.success) {
|
|
551
|
+
options?.onWarning?.({
|
|
552
|
+
path: configPath,
|
|
553
|
+
kind: "invalid-schema",
|
|
554
|
+
message: "Config does not match schema",
|
|
555
|
+
formatted: result.error.format()
|
|
556
|
+
});
|
|
557
|
+
if (!options?.silent) {
|
|
558
|
+
console.warn(`[oh-my-opencode-slim] Invalid config at ${configPath}:`);
|
|
559
|
+
console.warn(result.error.format());
|
|
560
|
+
}
|
|
561
|
+
return null;
|
|
562
|
+
}
|
|
563
|
+
return result.data;
|
|
564
|
+
} catch (error) {
|
|
565
|
+
if (error instanceof Error && "code" in error && error.code !== "ENOENT") {
|
|
566
|
+
options?.onWarning?.({
|
|
567
|
+
path: configPath,
|
|
568
|
+
kind: "read-error",
|
|
569
|
+
message: error.message
|
|
570
|
+
});
|
|
571
|
+
if (!options?.silent) {
|
|
572
|
+
console.warn(`[oh-my-opencode-slim] Error reading config from ${configPath}:`, error.message);
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
return null;
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
function findConfigPath(basePath) {
|
|
579
|
+
const jsoncPath = `${basePath}.jsonc`;
|
|
580
|
+
const jsonPath = `${basePath}.json`;
|
|
581
|
+
if (fs.existsSync(jsoncPath)) {
|
|
582
|
+
return jsoncPath;
|
|
583
|
+
}
|
|
584
|
+
if (fs.existsSync(jsonPath)) {
|
|
585
|
+
return jsonPath;
|
|
586
|
+
}
|
|
587
|
+
return null;
|
|
588
|
+
}
|
|
589
|
+
function findConfigPathInDirs(configDirs, baseName) {
|
|
590
|
+
for (const configDir of configDirs) {
|
|
591
|
+
const configPath = findConfigPath(path.join(configDir, baseName));
|
|
592
|
+
if (configPath) {
|
|
593
|
+
return configPath;
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
return null;
|
|
597
|
+
}
|
|
598
|
+
function findPluginConfigPaths(directory) {
|
|
599
|
+
const userConfigPath = findConfigPathInDirs(getConfigSearchDirs(), "oh-my-opencode-slim");
|
|
600
|
+
const projectConfigBasePath = path.join(directory, ".opencode", "oh-my-opencode-slim");
|
|
601
|
+
const projectConfigPath = findConfigPath(projectConfigBasePath);
|
|
602
|
+
return { userConfigPath, projectConfigPath };
|
|
603
|
+
}
|
|
604
|
+
function mergePluginConfigs(base, override) {
|
|
605
|
+
return {
|
|
606
|
+
...base,
|
|
607
|
+
...override,
|
|
608
|
+
agents: deepMerge(base.agents, override.agents),
|
|
609
|
+
tmux: deepMerge(base.tmux, override.tmux),
|
|
610
|
+
multiplexer: deepMerge(base.multiplexer, override.multiplexer),
|
|
611
|
+
interview: deepMerge(base.interview, override.interview),
|
|
612
|
+
sessionManager: deepMerge(base.sessionManager, override.sessionManager),
|
|
613
|
+
divoom: deepMerge(base.divoom, override.divoom),
|
|
614
|
+
fallback: deepMerge(base.fallback, override.fallback),
|
|
615
|
+
council: deepMerge(base.council, override.council)
|
|
616
|
+
};
|
|
617
|
+
}
|
|
618
|
+
function deepMerge(base, override) {
|
|
619
|
+
if (!base)
|
|
620
|
+
return override;
|
|
621
|
+
if (!override)
|
|
622
|
+
return base;
|
|
623
|
+
const result = { ...base };
|
|
624
|
+
for (const key of Object.keys(override)) {
|
|
625
|
+
const baseVal = base[key];
|
|
626
|
+
const overrideVal = override[key];
|
|
627
|
+
if (typeof baseVal === "object" && baseVal !== null && typeof overrideVal === "object" && overrideVal !== null && !Array.isArray(baseVal) && !Array.isArray(overrideVal)) {
|
|
628
|
+
result[key] = deepMerge(baseVal, overrideVal);
|
|
629
|
+
} else {
|
|
630
|
+
result[key] = overrideVal;
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
return result;
|
|
634
|
+
}
|
|
635
|
+
function loadPluginConfig(directory, options) {
|
|
636
|
+
const { userConfigPath, projectConfigPath } = findPluginConfigPaths(directory);
|
|
637
|
+
let config = userConfigPath ? loadConfigFromPath(userConfigPath, options) ?? {} : {};
|
|
638
|
+
const projectConfig = projectConfigPath ? loadConfigFromPath(projectConfigPath, options) : null;
|
|
639
|
+
if (projectConfig) {
|
|
640
|
+
config = mergePluginConfigs(config, projectConfig);
|
|
641
|
+
}
|
|
642
|
+
config = migrateTmuxToMultiplexer(config);
|
|
643
|
+
const envPreset = process.env.OH_MY_OPENCODE_SLIM_PRESET;
|
|
644
|
+
if (envPreset) {
|
|
645
|
+
config.preset = envPreset;
|
|
646
|
+
}
|
|
647
|
+
if (config.preset) {
|
|
648
|
+
const preset = config.presets?.[config.preset];
|
|
649
|
+
if (preset) {
|
|
650
|
+
config.agents = deepMerge(preset, config.agents);
|
|
651
|
+
} else {
|
|
652
|
+
const presetSource = envPreset === config.preset ? "environment variable" : "config file";
|
|
653
|
+
const availablePresets = config.presets ? Object.keys(config.presets).join(", ") : "none";
|
|
654
|
+
const message = `Preset "${config.preset}" not found (from ${presetSource}). Available presets: ${availablePresets}`;
|
|
655
|
+
options?.onWarning?.({
|
|
656
|
+
path: projectConfigPath ?? userConfigPath ?? "",
|
|
657
|
+
kind: "missing-preset",
|
|
658
|
+
message
|
|
659
|
+
});
|
|
660
|
+
if (!options?.silent) {
|
|
661
|
+
console.warn(`[oh-my-opencode-slim] ${message}`);
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
return config;
|
|
666
|
+
}
|
|
667
|
+
function loadAgentPrompt(agentName, preset) {
|
|
668
|
+
const presetDirName = preset && /^[a-zA-Z0-9_-]+$/.test(preset) ? preset : undefined;
|
|
669
|
+
const promptSearchDirs = getConfigSearchDirs().flatMap((configDir) => {
|
|
670
|
+
const promptsDir = path.join(configDir, PROMPTS_DIR_NAME);
|
|
671
|
+
return presetDirName ? [path.join(promptsDir, presetDirName), promptsDir] : [promptsDir];
|
|
672
|
+
});
|
|
673
|
+
const result = {};
|
|
674
|
+
const readFirstPrompt = (fileName, errorPrefix) => {
|
|
675
|
+
for (const dir of promptSearchDirs) {
|
|
676
|
+
const promptPath = path.join(dir, fileName);
|
|
677
|
+
if (!fs.existsSync(promptPath)) {
|
|
678
|
+
continue;
|
|
679
|
+
}
|
|
680
|
+
try {
|
|
681
|
+
return fs.readFileSync(promptPath, "utf-8");
|
|
682
|
+
} catch (error) {
|
|
683
|
+
console.warn(`[oh-my-opencode-slim] ${errorPrefix} ${promptPath}:`, error instanceof Error ? error.message : String(error));
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
return;
|
|
687
|
+
};
|
|
688
|
+
result.prompt = readFirstPrompt(`${agentName}.md`, "Error reading prompt file");
|
|
689
|
+
result.appendPrompt = readFirstPrompt(`${agentName}_append.md`, "Error reading append prompt file");
|
|
690
|
+
return result;
|
|
691
|
+
}
|
|
692
|
+
function migrateTmuxToMultiplexer(config) {
|
|
693
|
+
if (config.multiplexer?.type && config.multiplexer.type !== "none") {
|
|
694
|
+
return config;
|
|
695
|
+
}
|
|
696
|
+
if (config.tmux?.enabled) {
|
|
697
|
+
return {
|
|
698
|
+
...config,
|
|
699
|
+
multiplexer: {
|
|
700
|
+
type: "tmux",
|
|
701
|
+
layout: config.tmux.layout ?? "main-vertical",
|
|
702
|
+
main_pane_size: config.tmux.main_pane_size ?? 60
|
|
703
|
+
}
|
|
704
|
+
};
|
|
705
|
+
}
|
|
706
|
+
return config;
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
// src/tui-state.ts
|
|
710
|
+
import * as fs2 from "node:fs";
|
|
711
|
+
import * as os from "node:os";
|
|
712
|
+
import * as path2 from "node:path";
|
|
82
713
|
var STATE_DIR = "oh-my-opencode-slim";
|
|
83
714
|
var STATE_FILE = "tui-state.json";
|
|
84
715
|
function dataDir() {
|
|
85
|
-
return process.env.XDG_DATA_HOME ??
|
|
716
|
+
return process.env.XDG_DATA_HOME ?? path2.join(os.homedir(), ".local", "share");
|
|
86
717
|
}
|
|
87
718
|
function getTuiStatePath() {
|
|
88
|
-
return
|
|
719
|
+
return path2.join(dataDir(), "opencode", "storage", STATE_DIR, STATE_FILE);
|
|
89
720
|
}
|
|
90
721
|
function emptySnapshot() {
|
|
91
722
|
return {
|
|
@@ -106,14 +737,14 @@ function parseSnapshot(value) {
|
|
|
106
737
|
}
|
|
107
738
|
function readTuiSnapshot() {
|
|
108
739
|
try {
|
|
109
|
-
return parseSnapshot(
|
|
740
|
+
return parseSnapshot(fs2.readFileSync(getTuiStatePath(), "utf8"));
|
|
110
741
|
} catch {
|
|
111
742
|
return emptySnapshot();
|
|
112
743
|
}
|
|
113
744
|
}
|
|
114
745
|
async function readTuiSnapshotAsync() {
|
|
115
746
|
try {
|
|
116
|
-
return parseSnapshot(await
|
|
747
|
+
return parseSnapshot(await fs2.promises.readFile(getTuiStatePath(), "utf8"));
|
|
117
748
|
} catch {
|
|
118
749
|
return emptySnapshot();
|
|
119
750
|
}
|
|
@@ -121,8 +752,8 @@ async function readTuiSnapshotAsync() {
|
|
|
121
752
|
function writeTuiSnapshot(snapshot) {
|
|
122
753
|
try {
|
|
123
754
|
const filePath = getTuiStatePath();
|
|
124
|
-
|
|
125
|
-
|
|
755
|
+
fs2.mkdirSync(path2.dirname(filePath), { recursive: true });
|
|
756
|
+
fs2.writeFileSync(filePath, `${JSON.stringify(snapshot)}
|
|
126
757
|
`);
|
|
127
758
|
} catch {}
|
|
128
759
|
}
|
|
@@ -145,6 +776,7 @@ function recordTuiAgentModel(input) {
|
|
|
145
776
|
|
|
146
777
|
// src/tui.ts
|
|
147
778
|
var PLUGIN_NAME = "oh-my-opencode-slim";
|
|
779
|
+
var CONFIG_WARNING_COLOR = "orange";
|
|
148
780
|
var FALLBACK_SIDEBAR_AGENTS = SUBAGENT_NAMES.filter((agent) => agent !== "councillor" && agent !== "council" && !DEFAULT_DISABLED_AGENTS.includes(agent));
|
|
149
781
|
var BORDER = { type: "single" };
|
|
150
782
|
async function readPackageVersion() {
|
|
@@ -177,6 +809,9 @@ function box(props, children = []) {
|
|
|
177
809
|
function truncate(value, max = 24) {
|
|
178
810
|
return value.length > max ? `${value.slice(0, max - 1)}…` : value;
|
|
179
811
|
}
|
|
812
|
+
function getTuiDirectory(api) {
|
|
813
|
+
return api.state?.path?.directory ?? process.cwd();
|
|
814
|
+
}
|
|
180
815
|
function formatSidebarModelName(model) {
|
|
181
816
|
const lastSlash = model.lastIndexOf("/");
|
|
182
817
|
return lastSlash === -1 ? model : model.slice(lastSlash + 1);
|
|
@@ -191,7 +826,8 @@ function row(label, value, theme, valueColor) {
|
|
|
191
826
|
text({ fg: valueColor ?? theme.text }, [value])
|
|
192
827
|
]);
|
|
193
828
|
}
|
|
194
|
-
function renderSidebar(snapshot, version, theme) {
|
|
829
|
+
function renderSidebar(snapshot, version, theme, configInvalid) {
|
|
830
|
+
const configStatusRow = buildConfigStatusRow(configInvalid, theme);
|
|
195
831
|
return box({
|
|
196
832
|
width: "100%",
|
|
197
833
|
flexDirection: "column",
|
|
@@ -208,9 +844,10 @@ function renderSidebar(snapshot, version, theme) {
|
|
|
208
844
|
justifyContent: "space-between",
|
|
209
845
|
alignItems: "center"
|
|
210
846
|
}, [
|
|
211
|
-
box({ paddingLeft: 1, paddingRight: 1, backgroundColor: theme.accent }, [text({ fg: theme.background }, ["
|
|
847
|
+
box({ paddingLeft: 1, paddingRight: 1, backgroundColor: theme.accent }, [text({ fg: theme.background }, ["OMO-Slim"])]),
|
|
212
848
|
text({ fg: theme.textMuted }, [`v${version}`])
|
|
213
849
|
]),
|
|
850
|
+
configStatusRow,
|
|
214
851
|
box({ width: "100%", marginTop: 1 }, [
|
|
215
852
|
text({ fg: theme.text }, ["Agents"])
|
|
216
853
|
]),
|
|
@@ -220,14 +857,44 @@ function renderSidebar(snapshot, version, theme) {
|
|
|
220
857
|
})
|
|
221
858
|
]);
|
|
222
859
|
}
|
|
860
|
+
function buildConfigStatusRow(configInvalid, theme) {
|
|
861
|
+
if (!configInvalid)
|
|
862
|
+
return null;
|
|
863
|
+
return box({
|
|
864
|
+
width: "100%",
|
|
865
|
+
flexDirection: "column",
|
|
866
|
+
marginTop: 1,
|
|
867
|
+
marginBottom: 1
|
|
868
|
+
}, [
|
|
869
|
+
text({ fg: CONFIG_WARNING_COLOR }, ["Config invalid"]),
|
|
870
|
+
text({ fg: theme.textMuted }, ["Run doctor for details"])
|
|
871
|
+
]);
|
|
872
|
+
}
|
|
873
|
+
function readConfigInvalid(directory) {
|
|
874
|
+
let configInvalid = false;
|
|
875
|
+
loadPluginConfig(directory, {
|
|
876
|
+
silent: true,
|
|
877
|
+
onWarning: () => {
|
|
878
|
+
configInvalid = true;
|
|
879
|
+
}
|
|
880
|
+
});
|
|
881
|
+
return configInvalid;
|
|
882
|
+
}
|
|
223
883
|
var plugin = {
|
|
224
884
|
id: `${PLUGIN_NAME}:tui`,
|
|
225
885
|
tui: async (api, _options, meta) => {
|
|
226
886
|
const version = meta.version ?? await readPackageVersion() ?? "dev";
|
|
887
|
+
let configDirectory = getTuiDirectory(api);
|
|
888
|
+
let configInvalid = readConfigInvalid(configDirectory);
|
|
227
889
|
let snapshot = readTuiSnapshot();
|
|
228
890
|
const renderTimer = setInterval(async () => {
|
|
229
891
|
try {
|
|
230
892
|
snapshot = await readTuiSnapshotAsync();
|
|
893
|
+
const currentDirectory = getTuiDirectory(api);
|
|
894
|
+
if (currentDirectory !== configDirectory) {
|
|
895
|
+
configDirectory = currentDirectory;
|
|
896
|
+
configInvalid = readConfigInvalid(configDirectory);
|
|
897
|
+
}
|
|
231
898
|
api.renderer.requestRender();
|
|
232
899
|
} catch {}
|
|
233
900
|
}, 1000);
|
|
@@ -238,7 +905,7 @@ var plugin = {
|
|
|
238
905
|
order: 900,
|
|
239
906
|
slots: {
|
|
240
907
|
sidebar_content() {
|
|
241
|
-
return renderSidebar(snapshot, version, api.theme.current);
|
|
908
|
+
return renderSidebar(snapshot, version, api.theme.current, configInvalid);
|
|
242
909
|
}
|
|
243
910
|
}
|
|
244
911
|
});
|
|
@@ -246,6 +913,7 @@ var plugin = {
|
|
|
246
913
|
};
|
|
247
914
|
var tui_default = plugin;
|
|
248
915
|
export {
|
|
916
|
+
readConfigInvalid,
|
|
249
917
|
getSidebarAgentNames,
|
|
250
918
|
formatSidebarModelName,
|
|
251
919
|
tui_default as default
|