refactor(core): split index.ts and config-handler.ts into focused modules
Main entry point: - create-hooks.ts, create-tools.ts, create-managers.ts - plugin-interface.ts: plugin interface types - plugin/ directory: plugin lifecycle modules Config handler: - agent-config-handler.ts, command-config-handler.ts - tool-config-handler.ts, mcp-config-handler.ts - provider-config-handler.ts, category-config-resolver.ts - agent-priority-order.ts, prometheus-agent-config-builder.ts - plugin-components-loader.ts
This commit is contained in:
@@ -1,39 +1,14 @@
|
||||
import { createBuiltinAgents } from "../agents";
|
||||
import { createSisyphusJuniorAgentWithOverrides } from "../agents/sisyphus-junior";
|
||||
import {
|
||||
loadUserCommands,
|
||||
loadProjectCommands,
|
||||
loadOpencodeGlobalCommands,
|
||||
loadOpencodeProjectCommands,
|
||||
} from "../features/claude-code-command-loader";
|
||||
import { loadBuiltinCommands } from "../features/builtin-commands";
|
||||
import {
|
||||
loadUserSkills,
|
||||
loadProjectSkills,
|
||||
loadOpencodeGlobalSkills,
|
||||
loadOpencodeProjectSkills,
|
||||
discoverUserClaudeSkills,
|
||||
discoverProjectClaudeSkills,
|
||||
discoverOpencodeGlobalSkills,
|
||||
discoverOpencodeProjectSkills,
|
||||
} from "../features/opencode-skill-loader";
|
||||
import {
|
||||
loadUserAgents,
|
||||
loadProjectAgents,
|
||||
} from "../features/claude-code-agent-loader";
|
||||
import { loadMcpConfigs } from "../features/claude-code-mcp-loader";
|
||||
import { loadAllPluginComponents } from "../features/claude-code-plugin-loader";
|
||||
import { createBuiltinMcps } from "../mcp";
|
||||
import type { OhMyOpenCodeConfig } from "../config";
|
||||
import { log, fetchAvailableModels, readConnectedProvidersCache, resolveModelPipeline, addConfigLoadError } from "../shared";
|
||||
import { getOpenCodeConfigPaths } from "../shared/opencode-config-dir";
|
||||
import { migrateAgentConfig } from "../shared/permission-compat";
|
||||
import { AGENT_NAME_MAP } from "../shared/migration";
|
||||
import { AGENT_MODEL_REQUIREMENTS } from "../shared/model-requirements";
|
||||
import { PROMETHEUS_SYSTEM_PROMPT, PROMETHEUS_PERMISSION } from "../agents/prometheus";
|
||||
import { DEFAULT_CATEGORIES } from "../tools/delegate-task/constants";
|
||||
import type { ModelCacheState } from "../plugin-state";
|
||||
import type { CategoryConfig } from "../config/schema";
|
||||
import { log } from "../shared";
|
||||
import { applyAgentConfig } from "./agent-config-handler";
|
||||
import { applyCommandConfig } from "./command-config-handler";
|
||||
import { applyMcpConfig } from "./mcp-config-handler";
|
||||
import { applyProviderConfig } from "./provider-config-handler";
|
||||
import { loadPluginComponents } from "./plugin-components-loader";
|
||||
import { applyToolConfig } from "./tool-config-handler";
|
||||
|
||||
export { resolveCategoryConfig } from "./category-config-resolver";
|
||||
|
||||
export interface ConfigHandlerDeps {
|
||||
ctx: { directory: string; client?: any };
|
||||
@@ -41,486 +16,29 @@ export interface ConfigHandlerDeps {
|
||||
modelCacheState: ModelCacheState;
|
||||
}
|
||||
|
||||
export function resolveCategoryConfig(
|
||||
categoryName: string,
|
||||
userCategories?: Record<string, CategoryConfig>
|
||||
): CategoryConfig | undefined {
|
||||
return userCategories?.[categoryName] ?? DEFAULT_CATEGORIES[categoryName];
|
||||
}
|
||||
|
||||
const CORE_AGENT_ORDER = ["sisyphus", "hephaestus", "prometheus", "atlas"] as const;
|
||||
|
||||
function reorderAgentsByPriority(agents: Record<string, unknown>): Record<string, unknown> {
|
||||
const ordered: Record<string, unknown> = {};
|
||||
const seen = new Set<string>();
|
||||
|
||||
for (const key of CORE_AGENT_ORDER) {
|
||||
if (Object.prototype.hasOwnProperty.call(agents, key)) {
|
||||
ordered[key] = agents[key];
|
||||
seen.add(key);
|
||||
}
|
||||
}
|
||||
|
||||
for (const [key, value] of Object.entries(agents)) {
|
||||
if (!seen.has(key)) {
|
||||
ordered[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
return ordered;
|
||||
}
|
||||
|
||||
export function createConfigHandler(deps: ConfigHandlerDeps) {
|
||||
const { ctx, pluginConfig, modelCacheState } = deps;
|
||||
|
||||
return async (config: Record<string, unknown>) => {
|
||||
type ProviderConfig = {
|
||||
options?: { headers?: Record<string, string> };
|
||||
models?: Record<string, { limit?: { context?: number } }>;
|
||||
};
|
||||
const providers = config.provider as
|
||||
| Record<string, ProviderConfig>
|
||||
| undefined;
|
||||
applyProviderConfig({ config, modelCacheState });
|
||||
|
||||
const anthropicBeta =
|
||||
providers?.anthropic?.options?.headers?.["anthropic-beta"];
|
||||
modelCacheState.anthropicContext1MEnabled =
|
||||
anthropicBeta?.includes("context-1m") ?? false;
|
||||
const pluginComponents = await loadPluginComponents({ pluginConfig });
|
||||
|
||||
if (providers) {
|
||||
for (const [providerID, providerConfig] of Object.entries(providers)) {
|
||||
const models = providerConfig?.models;
|
||||
if (models) {
|
||||
for (const [modelID, modelConfig] of Object.entries(models)) {
|
||||
const contextLimit = modelConfig?.limit?.context;
|
||||
if (contextLimit) {
|
||||
modelCacheState.modelContextLimitsCache.set(
|
||||
`${providerID}/${modelID}`,
|
||||
contextLimit
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const agentResult = await applyAgentConfig({
|
||||
config,
|
||||
pluginConfig,
|
||||
ctx,
|
||||
pluginComponents,
|
||||
});
|
||||
|
||||
const emptyPluginDefaults = {
|
||||
commands: {},
|
||||
skills: {},
|
||||
agents: {},
|
||||
mcpServers: {},
|
||||
hooksConfigs: [] as { hooks?: Record<string, unknown> }[],
|
||||
plugins: [] as { name: string; version: string }[],
|
||||
errors: [] as { pluginKey: string; installPath: string; error: string }[],
|
||||
};
|
||||
applyToolConfig({ config, pluginConfig, agentResult });
|
||||
await applyMcpConfig({ config, pluginConfig, pluginComponents });
|
||||
await applyCommandConfig({ config, pluginConfig, pluginComponents });
|
||||
|
||||
let pluginComponents: typeof emptyPluginDefaults;
|
||||
const pluginsEnabled = pluginConfig.claude_code?.plugins ?? true;
|
||||
|
||||
if (pluginsEnabled) {
|
||||
const timeoutMs = pluginConfig.experimental?.plugin_load_timeout_ms ?? 10000;
|
||||
try {
|
||||
let timeoutId: ReturnType<typeof setTimeout>;
|
||||
const timeoutPromise = new Promise<never>((_, reject) => {
|
||||
timeoutId = setTimeout(
|
||||
() => reject(new Error(`Plugin loading timed out after ${timeoutMs}ms`)),
|
||||
timeoutMs,
|
||||
);
|
||||
});
|
||||
pluginComponents = await Promise.race([
|
||||
loadAllPluginComponents({
|
||||
enabledPluginsOverride: pluginConfig.claude_code?.plugins_override,
|
||||
}),
|
||||
timeoutPromise,
|
||||
]).finally(() => clearTimeout(timeoutId));
|
||||
} catch (error) {
|
||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||
log("[config-handler] Plugin loading failed", { error: errorMessage });
|
||||
addConfigLoadError({ path: "plugin-loading", error: errorMessage });
|
||||
pluginComponents = emptyPluginDefaults;
|
||||
}
|
||||
} else {
|
||||
pluginComponents = emptyPluginDefaults;
|
||||
}
|
||||
|
||||
if (pluginComponents.plugins.length > 0) {
|
||||
log(`Loaded ${pluginComponents.plugins.length} Claude Code plugins`, {
|
||||
plugins: pluginComponents.plugins.map((p) => `${p.name}@${p.version}`),
|
||||
});
|
||||
}
|
||||
|
||||
if (pluginComponents.errors.length > 0) {
|
||||
log(`Plugin load errors`, { errors: pluginComponents.errors });
|
||||
}
|
||||
|
||||
// Migrate disabled_agents from old names to new names
|
||||
const migratedDisabledAgents = (pluginConfig.disabled_agents ?? []).map(agent => {
|
||||
return AGENT_NAME_MAP[agent.toLowerCase()] ?? AGENT_NAME_MAP[agent] ?? agent
|
||||
}) as typeof pluginConfig.disabled_agents
|
||||
|
||||
const includeClaudeSkillsForAwareness = pluginConfig.claude_code?.skills ?? true;
|
||||
const [
|
||||
discoveredUserSkills,
|
||||
discoveredProjectSkills,
|
||||
discoveredOpencodeGlobalSkills,
|
||||
discoveredOpencodeProjectSkills,
|
||||
] = await Promise.all([
|
||||
includeClaudeSkillsForAwareness ? discoverUserClaudeSkills() : Promise.resolve([]),
|
||||
includeClaudeSkillsForAwareness ? discoverProjectClaudeSkills() : Promise.resolve([]),
|
||||
discoverOpencodeGlobalSkills(),
|
||||
discoverOpencodeProjectSkills(),
|
||||
]);
|
||||
|
||||
const allDiscoveredSkills = [
|
||||
...discoveredOpencodeProjectSkills,
|
||||
...discoveredProjectSkills,
|
||||
...discoveredOpencodeGlobalSkills,
|
||||
...discoveredUserSkills,
|
||||
];
|
||||
|
||||
const browserProvider = pluginConfig.browser_automation_engine?.provider ?? "playwright";
|
||||
// config.model represents the currently active model in OpenCode (including UI selection)
|
||||
// Pass it as uiSelectedModel so it takes highest priority in model resolution
|
||||
const currentModel = config.model as string | undefined;
|
||||
const disabledSkills = new Set<string>(pluginConfig.disabled_skills ?? []);
|
||||
const builtinAgents = await createBuiltinAgents(
|
||||
migratedDisabledAgents,
|
||||
pluginConfig.agents,
|
||||
ctx.directory,
|
||||
undefined, // systemDefaultModel - let fallback chain handle this
|
||||
pluginConfig.categories,
|
||||
pluginConfig.git_master,
|
||||
allDiscoveredSkills,
|
||||
ctx.client,
|
||||
browserProvider,
|
||||
currentModel, // uiSelectedModel - takes highest priority
|
||||
disabledSkills
|
||||
);
|
||||
|
||||
// Claude Code agents: Do NOT apply permission migration
|
||||
// Claude Code uses whitelist-based tools format which is semantically different
|
||||
// from OpenCode's denylist-based permission system
|
||||
const userAgents = (pluginConfig.claude_code?.agents ?? true)
|
||||
? loadUserAgents()
|
||||
: {};
|
||||
const projectAgents = (pluginConfig.claude_code?.agents ?? true)
|
||||
? loadProjectAgents()
|
||||
: {};
|
||||
|
||||
// Plugin agents: Apply permission migration for compatibility
|
||||
const rawPluginAgents = pluginComponents.agents;
|
||||
const pluginAgents = Object.fromEntries(
|
||||
Object.entries(rawPluginAgents).map(([k, v]) => [
|
||||
k,
|
||||
v ? migrateAgentConfig(v as Record<string, unknown>) : v,
|
||||
])
|
||||
);
|
||||
|
||||
const isSisyphusEnabled = pluginConfig.sisyphus_agent?.disabled !== true;
|
||||
const builderEnabled =
|
||||
pluginConfig.sisyphus_agent?.default_builder_enabled ?? false;
|
||||
const plannerEnabled =
|
||||
pluginConfig.sisyphus_agent?.planner_enabled ?? true;
|
||||
const replacePlan = pluginConfig.sisyphus_agent?.replace_plan ?? true;
|
||||
const shouldDemotePlan = plannerEnabled && replacePlan;
|
||||
|
||||
type AgentConfig = Record<
|
||||
string,
|
||||
Record<string, unknown> | undefined
|
||||
> & {
|
||||
build?: Record<string, unknown>;
|
||||
plan?: Record<string, unknown>;
|
||||
explore?: { tools?: Record<string, unknown> };
|
||||
librarian?: { tools?: Record<string, unknown> };
|
||||
"multimodal-looker"?: { tools?: Record<string, unknown> };
|
||||
atlas?: { tools?: Record<string, unknown> };
|
||||
sisyphus?: { tools?: Record<string, unknown> };
|
||||
};
|
||||
const configAgent = config.agent as AgentConfig | undefined;
|
||||
|
||||
if (isSisyphusEnabled && builtinAgents.sisyphus) {
|
||||
(config as { default_agent?: string }).default_agent = "sisyphus";
|
||||
|
||||
const agentConfig: Record<string, unknown> = {
|
||||
sisyphus: builtinAgents.sisyphus,
|
||||
};
|
||||
|
||||
agentConfig["sisyphus-junior"] = createSisyphusJuniorAgentWithOverrides(
|
||||
pluginConfig.agents?.["sisyphus-junior"],
|
||||
undefined
|
||||
);
|
||||
|
||||
if (builderEnabled) {
|
||||
const { name: _buildName, ...buildConfigWithoutName } =
|
||||
configAgent?.build ?? {};
|
||||
const migratedBuildConfig = migrateAgentConfig(
|
||||
buildConfigWithoutName as Record<string, unknown>
|
||||
);
|
||||
const openCodeBuilderOverride =
|
||||
pluginConfig.agents?.["OpenCode-Builder"];
|
||||
const openCodeBuilderBase = {
|
||||
...migratedBuildConfig,
|
||||
description: `${configAgent?.build?.description ?? "Build agent"} (OpenCode default)`,
|
||||
};
|
||||
|
||||
agentConfig["OpenCode-Builder"] = openCodeBuilderOverride
|
||||
? { ...openCodeBuilderBase, ...openCodeBuilderOverride }
|
||||
: openCodeBuilderBase;
|
||||
}
|
||||
|
||||
if (plannerEnabled) {
|
||||
const prometheusOverride =
|
||||
pluginConfig.agents?.["prometheus"] as
|
||||
| (Record<string, unknown> & {
|
||||
category?: string
|
||||
model?: string
|
||||
variant?: string
|
||||
reasoningEffort?: string
|
||||
textVerbosity?: string
|
||||
thinking?: { type: string; budgetTokens?: number }
|
||||
temperature?: number
|
||||
top_p?: number
|
||||
maxTokens?: number
|
||||
})
|
||||
| undefined;
|
||||
|
||||
const categoryConfig = prometheusOverride?.category
|
||||
? resolveCategoryConfig(
|
||||
prometheusOverride.category,
|
||||
pluginConfig.categories
|
||||
)
|
||||
: undefined;
|
||||
|
||||
const prometheusRequirement = AGENT_MODEL_REQUIREMENTS["prometheus"];
|
||||
const connectedProviders = readConnectedProvidersCache();
|
||||
// IMPORTANT: Do NOT pass ctx.client to fetchAvailableModels during plugin initialization.
|
||||
// Calling client API (e.g., client.provider.list()) from config handler causes deadlock:
|
||||
// - Plugin init waits for server response
|
||||
// - Server waits for plugin init to complete before handling requests
|
||||
// Use cache-only mode instead. If cache is unavailable, fallback chain uses first model.
|
||||
// See: https://github.com/code-yeongyu/oh-my-opencode/issues/1301
|
||||
const availableModels = await fetchAvailableModels(undefined, {
|
||||
connectedProviders: connectedProviders ?? undefined,
|
||||
});
|
||||
|
||||
const modelResolution = resolveModelPipeline({
|
||||
intent: {
|
||||
uiSelectedModel: currentModel,
|
||||
userModel: prometheusOverride?.model ?? categoryConfig?.model,
|
||||
},
|
||||
constraints: { availableModels },
|
||||
policy: {
|
||||
fallbackChain: prometheusRequirement?.fallbackChain,
|
||||
systemDefaultModel: undefined,
|
||||
},
|
||||
});
|
||||
const resolvedModel = modelResolution?.model;
|
||||
const resolvedVariant = modelResolution?.variant;
|
||||
|
||||
const variantToUse = prometheusOverride?.variant ?? resolvedVariant;
|
||||
const reasoningEffortToUse = prometheusOverride?.reasoningEffort ?? categoryConfig?.reasoningEffort;
|
||||
const textVerbosityToUse = prometheusOverride?.textVerbosity ?? categoryConfig?.textVerbosity;
|
||||
const thinkingToUse = prometheusOverride?.thinking ?? categoryConfig?.thinking;
|
||||
const temperatureToUse = prometheusOverride?.temperature ?? categoryConfig?.temperature;
|
||||
const topPToUse = prometheusOverride?.top_p ?? categoryConfig?.top_p;
|
||||
const maxTokensToUse = prometheusOverride?.maxTokens ?? categoryConfig?.maxTokens;
|
||||
const prometheusBase = {
|
||||
name: "prometheus",
|
||||
...(resolvedModel ? { model: resolvedModel } : {}),
|
||||
...(variantToUse ? { variant: variantToUse } : {}),
|
||||
mode: "all" as const,
|
||||
prompt: PROMETHEUS_SYSTEM_PROMPT,
|
||||
permission: PROMETHEUS_PERMISSION,
|
||||
description: `${configAgent?.plan?.description ?? "Plan agent"} (Prometheus - OhMyOpenCode)`,
|
||||
color: (configAgent?.plan?.color as string) ?? "#FF5722", // Deep Orange - Fire/Flame theme
|
||||
...(temperatureToUse !== undefined ? { temperature: temperatureToUse } : {}),
|
||||
...(topPToUse !== undefined ? { top_p: topPToUse } : {}),
|
||||
...(maxTokensToUse !== undefined ? { maxTokens: maxTokensToUse } : {}),
|
||||
...(categoryConfig?.tools ? { tools: categoryConfig.tools } : {}),
|
||||
...(thinkingToUse ? { thinking: thinkingToUse } : {}),
|
||||
...(reasoningEffortToUse !== undefined
|
||||
? { reasoningEffort: reasoningEffortToUse }
|
||||
: {}),
|
||||
...(textVerbosityToUse !== undefined
|
||||
? { textVerbosity: textVerbosityToUse }
|
||||
: {}),
|
||||
};
|
||||
|
||||
// Properly handle prompt_append for Prometheus
|
||||
// Extract prompt_append and append it to prompt instead of shallow spread
|
||||
// Fixes: https://github.com/code-yeongyu/oh-my-opencode/issues/723
|
||||
if (prometheusOverride) {
|
||||
const { prompt_append, ...restOverride } = prometheusOverride as Record<string, unknown> & { prompt_append?: string };
|
||||
const merged = { ...prometheusBase, ...restOverride };
|
||||
if (prompt_append && merged.prompt) {
|
||||
merged.prompt = merged.prompt + "\n" + prompt_append;
|
||||
}
|
||||
agentConfig["prometheus"] = merged;
|
||||
} else {
|
||||
agentConfig["prometheus"] = prometheusBase;
|
||||
}
|
||||
}
|
||||
|
||||
const filteredConfigAgents = configAgent
|
||||
? Object.fromEntries(
|
||||
Object.entries(configAgent)
|
||||
.filter(([key]) => {
|
||||
if (key === "build") return false;
|
||||
if (key === "plan" && shouldDemotePlan) return false;
|
||||
// Filter out agents that oh-my-opencode provides to prevent
|
||||
// OpenCode defaults from overwriting user config in oh-my-opencode.json
|
||||
// See: https://github.com/code-yeongyu/oh-my-opencode/issues/472
|
||||
if (key in builtinAgents) return false;
|
||||
return true;
|
||||
})
|
||||
.map(([key, value]) => [
|
||||
key,
|
||||
value ? migrateAgentConfig(value as Record<string, unknown>) : value,
|
||||
])
|
||||
)
|
||||
: {};
|
||||
|
||||
const migratedBuild = configAgent?.build
|
||||
? migrateAgentConfig(configAgent.build as Record<string, unknown>)
|
||||
: {};
|
||||
|
||||
const planDemoteConfig = shouldDemotePlan
|
||||
? { mode: "subagent" as const
|
||||
}
|
||||
: undefined;
|
||||
|
||||
config.agent = {
|
||||
...agentConfig,
|
||||
...Object.fromEntries(
|
||||
Object.entries(builtinAgents).filter(([k]) => k !== "sisyphus")
|
||||
),
|
||||
...userAgents,
|
||||
...projectAgents,
|
||||
...pluginAgents,
|
||||
...filteredConfigAgents,
|
||||
build: { ...migratedBuild, mode: "subagent", hidden: true },
|
||||
...(planDemoteConfig ? { plan: planDemoteConfig } : {}),
|
||||
};
|
||||
} else {
|
||||
config.agent = {
|
||||
...builtinAgents,
|
||||
...userAgents,
|
||||
...projectAgents,
|
||||
...pluginAgents,
|
||||
...configAgent,
|
||||
};
|
||||
}
|
||||
|
||||
if (config.agent) {
|
||||
config.agent = reorderAgentsByPriority(config.agent as Record<string, unknown>);
|
||||
}
|
||||
|
||||
const agentResult = config.agent as AgentConfig;
|
||||
|
||||
config.tools = {
|
||||
...(config.tools as Record<string, unknown>),
|
||||
"grep_app_*": false,
|
||||
LspHover: false,
|
||||
LspCodeActions: false,
|
||||
LspCodeActionResolve: false,
|
||||
"task_*": false,
|
||||
teammate: false,
|
||||
...(pluginConfig.experimental?.task_system ? { todowrite: false, todoread: false } : {}),
|
||||
};
|
||||
|
||||
type AgentWithPermission = { permission?: Record<string, unknown> };
|
||||
|
||||
// In CLI run mode, deny Question tool for all agents (no TUI to answer questions)
|
||||
const isCliRunMode = process.env.OPENCODE_CLI_RUN_MODE === "true";
|
||||
const questionPermission = isCliRunMode ? "deny" : "allow";
|
||||
|
||||
if (agentResult.librarian) {
|
||||
const agent = agentResult.librarian as AgentWithPermission;
|
||||
agent.permission = { ...agent.permission, "grep_app_*": "allow" };
|
||||
}
|
||||
if (agentResult["multimodal-looker"]) {
|
||||
const agent = agentResult["multimodal-looker"] as AgentWithPermission;
|
||||
agent.permission = { ...agent.permission, task: "deny", look_at: "deny" };
|
||||
}
|
||||
if (agentResult["atlas"]) {
|
||||
const agent = agentResult["atlas"] as AgentWithPermission;
|
||||
agent.permission = { ...agent.permission, task: "allow", call_omo_agent: "deny", "task_*": "allow", teammate: "allow" };
|
||||
}
|
||||
if (agentResult.sisyphus) {
|
||||
const agent = agentResult.sisyphus as AgentWithPermission;
|
||||
agent.permission = { ...agent.permission, call_omo_agent: "deny", task: "allow", question: questionPermission, "task_*": "allow", teammate: "allow" };
|
||||
}
|
||||
if (agentResult.hephaestus) {
|
||||
const agent = agentResult.hephaestus as AgentWithPermission;
|
||||
agent.permission = { ...agent.permission, call_omo_agent: "deny", task: "allow", question: questionPermission };
|
||||
}
|
||||
if (agentResult["prometheus"]) {
|
||||
const agent = agentResult["prometheus"] as AgentWithPermission;
|
||||
agent.permission = { ...agent.permission, call_omo_agent: "deny", task: "allow", question: questionPermission, "task_*": "allow", teammate: "allow" };
|
||||
}
|
||||
if (agentResult["sisyphus-junior"]) {
|
||||
const agent = agentResult["sisyphus-junior"] as AgentWithPermission;
|
||||
agent.permission = { ...agent.permission, task: "allow", "task_*": "allow", teammate: "allow" };
|
||||
}
|
||||
|
||||
config.permission = {
|
||||
...(config.permission as Record<string, unknown>),
|
||||
webfetch: "allow",
|
||||
external_directory: "allow",
|
||||
task: "deny",
|
||||
};
|
||||
|
||||
const mcpResult = (pluginConfig.claude_code?.mcp ?? true)
|
||||
? await loadMcpConfigs()
|
||||
: { servers: {} };
|
||||
|
||||
config.mcp = {
|
||||
...createBuiltinMcps(pluginConfig.disabled_mcps, pluginConfig),
|
||||
...(config.mcp as Record<string, unknown>),
|
||||
...mcpResult.servers,
|
||||
...pluginComponents.mcpServers,
|
||||
};
|
||||
|
||||
const builtinCommands = loadBuiltinCommands(pluginConfig.disabled_commands);
|
||||
const systemCommands = (config.command as Record<string, unknown>) ?? {};
|
||||
|
||||
// Parallel loading of all commands and skills for faster startup
|
||||
const includeClaudeCommands = pluginConfig.claude_code?.commands ?? true;
|
||||
const includeClaudeSkills = pluginConfig.claude_code?.skills ?? true;
|
||||
|
||||
const [
|
||||
userCommands,
|
||||
projectCommands,
|
||||
opencodeGlobalCommands,
|
||||
opencodeProjectCommands,
|
||||
userSkills,
|
||||
projectSkills,
|
||||
opencodeGlobalSkills,
|
||||
opencodeProjectSkills,
|
||||
] = await Promise.all([
|
||||
includeClaudeCommands ? loadUserCommands() : Promise.resolve({}),
|
||||
includeClaudeCommands ? loadProjectCommands() : Promise.resolve({}),
|
||||
loadOpencodeGlobalCommands(),
|
||||
loadOpencodeProjectCommands(),
|
||||
includeClaudeSkills ? loadUserSkills() : Promise.resolve({}),
|
||||
includeClaudeSkills ? loadProjectSkills() : Promise.resolve({}),
|
||||
loadOpencodeGlobalSkills(),
|
||||
loadOpencodeProjectSkills(),
|
||||
]);
|
||||
|
||||
config.command = {
|
||||
...builtinCommands,
|
||||
...userCommands,
|
||||
...userSkills,
|
||||
...opencodeGlobalCommands,
|
||||
...opencodeGlobalSkills,
|
||||
...systemCommands,
|
||||
...projectCommands,
|
||||
...projectSkills,
|
||||
...opencodeProjectCommands,
|
||||
...opencodeProjectSkills,
|
||||
...pluginComponents.commands,
|
||||
...pluginComponents.skills,
|
||||
};
|
||||
log("[config-handler] config handler applied", {
|
||||
agentCount: Object.keys(agentResult).length,
|
||||
commandCount: Object.keys((config.command as Record<string, unknown>) ?? {})
|
||||
.length,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user