@serkanalgur/opencode-nexus 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/tui.js ADDED
@@ -0,0 +1,487 @@
1
+ // node_modules/@opencode/plugin/dist/tui/plugin.js
2
+ function define(plugin) {
3
+ return plugin;
4
+ }
5
+ // node_modules/solid-js/dist/server.js
6
+ var $PROXY = Symbol("solid-proxy");
7
+ var $TRACK = Symbol("solid-track");
8
+ var $DEVCOMP = Symbol("solid-dev-component");
9
+ var ERROR = Symbol("error");
10
+ function castError(err) {
11
+ if (err instanceof Error)
12
+ return err;
13
+ return new Error(typeof err === "string" ? err : "Unknown error", {
14
+ cause: err
15
+ });
16
+ }
17
+ function handleError(err, owner = Owner) {
18
+ const fns = owner && owner.context && owner.context[ERROR];
19
+ const error = castError(err);
20
+ if (!fns)
21
+ throw error;
22
+ try {
23
+ for (const f of fns)
24
+ f(error);
25
+ } catch (e) {
26
+ handleError(e, owner && owner.owner || null);
27
+ }
28
+ }
29
+ var Owner = null;
30
+ function createOwner() {
31
+ const o = {
32
+ owner: Owner,
33
+ context: Owner ? Owner.context : null,
34
+ owned: null,
35
+ cleanups: null
36
+ };
37
+ if (Owner) {
38
+ if (!Owner.owned)
39
+ Owner.owned = [o];
40
+ else
41
+ Owner.owned.push(o);
42
+ }
43
+ return o;
44
+ }
45
+ function createMemo(fn, value) {
46
+ Owner = createOwner();
47
+ let v;
48
+ try {
49
+ v = fn(value);
50
+ } catch (err) {
51
+ handleError(err);
52
+ } finally {
53
+ Owner = Owner.owner;
54
+ }
55
+ return () => v;
56
+ }
57
+ function createContext(defaultValue) {
58
+ const id = Symbol("context");
59
+ return {
60
+ id,
61
+ Provider: createProvider(id),
62
+ defaultValue
63
+ };
64
+ }
65
+ function children(fn) {
66
+ const memo = createMemo(() => resolveChildren(fn()));
67
+ memo.toArray = () => {
68
+ const c = memo();
69
+ return Array.isArray(c) ? c : c != null ? [c] : [];
70
+ };
71
+ return memo;
72
+ }
73
+ function resolveChildren(children) {
74
+ if (typeof children === "function" && !children.length)
75
+ return resolveChildren(children());
76
+ if (Array.isArray(children)) {
77
+ const results = [];
78
+ for (let i = 0;i < children.length; i++) {
79
+ const result = resolveChildren(children[i]);
80
+ if (Array.isArray(result)) {
81
+ if (result.length < 32768)
82
+ results.push.apply(results, result);
83
+ else
84
+ for (let j = 0;j < result.length; j++)
85
+ results.push(result[j]);
86
+ } else {
87
+ results.push(result);
88
+ }
89
+ }
90
+ return results;
91
+ }
92
+ return children;
93
+ }
94
+ function createProvider(id) {
95
+ return function provider(props) {
96
+ return createMemo(() => {
97
+ Owner.context = {
98
+ ...Owner.context,
99
+ [id]: props.value
100
+ };
101
+ return children(() => props.children);
102
+ });
103
+ };
104
+ }
105
+ var SuspenseContext = createContext();
106
+
107
+ // node_modules/@opencode/plugin/dist/tui/solid.js
108
+ var PluginContext = createContext();
109
+ // src/config.ts
110
+ var DEFAULT_CONFIG = {
111
+ models: {
112
+ architect: "anthropic/claude-sonnet-4-6",
113
+ coder: "anthropic/claude-sonnet-4-6",
114
+ reviewer: "openai/gpt-5-mini",
115
+ tester: "anthropic/claude-haiku-4-5",
116
+ explorer: "google/gemini-2.5-flash",
117
+ documenter: "anthropic/claude-haiku-4-5"
118
+ },
119
+ budget: {
120
+ maxTotalCost: 10,
121
+ maxCostPerTask: 1,
122
+ maxCostPerAgent: 2,
123
+ alertThreshold: 0.2
124
+ },
125
+ selfHealing: {
126
+ enabled: true,
127
+ maxRetries: 3,
128
+ contextTransfer: true
129
+ }
130
+ };
131
+
132
+ class NexusConfigManager {
133
+ projectConfig = null;
134
+ globalConfig = null;
135
+ storageConfig = null;
136
+ constructor() {
137
+ this.loadConfigs();
138
+ }
139
+ loadConfigs() {
140
+ this.projectConfig = null;
141
+ this.globalConfig = null;
142
+ }
143
+ getConfig() {
144
+ return {
145
+ models: {
146
+ ...DEFAULT_CONFIG.models,
147
+ ...this.globalConfig?.models,
148
+ ...this.projectConfig?.models,
149
+ ...this.storageConfig?.models
150
+ },
151
+ budget: {
152
+ ...DEFAULT_CONFIG.budget,
153
+ ...this.globalConfig?.budget,
154
+ ...this.projectConfig?.budget,
155
+ ...this.storageConfig?.budget
156
+ },
157
+ selfHealing: {
158
+ ...DEFAULT_CONFIG.selfHealing,
159
+ ...this.globalConfig?.selfHealing,
160
+ ...this.projectConfig?.selfHealing,
161
+ ...this.storageConfig?.selfHealing
162
+ }
163
+ };
164
+ }
165
+ getModelForRole(role) {
166
+ const config = this.getConfig();
167
+ return config.models[role] || config.models.coder || DEFAULT_CONFIG.models.coder;
168
+ }
169
+ updateStorageConfig(update) {
170
+ this.storageConfig = {
171
+ ...this.storageConfig,
172
+ ...update,
173
+ models: {
174
+ ...this.storageConfig?.models,
175
+ ...update.models
176
+ },
177
+ budget: {
178
+ maxTotalCost: update.budget?.maxTotalCost ?? this.storageConfig?.budget?.maxTotalCost ?? DEFAULT_CONFIG.budget.maxTotalCost,
179
+ maxCostPerTask: update.budget?.maxCostPerTask ?? this.storageConfig?.budget?.maxCostPerTask ?? DEFAULT_CONFIG.budget.maxCostPerTask,
180
+ maxCostPerAgent: update.budget?.maxCostPerAgent ?? this.storageConfig?.budget?.maxCostPerAgent ?? DEFAULT_CONFIG.budget.maxCostPerAgent,
181
+ alertThreshold: update.budget?.alertThreshold ?? this.storageConfig?.budget?.alertThreshold ?? DEFAULT_CONFIG.budget.alertThreshold
182
+ },
183
+ selfHealing: {
184
+ enabled: update.selfHealing?.enabled ?? this.storageConfig?.selfHealing?.enabled ?? DEFAULT_CONFIG.selfHealing.enabled,
185
+ maxRetries: update.selfHealing?.maxRetries ?? this.storageConfig?.selfHealing?.maxRetries ?? DEFAULT_CONFIG.selfHealing.maxRetries,
186
+ contextTransfer: update.selfHealing?.contextTransfer ?? this.storageConfig?.selfHealing?.contextTransfer ?? DEFAULT_CONFIG.selfHealing.contextTransfer
187
+ }
188
+ };
189
+ }
190
+ setModel(role, model) {
191
+ this.storageConfig = this.storageConfig || { ...DEFAULT_CONFIG };
192
+ this.storageConfig.models = this.storageConfig.models || {};
193
+ this.storageConfig.models[role] = model;
194
+ }
195
+ getRoles() {
196
+ return ["architect", "coder", "reviewer", "tester", "explorer", "documenter"];
197
+ }
198
+ getRoleDisplayName(role) {
199
+ const names = {
200
+ architect: "\uD83C\uDFD7️ Architect",
201
+ coder: "\uD83D\uDCBB Coder",
202
+ reviewer: "\uD83D\uDD0D Reviewer",
203
+ tester: "\uD83E\uDDEA Tester",
204
+ explorer: "\uD83D\uDD2C Explorer",
205
+ documenter: "\uD83D\uDCDD Documenter"
206
+ };
207
+ return names[role] || role;
208
+ }
209
+ exportConfig() {
210
+ return this.getConfig();
211
+ }
212
+ importConfig(config) {
213
+ this.storageConfig = { ...config };
214
+ }
215
+ resetToDefaults() {
216
+ this.storageConfig = null;
217
+ }
218
+ getSummary() {
219
+ const config = this.getConfig();
220
+ const lines = [
221
+ "\uD83D\uDCCA Nexus Configuration",
222
+ "═══════════════════════",
223
+ "",
224
+ "\uD83E\uDD16 Agent Models:"
225
+ ];
226
+ for (const role of this.getRoles()) {
227
+ const model = config.models[role] || "(not set)";
228
+ const displayName = this.getRoleDisplayName(role);
229
+ lines.push(` ${displayName}: ${model}`);
230
+ }
231
+ lines.push("");
232
+ lines.push("\uD83D\uDCB0 Budget:");
233
+ lines.push(` Max Total: $${config.budget.maxTotalCost}`);
234
+ lines.push(` Max Per Task: $${config.budget.maxCostPerTask}`);
235
+ lines.push(` Alert Threshold: ${config.budget.alertThreshold * 100}%`);
236
+ lines.push("");
237
+ lines.push("\uD83D\uDEE1️ Self-Healing:");
238
+ lines.push(` Enabled: ${config.selfHealing.enabled ? "✅" : "❌"}`);
239
+ lines.push(` Max Retries: ${config.selfHealing.maxRetries}`);
240
+ lines.push(` Context Transfer: ${config.selfHealing.contextTransfer ? "✅" : "❌"}`);
241
+ return lines.join(`
242
+ `);
243
+ }
244
+ }
245
+
246
+ // src/tui.tsx
247
+ var tui_default = define({
248
+ id: "nexus.cli",
249
+ setup(context) {
250
+ const configManager = new NexusConfigManager;
251
+ const handleModelSelect = async (role) => {
252
+ if (!configManager.getRoles().includes(role)) {
253
+ context.ui.toast.show({
254
+ title: "Nexus",
255
+ message: `Unknown role: ${role}. Valid: ${configManager.getRoles().join(", ")}`,
256
+ variant: "error"
257
+ });
258
+ return;
259
+ }
260
+ const location = context.location ?? context.data.location.default();
261
+ await context.data.location.model.sync(location);
262
+ const availableModels = context.data.location.model.list(location) ?? [];
263
+ const options = availableModels.map((m) => ({
264
+ title: m.name || m.id,
265
+ value: `${m.providerID}/${m.id}`,
266
+ description: m.providerID
267
+ }));
268
+ options.push({
269
+ title: "Use default",
270
+ value: "",
271
+ description: "Reset to default model"
272
+ });
273
+ const current = configManager.getModelForRole(role);
274
+ const selected = await context.ui.dialog.select({
275
+ title: `${configManager.getRoleDisplayName(role)} Model`,
276
+ current: current || "",
277
+ options
278
+ });
279
+ if (selected !== null && selected !== undefined) {
280
+ configManager.setModel(role, selected);
281
+ context.ui.toast.show({
282
+ title: "Nexus",
283
+ message: `${role} → ${selected || "default"}`,
284
+ variant: "success"
285
+ });
286
+ }
287
+ };
288
+ const handleConfigDialog = async () => {
289
+ const config = configManager.getConfig();
290
+ const budgetStr = await context.ui.dialog.prompt({
291
+ title: "Max Total Budget ($)",
292
+ placeholder: String(config.budget.maxTotalCost)
293
+ });
294
+ if (budgetStr !== null && budgetStr !== undefined) {
295
+ const budget = parseFloat(budgetStr);
296
+ if (!isNaN(budget) && budget > 0) {
297
+ configManager.updateStorageConfig({
298
+ budget: { ...config.budget, maxTotalCost: budget }
299
+ });
300
+ context.ui.toast.show({
301
+ title: "Nexus",
302
+ message: `Budget updated: $${budget}`,
303
+ variant: "success"
304
+ });
305
+ }
306
+ }
307
+ };
308
+ const handleFullConfig = async () => {
309
+ for (const role of configManager.getRoles()) {
310
+ await handleModelSelect(role);
311
+ }
312
+ await handleConfigDialog();
313
+ context.ui.toast.show({
314
+ title: "Nexus",
315
+ message: "Configuration complete!",
316
+ variant: "success"
317
+ });
318
+ };
319
+ context.keymap.layer(() => ({
320
+ mode: "global",
321
+ priority: 10,
322
+ commands: [
323
+ {
324
+ id: "nexus",
325
+ title: "Nexus Configuration",
326
+ group: "Nexus",
327
+ bind: "ctrl+n",
328
+ palette: true,
329
+ slash: { name: "nexus", aliases: [], arguments: true },
330
+ enabled: () => true,
331
+ suggested: true,
332
+ run: async (input) => {
333
+ if (input) {
334
+ const parts = input.split(" ");
335
+ const cmd = parts[0];
336
+ switch (cmd) {
337
+ case "config":
338
+ case "c":
339
+ await handleFullConfig();
340
+ break;
341
+ case "status":
342
+ case "s":
343
+ context.ui.toast.show({
344
+ title: "Nexus Status",
345
+ message: configManager.getSummary(),
346
+ variant: "info",
347
+ duration: 8000
348
+ });
349
+ break;
350
+ case "model":
351
+ case "m":
352
+ if (parts[1]) {
353
+ await handleModelSelect(parts[1]);
354
+ } else {
355
+ const role = await context.ui.dialog.select({
356
+ title: "Select Agent Role",
357
+ options: configManager.getRoles().map((r) => ({
358
+ title: configManager.getRoleDisplayName(r),
359
+ value: r,
360
+ description: `Current: ${configManager.getModelForRole(r)}`
361
+ }))
362
+ });
363
+ if (role) {
364
+ await handleModelSelect(role);
365
+ }
366
+ }
367
+ break;
368
+ case "reset":
369
+ const confirmed = await context.ui.dialog.confirm({
370
+ title: "Reset Configuration",
371
+ message: "Reset all Nexus settings to defaults?",
372
+ label: { confirm: "Reset", cancel: "Cancel" }
373
+ });
374
+ if (confirmed) {
375
+ configManager.resetToDefaults();
376
+ context.ui.toast.show({
377
+ title: "Nexus",
378
+ message: "Configuration reset to defaults",
379
+ variant: "success"
380
+ });
381
+ }
382
+ break;
383
+ default:
384
+ context.ui.toast.show({
385
+ title: "Nexus",
386
+ message: "Commands: config, status, model <role>, reset",
387
+ variant: "info"
388
+ });
389
+ }
390
+ } else {
391
+ await handleFullConfig();
392
+ }
393
+ }
394
+ },
395
+ {
396
+ id: "nexus.config",
397
+ title: "Nexus Configuration",
398
+ group: "Nexus",
399
+ palette: true,
400
+ slash: { name: "nexus-config", aliases: ["nc"], arguments: true },
401
+ enabled: () => true,
402
+ suggested: true,
403
+ run: async () => {
404
+ await handleFullConfig();
405
+ }
406
+ },
407
+ {
408
+ id: "nexus.model",
409
+ title: "Select Agent Model",
410
+ group: "Nexus",
411
+ palette: true,
412
+ slash: { name: "nexus-model", aliases: ["nm"], arguments: true },
413
+ enabled: () => true,
414
+ suggested: true,
415
+ run: async (input) => {
416
+ if (input) {
417
+ await handleModelSelect(input);
418
+ } else {
419
+ const role = await context.ui.dialog.select({
420
+ title: "Select Agent Role",
421
+ options: configManager.getRoles().map((r) => ({
422
+ title: configManager.getRoleDisplayName(r),
423
+ value: r,
424
+ description: `Current: ${configManager.getModelForRole(r)}`
425
+ }))
426
+ });
427
+ if (role) {
428
+ await handleModelSelect(role);
429
+ }
430
+ }
431
+ }
432
+ },
433
+ {
434
+ id: "nexus.status",
435
+ title: "Nexus Status",
436
+ group: "Nexus",
437
+ palette: true,
438
+ slash: { name: "nexus-status", aliases: ["ns"], arguments: true },
439
+ enabled: () => true,
440
+ suggested: true,
441
+ run: async () => {
442
+ context.ui.toast.show({
443
+ title: "Nexus Status",
444
+ message: configManager.getSummary(),
445
+ variant: "info",
446
+ duration: 8000
447
+ });
448
+ }
449
+ },
450
+ {
451
+ id: "nexus.reset",
452
+ title: "Reset Nexus Config",
453
+ group: "Nexus",
454
+ palette: true,
455
+ slash: { name: "nexus-reset", aliases: [], arguments: true },
456
+ enabled: () => true,
457
+ run: async () => {
458
+ const confirmed = await context.ui.dialog.confirm({
459
+ title: "Reset Configuration",
460
+ message: "Reset all Nexus settings to defaults?",
461
+ label: { confirm: "Reset", cancel: "Cancel" }
462
+ });
463
+ if (confirmed) {
464
+ configManager.resetToDefaults();
465
+ context.ui.toast.show({
466
+ title: "Nexus",
467
+ message: "Configuration reset to defaults",
468
+ variant: "success"
469
+ });
470
+ }
471
+ }
472
+ }
473
+ ],
474
+ bindings: ["nexus"]
475
+ }));
476
+ context.ui.toast.show({
477
+ title: "⚡ Nexus Loaded",
478
+ message: "Type /nexus or Ctrl+N to configure",
479
+ variant: "info",
480
+ duration: 3000
481
+ });
482
+ return () => {};
483
+ }
484
+ });
485
+ export {
486
+ tui_default as default
487
+ };
package/package.json ADDED
@@ -0,0 +1,71 @@
1
+ {
2
+ "name": "@serkanalgur/opencode-nexus",
3
+ "version": "0.1.0",
4
+ "description": "Adaptive Multi-Agent Orchestration with Cost Intelligence for OpenCode V2",
5
+ "keywords": [
6
+ "opencode",
7
+ "opencode-plugin",
8
+ "multi-agent",
9
+ "orchestration",
10
+ "ai",
11
+ "coding-agent",
12
+ "parallel-execution",
13
+ "cost-optimization"
14
+ ],
15
+ "homepage": "https://github.com/serkanalgur/opencode-nexus#readme",
16
+ "bugs": {
17
+ "url": "https://github.com/serkanalgur/opencode-nexus/issues"
18
+ },
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "git+https://github.com/serkanalgur/opencode-nexus.git"
22
+ },
23
+ "license": "MIT",
24
+ "author": "Serkan Algur",
25
+ "type": "module",
26
+ "exports": {
27
+ ".": "./dist/index.js",
28
+ "./tui": "./dist/tui.js"
29
+ },
30
+ "main": "./dist/index.js",
31
+ "types": "./dist/index.d.ts",
32
+ "directories": {
33
+ "test": "test"
34
+ },
35
+ "files": [
36
+ "dist",
37
+ "README.md",
38
+ "LICENSE"
39
+ ],
40
+ "scripts": {
41
+ "build": "bun run build:server && bun run build:tui",
42
+ "build:server": "bun build ./src/index.ts --outdir ./dist --target node",
43
+ "build:tui": "bun build ./src/tui.tsx --outdir ./dist --target node",
44
+ "dev": "bun run --watch src/index.ts",
45
+ "typecheck": "tsc --noEmit",
46
+ "test": "bun test",
47
+ "lint": "biome check src/",
48
+ "format": "biome format --write src/",
49
+ "prepublishOnly": "bun run build"
50
+ },
51
+ "dependencies": {
52
+ "@opencode/plugin": "latest"
53
+ },
54
+ "devDependencies": {
55
+ "@opentui/core": "^0.5.8",
56
+ "@opentui/solid": "^0.5.8",
57
+ "@types/bun": "latest",
58
+ "biome": "latest",
59
+ "bun-types": "^1.4.2",
60
+ "solid-js": "^1.9.0",
61
+ "typescript": "^5.5.0"
62
+ },
63
+ "peerDependencies": {
64
+ "@opentui/core": ">=0.5.8",
65
+ "@opentui/solid": ">=0.5.8",
66
+ "solid-js": ">=1.9.0"
67
+ },
68
+ "engines": {
69
+ "node": ">=22.0.0"
70
+ }
71
+ }