oh-my-opencode-slim 1.0.4 → 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 +8 -3
- package/dist/cli/config-io.d.ts +1 -0
- package/dist/cli/index.js +179 -10
- package/dist/cli/paths.d.ts +4 -0
- package/dist/cli/providers.d.ts +36 -2
- package/dist/cli/types.d.ts +3 -0
- 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 +8 -0
- package/dist/tui.js +252 -0
- package/oh-my-opencode-slim.schema.json +0 -4
- package/package.json +15 -2
package/README.md
CHANGED
|
@@ -37,9 +37,14 @@ Install and configure oh-my-opencode-slim: https://raw.githubusercontent.com/alv
|
|
|
37
37
|
bunx oh-my-opencode-slim@latest install
|
|
38
38
|
```
|
|
39
39
|
|
|
40
|
+
The installer also registers the companion TUI plugin in OpenCode's
|
|
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`.
|
|
44
|
+
|
|
40
45
|
### Getting Started
|
|
41
46
|
|
|
42
|
-
The installer generates
|
|
47
|
+
The installer generates both OpenAI and OpenCode Go presets, with OpenAI active by default. OpenAI uses `openai/gpt-5.5` for the higher-judgment agents and `openai/gpt-5.4-mini` for the faster scoped agents. To make OpenCode Go active during install, run `bunx oh-my-opencode-slim@latest install --preset=opencode-go`.
|
|
43
48
|
|
|
44
49
|
Then:
|
|
45
50
|
|
|
@@ -60,7 +65,7 @@ Then:
|
|
|
60
65
|
> [!TIP]
|
|
61
66
|
> Want to understand how automatic delegation works in practice? Review the **[Orchestrator prompt](https://github.com/alvinunreal/oh-my-opencode-slim/blob/master/src/agents/orchestrator.ts#L28)** — it contains the delegation rules, specialist routing logic, and the thresholds for when the main agent should hand work off to subagents.
|
|
62
67
|
|
|
63
|
-
The default generated configuration
|
|
68
|
+
The default generated configuration includes both `openai` and `opencode-go` presets. Abbreviated:
|
|
64
69
|
|
|
65
70
|
```jsonc
|
|
66
71
|
{
|
|
@@ -85,7 +90,7 @@ want to customize how many resumable child-agent sessions are remembered.
|
|
|
85
90
|
|
|
86
91
|
### For Alternative Providers
|
|
87
92
|
|
|
88
|
-
To use Kimi, GitHub Copilot, ZAI Coding Plan, or a mixed-provider setup, use **[Configuration](docs/configuration.md)** for the full reference. If you want a ready-made starting point, check the **[Author's Preset](docs/authors-preset.md)** and **[$30 Preset](docs/thirty-dollars-preset.md)** - the `$30` preset is the best cheap setup.
|
|
93
|
+
To use OpenCode Go, Kimi, GitHub Copilot, ZAI Coding Plan, or a mixed-provider setup, use **[Configuration](docs/configuration.md)** for the full reference. If you want a ready-made starting point, check the **[Author's Preset](docs/authors-preset.md)** and **[$30 Preset](docs/thirty-dollars-preset.md)** - the `$30` preset is the best cheap setup.
|
|
89
94
|
|
|
90
95
|
The configuration guide also covers custom subagents via `agents.<name>`, where
|
|
91
96
|
you can define both a normal `prompt` and an `orchestratorPrompt` block for
|
package/dist/cli/config-io.d.ts
CHANGED
|
@@ -16,6 +16,7 @@ export declare function parseConfig(path: string): {
|
|
|
16
16
|
*/
|
|
17
17
|
export declare function writeConfig(configPath: string, config: OpenCodeConfig): void;
|
|
18
18
|
export declare function addPluginToOpenCodeConfig(): Promise<ConfigMergeResult>;
|
|
19
|
+
export declare function addPluginToOpenCodeTuiConfig(): Promise<ConfigMergeResult>;
|
|
19
20
|
export declare function writeLiteConfig(installConfig: InstallConfig, targetPath?: string): ConfigMergeResult;
|
|
20
21
|
export declare function disableDefaultAgents(): ConfigMergeResult;
|
|
21
22
|
export declare function enableLspByDefault(): ConfigMergeResult;
|
package/dist/cli/index.js
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
// @bun
|
|
3
|
+
import { createRequire } from "node:module";
|
|
4
|
+
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
3
5
|
|
|
4
6
|
// src/cli/install.ts
|
|
5
7
|
import { existsSync as existsSync4 } from "node:fs";
|
|
8
|
+
import { createInterface } from "node:readline/promises";
|
|
6
9
|
|
|
7
10
|
// src/cli/config-io.ts
|
|
8
11
|
import {
|
|
@@ -27,6 +30,10 @@ function getCustomOpenCodeConfigDir() {
|
|
|
27
30
|
const configDir = process.env.OPENCODE_CONFIG_DIR?.trim();
|
|
28
31
|
return configDir || undefined;
|
|
29
32
|
}
|
|
33
|
+
function getCustomTuiConfigPath() {
|
|
34
|
+
const configPath = process.env.OPENCODE_TUI_CONFIG?.trim();
|
|
35
|
+
return configPath || undefined;
|
|
36
|
+
}
|
|
30
37
|
function getConfigDir() {
|
|
31
38
|
const customConfigDir = getCustomOpenCodeConfigDir();
|
|
32
39
|
if (customConfigDir) {
|
|
@@ -50,6 +57,15 @@ function getLiteConfig() {
|
|
|
50
57
|
function getLiteConfigJsonc() {
|
|
51
58
|
return join(getConfigDir(), "oh-my-opencode-slim.jsonc");
|
|
52
59
|
}
|
|
60
|
+
function getTuiConfig() {
|
|
61
|
+
const customConfigPath = getCustomTuiConfigPath();
|
|
62
|
+
if (customConfigPath)
|
|
63
|
+
return customConfigPath;
|
|
64
|
+
return join(getConfigDir(), "tui.json");
|
|
65
|
+
}
|
|
66
|
+
function getTuiConfigJsonc() {
|
|
67
|
+
return join(getConfigDir(), "tui.jsonc");
|
|
68
|
+
}
|
|
53
69
|
function getExistingLiteConfigPath() {
|
|
54
70
|
const jsonPath = getLiteConfig();
|
|
55
71
|
if (existsSync(jsonPath))
|
|
@@ -59,6 +75,18 @@ function getExistingLiteConfigPath() {
|
|
|
59
75
|
return jsoncPath;
|
|
60
76
|
return jsonPath;
|
|
61
77
|
}
|
|
78
|
+
function getExistingTuiConfigPath() {
|
|
79
|
+
const customConfigPath = getCustomTuiConfigPath();
|
|
80
|
+
if (customConfigPath)
|
|
81
|
+
return customConfigPath;
|
|
82
|
+
const jsonPath = join(getConfigDir(), "tui.json");
|
|
83
|
+
if (existsSync(jsonPath))
|
|
84
|
+
return jsonPath;
|
|
85
|
+
const jsoncPath = getTuiConfigJsonc();
|
|
86
|
+
if (existsSync(jsoncPath))
|
|
87
|
+
return jsoncPath;
|
|
88
|
+
return jsonPath;
|
|
89
|
+
}
|
|
62
90
|
function getExistingConfigPath() {
|
|
63
91
|
const jsonPath = getConfigJson();
|
|
64
92
|
if (existsSync(jsonPath))
|
|
@@ -74,6 +102,12 @@ function ensureConfigDir() {
|
|
|
74
102
|
mkdirSync(configDir, { recursive: true });
|
|
75
103
|
}
|
|
76
104
|
}
|
|
105
|
+
function ensureTuiConfigDir() {
|
|
106
|
+
const configDir = dirname(getTuiConfig());
|
|
107
|
+
if (!existsSync(configDir)) {
|
|
108
|
+
mkdirSync(configDir, { recursive: true });
|
|
109
|
+
}
|
|
110
|
+
}
|
|
77
111
|
function ensureOpenCodeConfigDir() {
|
|
78
112
|
const configDir = dirname(getConfigJson());
|
|
79
113
|
if (!existsSync(configDir)) {
|
|
@@ -304,7 +338,6 @@ var PluginConfigSchema = z2.object({
|
|
|
304
338
|
setDefaultAgent: z2.boolean().optional(),
|
|
305
339
|
scoringEngineVersion: z2.enum(["v1", "v2-shadow", "v2"]).optional(),
|
|
306
340
|
balanceProviderUsage: z2.boolean().optional(),
|
|
307
|
-
showStartupToast: z2.boolean().optional().describe("Show the startup activation toast when OpenCode starts. Defaults to true."),
|
|
308
341
|
autoUpdate: z2.boolean().optional().describe("Disable automatic installation of plugin updates when false. Defaults to true."),
|
|
309
342
|
manualPlan: ManualPlanSchema.optional(),
|
|
310
343
|
presets: z2.record(z2.string(), PresetSchema).optional(),
|
|
@@ -458,6 +491,7 @@ function installSkill(skill) {
|
|
|
458
491
|
|
|
459
492
|
// src/cli/providers.ts
|
|
460
493
|
var SCHEMA_URL = "https://unpkg.com/oh-my-opencode-slim@latest/oh-my-opencode-slim.schema.json";
|
|
494
|
+
var GENERATED_PRESETS = ["openai", "opencode-go"];
|
|
461
495
|
var MODEL_MAPPINGS = {
|
|
462
496
|
openai: {
|
|
463
497
|
orchestrator: { model: "openai/gpt-5.5" },
|
|
@@ -493,12 +527,31 @@ var MODEL_MAPPINGS = {
|
|
|
493
527
|
explorer: { model: "zai-coding-plan/glm-5", variant: "low" },
|
|
494
528
|
designer: { model: "zai-coding-plan/glm-5", variant: "medium" },
|
|
495
529
|
fixer: { model: "zai-coding-plan/glm-5", variant: "low" }
|
|
530
|
+
},
|
|
531
|
+
"opencode-go": {
|
|
532
|
+
orchestrator: { model: "opencode-go/glm-5.1" },
|
|
533
|
+
oracle: { model: "opencode-go/deepseek-v4-pro", variant: "max" },
|
|
534
|
+
council: { model: "opencode-go/deepseek-v4-pro", variant: "high" },
|
|
535
|
+
librarian: { model: "opencode-go/minimax-m2.7" },
|
|
536
|
+
explorer: { model: "opencode-go/minimax-m2.7" },
|
|
537
|
+
designer: { model: "opencode-go/kimi-k2.6", variant: "medium" },
|
|
538
|
+
fixer: { model: "opencode-go/deepseek-v4-flash", variant: "high" }
|
|
496
539
|
}
|
|
497
540
|
};
|
|
541
|
+
function isGeneratedPresetName(value) {
|
|
542
|
+
return GENERATED_PRESETS.includes(value);
|
|
543
|
+
}
|
|
544
|
+
function getGeneratedPresetNames() {
|
|
545
|
+
return [...GENERATED_PRESETS];
|
|
546
|
+
}
|
|
498
547
|
function generateLiteConfig(installConfig) {
|
|
548
|
+
const preset = installConfig.preset ?? "openai";
|
|
549
|
+
if (!isGeneratedPresetName(preset)) {
|
|
550
|
+
throw new Error(`Unsupported preset "${preset}". Available generated presets: ${getGeneratedPresetNames().join(", ")}`);
|
|
551
|
+
}
|
|
499
552
|
const config = {
|
|
500
553
|
$schema: SCHEMA_URL,
|
|
501
|
-
preset
|
|
554
|
+
preset,
|
|
502
555
|
presets: {}
|
|
503
556
|
};
|
|
504
557
|
const createAgentConfig = (agentName, modelInfo) => {
|
|
@@ -524,7 +577,10 @@ function generateLiteConfig(installConfig) {
|
|
|
524
577
|
createAgentConfig(agentName, modelInfo)
|
|
525
578
|
]));
|
|
526
579
|
};
|
|
527
|
-
|
|
580
|
+
const presets = config.presets;
|
|
581
|
+
for (const presetName of GENERATED_PRESETS) {
|
|
582
|
+
presets[presetName] = buildPreset(presetName);
|
|
583
|
+
}
|
|
528
584
|
if (installConfig.hasTmux) {
|
|
529
585
|
config.tmux = {
|
|
530
586
|
enabled: true,
|
|
@@ -546,6 +602,14 @@ function getPlugins(config) {
|
|
|
546
602
|
function getPluginEntries(config) {
|
|
547
603
|
return getPlugins(config).filter(isString);
|
|
548
604
|
}
|
|
605
|
+
function getPluginSpec(entry) {
|
|
606
|
+
if (isString(entry))
|
|
607
|
+
return entry;
|
|
608
|
+
if (!Array.isArray(entry))
|
|
609
|
+
return;
|
|
610
|
+
const spec = entry[0];
|
|
611
|
+
return isString(spec) ? spec : undefined;
|
|
612
|
+
}
|
|
549
613
|
function normalizePathForMatch(path) {
|
|
550
614
|
return path.replaceAll("\\", "/");
|
|
551
615
|
}
|
|
@@ -590,6 +654,10 @@ function isLocalPackageRootEntry(entry) {
|
|
|
590
654
|
function isPluginEntry(entry) {
|
|
591
655
|
return entry === PACKAGE_NAME || entry.startsWith(`${PACKAGE_NAME}@`) || entry.startsWith("file://") && entry.includes(PACKAGE_NAME) || isLocalPackageRootEntry(entry);
|
|
592
656
|
}
|
|
657
|
+
function isMatchingPluginEntry(entry) {
|
|
658
|
+
const spec = getPluginSpec(entry);
|
|
659
|
+
return spec ? isPluginEntry(spec) : false;
|
|
660
|
+
}
|
|
593
661
|
function getPluginEntry() {
|
|
594
662
|
const cliEntryPath = process.argv[1];
|
|
595
663
|
if (!cliEntryPath) {
|
|
@@ -672,7 +740,7 @@ async function addPluginToOpenCodeConfig() {
|
|
|
672
740
|
const config = parsedConfig ?? {};
|
|
673
741
|
const plugins = getPlugins(config);
|
|
674
742
|
const pluginEntry = getPluginEntry();
|
|
675
|
-
const filteredPlugins = plugins.filter((plugin) => !
|
|
743
|
+
const filteredPlugins = plugins.filter((plugin) => !isMatchingPluginEntry(plugin));
|
|
676
744
|
filteredPlugins.push(pluginEntry);
|
|
677
745
|
config.plugin = filteredPlugins;
|
|
678
746
|
writeConfig(configPath, config);
|
|
@@ -685,6 +753,42 @@ async function addPluginToOpenCodeConfig() {
|
|
|
685
753
|
};
|
|
686
754
|
}
|
|
687
755
|
}
|
|
756
|
+
async function addPluginToOpenCodeTuiConfig() {
|
|
757
|
+
const configPath = getExistingTuiConfigPath();
|
|
758
|
+
try {
|
|
759
|
+
ensureTuiConfigDir();
|
|
760
|
+
} catch (err) {
|
|
761
|
+
return {
|
|
762
|
+
success: false,
|
|
763
|
+
configPath,
|
|
764
|
+
error: `Failed to create config directory: ${err}`
|
|
765
|
+
};
|
|
766
|
+
}
|
|
767
|
+
try {
|
|
768
|
+
const { config: parsedConfig, error } = parseConfig(configPath);
|
|
769
|
+
if (error) {
|
|
770
|
+
return {
|
|
771
|
+
success: false,
|
|
772
|
+
configPath,
|
|
773
|
+
error: `Failed to parse TUI config: ${error}`
|
|
774
|
+
};
|
|
775
|
+
}
|
|
776
|
+
const config = parsedConfig ?? {};
|
|
777
|
+
const plugins = getPlugins(config);
|
|
778
|
+
const pluginEntry = getPluginEntry();
|
|
779
|
+
const filteredPlugins = plugins.filter((plugin) => !isMatchingPluginEntry(plugin));
|
|
780
|
+
filteredPlugins.push(pluginEntry);
|
|
781
|
+
config.plugin = filteredPlugins;
|
|
782
|
+
writeConfig(configPath, config);
|
|
783
|
+
return { success: true, configPath };
|
|
784
|
+
} catch (err) {
|
|
785
|
+
return {
|
|
786
|
+
success: false,
|
|
787
|
+
configPath,
|
|
788
|
+
error: `Failed to update opencode TUI config: ${err}`
|
|
789
|
+
};
|
|
790
|
+
}
|
|
791
|
+
}
|
|
688
792
|
function writeLiteConfig(installConfig, targetPath) {
|
|
689
793
|
const configPath = targetPath ?? getLiteConfig();
|
|
690
794
|
try {
|
|
@@ -1005,8 +1109,10 @@ var SYMBOLS = {
|
|
|
1005
1109
|
bullet: `${DIM}-${RESET}`,
|
|
1006
1110
|
info: `${BLUE}[i]${RESET}`,
|
|
1007
1111
|
warn: `${YELLOW}[!]${RESET}`,
|
|
1008
|
-
star: `${YELLOW}
|
|
1112
|
+
star: `${YELLOW}★${RESET}`
|
|
1009
1113
|
};
|
|
1114
|
+
var GITHUB_REPO = "alvinunreal/oh-my-opencode-slim";
|
|
1115
|
+
var GITHUB_URL = `https://github.com/${GITHUB_REPO}`;
|
|
1010
1116
|
function printHeader(isUpdate) {
|
|
1011
1117
|
console.log();
|
|
1012
1118
|
console.log(`${BOLD}oh-my-opencode-slim ${isUpdate ? "Update" : "Install"}${RESET}`);
|
|
@@ -1025,6 +1131,34 @@ function printError(message) {
|
|
|
1025
1131
|
function printInfo(message) {
|
|
1026
1132
|
console.log(`${SYMBOLS.info} ${message}`);
|
|
1027
1133
|
}
|
|
1134
|
+
async function confirm(message, defaultYes = true) {
|
|
1135
|
+
const suffix = defaultYes ? " (Y/n) " : " (y/N) ";
|
|
1136
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
1137
|
+
try {
|
|
1138
|
+
const answer = (await rl.question(`${message}${suffix}`)).trim().toLowerCase();
|
|
1139
|
+
if (!answer)
|
|
1140
|
+
return defaultYes;
|
|
1141
|
+
return answer === "y" || answer === "yes";
|
|
1142
|
+
} finally {
|
|
1143
|
+
rl.close();
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
async function askToStarRepo(config) {
|
|
1147
|
+
if (!config.promptForStar || config.dryRun || !process.stdin.isTTY)
|
|
1148
|
+
return;
|
|
1149
|
+
console.log();
|
|
1150
|
+
const shouldStar = await confirm(`${SYMBOLS.star} Star the repo on GitHub?`, true);
|
|
1151
|
+
if (!shouldStar)
|
|
1152
|
+
return;
|
|
1153
|
+
try {
|
|
1154
|
+
const { execFileSync } = await import("node:child_process");
|
|
1155
|
+
execFileSync("gh", ["api", "--silent", "--method", "PUT", `/user/starred/${GITHUB_REPO}`], { stdio: "ignore", timeout: 1e4 });
|
|
1156
|
+
printSuccess("Thanks for starring! ★");
|
|
1157
|
+
} catch {
|
|
1158
|
+
printInfo(`Couldn't star automatically. You can star manually:
|
|
1159
|
+
${BLUE}${GITHUB_URL}${RESET}`);
|
|
1160
|
+
}
|
|
1161
|
+
}
|
|
1028
1162
|
async function checkOpenCodeInstalled() {
|
|
1029
1163
|
const installed = await isOpenCodeInstalled();
|
|
1030
1164
|
if (!installed) {
|
|
@@ -1056,7 +1190,7 @@ async function runInstall(config) {
|
|
|
1056
1190
|
const detected = detectCurrentConfig();
|
|
1057
1191
|
const isUpdate = detected.isInstalled;
|
|
1058
1192
|
printHeader(isUpdate);
|
|
1059
|
-
let totalSteps =
|
|
1193
|
+
let totalSteps = 6;
|
|
1060
1194
|
if (config.installSkills)
|
|
1061
1195
|
totalSteps += 1;
|
|
1062
1196
|
if (config.installCustomSkills)
|
|
@@ -1078,6 +1212,17 @@ async function runInstall(config) {
|
|
|
1078
1212
|
if (!handleStepResult(pluginResult, "Plugin added"))
|
|
1079
1213
|
return 1;
|
|
1080
1214
|
}
|
|
1215
|
+
printStep(step++, totalSteps, "Adding TUI version badge...");
|
|
1216
|
+
if (config.dryRun) {
|
|
1217
|
+
printInfo("Dry run mode - skipping TUI plugin installation");
|
|
1218
|
+
} else {
|
|
1219
|
+
const tuiResult = await addPluginToOpenCodeTuiConfig();
|
|
1220
|
+
if (!tuiResult.success) {
|
|
1221
|
+
printInfo(`Skipped TUI badge: ${tuiResult.error}`);
|
|
1222
|
+
} else {
|
|
1223
|
+
handleStepResult(tuiResult, "TUI badge added");
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1081
1226
|
printStep(step++, totalSteps, "Disabling OpenCode default agents...");
|
|
1082
1227
|
if (config.dryRun) {
|
|
1083
1228
|
printInfo("Dry run mode - skipping agent disabling");
|
|
@@ -1105,7 +1250,7 @@ ${JSON.stringify(liteConfig, null, 2)}
|
|
|
1105
1250
|
const configPath2 = getExistingLiteConfigPath();
|
|
1106
1251
|
const configExists = existsSync4(configPath2);
|
|
1107
1252
|
if (configExists && !config.reset) {
|
|
1108
|
-
printInfo(`Configuration already exists at ${configPath2}.
|
|
1253
|
+
printInfo(`Configuration already exists at ${configPath2}. Use --reset to overwrite.`);
|
|
1109
1254
|
} else {
|
|
1110
1255
|
const liteResult = writeLiteConfig(config, configExists ? configPath2 : undefined);
|
|
1111
1256
|
if (!handleStepResult(liteResult, configExists ? "Config reset" : "Config written"))
|
|
@@ -1176,13 +1321,14 @@ ${JSON.stringify(liteConfig, null, 2)}
|
|
|
1176
1321
|
console.log(" 5. Verify the agents are responding:");
|
|
1177
1322
|
console.log(` ${BLUE}> ping all agents${RESET}`);
|
|
1178
1323
|
console.log();
|
|
1179
|
-
const modelsInfo = "
|
|
1324
|
+
const modelsInfo = config.preset && config.preset !== "openai" ? `Generated OpenAI and OpenCode Go presets; ${config.preset} is active.` : "Generated OpenAI and OpenCode Go presets; OpenAI is active by default.";
|
|
1180
1325
|
console.log(`${modelsInfo}`);
|
|
1181
1326
|
const altProviders = "For the full configuration reference, see:";
|
|
1182
1327
|
console.log(altProviders);
|
|
1183
|
-
const docsUrl = "https://github.com/alvinunreal/oh-my-opencode-slim/
|
|
1328
|
+
const docsUrl = "https://github.com/alvinunreal/oh-my-opencode-slim/blob/master/docs/configuration.md";
|
|
1184
1329
|
console.log(` ${BLUE}${docsUrl}${RESET}`);
|
|
1185
1330
|
console.log();
|
|
1331
|
+
await askToStarRepo(config);
|
|
1186
1332
|
return 0;
|
|
1187
1333
|
}
|
|
1188
1334
|
async function install(args) {
|
|
@@ -1190,12 +1336,23 @@ async function install(args) {
|
|
|
1190
1336
|
hasTmux: false,
|
|
1191
1337
|
installSkills: args.skills === "yes",
|
|
1192
1338
|
installCustomSkills: args.skills === "yes",
|
|
1339
|
+
preset: args.preset,
|
|
1340
|
+
promptForStar: args.tui,
|
|
1193
1341
|
dryRun: args.dryRun,
|
|
1194
1342
|
reset: args.reset ?? false
|
|
1195
1343
|
};
|
|
1196
1344
|
return runInstall(config);
|
|
1197
1345
|
}
|
|
1198
1346
|
|
|
1347
|
+
// src/cli/providers.ts
|
|
1348
|
+
var GENERATED_PRESETS2 = ["openai", "opencode-go"];
|
|
1349
|
+
function isGeneratedPresetName2(value) {
|
|
1350
|
+
return GENERATED_PRESETS2.includes(value);
|
|
1351
|
+
}
|
|
1352
|
+
function getGeneratedPresetNames2() {
|
|
1353
|
+
return [...GENERATED_PRESETS2];
|
|
1354
|
+
}
|
|
1355
|
+
|
|
1199
1356
|
// src/cli/index.ts
|
|
1200
1357
|
function parseArgs(args) {
|
|
1201
1358
|
const result = {
|
|
@@ -1207,6 +1364,13 @@ function parseArgs(args) {
|
|
|
1207
1364
|
result.tui = false;
|
|
1208
1365
|
} else if (arg.startsWith("--skills=")) {
|
|
1209
1366
|
result.skills = arg.split("=")[1];
|
|
1367
|
+
} else if (arg.startsWith("--preset=")) {
|
|
1368
|
+
const preset = arg.split("=")[1];
|
|
1369
|
+
if (!isGeneratedPresetName2(preset)) {
|
|
1370
|
+
console.error(`Unsupported preset: ${preset}. Available presets: ${getGeneratedPresetNames2().join(", ")}`);
|
|
1371
|
+
process.exit(1);
|
|
1372
|
+
}
|
|
1373
|
+
result.preset = preset;
|
|
1210
1374
|
} else if (arg === "--dry-run") {
|
|
1211
1375
|
result.dryRun = true;
|
|
1212
1376
|
} else if (arg === "--reset") {
|
|
@@ -1226,17 +1390,22 @@ Usage: bunx oh-my-opencode-slim install [OPTIONS]
|
|
|
1226
1390
|
|
|
1227
1391
|
Options:
|
|
1228
1392
|
--skills=yes|no Install recommended and bundled skills (default: yes)
|
|
1393
|
+
--preset=<name> Active generated config preset (default: openai)
|
|
1229
1394
|
--no-tui Non-interactive mode
|
|
1230
1395
|
--dry-run Simulate install without writing files
|
|
1231
1396
|
--reset Force overwrite of existing configuration
|
|
1232
1397
|
-h, --help Show this help message
|
|
1233
1398
|
|
|
1234
|
-
|
|
1399
|
+
Available presets: ${getGeneratedPresetNames2().join(", ")}
|
|
1400
|
+
|
|
1401
|
+
The installer generates OpenAI and OpenCode Go presets by default.
|
|
1402
|
+
OpenAI is active unless --preset selects another generated preset.
|
|
1235
1403
|
For the full config reference, see docs/configuration.md.
|
|
1236
1404
|
|
|
1237
1405
|
Examples:
|
|
1238
1406
|
bunx oh-my-opencode-slim install
|
|
1239
1407
|
bunx oh-my-opencode-slim install --no-tui --skills=yes
|
|
1408
|
+
bunx oh-my-opencode-slim install --preset=opencode-go
|
|
1240
1409
|
bunx oh-my-opencode-slim install --reset
|
|
1241
1410
|
`);
|
|
1242
1411
|
}
|
package/dist/cli/paths.d.ts
CHANGED
|
@@ -22,9 +22,13 @@ export declare function getConfigJson(): string;
|
|
|
22
22
|
export declare function getConfigJsonc(): string;
|
|
23
23
|
export declare function getLiteConfig(): string;
|
|
24
24
|
export declare function getLiteConfigJsonc(): string;
|
|
25
|
+
export declare function getTuiConfig(): string;
|
|
26
|
+
export declare function getTuiConfigJsonc(): string;
|
|
25
27
|
export declare function getExistingLiteConfigPath(): string;
|
|
28
|
+
export declare function getExistingTuiConfigPath(): string;
|
|
26
29
|
export declare function getExistingConfigPath(): string;
|
|
27
30
|
export declare function ensureConfigDir(): void;
|
|
31
|
+
export declare function ensureTuiConfigDir(): void;
|
|
28
32
|
/**
|
|
29
33
|
* Ensure the directory for OpenCode's main config file exists.
|
|
30
34
|
*/
|
package/dist/cli/providers.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type { InstallConfig } from
|
|
1
|
+
import type { InstallConfig } from './types';
|
|
2
|
+
export declare const GENERATED_PRESETS: readonly ["openai", "opencode-go"];
|
|
2
3
|
export declare const MODEL_MAPPINGS: {
|
|
3
4
|
readonly openai: {
|
|
4
5
|
readonly orchestrator: {
|
|
@@ -75,7 +76,7 @@ export declare const MODEL_MAPPINGS: {
|
|
|
75
76
|
readonly variant: "low";
|
|
76
77
|
};
|
|
77
78
|
};
|
|
78
|
-
readonly
|
|
79
|
+
readonly 'zai-plan': {
|
|
79
80
|
readonly orchestrator: {
|
|
80
81
|
readonly model: "zai-coding-plan/glm-5";
|
|
81
82
|
};
|
|
@@ -100,5 +101,38 @@ export declare const MODEL_MAPPINGS: {
|
|
|
100
101
|
readonly variant: "low";
|
|
101
102
|
};
|
|
102
103
|
};
|
|
104
|
+
readonly 'opencode-go': {
|
|
105
|
+
readonly orchestrator: {
|
|
106
|
+
readonly model: "opencode-go/glm-5.1";
|
|
107
|
+
};
|
|
108
|
+
readonly oracle: {
|
|
109
|
+
readonly model: "opencode-go/deepseek-v4-pro";
|
|
110
|
+
readonly variant: "max";
|
|
111
|
+
};
|
|
112
|
+
readonly council: {
|
|
113
|
+
readonly model: "opencode-go/deepseek-v4-pro";
|
|
114
|
+
readonly variant: "high";
|
|
115
|
+
};
|
|
116
|
+
readonly librarian: {
|
|
117
|
+
readonly model: "opencode-go/minimax-m2.7";
|
|
118
|
+
};
|
|
119
|
+
readonly explorer: {
|
|
120
|
+
readonly model: "opencode-go/minimax-m2.7";
|
|
121
|
+
};
|
|
122
|
+
readonly designer: {
|
|
123
|
+
readonly model: "opencode-go/kimi-k2.6";
|
|
124
|
+
readonly variant: "medium";
|
|
125
|
+
};
|
|
126
|
+
readonly fixer: {
|
|
127
|
+
readonly model: "opencode-go/deepseek-v4-flash";
|
|
128
|
+
readonly variant: "high";
|
|
129
|
+
};
|
|
130
|
+
};
|
|
103
131
|
};
|
|
132
|
+
export type PresetName = keyof typeof MODEL_MAPPINGS;
|
|
133
|
+
export type GeneratedPresetName = (typeof GENERATED_PRESETS)[number];
|
|
134
|
+
export declare function isPresetName(value: string): value is PresetName;
|
|
135
|
+
export declare function getPresetNames(): PresetName[];
|
|
136
|
+
export declare function isGeneratedPresetName(value: string): value is GeneratedPresetName;
|
|
137
|
+
export declare function getGeneratedPresetNames(): GeneratedPresetName[];
|
|
104
138
|
export declare function generateLiteConfig(installConfig: InstallConfig): Record<string, unknown>;
|
package/dist/cli/types.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ export type BooleanArg = 'yes' | 'no';
|
|
|
2
2
|
export interface InstallArgs {
|
|
3
3
|
tui: boolean;
|
|
4
4
|
skills?: BooleanArg;
|
|
5
|
+
preset?: string;
|
|
5
6
|
dryRun?: boolean;
|
|
6
7
|
reset?: boolean;
|
|
7
8
|
}
|
|
@@ -15,6 +16,8 @@ export interface InstallConfig {
|
|
|
15
16
|
hasTmux: boolean;
|
|
16
17
|
installSkills: boolean;
|
|
17
18
|
installCustomSkills: boolean;
|
|
19
|
+
preset?: string;
|
|
20
|
+
promptForStar?: boolean;
|
|
18
21
|
dryRun?: boolean;
|
|
19
22
|
reset: boolean;
|
|
20
23
|
}
|
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
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
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[];
|
|
5
|
+
declare const plugin: TuiPluginModule & {
|
|
6
|
+
id: string;
|
|
7
|
+
};
|
|
8
|
+
export default plugin;
|
package/dist/tui.js
ADDED
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
function __accessProp(key) {
|
|
8
|
+
return this[key];
|
|
9
|
+
}
|
|
10
|
+
var __toESMCache_node;
|
|
11
|
+
var __toESMCache_esm;
|
|
12
|
+
var __toESM = (mod, isNodeMode, target) => {
|
|
13
|
+
var canCache = mod != null && typeof mod === "object";
|
|
14
|
+
if (canCache) {
|
|
15
|
+
var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
|
|
16
|
+
var cached = cache.get(mod);
|
|
17
|
+
if (cached)
|
|
18
|
+
return cached;
|
|
19
|
+
}
|
|
20
|
+
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
21
|
+
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
22
|
+
for (let key of __getOwnPropNames(mod))
|
|
23
|
+
if (!__hasOwnProp.call(to, key))
|
|
24
|
+
__defProp(to, key, {
|
|
25
|
+
get: __accessProp.bind(mod, key),
|
|
26
|
+
enumerable: true
|
|
27
|
+
});
|
|
28
|
+
if (canCache)
|
|
29
|
+
cache.set(mod, to);
|
|
30
|
+
return to;
|
|
31
|
+
};
|
|
32
|
+
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
33
|
+
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
34
|
+
|
|
35
|
+
// src/tui.ts
|
|
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
|
|
147
|
+
var PLUGIN_NAME = "oh-my-opencode-slim";
|
|
148
|
+
var FALLBACK_SIDEBAR_AGENTS = SUBAGENT_NAMES.filter((agent) => agent !== "councillor" && agent !== "council" && !DEFAULT_DISABLED_AGENTS.includes(agent));
|
|
149
|
+
var BORDER = { type: "single" };
|
|
150
|
+
async function readPackageVersion() {
|
|
151
|
+
try {
|
|
152
|
+
const packageJson = await Bun.file(new URL("../package.json", import.meta.url)).json();
|
|
153
|
+
return typeof packageJson.version === "string" ? packageJson.version : undefined;
|
|
154
|
+
} catch {
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
function element(tag, props, children = []) {
|
|
159
|
+
const node = createElement(tag);
|
|
160
|
+
for (const [key, value] of Object.entries(props)) {
|
|
161
|
+
if (value !== undefined)
|
|
162
|
+
setProp(node, key, value);
|
|
163
|
+
}
|
|
164
|
+
for (const child of children) {
|
|
165
|
+
if (child === null || child === undefined || child === false)
|
|
166
|
+
continue;
|
|
167
|
+
insert(node, child);
|
|
168
|
+
}
|
|
169
|
+
return node;
|
|
170
|
+
}
|
|
171
|
+
function text(props, children) {
|
|
172
|
+
return element("text", props, children);
|
|
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
|
+
}
|
|
223
|
+
var plugin = {
|
|
224
|
+
id: `${PLUGIN_NAME}:tui`,
|
|
225
|
+
tui: async (api, _options, meta) => {
|
|
226
|
+
const version = meta.version ?? await readPackageVersion() ?? "dev";
|
|
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
|
+
});
|
|
237
|
+
api.slots.register({
|
|
238
|
+
order: 900,
|
|
239
|
+
slots: {
|
|
240
|
+
sidebar_content() {
|
|
241
|
+
return renderSidebar(snapshot, version, api.theme.current);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
};
|
|
247
|
+
var tui_default = plugin;
|
|
248
|
+
export {
|
|
249
|
+
getSidebarAgentNames,
|
|
250
|
+
formatSidebarModelName,
|
|
251
|
+
tui_default as default
|
|
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,9 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "oh-my-opencode-slim",
|
|
3
|
-
"version": "1.0.
|
|
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",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"import": "./dist/index.js",
|
|
10
|
+
"types": "./dist/index.d.ts"
|
|
11
|
+
},
|
|
12
|
+
"./tui": {
|
|
13
|
+
"import": "./dist/tui.js",
|
|
14
|
+
"types": "./dist/tui.d.ts"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
7
17
|
"bin": {
|
|
8
18
|
"oh-my-opencode-slim": "./dist/cli/index.js"
|
|
9
19
|
},
|
|
@@ -36,7 +46,7 @@
|
|
|
36
46
|
"LICENSE"
|
|
37
47
|
],
|
|
38
48
|
"scripts": {
|
|
39
|
-
"build:plugin": "bun build src/index.ts --outdir dist --target node --format esm --external @ast-grep/napi --external @opencode-ai/plugin --external @opencode-ai/sdk --external jsdom --external zod",
|
|
49
|
+
"build:plugin": "bun build src/index.ts src/tui.ts --outdir dist --target node --format esm --external @ast-grep/napi --external @opencode-ai/plugin --external @opencode-ai/sdk --external @opentui/core --external @opentui/solid --external jsdom --external zod",
|
|
40
50
|
"build:cli": "bun build src/cli/index.ts --outdir dist/cli --target node --format esm --external @ast-grep/napi --external @opencode-ai/plugin --external @opencode-ai/sdk --external jsdom --external zod",
|
|
41
51
|
"build": "bun run build:plugin && bun run build:cli && tsc --emitDeclarationOnly && bun run generate-schema",
|
|
42
52
|
"prepare": "bun run build",
|
|
@@ -68,6 +78,9 @@
|
|
|
68
78
|
"lru-cache": "^11.3.3",
|
|
69
79
|
"turndown": "^7.2.4"
|
|
70
80
|
},
|
|
81
|
+
"optionalDependencies": {
|
|
82
|
+
"@opentui/solid": "^0.1.97"
|
|
83
|
+
},
|
|
71
84
|
"devDependencies": {
|
|
72
85
|
"@biomejs/biome": "2.4.11",
|
|
73
86
|
"@types/jsdom": "^21.1.7",
|