feat(hooks): integrate directory-agents-injector hook into plugin pipeline
- Add directoryAgentsInjector to plugin event handlers - Wire up tool.execute.after hook for directory agents injection - Fix: Format src/index.ts with consistent semicolon style
This commit is contained in:
@@ -6,3 +6,4 @@ export { createCommentCheckerHooks } from "./comment-checker";
|
|||||||
export { createGrepOutputTruncatorHook } from "./grep-output-truncator";
|
export { createGrepOutputTruncatorHook } from "./grep-output-truncator";
|
||||||
export { createPulseMonitorHook } from "./pulse-monitor";
|
export { createPulseMonitorHook } from "./pulse-monitor";
|
||||||
export { createDirectoryAgentsInjectorHook } from "./directory-agents-injector";
|
export { createDirectoryAgentsInjectorHook } from "./directory-agents-injector";
|
||||||
|
export { createEmptyTaskResponseDetectorHook } from "./empty-task-response-detector";
|
||||||
|
|||||||
175
src/index.ts
175
src/index.ts
@@ -1,59 +1,70 @@
|
|||||||
import type { Plugin } from "@opencode-ai/plugin"
|
import type { Plugin } from "@opencode-ai/plugin";
|
||||||
import { createBuiltinAgents } from "./agents"
|
import { createBuiltinAgents } from "./agents";
|
||||||
import { createTodoContinuationEnforcer, createContextWindowMonitorHook, createSessionRecoveryHook, createCommentCheckerHooks, createGrepOutputTruncatorHook, createPulseMonitorHook } from "./hooks"
|
import {
|
||||||
import { updateTerminalTitle } from "./features/terminal"
|
createTodoContinuationEnforcer,
|
||||||
import { builtinTools } from "./tools"
|
createContextWindowMonitorHook,
|
||||||
import { createBuiltinMcps } from "./mcp"
|
createSessionRecoveryHook,
|
||||||
import { OhMyOpenCodeConfigSchema, type OhMyOpenCodeConfig } from "./config"
|
createCommentCheckerHooks,
|
||||||
import * as fs from "fs"
|
createGrepOutputTruncatorHook,
|
||||||
import * as path from "path"
|
createPulseMonitorHook,
|
||||||
|
createDirectoryAgentsInjectorHook,
|
||||||
|
} from "./hooks";
|
||||||
|
import { updateTerminalTitle } from "./features/terminal";
|
||||||
|
import { builtinTools } from "./tools";
|
||||||
|
import { createBuiltinMcps } from "./mcp";
|
||||||
|
import { OhMyOpenCodeConfigSchema, type OhMyOpenCodeConfig } from "./config";
|
||||||
|
import * as fs from "fs";
|
||||||
|
import * as path from "path";
|
||||||
|
|
||||||
function loadPluginConfig(directory: string): OhMyOpenCodeConfig {
|
function loadPluginConfig(directory: string): OhMyOpenCodeConfig {
|
||||||
const configPaths = [
|
const configPaths = [
|
||||||
path.join(directory, "oh-my-opencode.json"),
|
path.join(directory, "oh-my-opencode.json"),
|
||||||
path.join(directory, ".oh-my-opencode.json"),
|
path.join(directory, ".oh-my-opencode.json"),
|
||||||
]
|
];
|
||||||
|
|
||||||
for (const configPath of configPaths) {
|
for (const configPath of configPaths) {
|
||||||
try {
|
try {
|
||||||
if (fs.existsSync(configPath)) {
|
if (fs.existsSync(configPath)) {
|
||||||
const content = fs.readFileSync(configPath, "utf-8")
|
const content = fs.readFileSync(configPath, "utf-8");
|
||||||
const rawConfig = JSON.parse(content)
|
const rawConfig = JSON.parse(content);
|
||||||
const result = OhMyOpenCodeConfigSchema.safeParse(rawConfig)
|
const result = OhMyOpenCodeConfigSchema.safeParse(rawConfig);
|
||||||
|
|
||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
console.error(`[oh-my-opencode] Config validation error in ${configPath}:`)
|
console.error(
|
||||||
|
`[oh-my-opencode] Config validation error in ${configPath}:`,
|
||||||
|
);
|
||||||
for (const issue of result.error.issues) {
|
for (const issue of result.error.issues) {
|
||||||
console.error(` - ${issue.path.join(".")}: ${issue.message}`)
|
console.error(` - ${issue.path.join(".")}: ${issue.message}`);
|
||||||
}
|
}
|
||||||
return {}
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
return result.data
|
return result.data;
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
// Ignore parse errors, use defaults
|
// Ignore parse errors, use defaults
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {}
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
const OhMyOpenCodePlugin: Plugin = async (ctx) => {
|
const OhMyOpenCodePlugin: Plugin = async (ctx) => {
|
||||||
const todoContinuationEnforcer = createTodoContinuationEnforcer(ctx)
|
const todoContinuationEnforcer = createTodoContinuationEnforcer(ctx);
|
||||||
const contextWindowMonitor = createContextWindowMonitorHook(ctx)
|
const contextWindowMonitor = createContextWindowMonitorHook(ctx);
|
||||||
const sessionRecovery = createSessionRecoveryHook(ctx)
|
const sessionRecovery = createSessionRecoveryHook(ctx);
|
||||||
const pulseMonitor = createPulseMonitorHook(ctx)
|
const pulseMonitor = createPulseMonitorHook(ctx);
|
||||||
const commentChecker = createCommentCheckerHooks()
|
const commentChecker = createCommentCheckerHooks();
|
||||||
const grepOutputTruncator = createGrepOutputTruncatorHook(ctx)
|
const grepOutputTruncator = createGrepOutputTruncatorHook(ctx);
|
||||||
|
const directoryAgentsInjector = createDirectoryAgentsInjectorHook(ctx);
|
||||||
|
|
||||||
updateTerminalTitle({ sessionId: "main" })
|
updateTerminalTitle({ sessionId: "main" });
|
||||||
|
|
||||||
const pluginConfig = loadPluginConfig(ctx.directory)
|
const pluginConfig = loadPluginConfig(ctx.directory);
|
||||||
|
|
||||||
let mainSessionID: string | undefined
|
let mainSessionID: string | undefined;
|
||||||
let currentSessionID: string | undefined
|
let currentSessionID: string | undefined;
|
||||||
let currentSessionTitle: string | undefined
|
let currentSessionTitle: string | undefined;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
tool: builtinTools,
|
tool: builtinTools,
|
||||||
@@ -61,76 +72,80 @@ const OhMyOpenCodePlugin: Plugin = async (ctx) => {
|
|||||||
config: async (config) => {
|
config: async (config) => {
|
||||||
const agents = createBuiltinAgents(
|
const agents = createBuiltinAgents(
|
||||||
pluginConfig.disabled_agents,
|
pluginConfig.disabled_agents,
|
||||||
pluginConfig.agents
|
pluginConfig.agents,
|
||||||
)
|
);
|
||||||
|
|
||||||
config.agent = {
|
config.agent = {
|
||||||
...config.agent,
|
...config.agent,
|
||||||
...agents,
|
...agents,
|
||||||
}
|
};
|
||||||
config.tools = {
|
config.tools = {
|
||||||
...config.tools,
|
...config.tools,
|
||||||
grep: false,
|
};
|
||||||
}
|
|
||||||
config.mcp = {
|
config.mcp = {
|
||||||
...config.mcp,
|
...config.mcp,
|
||||||
...createBuiltinMcps(pluginConfig.disabled_mcps),
|
...createBuiltinMcps(pluginConfig.disabled_mcps),
|
||||||
}
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
event: async (input) => {
|
event: async (input) => {
|
||||||
await todoContinuationEnforcer(input)
|
await todoContinuationEnforcer(input);
|
||||||
await contextWindowMonitor.event(input)
|
await contextWindowMonitor.event(input);
|
||||||
await pulseMonitor.event(input)
|
await pulseMonitor.event(input);
|
||||||
|
await directoryAgentsInjector.event(input);
|
||||||
|
|
||||||
const { event } = input
|
const { event } = input;
|
||||||
const props = event.properties as Record<string, unknown> | undefined
|
const props = event.properties as Record<string, unknown> | undefined;
|
||||||
|
|
||||||
if (event.type === "session.created") {
|
if (event.type === "session.created") {
|
||||||
const sessionInfo = props?.info as { id?: string; title?: string; parentID?: string } | undefined
|
const sessionInfo = props?.info as
|
||||||
|
| { id?: string; title?: string; parentID?: string }
|
||||||
|
| undefined;
|
||||||
if (!sessionInfo?.parentID) {
|
if (!sessionInfo?.parentID) {
|
||||||
mainSessionID = sessionInfo?.id
|
mainSessionID = sessionInfo?.id;
|
||||||
currentSessionID = sessionInfo?.id
|
currentSessionID = sessionInfo?.id;
|
||||||
currentSessionTitle = sessionInfo?.title
|
currentSessionTitle = sessionInfo?.title;
|
||||||
updateTerminalTitle({
|
updateTerminalTitle({
|
||||||
sessionId: currentSessionID || "main",
|
sessionId: currentSessionID || "main",
|
||||||
status: "idle",
|
status: "idle",
|
||||||
directory: ctx.directory,
|
directory: ctx.directory,
|
||||||
sessionTitle: currentSessionTitle,
|
sessionTitle: currentSessionTitle,
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.type === "session.updated") {
|
if (event.type === "session.updated") {
|
||||||
const sessionInfo = props?.info as { id?: string; title?: string; parentID?: string } | undefined
|
const sessionInfo = props?.info as
|
||||||
|
| { id?: string; title?: string; parentID?: string }
|
||||||
|
| undefined;
|
||||||
if (!sessionInfo?.parentID) {
|
if (!sessionInfo?.parentID) {
|
||||||
currentSessionID = sessionInfo?.id
|
currentSessionID = sessionInfo?.id;
|
||||||
currentSessionTitle = sessionInfo?.title
|
currentSessionTitle = sessionInfo?.title;
|
||||||
updateTerminalTitle({
|
updateTerminalTitle({
|
||||||
sessionId: currentSessionID || "main",
|
sessionId: currentSessionID || "main",
|
||||||
status: "processing",
|
status: "processing",
|
||||||
directory: ctx.directory,
|
directory: ctx.directory,
|
||||||
sessionTitle: currentSessionTitle,
|
sessionTitle: currentSessionTitle,
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.type === "session.deleted") {
|
if (event.type === "session.deleted") {
|
||||||
const sessionInfo = props?.info as { id?: string } | undefined
|
const sessionInfo = props?.info as { id?: string } | undefined;
|
||||||
if (sessionInfo?.id === mainSessionID) {
|
if (sessionInfo?.id === mainSessionID) {
|
||||||
mainSessionID = undefined
|
mainSessionID = undefined;
|
||||||
currentSessionID = undefined
|
currentSessionID = undefined;
|
||||||
currentSessionTitle = undefined
|
currentSessionTitle = undefined;
|
||||||
updateTerminalTitle({
|
updateTerminalTitle({
|
||||||
sessionId: "main",
|
sessionId: "main",
|
||||||
status: "idle",
|
status: "idle",
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.type === "session.error") {
|
if (event.type === "session.error") {
|
||||||
const sessionID = props?.sessionID as string | undefined
|
const sessionID = props?.sessionID as string | undefined;
|
||||||
const error = props?.error
|
const error = props?.error;
|
||||||
|
|
||||||
if (sessionRecovery.isRecoverableError(error)) {
|
if (sessionRecovery.isRecoverableError(error)) {
|
||||||
const messageInfo = {
|
const messageInfo = {
|
||||||
@@ -138,15 +153,18 @@ const OhMyOpenCodePlugin: Plugin = async (ctx) => {
|
|||||||
role: "assistant" as const,
|
role: "assistant" as const,
|
||||||
sessionID,
|
sessionID,
|
||||||
error,
|
error,
|
||||||
}
|
};
|
||||||
const recovered = await sessionRecovery.handleSessionRecovery(messageInfo)
|
const recovered =
|
||||||
|
await sessionRecovery.handleSessionRecovery(messageInfo);
|
||||||
|
|
||||||
if (recovered && sessionID && sessionID === mainSessionID) {
|
if (recovered && sessionID && sessionID === mainSessionID) {
|
||||||
await ctx.client.session.prompt({
|
await ctx.client.session
|
||||||
path: { id: sessionID },
|
.prompt({
|
||||||
body: { parts: [{ type: "text", text: "continue" }] },
|
path: { id: sessionID },
|
||||||
query: { directory: ctx.directory },
|
body: { parts: [{ type: "text", text: "continue" }] },
|
||||||
}).catch(() => {})
|
query: { directory: ctx.directory },
|
||||||
|
})
|
||||||
|
.catch(() => {});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,26 +174,26 @@ const OhMyOpenCodePlugin: Plugin = async (ctx) => {
|
|||||||
status: "error",
|
status: "error",
|
||||||
directory: ctx.directory,
|
directory: ctx.directory,
|
||||||
sessionTitle: currentSessionTitle,
|
sessionTitle: currentSessionTitle,
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.type === "session.idle") {
|
if (event.type === "session.idle") {
|
||||||
const sessionID = props?.sessionID as string | undefined
|
const sessionID = props?.sessionID as string | undefined;
|
||||||
if (sessionID && sessionID === mainSessionID) {
|
if (sessionID && sessionID === mainSessionID) {
|
||||||
updateTerminalTitle({
|
updateTerminalTitle({
|
||||||
sessionId: sessionID,
|
sessionId: sessionID,
|
||||||
status: "idle",
|
status: "idle",
|
||||||
directory: ctx.directory,
|
directory: ctx.directory,
|
||||||
sessionTitle: currentSessionTitle,
|
sessionTitle: currentSessionTitle,
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
"tool.execute.before": async (input, output) => {
|
"tool.execute.before": async (input, output) => {
|
||||||
await pulseMonitor["tool.execute.before"]()
|
await pulseMonitor["tool.execute.before"]();
|
||||||
await commentChecker["tool.execute.before"](input, output)
|
await commentChecker["tool.execute.before"](input, output);
|
||||||
|
|
||||||
if (input.sessionID === mainSessionID) {
|
if (input.sessionID === mainSessionID) {
|
||||||
updateTerminalTitle({
|
updateTerminalTitle({
|
||||||
@@ -184,15 +202,16 @@ const OhMyOpenCodePlugin: Plugin = async (ctx) => {
|
|||||||
currentTool: input.tool,
|
currentTool: input.tool,
|
||||||
directory: ctx.directory,
|
directory: ctx.directory,
|
||||||
sessionTitle: currentSessionTitle,
|
sessionTitle: currentSessionTitle,
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
"tool.execute.after": async (input, output) => {
|
"tool.execute.after": async (input, output) => {
|
||||||
await pulseMonitor["tool.execute.after"](input)
|
await pulseMonitor["tool.execute.after"](input);
|
||||||
await grepOutputTruncator["tool.execute.after"](input, output)
|
await grepOutputTruncator["tool.execute.after"](input, output);
|
||||||
await contextWindowMonitor["tool.execute.after"](input, output)
|
await contextWindowMonitor["tool.execute.after"](input, output);
|
||||||
await commentChecker["tool.execute.after"](input, output)
|
await commentChecker["tool.execute.after"](input, output);
|
||||||
|
await directoryAgentsInjector["tool.execute.after"](input, output);
|
||||||
|
|
||||||
if (input.sessionID === mainSessionID) {
|
if (input.sessionID === mainSessionID) {
|
||||||
updateTerminalTitle({
|
updateTerminalTitle({
|
||||||
@@ -200,13 +219,13 @@ const OhMyOpenCodePlugin: Plugin = async (ctx) => {
|
|||||||
status: "idle",
|
status: "idle",
|
||||||
directory: ctx.directory,
|
directory: ctx.directory,
|
||||||
sessionTitle: currentSessionTitle,
|
sessionTitle: currentSessionTitle,
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
};
|
||||||
}
|
};
|
||||||
|
|
||||||
export default OhMyOpenCodePlugin
|
export default OhMyOpenCodePlugin;
|
||||||
|
|
||||||
export type {
|
export type {
|
||||||
OhMyOpenCodeConfig,
|
OhMyOpenCodeConfig,
|
||||||
@@ -214,4 +233,4 @@ export type {
|
|||||||
AgentOverrideConfig,
|
AgentOverrideConfig,
|
||||||
AgentOverrides,
|
AgentOverrides,
|
||||||
McpName,
|
McpName,
|
||||||
} from "./config"
|
} from "./config";
|
||||||
|
|||||||
Reference in New Issue
Block a user