From c0b7272194949c30646ec1bfe1409af5401ec10c Mon Sep 17 00:00:00 2001 From: Rahul Tyagi Date: Sat, 4 Jul 2026 18:33:10 +0530 Subject: [PATCH 01/16] Add opencode port design spec --- .../2026-07-04-nexum-opencode-port-design.md | 264 ++++++++++++++++++ 1 file changed, 264 insertions(+) create mode 100644 docs/superpowers/specs/2026-07-04-nexum-opencode-port-design.md diff --git a/docs/superpowers/specs/2026-07-04-nexum-opencode-port-design.md b/docs/superpowers/specs/2026-07-04-nexum-opencode-port-design.md new file mode 100644 index 0000000..207acb0 --- /dev/null +++ b/docs/superpowers/specs/2026-07-04-nexum-opencode-port-design.md @@ -0,0 +1,264 @@ +# Nexum → OpenCode Port + +**Date:** 2026-07-04 +**Status:** Design + +Port the nexum Claude Code plugin to OpenCode. Nexum is a context-token and model-cost optimization plugin. The port keeps the existing Python scripts as-is and wires them into OpenCode's plugin, command, and agent system. + +--- + +## 1. Architecture + +The Python scripts in `scripts/` are the engine — they stay entirely unchanged. The port is a **wiring layer**: + +``` +┌─────────────────────────────────────────────┐ +│ OpenCode (.opencode/) │ +│ │ +│ plugins/nexum-hooks.ts ← event → │──── calls ──→ scripts/*.py +│ commands/nx-*.md ← /nx-* │ (via subprocess) +│ agents/nexum-*.md ← @nexum-* │ +│ opencode.json ← config │ +└─────────────────────────────────────────────┘ +``` + +Every Python script reads JSON from stdin and writes JSON to stdout (the same contract they already follow). The TS plugin transforms OpenCode event objects to/from the nexum format. + +**Env var set by plugin (via `shell.env`):** + +| Var | Value | Used by | +|---|---|---| +| `NEXUM_ROOT` | Absolute path to nexum repo root | Commands/agents resolve `scripts/` | + +The plugin sets `NEXUM_ROOT` at startup so commands and agents can find the Python scripts without hardcoding paths. Session ID is passed per-call by the plugin (it has access to `sessionID` from the plugin context). + +`NEXUM_ROOT` is set by resolving `import.meta.dirname` (Bun) or `__dirname` minus the `.opencode/plugins/` suffix — the plugin always knows where it lives relative to the repo root. + +--- + +## 2. Plugin — event wiring (`.opencode/plugins/nexum-hooks.ts`) + +Single TypeScript file that wires Python scripts into OpenCode events. Each handler transforms the OpenCode event shape into nexum's stdin JSON format, calls the script, and applies the result. + +### Events mapped + +| OpenCode Event | Nexum Script | What it does | +|---|---|---| +| `tool.execute.before` | `scan_guard.py` | Block/limit unscoped reads, recursive searches | +| `tool.execute.before` | `predup.py` | Block re-read of already-loaded file content | +| `tool.execute.after` | `dedup.py` | Collapse repeated tool output + truncate oversized | +| `session.created` | `resume_nudge.py` | Nudge user about unfinished handoff | +| `session.created` | `audit_nudge.py` | Periodic ignore-file audit reminder | +| `session.created` | `session_reset.py` | Clear stale tool_calls, throttle retention prune | +| `session.compacted` | `precompact.py` | Clear tool_calls at compaction boundary | + +### Missing Claude Code hooks (no OpenCode equivalent) + +| Claude Hook | Script | Resolution | +|---|---|---| +| `UserPromptSubmit` | `context_watch.py` | **Skipped.** Intent-guard and auto-handoff-write are non-critical. The auto-handoff can run on `session.compacted` instead. | +| `SubagentStop` | `subagent_usage.py` | **Skipped.** OpenCode tracks subagent usage natively. | + +### Plugin contract + +```typescript +// NexumScript Input (from plugin handler, matching what each script expects): +// scan_guard: { tool_name: string, tool_input: object } +// predup: { tool_name: string, tool_input: object, session_id: string } +// dedup: { tool_name: string, tool_response: string|object, session_id: string } +// resume_nudge/audit_nudge/session_reset: { session_id: string } +// precompact: { session_id: string } + +// NexumScript Output (parsed from script stdout): +// scan_guard: { hookSpecificOutput: { permissionDecision?, permissionDecisionReason?, updatedInput? } } +// predup: { hookSpecificOutput: { permissionDecision?, permissionDecisionReason? } } +// dedup: { hookSpecificOutput: { updatedToolOutput? } } +// others: {} (fail-open) +``` + +The plugin handler: +1. Serializes the OpenCode event into the script's expected input shape +2. Spawns `python3 /scripts/