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 +3 -4
- package/dist/cli/index.js +0 -1
- package/dist/config/schema.d.ts +0 -1
- package/dist/hooks/auto-update-checker/types.d.ts +0 -1
- package/dist/index.js +156 -67
- package/dist/tui-state.d.ts +15 -0
- package/dist/tui.d.ts +3 -0
- package/dist/tui.js +175 -9
- package/oh-my-opencode-slim.schema.json +0 -4
- package/package.json +1 -1
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
|
|
42
|
-
|
|
43
|
-
|
|
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(),
|
package/dist/config/schema.d.ts
CHANGED
|
@@ -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<{
|
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(
|
|
6250
|
-
if (!
|
|
6251
|
-
return
|
|
6249
|
+
function remove_dot_segments(path15) {
|
|
6250
|
+
if (!path15)
|
|
6251
|
+
return path15;
|
|
6252
6252
|
var output = "";
|
|
6253
|
-
while (
|
|
6254
|
-
if (
|
|
6255
|
-
|
|
6253
|
+
while (path15.length > 0) {
|
|
6254
|
+
if (path15 === "." || path15 === "..") {
|
|
6255
|
+
path15 = "";
|
|
6256
6256
|
break;
|
|
6257
6257
|
}
|
|
6258
|
-
var twochars =
|
|
6259
|
-
var threechars =
|
|
6260
|
-
var fourchars =
|
|
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
|
-
|
|
6262
|
+
path15 = path15.substring(3);
|
|
6263
6263
|
} else if (twochars === "./") {
|
|
6264
|
-
|
|
6264
|
+
path15 = path15.substring(2);
|
|
6265
6265
|
} else if (threechars === "/./") {
|
|
6266
|
-
|
|
6267
|
-
} else if (twochars === "/." &&
|
|
6268
|
-
|
|
6269
|
-
} else if (fourchars === "/../" || threechars === "/.." &&
|
|
6270
|
-
|
|
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 =
|
|
6273
|
+
var segment = path15.match(/(\/?([^\/]*))/)[0];
|
|
6274
6274
|
output += segment;
|
|
6275
|
-
|
|
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
|
|
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 =
|
|
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
|
|
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 {
|
|
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
|
-
|
|
23401
|
+
function takePendingCall(callId, parentSessionId) {
|
|
23402
|
+
const resolvedCallId = callId ?? firstPendingCallForParent(parentSessionId);
|
|
23403
|
+
if (!resolvedCallId)
|
|
23408
23404
|
return;
|
|
23409
|
-
const pending = pendingCalls.get(
|
|
23410
|
-
pendingCalls.delete(
|
|
23411
|
-
const orderIndex = pendingCallOrder.indexOf(
|
|
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
|
-
|
|
23435
|
-
|
|
23436
|
-
|
|
23437
|
-
|
|
23438
|
-
|
|
23439
|
-
|
|
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
|
-
|
|
23455
|
-
|
|
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
|
|
29812
|
-
import
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
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 =
|
|
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 ||
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
72
|
-
|
|
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