oh-my-opencode-slim 1.0.5 → 1.0.6

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 CHANGED
@@ -38,10 +38,9 @@ bunx oh-my-opencode-slim@latest install
38
38
  ```
39
39
 
40
40
  The installer also registers the companion TUI plugin in OpenCode's
41
- `tui.json`, which adds an `OMOS <version>` badge beside the prompt without
42
- replacing OpenCode's built-in TUI footer. For manual setups, add
43
- `oh-my-opencode-slim` to the `plugin` array in both `opencode.json` and
44
- `tui.json`.
41
+ `tui.json`, which adds a small sidebar showing specialist-agent status plus
42
+ active/reusable task sessions. For manual setups, add `oh-my-opencode-slim` to
43
+ the `plugin` array in both `opencode.json` and `tui.json`.
45
44
 
46
45
  ### Getting Started
47
46
 
package/dist/cli/index.js CHANGED
@@ -338,7 +338,6 @@ var PluginConfigSchema = z2.object({
338
338
  setDefaultAgent: z2.boolean().optional(),
339
339
  scoringEngineVersion: z2.enum(["v1", "v2-shadow", "v2"]).optional(),
340
340
  balanceProviderUsage: z2.boolean().optional(),
341
- showStartupToast: z2.boolean().optional().describe("Show the startup activation toast when OpenCode starts. Defaults to true."),
342
341
  autoUpdate: z2.boolean().optional().describe("Disable automatic installation of plugin updates when false. Defaults to true."),
343
342
  manualPlan: ManualPlanSchema.optional(),
344
343
  presets: z2.record(z2.string(), PresetSchema).optional(),
@@ -195,7 +195,6 @@ export declare const PluginConfigSchema: z.ZodObject<{
195
195
  v2: "v2";
196
196
  }>>;
197
197
  balanceProviderUsage: z.ZodOptional<z.ZodBoolean>;
198
- showStartupToast: z.ZodOptional<z.ZodBoolean>;
199
198
  autoUpdate: z.ZodOptional<z.ZodBoolean>;
200
199
  manualPlan: z.ZodOptional<z.ZodObject<{
201
200
  orchestrator: z.ZodObject<{
@@ -12,7 +12,6 @@ export interface PackageJson {
12
12
  [key: string]: unknown;
13
13
  }
14
14
  export interface AutoUpdateCheckerOptions {
15
- showStartupToast?: boolean;
16
15
  autoUpdate?: boolean;
17
16
  }
18
17
  export interface PluginEntryInfo {
package/dist/index.js CHANGED
@@ -6246,33 +6246,33 @@ var require_URL = __commonJS((exports, module) => {
6246
6246
  else
6247
6247
  return basepath.substring(0, lastslash + 1) + refpath;
6248
6248
  }
6249
- function remove_dot_segments(path14) {
6250
- if (!path14)
6251
- return path14;
6249
+ function remove_dot_segments(path15) {
6250
+ if (!path15)
6251
+ return path15;
6252
6252
  var output = "";
6253
- while (path14.length > 0) {
6254
- if (path14 === "." || path14 === "..") {
6255
- path14 = "";
6253
+ while (path15.length > 0) {
6254
+ if (path15 === "." || path15 === "..") {
6255
+ path15 = "";
6256
6256
  break;
6257
6257
  }
6258
- var twochars = path14.substring(0, 2);
6259
- var threechars = path14.substring(0, 3);
6260
- var fourchars = path14.substring(0, 4);
6258
+ var twochars = path15.substring(0, 2);
6259
+ var threechars = path15.substring(0, 3);
6260
+ var fourchars = path15.substring(0, 4);
6261
6261
  if (threechars === "../") {
6262
- path14 = path14.substring(3);
6262
+ path15 = path15.substring(3);
6263
6263
  } else if (twochars === "./") {
6264
- path14 = path14.substring(2);
6264
+ path15 = path15.substring(2);
6265
6265
  } else if (threechars === "/./") {
6266
- path14 = "/" + path14.substring(3);
6267
- } else if (twochars === "/." && path14.length === 2) {
6268
- path14 = "/";
6269
- } else if (fourchars === "/../" || threechars === "/.." && path14.length === 3) {
6270
- path14 = "/" + path14.substring(4);
6266
+ path15 = "/" + path15.substring(3);
6267
+ } else if (twochars === "/." && path15.length === 2) {
6268
+ path15 = "/";
6269
+ } else if (fourchars === "/../" || threechars === "/.." && path15.length === 3) {
6270
+ path15 = "/" + path15.substring(4);
6271
6271
  output = output.replace(/\/?[^\/]*$/, "");
6272
6272
  } else {
6273
- var segment = path14.match(/(\/?([^\/]*))/)[0];
6273
+ var segment = path15.match(/(\/?([^\/]*))/)[0];
6274
6274
  output += segment;
6275
- path14 = path14.substring(segment.length);
6275
+ path15 = path15.substring(segment.length);
6276
6276
  }
6277
6277
  }
6278
6278
  return output;
@@ -18150,14 +18150,14 @@ var require_turndown_cjs = __commonJS((exports, module) => {
18150
18150
  } else if (node.nodeType === 1) {
18151
18151
  replacement = replacementForNode.call(self, node);
18152
18152
  }
18153
- return join12(output, replacement);
18153
+ return join13(output, replacement);
18154
18154
  }, "");
18155
18155
  }
18156
18156
  function postProcess(output) {
18157
18157
  var self = this;
18158
18158
  this.rules.forEach(function(rule) {
18159
18159
  if (typeof rule.append === "function") {
18160
- output = join12(output, rule.append(self.options));
18160
+ output = join13(output, rule.append(self.options));
18161
18161
  }
18162
18162
  });
18163
18163
  return output.replace(/^[\t\r\n]+/, "").replace(/[\t\r\n\s]+$/, "");
@@ -18170,7 +18170,7 @@ var require_turndown_cjs = __commonJS((exports, module) => {
18170
18170
  content = content.trim();
18171
18171
  return whitespace.leading + rule.replacement(content, node, this.options) + whitespace.trailing;
18172
18172
  }
18173
- function join12(output, replacement) {
18173
+ function join13(output, replacement) {
18174
18174
  var s1 = trimTrailingNewlines(output);
18175
18175
  var s2 = trimLeadingNewlines(replacement);
18176
18176
  var nls = Math.max(output.length - s1.length, replacement.length - s2.length);
@@ -18570,7 +18570,6 @@ var PluginConfigSchema = z2.object({
18570
18570
  setDefaultAgent: z2.boolean().optional(),
18571
18571
  scoringEngineVersion: z2.enum(["v1", "v2-shadow", "v2"]).optional(),
18572
18572
  balanceProviderUsage: z2.boolean().optional(),
18573
- showStartupToast: z2.boolean().optional().describe("Show the startup activation toast when OpenCode starts. Defaults to true."),
18574
18573
  autoUpdate: z2.boolean().optional().describe("Disable automatic installation of plugin updates when false. Defaults to true."),
18575
18574
  manualPlan: ManualPlanSchema.optional(),
18576
18575
  presets: z2.record(z2.string(), PresetSchema).optional(),
@@ -21995,7 +21994,7 @@ function preparePackageUpdate(version, packageName = PACKAGE_NAME, runtimePackag
21995
21994
 
21996
21995
  // src/hooks/auto-update-checker/index.ts
21997
21996
  function createAutoUpdateCheckerHook(ctx, options = {}) {
21998
- const { showStartupToast = true, autoUpdate = true } = options;
21997
+ const { autoUpdate = true } = options;
21999
21998
  let hasChecked = false;
22000
21999
  return {
22001
22000
  event: ({ event }) => {
@@ -22008,19 +22007,11 @@ function createAutoUpdateCheckerHook(ctx, options = {}) {
22008
22007
  return;
22009
22008
  hasChecked = true;
22010
22009
  setTimeout(async () => {
22011
- const cachedVersion = getCachedVersion();
22012
22010
  const localDevVersion = getLocalDevVersion(ctx.directory);
22013
- const displayVersion = localDevVersion ?? cachedVersion;
22014
22011
  if (localDevVersion) {
22015
- if (showStartupToast) {
22016
- showToast(ctx, `OMO-Slim ${displayVersion} (dev)`, "Running in local development mode.", "info");
22017
- }
22018
22012
  log("[auto-update-checker] Local development mode");
22019
22013
  return;
22020
22014
  }
22021
- if (showStartupToast) {
22022
- showToast(ctx, `OMO-Slim ${displayVersion ?? "unknown"}`, "oh-my-opencode-slim is active.", "info");
22023
- }
22024
22015
  runBackgroundUpdateCheck(ctx, autoUpdate).catch((err) => {
22025
22016
  log("[auto-update-checker] Background update check failed:", err);
22026
22017
  });
@@ -23342,6 +23333,7 @@ function createTaskSessionManagerHook(_ctx, options) {
23342
23333
  const pendingCallOrder = [];
23343
23334
  const contextByTask = new Map;
23344
23335
  const pendingManagedTaskIds = new Set;
23336
+ let anonymousPendingCallId = 0;
23345
23337
  function addTaskContext(taskId, files) {
23346
23338
  if (files.length === 0)
23347
23339
  return;
@@ -23388,6 +23380,9 @@ function createTaskSessionManagerHook(_ctx, options) {
23388
23380
  const firstLine = output.split(/\r?\n/, 1)[0]?.trim().toLowerCase() ?? "";
23389
23381
  return firstLine.startsWith("[error]") && firstLine.includes("session") && (firstLine.includes("not found") || firstLine.includes("no session"));
23390
23382
  }
23383
+ function pendingCallId(input) {
23384
+ return input.callID ?? `${input.sessionID ?? "unknown"}:anonymous-${++anonymousPendingCallId}`;
23385
+ }
23391
23386
  function rememberPendingCall(call) {
23392
23387
  const existingIndex = pendingCallOrder.indexOf(call.callId);
23393
23388
  if (existingIndex >= 0) {
@@ -23403,17 +23398,23 @@ function createTaskSessionManagerHook(_ctx, options) {
23403
23398
  pendingCalls.delete(evictedCallId);
23404
23399
  }
23405
23400
  }
23406
- function takePendingCall(callId) {
23407
- if (!callId)
23401
+ function takePendingCall(callId, parentSessionId) {
23402
+ const resolvedCallId = callId ?? firstPendingCallForParent(parentSessionId);
23403
+ if (!resolvedCallId)
23408
23404
  return;
23409
- const pending = pendingCalls.get(callId);
23410
- pendingCalls.delete(callId);
23411
- const orderIndex = pendingCallOrder.indexOf(callId);
23405
+ const pending = pendingCalls.get(resolvedCallId);
23406
+ pendingCalls.delete(resolvedCallId);
23407
+ const orderIndex = pendingCallOrder.indexOf(resolvedCallId);
23412
23408
  if (orderIndex >= 0) {
23413
23409
  pendingCallOrder.splice(orderIndex, 1);
23414
23410
  }
23415
23411
  return pending;
23416
23412
  }
23413
+ function firstPendingCallForParent(parentSessionId) {
23414
+ if (!parentSessionId)
23415
+ return;
23416
+ return pendingCallOrder.find((callId) => pendingCalls.get(callId)?.parentSessionId === parentSessionId);
23417
+ }
23417
23418
  return {
23418
23419
  "tool.execute.before": async (input, output) => {
23419
23420
  if (input.tool.toLowerCase() !== "task")
@@ -23431,14 +23432,16 @@ function createTaskSessionManagerHook(_ctx, options) {
23431
23432
  prompt: typeof args.prompt === "string" ? args.prompt : undefined,
23432
23433
  agentType: args.subagent_type
23433
23434
  });
23434
- if (input.callID) {
23435
- rememberPendingCall({
23436
- callId: input.callID,
23437
- parentSessionId: input.sessionID,
23438
- agentType: args.subagent_type,
23439
- label
23440
- });
23441
- }
23435
+ const pendingCall = {
23436
+ callId: pendingCallId({
23437
+ callID: input.callID,
23438
+ sessionID: input.sessionID
23439
+ }),
23440
+ parentSessionId: input.sessionID,
23441
+ agentType: args.subagent_type,
23442
+ label
23443
+ };
23444
+ rememberPendingCall(pendingCall);
23442
23445
  if (typeof args.task_id !== "string" || args.task_id.trim() === "") {
23443
23446
  return;
23444
23447
  }
@@ -23451,15 +23454,8 @@ function createTaskSessionManagerHook(_ctx, options) {
23451
23454
  args.task_id = remembered.taskId;
23452
23455
  pendingManagedTaskIds.add(remembered.taskId);
23453
23456
  sessionManager.markUsed(input.sessionID, args.subagent_type, remembered.taskId);
23454
- if (input.callID) {
23455
- rememberPendingCall({
23456
- callId: input.callID,
23457
- parentSessionId: input.sessionID,
23458
- agentType: args.subagent_type,
23459
- label,
23460
- resumedTaskId: remembered.taskId
23461
- });
23462
- }
23457
+ pendingCall.resumedTaskId = remembered.taskId;
23458
+ rememberPendingCall(pendingCall);
23463
23459
  },
23464
23460
  "tool.execute.after": async (input, output) => {
23465
23461
  if (input.tool.toLowerCase() === "read") {
@@ -23470,7 +23466,7 @@ function createTaskSessionManagerHook(_ctx, options) {
23470
23466
  }
23471
23467
  if (input.tool.toLowerCase() !== "task")
23472
23468
  return;
23473
- const pending = takePendingCall(input.callID);
23469
+ const pending = takePendingCall(input.callID, input.sessionID);
23474
23470
  if (!pending || typeof output.output !== "string")
23475
23471
  return;
23476
23472
  const taskId = parseTaskIdFromTaskOutput(output.output);
@@ -23538,8 +23534,8 @@ function createTaskSessionManagerHook(_ctx, options) {
23538
23534
  const sessionId = input.event.properties?.info?.id ?? input.event.properties?.sessionID;
23539
23535
  if (!sessionId)
23540
23536
  return;
23541
- sessionManager.clearParent(sessionId);
23542
23537
  sessionManager.dropTask(sessionId);
23538
+ sessionManager.clearParent(sessionId);
23543
23539
  contextByTask.delete(sessionId);
23544
23540
  pendingManagedTaskIds.delete(sessionId);
23545
23541
  pruneContext();
@@ -29622,6 +29618,74 @@ Returns the councillor responses with a summary footer.`,
29622
29618
  });
