forked from kent/consciousness
114 lines
3.4 KiB
TypeScript
114 lines
3.4 KiB
TypeScript
|
|
// opencode-plugin/index.ts — Consciousness integration for OpenCode.
|
||
|
|
//
|
||
|
|
// Bridges OpenCode events to the consciousness system:
|
||
|
|
// - chat.message → forwards to poc-hook-opencode, appends output as text part
|
||
|
|
// - tool.execute.after → signals response activity
|
||
|
|
// - event → tracks session lifecycle (idle, compacted, etc.)
|
||
|
|
// - shell.env → injects POC_SESSION_ID into subprocesses
|
||
|
|
//
|
||
|
|
// Install: copy this directory to your project's `plugin/` or `plugins/` dir,
|
||
|
|
// or add to opencode.json:
|
||
|
|
// "plugin": ["/home/kent/poc/consciousness-claude/opencode-plugin"]
|
||
|
|
|
||
|
|
import type { Plugin, Hooks } from "@opencode-ai/plugin"
|
||
|
|
import path from "path"
|
||
|
|
import { $ } from "bun"
|
||
|
|
import { $ } from "bun"
|
||
|
|
|
||
|
|
// Find the poc-hook-opencode binary
|
||
|
|
function findHookBinary(): string {
|
||
|
|
const candidates = [
|
||
|
|
path.join(process.env.HOME || "", ".cargo/bin/poc-hook-opencode"),
|
||
|
|
path.join(process.env.HOME || "", "poc/consciousness-claude/target/debug/poc-hook-opencode"),
|
||
|
|
path.join(process.env.HOME || "", "poc/consciousness-claude/target/release/poc-hook-opencode"),
|
||
|
|
]
|
||
|
|
for (const c of candidates) {
|
||
|
|
try {
|
||
|
|
const stat = Bun.file(c).statSync()
|
||
|
|
if (stat?.isFile()) return c
|
||
|
|
} catch {}
|
||
|
|
}
|
||
|
|
return "poc-hook-opencode"
|
||
|
|
}
|
||
|
|
|
||
|
|
const HOOK_BINARY = findHookBinary()
|
||
|
|
|
||
|
|
// Generate a unique part ID (opencode uses ulid-like ascending IDs)
|
||
|
|
let partCounter = 0
|
||
|
|
function nextPartId(): string {
|
||
|
|
partCounter += 1
|
||
|
|
return `poc_part_${Date.now()}_${partCounter}`
|
||
|
|
}
|
||
|
|
|
||
|
|
export const ConsciousnessPlugin: Plugin = async (ctx) => {
|
||
|
|
const hooks: Hooks = {}
|
||
|
|
|
||
|
|
// Main hook: forward user messages to consciousness, inject context
|
||
|
|
hooks["chat.message"] = async (input, output) => {
|
||
|
|
const hookInput = JSON.stringify({
|
||
|
|
session_id: input.sessionID,
|
||
|
|
hook_event: "UserPromptSubmit",
|
||
|
|
})
|
||
|
|
|
||
|
|
try {
|
||
|
|
const proc = Bun.spawn([HOOK_BINARY], {
|
||
|
|
stdin: hookInput,
|
||
|
|
stdout: "pipe",
|
||
|
|
stderr: "pipe",
|
||
|
|
})
|
||
|
|
const [stdout, stderr] = await Promise.all([
|
||
|
|
new Response(proc.stdout).text(),
|
||
|
|
new Response(proc.stderr).text(),
|
||
|
|
])
|
||
|
|
await proc.exited
|
||
|
|
|
||
|
|
if (stdout && stdout.trim()) {
|
||
|
|
// Append as a text part — must match MessageV2.TextPart schema:
|
||
|
|
// { id, sessionID, messageID, type: "text", text, time?, synthetic?, ignored? }
|
||
|
|
output.parts.push({
|
||
|
|
id: nextPartId(),
|
||
|
|
sessionID: input.sessionID,
|
||
|
|
messageID: output.message.id,
|
||
|
|
type: "text",
|
||
|
|
text: stdout,
|
||
|
|
synthetic: true,
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
if (stderr && stderr.trim()) {
|
||
|
|
console.error("[consciousness] hook stderr:", stderr.slice(0, 500))
|
||
|
|
}
|
||
|
|
} catch (e) {
|
||
|
|
console.error("[consciousness] hook error:", e)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Signal response after tool use
|
||
|
|
hooks["tool.execute.after"] = async () => {
|
||
|
|
try {
|
||
|
|
await $`poc-daemon response`.quiet()
|
||
|
|
} catch {
|
||
|
|
// Daemon might not be running
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Inject POC_SESSION_ID into all shell commands
|
||
|
|
hooks["shell.env"] = async (input, output) => {
|
||
|
|
if (input.sessionID) {
|
||
|
|
output.env["POC_SESSION_ID"] = input.sessionID
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Track session events
|
||
|
|
hooks["event"] = async ({ event }) => {
|
||
|
|
if (event.type === "session.compacted") {
|
||
|
|
// Compaction detected — next hook invocation will detect via SQLite
|
||
|
|
}
|
||
|
|
if (event.type === "session.idle") {
|
||
|
|
// Session went idle
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return hooks
|
||
|
|
}
|