Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions assets/oh-my-opencode.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,9 @@
"prompt": {
"type": "string"
},
"prompt_append": {
"type": "string"
},
"tools": {
"type": "object",
"propertyNames": {
Expand Down Expand Up @@ -196,6 +199,9 @@
"prompt": {
"type": "string"
},
"prompt_append": {
"type": "string"
},
"tools": {
"type": "object",
"propertyNames": {
Expand Down Expand Up @@ -307,6 +313,9 @@
"prompt": {
"type": "string"
},
"prompt_append": {
"type": "string"
},
"tools": {
"type": "object",
"propertyNames": {
Expand Down Expand Up @@ -418,6 +427,9 @@
"prompt": {
"type": "string"
},
"prompt_append": {
"type": "string"
},
"tools": {
"type": "object",
"propertyNames": {
Expand Down Expand Up @@ -529,6 +541,9 @@
"prompt": {
"type": "string"
},
"prompt_append": {
"type": "string"
},
"tools": {
"type": "object",
"propertyNames": {
Expand Down Expand Up @@ -640,6 +655,9 @@
"prompt": {
"type": "string"
},
"prompt_append": {
"type": "string"
},
"tools": {
"type": "object",
"propertyNames": {
Expand Down Expand Up @@ -751,6 +769,9 @@
"prompt": {
"type": "string"
},
"prompt_append": {
"type": "string"
},
"tools": {
"type": "object",
"propertyNames": {
Expand Down Expand Up @@ -862,6 +883,9 @@
"prompt": {
"type": "string"
},
"prompt_append": {
"type": "string"
},
"tools": {
"type": "object",
"propertyNames": {
Expand Down Expand Up @@ -973,6 +997,9 @@
"prompt": {
"type": "string"
},
"prompt_append": {
"type": "string"
},
"tools": {
"type": "object",
"propertyNames": {
Expand Down Expand Up @@ -1084,6 +1111,9 @@
"prompt": {
"type": "string"
},
"prompt_append": {
"type": "string"
},
"tools": {
"type": "object",
"propertyNames": {
Expand Down
22 changes: 14 additions & 8 deletions src/agents/librarian.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import type { AgentConfig } from "@opencode-ai/sdk"

export const librarianAgent: AgentConfig = {
description:
"Specialized codebase understanding agent for multi-repository analysis, searching remote codebases, retrieving official documentation, and finding implementation examples using GitHub CLI, Context7, and Web Search. MUST BE USED when users ask to look up code in remote repositories, explain library internals, or find usage examples in open source.",
mode: "subagent",
model: "anthropic/claude-sonnet-4-5",
temperature: 0.1,
tools: { write: false, edit: false, background_task: false },
prompt: `# THE LIBRARIAN
const DEFAULT_MODEL = "anthropic/claude-sonnet-4-5"

export function createLibrarianAgent(model: string = DEFAULT_MODEL): AgentConfig {
return {
description:
"Specialized codebase understanding agent for multi-repository analysis, searching remote codebases, retrieving official documentation, and finding implementation examples using GitHub CLI, Context7, and Web Search. MUST BE USED when users ask to look up code in remote repositories, explain library internals, or find usage examples in open source.",
mode: "subagent",
model,
temperature: 0.1,
tools: { write: false, edit: false, background_task: false },
prompt: `# THE LIBRARIAN

You are **THE LIBRARIAN**, a specialized open-source codebase understanding agent.

Expand Down Expand Up @@ -237,4 +240,7 @@ grep_app_searchGitHub(query: "useQuery")
5. **BE CONCISE**: Facts > opinions, evidence > speculation

`,
}
}

export const librarianAgent = createLibrarianAgent()
4 changes: 2 additions & 2 deletions src/agents/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { AgentConfig } from "@opencode-ai/sdk"
import type { BuiltinAgentName, AgentOverrideConfig, AgentOverrides, AgentFactory } from "./types"
import { createSisyphusAgent } from "./sisyphus"
import { createOracleAgent } from "./oracle"
import { librarianAgent } from "./librarian"
import { createLibrarianAgent } from "./librarian"
import { exploreAgent } from "./explore"
import { frontendUiUxEngineerAgent } from "./frontend-ui-ux-engineer"
import { documentWriterAgent } from "./document-writer"
Expand All @@ -14,7 +14,7 @@ type AgentSource = AgentFactory | AgentConfig
const agentSources: Record<BuiltinAgentName, AgentSource> = {
Sisyphus: createSisyphusAgent,
oracle: createOracleAgent,
librarian: librarianAgent,
librarian: createLibrarianAgent,
explore: exploreAgent,
"frontend-ui-ux-engineer": frontendUiUxEngineerAgent,
"document-writer": documentWriterAgent,
Expand Down
7 changes: 7 additions & 0 deletions src/features/background-agent/manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ export class BackgroundManager {
toolCalls: 0,
lastUpdate: new Date(),
},
parentModel: input.parentModel,
}

this.tasks.set(task.id, task)
Expand Down Expand Up @@ -322,10 +323,16 @@ export class BackgroundManager {
const messageDir = getMessageDir(task.parentSessionID)
const prevMessage = messageDir ? findNearestMessageWithFields(messageDir) : null

const modelContext = task.parentModel ?? prevMessage?.model
const modelField = modelContext?.providerID && modelContext?.modelID
? { providerID: modelContext.providerID, modelID: modelContext.modelID }
: undefined

await this.client.session.prompt({
path: { id: task.parentSessionID },
body: {
agent: prevMessage?.agent,
model: modelField,
parts: [{ type: "text", text: message }],
},
query: { directory: this.directory },
Expand Down
2 changes: 2 additions & 0 deletions src/features/background-agent/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export interface BackgroundTask {
result?: string
error?: string
progress?: TaskProgress
parentModel?: { providerID: string; modelID: string }
}

export interface LaunchInput {
Expand All @@ -34,4 +35,5 @@ export interface LaunchInput {
agent: string
parentSessionID: string
parentMessageID: string
parentModel?: { providerID: string; modelID: string }
}
24 changes: 24 additions & 0 deletions src/tools/background-task/tools.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,27 @@
import { tool, type PluginInput } from "@opencode-ai/plugin"
import { existsSync, readdirSync } from "node:fs"
import { join } from "node:path"
import type { BackgroundManager, BackgroundTask } from "../../features/background-agent"
import type { BackgroundTaskArgs, BackgroundOutputArgs, BackgroundCancelArgs } from "./types"
import { BACKGROUND_TASK_DESCRIPTION, BACKGROUND_OUTPUT_DESCRIPTION, BACKGROUND_CANCEL_DESCRIPTION } from "./constants"
import { findNearestMessageWithFields, MESSAGE_STORAGE } from "../../features/hook-message-injector"

type OpencodeClient = PluginInput["client"]

function getMessageDir(sessionID: string): string | null {
if (!existsSync(MESSAGE_STORAGE)) return null

const directPath = join(MESSAGE_STORAGE, sessionID)
if (existsSync(directPath)) return directPath

for (const dir of readdirSync(MESSAGE_STORAGE)) {
const sessionPath = join(MESSAGE_STORAGE, dir, sessionID)
if (existsSync(sessionPath)) return sessionPath
}

return null
}

function formatDuration(start: Date, end?: Date): string {
const duration = (end ?? new Date()).getTime() - start.getTime()
const seconds = Math.floor(duration / 1000)
Expand Down Expand Up @@ -34,12 +51,19 @@ export function createBackgroundTask(manager: BackgroundManager) {
}

try {
const messageDir = getMessageDir(toolContext.sessionID)
const prevMessage = messageDir ? findNearestMessageWithFields(messageDir) : null
const parentModel = prevMessage?.model?.providerID && prevMessage?.model?.modelID
? { providerID: prevMessage.model.providerID, modelID: prevMessage.model.modelID }
: undefined

const task = await manager.launch({
description: args.description,
prompt: args.prompt,
agent: args.agent.trim(),
parentSessionID: toolContext.sessionID,
parentMessageID: toolContext.messageID,
parentModel,
})

return `Background task launched successfully.
Expand Down