29623
29619
  return { council_session };
29624
29620
  }
29621
+ // src/tui-state.ts
29622
+ import * as fs9 from "node:fs";
29623
+ import * as os4 from "node:os";
29624
+ import * as path13 from "node:path";
29625
+ var STATE_DIR = "oh-my-opencode-slim";
29626
+ var STATE_FILE = "tui-state.json";
29627
+ function dataDir() {
29628
+ return process.env.XDG_DATA_HOME ?? path13.join(os4.homedir(), ".local", "share");
29629
+ }
29630
+ function getTuiStatePath() {
29631
+ return path13.join(dataDir(), "opencode", "storage", STATE_DIR, STATE_FILE);
29632
+ }
29633
+ function emptySnapshot() {
29634
+ return {
29635
+ version: 1,
29636
+ updatedAt: Date.now(),
29637
+ agentModels: {}
29638
+ };
29639
+ }
29640
+ function parseSnapshot(value) {
29641
+ const parsed = JSON.parse(value);
29642
+ if (parsed?.version !== 1)
29643
+ return emptySnapshot();
29644
+ return {
29645
+ version: 1,
29646
+ updatedAt: typeof parsed.updatedAt === "number" ? parsed.updatedAt : Date.now(),
29647
+ agentModels: parsed.agentModels ?? {}
29648
+ };
29649
+ }
29650
+ function readTuiSnapshot() {
29651
+ try {
29652
+ return parseSnapshot(fs9.readFileSync(getTuiStatePath(), "utf8"));
29653
+ } catch {
29654
+ return emptySnapshot();
29655
+ }
29656
+ }
29657
+ async function readTuiSnapshotAsync() {
29658
+ try {
29659
+ return parseSnapshot(await fs9.promises.readFile(getTuiStatePath(), "utf8"));
29660
+ } catch {
29661
+ return emptySnapshot();
29662
+ }
29663
+ }
29664
+ function writeTuiSnapshot(snapshot) {
29665
+ try {
29666
+ const filePath = getTuiStatePath();
29667
+ fs9.mkdirSync(path13.dirname(filePath), { recursive: true });
29668
+ fs9.writeFileSync(filePath, `${JSON.stringify(snapshot)}
29669
+ `);
29670
+ } catch {}
29671
+ }
29672
+ function updateSnapshot(mutator) {
29673
+ const snapshot = readTuiSnapshot();
29674
+ mutator(snapshot);
29675
+ snapshot.updatedAt = Date.now();
29676
+ writeTuiSnapshot(snapshot);
29677
+ }
29678
+ function recordTuiAgentModels(input) {
29679
+ updateSnapshot((snapshot) => {
29680
+ snapshot.agentModels = { ...input.agentModels };
29681
+ });
29682
+ }
29683
+ function recordTuiAgentModel(input) {
29684
+ updateSnapshot((snapshot) => {
29685
+ snapshot.agentModels[input.agentName] = input.model;
29686
+ });
29687
+ }
29688
+
29625
29689
  // src/tools/preset-manager.ts
