@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 +82 -8
- package/dist/index.js +100 -8
- package/dist/tui.js +20 -1
- package/package.json +1 -1
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
|
|
8993
|
+
import { join as join3, dirname as dirname2 } from "node:path";
|
|
8977
8994
|
var DEFAULT_CONFIG2 = {
|
|
8978
|
-
storagePath:
|
|
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
|
|
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:
|
|
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
|
|
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 ||
|
|
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 =
|
|
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
|
}
|