@serkanalgur/opencode-nexus 1.4.0 → 1.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -147,14 +147,14 @@ await ctx.session.prompt({ sessionID: session.id, text: 'You are a senior softwa
147
147
 
148
148
  Each agent role gets a specialized prompt:
149
149
 
150
- | Role | Focus |
151
- |------|-------|
152
- | **Architect** | System design, architecture patterns, high-level decisions |
153
- | **Coder** | Clean, efficient code following best practices |
154
- | **Reviewer** | Code review for correctness, security, performance |
155
- | **Tester** | Comprehensive tests, edge cases, quality assurance |
156
- | **Explorer** | Codebase navigation, architecture analysis |
157
- | **Documenter** | Clear technical documentation |
150
+ | Role | OpenCode Agent | Focus |
151
+ |------|----------------|-------|
152
+ | **Architect** | `architect` | System design, architecture patterns, high-level decisions |
153
+ | **Coder** | `build-orchestrator` | Clean, efficient code following best practices |
154
+ | **Reviewer** | `code-reviewer` | Code review for correctness, security, performance |
155
+ | **Tester** | `build-orchestrator` | Comprehensive tests, edge cases, quality assurance |
156
+ | **Explorer** | `explore` | Codebase navigation, architecture analysis |
157
+ | **Documenter** | `doc-writer` | Clear technical documentation |
158
158
 
159
159
  ### Cost-Aware Model Selection
160
160
 
@@ -278,6 +278,68 @@ NexusPlugin.register({
278
278
  })
279
279
  ```
280
280
 
281
+ ### Custom Agent Roles
282
+
283
+ Define your own agent roles with custom prompts:
284
+
285
+ ```jsonc
286
+ // .opencode/nexus.jsonc
287
+ {
288
+ "customRoles": [
289
+ {
290
+ "name": "security-auditor",
291
+ "displayName": "Security Auditor",
292
+ "emoji": "🔐",
293
+ "prompt": "You are a security auditor. Focus on OWASP Top 10, vulnerability scanning, and security best practices.",
294
+ "model": "anthropic/claude-sonnet-4-6"
295
+ }
296
+ ]
297
+ }
298
+ ```
299
+
300
+ Or register dynamically via tools: `nexus.roles.add(name="security-auditor", displayName="Security Auditor", prompt="...")`
301
+
302
+ ### Execution History
303
+
304
+ Track all task executions with costs, durations, and outcomes:
305
+
306
+ ```
307
+ nexus.history.list(count=10) — Recent executions
308
+ nexus.history.stats() — Success rate, avg cost, breakdown by role
309
+ ```
310
+
311
+ ### Cost Forecasting
312
+
313
+ Predict costs before executing tasks:
314
+
315
+ ```typescript
316
+ const forecast = orchestrator.forecaster.forecastAll([
317
+ { task: myTask, role: 'coder', model: 'claude-sonnet-4-6', complexity: score }
318
+ ], budgetRemaining)
319
+ // → { totalEstimatedCost: 0.0234, withinBudget: true }
320
+ ```
321
+
322
+ ### Agent Performance Scoring
323
+
324
+ Track which model/role combinations work best:
325
+
326
+ ```
327
+ nexus.performance.scores() — All model/role scores
328
+ nexus.performance.best(role="coder") — Best model for a role
329
+ ```
330
+
331
+ Score = 40% success rate + 30% speed + 30% cost efficiency.
332
+
333
+ ### Git Worktree Per Agent
334
+
335
+ Each agent works in its own isolated git worktree:
336
+
337
+ ```
338
+ nexus.worktree.enable() — Enable isolation
339
+ nexus.worktree.list() — List active worktrees
340
+ nexus.worktree.disable() — Clean up all
341
+ ```
342
+
281
343
  ---
282
344
 
283
345
  ## TUI Commands
@@ -313,6 +375,18 @@ Register these tools in your agent prompts:
313
375
  | `nexus.dashboard.stop` | Stop web dashboard | `{}` |
314
376
  | `nexus.preset` | Apply preset config | `{ name: string }` |
315
377
  | `nexus.template` | List/instantiate templates | `{ name?: string, baseDir?: string }` |
378
+ | `nexus.model.costs` | Show/set model pricing | `{ model?: string, setInput?: number, setOutput?: number }` |
379
+ | `nexus.security.scan` | Scan code for security issues | `{ content: string, filename?: string }` |
380
+ | `nexus.roles.list` | List custom agent roles | `{}` |
381
+ | `nexus.roles.add` | Add a custom role | `{ name, displayName, prompt, emoji?, model? }` |
382
+ | `nexus.history.list` | List execution history | `{ count?: number }` |
383
+ | `nexus.history.stats` | Execution statistics | `{}` |
384
+ | `nexus.forecast` | Predict costs before execution | `{ tasks: string }` |
385
+ | `nexus.performance.scores` | Model/role performance scores | `{}` |
386
+ | `nexus.performance.best` | Best model for a role | `{ role: string }` |
387
+ | `nexus.worktree.enable` | Enable git worktree isolation | `{ repoRoot?: string }` |
388
+ | `nexus.worktree.list` | List active worktrees | `{}` |
389
+ | `nexus.worktree.disable` | Disable and clean up | `{}` |
316
390
 
317
391
  ### Tool Examples
318
392
 
package/dist/index.js CHANGED
@@ -8817,6 +8817,7 @@ class StateBroadcaster {
8817
8817
  }
8818
8818
 
8819
8819
  // src/dashboard.ts
8820
+ import { join as join2 } from "node:path";
8820
8821
  var CORS_HEADERS = {
8821
8822
  "Access-Control-Allow-Origin": "*",
8822
8823
  "Access-Control-Allow-Methods": "GET, POST, OPTIONS",
@@ -8871,6 +8872,16 @@ class DashboardModule {
8871
8872
  if (url.pathname === "/api/health") {
8872
8873
  return jsonResponse({ ok: true, uptime: process.uptime() });
8873
8874
  }
8875
+ try {
8876
+ const spaPath = join2(process.cwd(), "dashboard", "index.html");
8877
+ const spaFile = Bun.file(spaPath);
8878
+ const exists = spaFile.size > 0;
8879
+ if (exists) {
8880
+ return new Response(spaFile, {
8881
+ headers: { "Content-Type": "text/html; charset=utf-8", ...CORS_HEADERS }
8882
+ });
8883
+ }
8884
+ } catch {}
8874
8885
  return new Response("Nexus Dashboard API", {
8875
8886
  headers: {
8876
8887
  "Content-Type": "text/plain",
@@ -8973,9 +8984,9 @@ function detectCycles(nodes) {
8973
8984
 
8974
8985
  // src/message-store.ts
8975
8986
  import { appendFileSync, readFileSync as readFileSync2, existsSync, mkdirSync as mkdirSync2 } from "node:fs";
8976
- import { join as join2, dirname as dirname2 } from "node:path";
8987
+ import { join as join3, dirname as dirname2 } from "node:path";
8977
8988
  var DEFAULT_CONFIG2 = {
8978
- storagePath: join2(process.env.HOME || "~", ".local", "share", "opencode-nexus", "messages.jsonl"),
8989
+ storagePath: join3(process.env.HOME || "~", ".local", "share", "opencode-nexus", "messages.jsonl"),
8979
8990
  maxMessages: 1e4,
8980
8991
  rotationSize: 1000
8981
8992
  };
@@ -9063,10 +9074,10 @@ class MessageStore {
9063
9074
 
9064
9075
  // src/memory-store.ts
9065
9076
  import { Database } from "bun:sqlite";
9066
- import { join as join3, dirname as dirname3 } from "node:path";
9077
+ import { join as join4, dirname as dirname3 } from "node:path";
9067
9078
  import { mkdirSync as mkdirSync3 } from "node:fs";
9068
9079
  var DEFAULT_CONFIG3 = {
9069
- dbPath: join3(process.env.HOME || "~", ".local", "share", "opencode-nexus", "memory.db"),
9080
+ dbPath: join4(process.env.HOME || "~", ".local", "share", "opencode-nexus", "memory.db"),
9070
9081
  defaultTTL: 0,
9071
9082
  maxEntries: 1e4
9072
9083
  };
@@ -9880,7 +9891,7 @@ class CostForecaster {
9880
9891
  // src/worktree.ts
9881
9892
  import { execSync } from "node:child_process";
9882
9893
  import { existsSync as existsSync2 } from "node:fs";
9883
- import { join as join4 } from "node:path";
9894
+ import { join as join5 } from "node:path";
9884
9895
 
9885
9896
  class WorktreeManager {
9886
9897
  worktrees = new Map;
@@ -9888,11 +9899,11 @@ class WorktreeManager {
9888
9899
  repoRoot;
9889
9900
  constructor(repoRoot, baseDir) {
9890
9901
  this.repoRoot = repoRoot;
9891
- this.baseDir = baseDir || join4(repoRoot, ".worktrees", "nexus-agents");
9902
+ this.baseDir = baseDir || join5(repoRoot, ".worktrees", "nexus-agents");
9892
9903
  }
9893
9904
  create(agentId) {
9894
9905
  const branch = `nexus-agent-${agentId}`;
9895
- const path = join4(this.baseDir, agentId);
9906
+ const path = join5(this.baseDir, agentId);
9896
9907
  try {
9897
9908
  if (!existsSync2(this.baseDir)) {
9898
9909
  execSync(`mkdir -p "${this.baseDir}"`, { cwd: this.repoRoot });
package/dist/tui.js CHANGED
@@ -34532,6 +34532,25 @@ var tui_default = define({
34532
34532
  case "d":
34533
34533
  await handleDashboard();
34534
34534
  break;
34535
+ case "web":
34536
+ case "w":
34537
+ try {
34538
+ const port = parts[1] ? parseInt(parts[1]) : 4747;
34539
+ context.ui.toast.show({
34540
+ title: "Nexus Dashboard",
34541
+ message: `Starting web dashboard on port ${port}...
34542
+ Open http://localhost:${port} in your browser`,
34543
+ variant: "success",
34544
+ duration: 5000
34545
+ });
34546
+ } catch (e) {
34547
+ context.ui.toast.show({
34548
+ title: "Nexus",
34549
+ message: `Failed to start dashboard: ${e.message}`,
34550
+ variant: "error"
34551
+ });
34552
+ }
34553
+ break;
34535
34554
  case "model":
34536
34555
  case "m":
34537
34556
  if (parts[1]) {
@@ -34568,7 +34587,7 @@ var tui_default = define({
34568
34587
  default:
34569
34588
  context.ui.toast.show({
34570
34589
  title: "Nexus",
34571
- message: "Commands: config, status, dashboard, model <role>, reset",
34590
+ message: "Commands: config, status, dashboard, web [port], model <role>, reset",
34572
34591
  variant: "info"
34573
34592
  });
34574
34593
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@serkanalgur/opencode-nexus",
3
- "version": "1.4.0",
3
+ "version": "1.4.1",
4
4
  "description": "Adaptive Multi-Agent Orchestration with Cost Intelligence for OpenCode V2",
5
5
  "keywords": [
6
6
  "opencode",