@serkanalgur/opencode-nexus 1.4.0 → 1.5.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/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",
@@ -8845,7 +8846,7 @@ class DashboardModule {
8845
8846
  this.server = Bun.serve({
8846
8847
  port,
8847
8848
  hostname: host,
8848
- fetch(req, server) {
8849
+ async fetch(req, server) {
8849
8850
  const url = new URL(req.url);
8850
8851
  if (req.method === "OPTIONS") {
8851
8852
  return new Response(null, { headers: CORS_HEADERS });
@@ -8871,6 +8872,22 @@ 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 spaContent = await Bun.file(spaPath).text();
8878
+ if (spaContent.length > 100) {
8879
+ return new Response(spaContent, {
8880
+ headers: { "Content-Type": "text/html; charset=utf-8", ...CORS_HEADERS }
8881
+ });
8882
+ }
8883
+ return new Response(`SPA not found at ${spaPath} (content: ${spaContent.length} bytes)`, {
8884
+ headers: { "Content-Type": "text/plain", ...CORS_HEADERS }
8885
+ });
8886
+ } catch (e) {
8887
+ return new Response(`SPA error: ${e}`, {
8888
+ headers: { "Content-Type": "text/plain", ...CORS_HEADERS }
8889
+ });
8890
+ }
8874
8891
  return new Response("Nexus Dashboard API", {
8875
8892
  headers: {
8876
8893
  "Content-Type": "text/plain",
@@ -8973,9 +8990,9 @@ function detectCycles(nodes) {
8973
8990
 
8974
8991
  // src/message-store.ts
8975
8992
  import { appendFileSync, readFileSync as readFileSync2, existsSync, mkdirSync as mkdirSync2 } from "node:fs";
8976
- import { join as join2, dirname as dirname2 } from "node:path";
8993
+ import { join as join3, dirname as dirname2 } from "node:path";
8977
8994
  var DEFAULT_CONFIG2 = {
8978
- storagePath: join2(process.env.HOME || "~", ".local", "share", "opencode-nexus", "messages.jsonl"),
8995
+ storagePath: join3(process.env.HOME || "~", ".local", "share", "opencode-nexus", "messages.jsonl"),
8979
8996
  maxMessages: 1e4,
8980
8997
  rotationSize: 1000
8981
8998
  };
@@ -9063,10 +9080,10 @@ class MessageStore {
9063
9080
 
9064
9081
  // src/memory-store.ts
9065
9082
  import { Database } from "bun:sqlite";
9066
- import { join as join3, dirname as dirname3 } from "node:path";
9083
+ import { join as join4, dirname as dirname3 } from "node:path";
9067
9084
  import { mkdirSync as mkdirSync3 } from "node:fs";
9068
9085
  var DEFAULT_CONFIG3 = {
9069
- dbPath: join3(process.env.HOME || "~", ".local", "share", "opencode-nexus", "memory.db"),
9086
+ dbPath: join4(process.env.HOME || "~", ".local", "share", "opencode-nexus", "memory.db"),
9070
9087
  defaultTTL: 0,
9071
9088
  maxEntries: 1e4
9072
9089
  };
@@ -9880,7 +9897,7 @@ class CostForecaster {
9880
9897
  // src/worktree.ts
9881
9898
  import { execSync } from "node:child_process";
9882
9899
  import { existsSync as existsSync2 } from "node:fs";
9883
- import { join as join4 } from "node:path";
9900
+ import { join as join5 } from "node:path";
9884
9901
 
9885
9902
  class WorktreeManager {
9886
9903
  worktrees = new Map;
@@ -9888,11 +9905,11 @@ class WorktreeManager {
9888
9905
  repoRoot;
9889
9906
  constructor(repoRoot, baseDir) {
9890
9907
  this.repoRoot = repoRoot;
9891
- this.baseDir = baseDir || join4(repoRoot, ".worktrees", "nexus-agents");
9908
+ this.baseDir = baseDir || join5(repoRoot, ".worktrees", "nexus-agents");
9892
9909
  }
9893
9910
  create(agentId) {
9894
9911
  const branch = `nexus-agent-${agentId}`;
9895
- const path = join4(this.baseDir, agentId);
9912
+ const path = join5(this.baseDir, agentId);
9896
9913
  try {
9897
9914
  if (!existsSync2(this.baseDir)) {
9898
9915
  execSync(`mkdir -p "${this.baseDir}"`, { cwd: this.repoRoot });
@@ -11083,9 +11100,84 @@ function getTemplate(name) {
11083
11100
  }
11084
11101
 
11085
11102
  // src/index.ts
11103
+ import { writeFileSync as writeFileSync2, mkdirSync as mkdirSync4, existsSync as existsSync3 } from "node:fs";
11104
+ import { join as join6 } from "node:path";
11105
+ import { homedir as homedir2 } from "node:os";
11106
+ var NEXUS_AGENT_CONTENT = `---
11107
+ description: Nexus multi-agent orchestrator — manages parallel sub-agents with cost-aware routing, self-healing, and intelligent task decomposition.
11108
+ mode: primary
11109
+ ---
11110
+
11111
+ # Nexus Orchestrator Agent
11112
+
11113
+ You are the Nexus Orchestrator — an intelligent multi-agent coordinator. You manage parallel sub-agents with cost-aware model selection, self-healing on failures, and DAG-based task execution.
11114
+
11115
+ ## Core Capabilities
11116
+
11117
+ - **Spawn specialized sub-agents** for each task (architect, coder, reviewer, tester, explorer, documenter)
11118
+ - **Cost-aware routing** — automatically selects optimal models based on task complexity and budget
11119
+ - **Self-healing** — retries failed tasks, transfers context to new agents, escalates if needed
11120
+ - **Performance tracking** — learns which model/role combinations work best
11121
+ - **Execution history** — tracks all past executions for analysis
11122
+
11123
+ ## How to Use Nexus Tools
11124
+
11125
+ ### Spawning Agents
11126
+ Use the \`spawn\` tool to create specialized agents:
11127
+ \`\`\`
11128
+ nexus.spawn(role="coder", task="Implement JWT authentication middleware")
11129
+ nexus.spawn(role="reviewer", task="Review the auth implementation for security")
11130
+ \`\`\`
11131
+
11132
+ Available roles: architect, coder, reviewer, tester, explorer, documenter
11133
+
11134
+ ### Checking Status
11135
+ \`\`\`
11136
+ nexus.status(detailed=true) — Full orchestrator state
11137
+ nexus.agents(filter="working") — List active agents
11138
+ nexus.costs() — Cost report and budget status
11139
+ \`\`\`
11140
+
11141
+ ### Cost Management
11142
+ \`\`\`
11143
+ nexus.forecast(tasks='[...]') — Predict costs
11144
+ nexus.model.costs() — Show real model pricing
11145
+ nexus.preset(name="balanced") — Apply a cost preset
11146
+ \`\`\`
11147
+
11148
+ ### Performance & History
11149
+ \`\`\`
11150
+ nexus.performance.scores() — Model/role performance scores
11151
+ nexus.history.list(count=10) — Recent execution history
11152
+ \`\`\`
11153
+
11154
+ ## Task Decomposition Strategy
11155
+
11156
+ When given a development request:
11157
+ 1. **Analyze** — Break the request into discrete tasks
11158
+ 2. **Estimate** — Use nexus.forecast to predict costs
11159
+ 3. **Execute** — Spawn agents in parallel where possible
11160
+ 4. **Review** — Use nexus.spawn(role="reviewer") for each delivery
11161
+ 5. **Report** — Summarize what was done, costs, and outcomes
11162
+
11163
+ ## Quality Gates
11164
+
11165
+ Never skip these steps:
11166
+ - Every code change must be reviewed by nexus.spawn(role="reviewer")
11167
+ - Run nexus.security.scan() on new code
11168
+ - Verify with nexus.spawn(role="tester") before marking complete
11169
+ `;
11086
11170
  var src_default = define({
11087
11171
  id: "nexus",
11088
11172
  async setup(ctx) {
11173
+ try {
11174
+ const agentDir = join6(homedir2(), ".config", "opencode", "agents");
11175
+ const agentFile = join6(agentDir, "nexus-orchestrator.md");
11176
+ if (!existsSync3(agentFile)) {
11177
+ mkdirSync4(agentDir, { recursive: true });
11178
+ writeFileSync2(agentFile, NEXUS_AGENT_CONTENT, "utf-8");
11179
+ }
11180
+ } catch {}
11089
11181
  const orchestrator = new NexusOrchestrator;
11090
11182
  await orchestrator.initialize(ctx, () => {
11091
11183
  const state = orchestrator.getState();
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.5.0",
4
4
  "description": "Adaptive Multi-Agent Orchestration with Cost Intelligence for OpenCode V2",
5
5
  "keywords": [
6
6
  "opencode",