29626
29690
  var COMMAND_NAME3 = "preset";
29627
29691
  function createPresetManager(ctx, config) {
@@ -29698,6 +29762,14 @@ function createPresetManager(ctx, config) {
29698
29762
  await ctx.client.config.update({
29699
29763
  body: { agent: allUpdates }
29700
29764
  });
29765
+ const snapshot = readTuiSnapshot();
29766
+ const agentModels = { ...snapshot.agentModels };
29767
+ for (const [agentName, agentConfig] of Object.entries(allUpdates)) {
29768
+ if (typeof agentConfig.model === "string") {
29769
+ agentModels[agentName] = agentConfig.model;
29770
+ }
29771
+ }
29772
+ recordTuiAgentModels({ agentModels });
29701
29773
  activePreset = presetName;
29702
29774
  const summaryParts = [];
29703
29775
  for (const [name, cfg] of Object.entries(agentUpdates)) {
@@ -29808,15 +29880,15 @@ var BINARY_PREFIXES = [
29808
29880
  ];
29809
29881
  var WEBFETCH_DESCRIPTION = "Fetch a URL with better extraction for static/docs pages. Supports llms.txt probing, content-focused HTML extraction, metadata, redirects, and an optional prompt processed by a cheap secondary model.";
29810
29882
  // src/tools/smartfetch/tool.ts
29811
- import os4 from "node:os";
29812
- import path16 from "node:path";
29883
+ import os5 from "node:os";
29884
+ import path17 from "node:path";
29813
29885
  import {
29814
29886
  tool as tool4
29815
29887
  } from "@opencode-ai/plugin";
29816
29888
 
29817
29889
  // src/tools/smartfetch/binary.ts
29818
29890
  import { mkdir as mkdir2, writeFile as writeFile2 } from "node:fs/promises";
29819
- import path13 from "node:path";
29891
+ import path14 from "node:path";
29820
29892
  function extensionForMime(contentType) {
29821
29893
  const mime = contentType.split(";")[0]?.trim().toLowerCase();
29822
29894
  const map = {
@@ -29837,10 +29909,10 @@ function buildBinaryResultMessage(fetchResult, savedPath) {
29837
29909
  async function saveBinary(binaryDir, data, contentType, filename) {
29838
29910
  await mkdir2(binaryDir, { recursive: true });
29839
29911
  const initialName = filename || `webfetch-${Date.now()}.${extensionForMime(contentType)}`;
29840
- const parsed = path13.parse(initialName);
29912
+ const parsed = path14.parse(initialName);
29841
29913
  for (let attempt = 0;attempt < 1000; attempt++) {
29842
29914
  const candidateName = attempt === 0 ? initialName : `${parsed.name}-${attempt}${parsed.ext || `.${extensionForMime(contentType)}`}`;
29843
- const file = path13.join(binaryDir, candidateName);
29915
+ const file = path14.join(binaryDir, candidateName);
29844
29916
  try {
29845
29917
  await writeFile2(file, data, { flag: "wx" });
29846
29918
  return file;
@@ -30494,7 +30566,7 @@ var L = class u2 {
30494
30566
  };
30495
30567
 
30496
30568
  // src/tools/smartfetch/network.ts
30497
- import path14 from "node:path";
30569
+ import path15 from "node:path";
30498
30570
 
30499
30571
  // src/tools/smartfetch/utils.ts
30500
30572
  var import_readability = __toESM(require_readability(), 1);
@@ -31219,7 +31291,7 @@ function inferFilenameFromUrl(url) {
31219
31291
  function truncateFilename(name, maxLength = 180) {
31220
31292
  if (name.length <= maxLength)
31221
31293
  return name;
31222
- const parsed = path14.parse(name);
31294
+ const parsed = path15.parse(name);
31223
31295
  const ext = parsed.ext || "";
31224
31296
  const baseLimit = Math.max(1, maxLength - ext.length);
31225
31297
  return `${parsed.name.slice(0, baseLimit)}${ext}`;
@@ -31391,7 +31463,7 @@ function isInvalidLlmsResult(fetchResult) {
31391
31463
  // src/tools/smartfetch/secondary-model.ts
31392
31464
  import { existsSync as existsSync9 } from "node:fs";
31393
31465
  import { readFile as readFile4 } from "node:fs/promises";
31394
- import path15 from "node:path";
31466
+ import path16 from "node:path";
31395
31467
  function parseModelRef(value) {
31396
31468
  if (!value)
31397
31469
  return;
@@ -31417,7 +31489,7 @@ function pickAgentModelRef(value) {
31417
31489
  }
31418
31490
  function findPreferredOpenCodeConfigPath(baseDir) {
31419
31491
  for (const file of ["opencode.jsonc", "opencode.json"]) {
31420
- const fullPath = path15.join(baseDir, file);
31492
+ const fullPath = path16.join(baseDir, file);
31421
31493
  if (existsSync9(fullPath))
31422
31494
  return fullPath;
31423
31495
  }
@@ -31434,7 +31506,7 @@ async function readOpenCodeConfigFile(configPath) {
31434
31506
  }
31435
31507
  }
31436
31508
  async function readEffectiveOpenCodeConfig(directory) {
31437
- const projectDir = path15.join(directory, ".opencode");
31509
+ const projectDir = path16.join(directory, ".opencode");
31438
31510
  const userDirs = getConfigSearchDirs();
31439
31511
  const projectPath = findPreferredOpenCodeConfigPath(projectDir);
31440
31512
  const userPath = userDirs.map((configDir) => findPreferredOpenCodeConfigPath(configDir)).find(Boolean);
@@ -31595,7 +31667,7 @@ async function runSecondaryModelWithFallback(client, directory, models, prompt,
31595
31667
  // src/tools/smartfetch/tool.ts
31596
31668
  var z5 = tool4.schema;
31597
31669
  function createWebfetchTool(pluginCtx, options = {}) {
31598
- const binaryDir = options.binaryDir || path16.join(os4.tmpdir(), "opencode-smartfetch");
31670
+ const binaryDir = options.binaryDir || path17.join(os5.tmpdir(), "opencode-smartfetch");
31599
31671
  return tool4({
31600
31672
  description: WEBFETCH_DESCRIPTION,
31601
31673
  args: {
@@ -32258,7 +32330,6 @@ var OhMyOpenCodeLite = async (ctx) => {
32258
32330
  webfetch = createWebfetchTool(ctx);
32259
32331
  multiplexerSessionManager = new MultiplexerSessionManager(ctx, multiplexerConfig);
32260
32332
  autoUpdateChecker = createAutoUpdateCheckerHook(ctx, {
32261
- showStartupToast: config.showStartupToast ?? true,
32262
32333
  autoUpdate: config.autoUpdate ?? true
32263
32334
  });
32264
32335
  phaseReminderHook = createPhaseReminderHook();
@@ -32478,6 +32549,15 @@ var OhMyOpenCodeLite = async (ctx) => {
32478
32549
  }
32479
32550
  }
32480
32551
  }
32552
+ const tuiAgentModels = {};
32553
+ for (const agentDef of agentDefs) {
32554
+ if (agentDef.name === "councillor")
32555
+ continue;
32556
+ const entry = configAgent[agentDef.name];
32557
+ const resolvedModel = typeof entry?.model === "string" ? entry.model : runtimeChains[agentDef.name]?.[0] ? runtimeChains[agentDef.name][0] : typeof agentDef.config.model === "string" ? agentDef.config.model : undefined;
32558
+ tuiAgentModels[agentDef.name] = resolvedModel ?? "default";
32559
+ }
32560
+ recordTuiAgentModels({ agentModels: tuiAgentModels });
32481
32561
  const configMcp = opencodeConfig.mcp;
32482
32562
  if (!configMcp) {
32483
32563
  opencodeConfig.mcp = { ...mcps };
@@ -32521,6 +32601,15 @@ var OhMyOpenCodeLite = async (ctx) => {
32521
32601
  },
32522
32602
  event: async (input) => {
32523
32603
  const event = input.event;
32604
+ if (event.type === "message.updated") {
32605
+ const info = event.properties?.info;
32606
+ if (typeof info?.agent === "string" && typeof info.providerID === "string" && typeof info.modelID === "string") {
32607
+ recordTuiAgentModel({
32608
+ agentName: resolveRuntimeAgentName(config, info.agent),
32609
+ model: `${info.providerID}/${info.modelID}`
32610
+ });
32611
+ }
32612
+ }
32524
32613
  if (event.type === "session.created") {
32525
32614
  const childSessionId = event.properties?.info?.id;
32526
32615
  const parentSessionId = event.properties?.info?.parentID;
@@ -0,0 +1,15 @@
1
+ export interface TuiSnapshot {
2
+ version: 1;
3
+ updatedAt: number;
4
+ agentModels: Record<string, string>;
5
+ }
6
+ export declare function getTuiStatePath(): string;
7
+ export declare function readTuiSnapshot(): TuiSnapshot;
8
+ export declare function readTuiSnapshotAsync(): Promise<TuiSnapshot>;
9
+ export declare function recordTuiAgentModels(input: {
10
+ agentModels: Record<string, string>;
11
+ }): void;
12
+ export declare function recordTuiAgentModel(input: {
13
+ agentName: string;
14
+ model: string;
15
+ }): void;
package/dist/tui.d.ts CHANGED
@@ -1,4 +1,7 @@
1
1
  import type { TuiPluginModule } from '@opencode-ai/plugin/tui';
2
+ import { type TuiSnapshot } from './tui-state';
3
+ export declare function formatSidebarModelName(model: string): string;
4
+ export declare function getSidebarAgentNames(snapshot: TuiSnapshot): string[];
2
5
  declare const plugin: TuiPluginModule & {
3
6
  id: string;
4
7
  };
package/dist/tui.js CHANGED
@@ -34,8 +34,119 @@ var __require = /* @__PURE__ */ createRequire(import.meta.url);
34
34
 
35
35
  // src/tui.ts
36
36
  import { createElement, insert, setProp } from "@opentui/solid";
37
+
38
+ // src/config/constants.ts
39
+ var AGENT_ALIASES = {
40
+ explore: "explorer",
41
+ "frontend-ui-ux-engineer": "designer"
42
+ };
43
+ var SUBAGENT_NAMES = [
44
+ "explorer",
45
+ "librarian",
46
+ "oracle",
47
+ "designer",
48
+ "fixer",
49
+ "observer",
50
+ "council",
51
+ "councillor"
52
+ ];
53
+ var ORCHESTRATOR_NAME = "orchestrator";
54
+ var ALL_AGENT_NAMES = [ORCHESTRATOR_NAME, ...SUBAGENT_NAMES];
55
+ var PROTECTED_AGENTS = new Set(["orchestrator", "councillor"]);
56
+ var DEFAULT_MODELS = {
57
+ orchestrator: undefined,
58
+ oracle: "openai/gpt-5.5",
59
+ librarian: "openai/gpt-5.4-mini",
60
+ explorer: "openai/gpt-5.4-mini",
61
+ designer: "openai/gpt-5.4-mini",
62
+ fixer: "openai/gpt-5.4-mini",
63
+ observer: "openai/gpt-5.4-mini",
64
+ council: "openai/gpt-5.4-mini",
65
+ councillor: "openai/gpt-5.4-mini"
66
+ };
67
+ var POLL_INTERVAL_BACKGROUND_MS = 2000;
68
+ var DEFAULT_TIMEOUT_MS = 2 * 60 * 1000;
69
+ var MAX_POLL_TIME_MS = 5 * 60 * 1000;
70
+ var DEFAULT_MAX_SUBAGENT_DEPTH = 3;
71
+ var PHASE_REMINDER_TEXT = `!IMPORTANT! Recall the workflow rules:
72
+ Understand → choose the best parallelized path based on your capabilities and agents delegation rules → recall session reuse rules → execute → verify.
73
+ If delegating, launch the specialist in the same turn you mention it !END!`;
74
+ var TMUX_SPAWN_DELAY_MS = 500;
75
+ var COUNCILLOR_STAGGER_MS = 250;
76
+ var DEFAULT_DISABLED_AGENTS = ["observer"];
77
+
78
+ // src/tui-state.ts
79
+ import * as fs from "node:fs";
80
+ import * as os from "node:os";
81
+ import * as path from "node:path";
82
+ var STATE_DIR = "oh-my-opencode-slim";
83
+ var STATE_FILE = "tui-state.json";
84
+ function dataDir() {
85
+ return process.env.XDG_DATA_HOME ?? path.join(os.homedir(), ".local", "share");
86
+ }
87
+ function getTuiStatePath() {
88
+ return path.join(dataDir(), "opencode", "storage", STATE_DIR, STATE_FILE);
89
+ }
90
+ function emptySnapshot() {
91
+ return {
92
+ version: 1,
93
+ updatedAt: Date.now(),
94
+ agentModels: {}
95
+ };
96
+ }
97
+ function parseSnapshot(value) {
98
+ const parsed = JSON.parse(value);
99
+ if (parsed?.version !== 1)
100
+ return emptySnapshot();
101
+ return {
102
+ version: 1,
103
+ updatedAt: typeof parsed.updatedAt === "number" ? parsed.updatedAt : Date.now(),
104
+ agentModels: parsed.agentModels ?? {}
105
+ };
106
+ }
107
+ function readTuiSnapshot() {
108
+ try {
109
+ return parseSnapshot(fs.readFileSync(getTuiStatePath(), "utf8"));
110
+ } catch {
111
+ return emptySnapshot();
112
+ }
113
+ }
114
+ async function readTuiSnapshotAsync() {
115
+ try {
116
+ return parseSnapshot(await fs.promises.readFile(getTuiStatePath(), "utf8"));
117
+ } catch {
118
+ return emptySnapshot();
119
+ }
120
+ }
121
+ function writeTuiSnapshot(snapshot) {
122
+ try {
123
+ const filePath = getTuiStatePath();
124
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
125
+ fs.writeFileSync(filePath, `${JSON.stringify(snapshot)}
126
+ `);
127
+ } catch {}
128
+ }
129
+ function updateSnapshot(mutator) {
130
+ const snapshot = readTuiSnapshot();
131
+ mutator(snapshot);
132
+ snapshot.updatedAt = Date.now();
133
+ writeTuiSnapshot(snapshot);
134
+ }
135
+ function recordTuiAgentModels(input) {
136
+ updateSnapshot((snapshot) => {
137
+ snapshot.agentModels = { ...input.agentModels };
138
+ });
139
+ }
140
+ function recordTuiAgentModel(input) {
141
+ updateSnapshot((snapshot) => {
142
+ snapshot.agentModels[input.agentName] = input.model;
143
+ });
144
+ }
145
+
146
+ // src/tui.ts
37
147
  var PLUGIN_NAME = "oh-my-opencode-slim";
38
- var PLUGIN_LABEL = "OMOS";
148
+ var FALLBACK_SIDEBAR_AGENTS = SUBAGENT_NAMES.filter((agent) => agent !== "councillor" && agent !== "council" && !DEFAULT_DISABLED_AGENTS.includes(agent));
149
+ var BORDER = { type: "single" };
39
150
  async function readPackageVersion() {
40
151
  try {
41
152
  const packageJson = await Bun.file(new URL("../package.json", import.meta.url)).json();
@@ -60,21 +171,74 @@ function element(tag, props, children = []) {
60
171
  function text(props, children) {
61
172
  return element("text", props, children);
62
173
  }
174
+ function box(props, children = []) {
175
+ return element("box", props, children);
176
+ }
177
+ function truncate(value, max = 24) {
178
+ return value.length > max ? `${value.slice(0, max - 1)}…` : value;
179
+ }
180
+ function formatSidebarModelName(model) {
181
+ const lastSlash = model.lastIndexOf("/");
182
+ return lastSlash === -1 ? model : model.slice(lastSlash + 1);
183
+ }
184
+ function getSidebarAgentNames(snapshot) {
185
+ const configuredAgents = Object.keys(snapshot.agentModels);
186
+ return configuredAgents.length > 0 ? configuredAgents : FALLBACK_SIDEBAR_AGENTS;
187
+ }
188
+ function row(label, value, theme, valueColor) {
189
+ return box({ width: "100%", flexDirection: "row", justifyContent: "space-between" }, [
190
+ text({ fg: theme.textMuted }, [label]),
191
+ text({ fg: valueColor ?? theme.text }, [value])
192
+ ]);
193
+ }
194
+ function renderSidebar(snapshot, version, theme) {
195
+ return box({
196
+ width: "100%",
197
+ flexDirection: "column",
198
+ border: BORDER,
199
+ borderColor: theme.borderActive,
200
+ paddingTop: 1,
201
+ paddingBottom: 1,
202
+ paddingLeft: 1,
203
+ paddingRight: 1
204
+ }, [
205
+ box({
206
+ width: "100%",
207
+ flexDirection: "row",
208
+ justifyContent: "space-between",
209
+ alignItems: "center"
210
+ }, [
211
+ box({ paddingLeft: 1, paddingRight: 1, backgroundColor: theme.accent }, [text({ fg: theme.background }, ["omo-slim"])]),
212
+ text({ fg: theme.textMuted }, [`v${version}`])
213
+ ]),
214
+ box({ width: "100%", marginTop: 1 }, [
215
+ text({ fg: theme.text }, ["Agents"])
216
+ ]),
217
+ ...getSidebarAgentNames(snapshot).map((agentName) => {
218
+ const model = snapshot.agentModels[agentName] ?? "pending";
219
+ return row(agentName, truncate(formatSidebarModelName(model), 26), theme, theme.textMuted);
220
+ })
221
+ ]);
222
+ }
63
223
  var plugin = {
64
224
  id: `${PLUGIN_NAME}:tui`,
65
225
  tui: async (api, _options, meta) => {
66
226
  const version = meta.version ?? await readPackageVersion() ?? "dev";
67
- const versionText = `${PLUGIN_LABEL} ${version}`;
227
+ let snapshot = readTuiSnapshot();
228
+ const renderTimer = setInterval(async () => {
229
+ try {
230
+ snapshot = await readTuiSnapshotAsync();
231
+ api.renderer.requestRender();
232
+ } catch {}
233
+ }, 1000);
234
+ api.lifecycle.onDispose(() => {
235
+ clearInterval(renderTimer);
236
+ });
68
237
  api.slots.register({
69
238
  order: 900,
70
239
  slots: {
71
- home_prompt_right() {
72
- const theme = api.theme.current;
73
- return text({ fg: theme.textMuted }, [versionText]);
74
- },
75
- session_prompt_right() {
76
- const theme = api.theme.current;
77
- return text({ fg: theme.textMuted }, [versionText]);
240
+ sidebar_content() {
241
+ return renderSidebar(snapshot, version, api.theme.current);
78
242
  }
79
243
  }
80
244
  });
@@ -82,5 +246,7 @@ var plugin = {
82
246
  };
83
247
  var tui_default = plugin;
84
248
  export {
249
+ getSidebarAgentNames,
250
+ formatSidebarModelName,
85
251
  tui_default as default
86
252
  };
@@ -19,10 +19,6 @@
19
19
  "balanceProviderUsage": {
20
20
  "type": "boolean"
21
21
  },
22
- "showStartupToast": {
23
- "description": "Show the startup activation toast when OpenCode starts. Defaults to true.",
24
- "type": "boolean"
25
- },
26
22
  "autoUpdate": {
27
23
  "description": "Disable automatic installation of plugin updates when false. Defaults to true.",
28
24
  "type": "boolean"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oh-my-opencode-slim",
3
- "version": "1.0.5",
3
+ "version": "1.0.6",
4
4
  "description": "Lightweight agent orchestration plugin for OpenCode - a slimmed-down fork of oh-my-opencode",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",