index.ts 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. import type { Plugin } from '@opencode-ai/plugin';
  2. import { getAgentConfigs } from './agents';
  3. import { BackgroundTaskManager, TmuxSessionManager } from './background';
  4. import { loadPluginConfig, type TmuxConfig } from './config';
  5. import {
  6. createAutoUpdateCheckerHook,
  7. createPhaseReminderHook,
  8. createPostReadNudgeHook,
  9. } from './hooks';
  10. import { createBuiltinMcps } from './mcp';
  11. import {
  12. antigravity_quota,
  13. ast_grep_replace,
  14. ast_grep_search,
  15. createBackgroundTools,
  16. createSkillTools,
  17. grep,
  18. lsp_diagnostics,
  19. lsp_find_references,
  20. lsp_goto_definition,
  21. lsp_rename,
  22. SkillMcpManager,
  23. } from './tools';
  24. import { startTmuxCheck } from './utils';
  25. import { log } from './utils/logger';
  26. import { parseList } from './tools/skill/builtin';
  27. const OhMyOpenCodeLite: Plugin = async (ctx) => {
  28. const config = loadPluginConfig(ctx.directory);
  29. const agents = getAgentConfigs(config);
  30. // Parse tmux config with defaults
  31. const tmuxConfig: TmuxConfig = {
  32. enabled: config.tmux?.enabled ?? false,
  33. layout: config.tmux?.layout ?? 'main-vertical',
  34. main_pane_size: config.tmux?.main_pane_size ?? 60,
  35. };
  36. log('[plugin] initialized with tmux config', {
  37. tmuxConfig,
  38. rawTmuxConfig: config.tmux,
  39. directory: ctx.directory,
  40. });
  41. // Start background tmux check if enabled
  42. if (tmuxConfig.enabled) {
  43. startTmuxCheck();
  44. }
  45. const backgroundManager = new BackgroundTaskManager(ctx, tmuxConfig, config);
  46. const backgroundTools = createBackgroundTools(
  47. ctx,
  48. backgroundManager,
  49. tmuxConfig,
  50. config,
  51. );
  52. const mcps = createBuiltinMcps(config.disabled_mcps);
  53. const skillMcpManager = SkillMcpManager.getInstance();
  54. const skillTools = createSkillTools(skillMcpManager, config);
  55. // Initialize TmuxSessionManager to handle OpenCode's built-in Task tool sessions
  56. const tmuxSessionManager = new TmuxSessionManager(ctx, tmuxConfig);
  57. // Initialize auto-update checker hook
  58. const autoUpdateChecker = createAutoUpdateCheckerHook(ctx, {
  59. showStartupToast: true,
  60. autoUpdate: true,
  61. });
  62. // Initialize phase reminder hook for workflow compliance
  63. const phaseReminderHook = createPhaseReminderHook();
  64. // Initialize post-read nudge hook
  65. const postReadNudgeHook = createPostReadNudgeHook();
  66. return {
  67. name: 'oh-my-opencode-slim',
  68. agent: agents,
  69. tool: {
  70. ...backgroundTools,
  71. lsp_goto_definition,
  72. lsp_find_references,
  73. lsp_diagnostics,
  74. lsp_rename,
  75. grep,
  76. ast_grep_search,
  77. ast_grep_replace,
  78. antigravity_quota,
  79. ...skillTools,
  80. },
  81. mcp: mcps,
  82. config: async (opencodeConfig: Record<string, unknown>) => {
  83. (opencodeConfig as { default_agent?: string }).default_agent =
  84. 'orchestrator';
  85. // Merge Agent configs
  86. if (!opencodeConfig.agent) {
  87. opencodeConfig.agent = { ...agents };
  88. } else {
  89. Object.assign(opencodeConfig.agent, agents);
  90. }
  91. const configAgent = opencodeConfig.agent as Record<string, any>;
  92. // Merge MCP configs
  93. const configMcp = opencodeConfig.mcp as
  94. | Record<string, unknown>
  95. | undefined;
  96. if (!configMcp) {
  97. opencodeConfig.mcp = { ...mcps };
  98. } else {
  99. Object.assign(configMcp, mcps);
  100. }
  101. // Get all MCP names from our config
  102. const allMcpNames = Object.keys(mcps);
  103. // For each agent, create permission rules based on their mcps list
  104. for (const [agentName, agentConfig] of Object.entries(agents)) {
  105. const agentMcps = (agentConfig as { mcps?: string[] })?.mcps;
  106. if (!agentMcps) continue;
  107. // Get or create agent permission config
  108. if (!configAgent[agentName]) {
  109. configAgent[agentName] = { ...agentConfig };
  110. }
  111. const agentPermission = (configAgent[agentName].permission ?? {}) as Record<string, unknown>;
  112. // Parse mcps list with wildcard and exclusion support
  113. const allowedMcps = parseList(agentMcps, allMcpNames);
  114. // Create permission rules for each MCP
  115. // MCP tools are named as <server>_<tool>, so we use <server>_*
  116. for (const mcpName of allMcpNames) {
  117. const sanitizedMcpName = mcpName.replace(/[^a-zA-Z0-9_-]/g, '_');
  118. const permissionKey = `${sanitizedMcpName}_*`;
  119. const action = allowedMcps.includes(mcpName) ? 'allow' : 'deny';
  120. // Only set if not already defined by user
  121. if (!(permissionKey in agentPermission)) {
  122. agentPermission[permissionKey] = action;
  123. }
  124. }
  125. // Update agent config with permissions
  126. configAgent[agentName].permission = agentPermission;
  127. }
  128. },
  129. event: async (input) => {
  130. // Handle auto-update checking
  131. await autoUpdateChecker.event(input);
  132. // Handle tmux pane spawning for OpenCode's Task tool sessions
  133. await tmuxSessionManager.onSessionCreated(
  134. input.event as {
  135. type: string;
  136. properties?: {
  137. info?: { id?: string; parentID?: string; title?: string };
  138. };
  139. },
  140. );
  141. },
  142. // Inject phase reminder before sending to API (doesn't show in UI)
  143. 'experimental.chat.messages.transform':
  144. phaseReminderHook['experimental.chat.messages.transform'],
  145. // Nudge after file reads to encourage delegation
  146. 'tool.execute.after': postReadNudgeHook['tool.execute.after'],
  147. };
  148. };
  149. export default OhMyOpenCodeLite;
  150. export type {
  151. AgentName,
  152. AgentOverrideConfig,
  153. McpName,
  154. PluginConfig,
  155. TmuxConfig,
  156. TmuxLayout,
  157. } from './config';
  158. export type { RemoteMcpConfig } from './mcp';