@hiai-gg/hiai-opencode 0.1.6 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (140) hide show
  1. package/.env.example +7 -0
  2. package/AGENTS.md +46 -1
  3. package/ARCHITECTURE.md +6 -3
  4. package/README.md +71 -10
  5. package/assets/cli/hiai-opencode.mjs +78 -0
  6. package/config/hiai-opencode.schema.json +16 -1
  7. package/dist/agents/agent-skills.d.ts +7 -0
  8. package/dist/agents/bob/default.d.ts +1 -0
  9. package/dist/agents/bob/gemini.d.ts +1 -0
  10. package/dist/agents/bob/gpt-pro.d.ts +1 -0
  11. package/dist/agents/brainstormer.d.ts +7 -0
  12. package/dist/agents/coder/gpt-codex.d.ts +1 -1
  13. package/dist/agents/coder/gpt-pro.d.ts +1 -0
  14. package/dist/agents/coder/gpt.d.ts +2 -1
  15. package/dist/agents/designer.d.ts +7 -0
  16. package/dist/agents/strategist/gemini.d.ts +1 -0
  17. package/dist/agents/strategist/gpt.d.ts +1 -0
  18. package/dist/agents/types.d.ts +3 -1
  19. package/dist/config/index.d.ts +0 -1
  20. package/dist/config/platform-schema.d.ts +32 -0
  21. package/dist/config/schema/hooks.d.ts +0 -2
  22. package/dist/config/schema/index.d.ts +0 -2
  23. package/dist/config/schema/oh-my-opencode-config.d.ts +0 -6
  24. package/dist/config/types.d.ts +3 -1
  25. package/dist/create-hooks.d.ts +0 -2
  26. package/dist/features/builtin-skills/skills/index.d.ts +1 -0
  27. package/dist/features/builtin-skills/skills/website-copywriting.d.ts +2 -0
  28. package/dist/hooks/agent-usage-reminder/constants.d.ts +1 -1
  29. package/dist/hooks/index.d.ts +0 -2
  30. package/dist/hooks/keyword-detector/ultrawork/default.d.ts +1 -1
  31. package/dist/hooks/keyword-detector/ultrawork/gemini.d.ts +1 -1
  32. package/dist/hooks/keyword-detector/ultrawork/gpt.d.ts +1 -1
  33. package/dist/hooks/keyword-detector/ultrawork/planner.d.ts +1 -1
  34. package/dist/index.js +8963 -153866
  35. package/dist/mcp/index.d.ts +0 -1
  36. package/dist/mcp/registry.d.ts +1 -1
  37. package/dist/plugin/hooks/create-core-hooks.d.ts +0 -2
  38. package/dist/plugin/hooks/create-session-hooks.d.ts +1 -3
  39. package/dist/plugin/startup-diagnostics.d.ts +1 -0
  40. package/dist/shared/logger.d.ts +2 -0
  41. package/dist/shared/mode-routing.d.ts +6 -0
  42. package/dist/tools/delegate-task/git-categories.d.ts +2 -0
  43. package/dist/tools/delegate-task/sub-agent.d.ts +2 -0
  44. package/dist/tools/skill-mcp/constants.d.ts +1 -1
  45. package/hiai-opencode.json +48 -19
  46. package/package.json +6 -3
  47. package/src/agents/agent-skills.ts +70 -0
  48. package/src/agents/bob/default.ts +1 -0
  49. package/src/agents/bob/gemini.ts +1 -0
  50. package/src/agents/bob/gpt-pro.ts +2 -1
  51. package/src/agents/bob.ts +2 -0
  52. package/src/agents/brainstormer.ts +72 -0
  53. package/src/agents/builtin-agents.ts +59 -3
  54. package/src/agents/coder/gpt-codex.ts +4 -3
  55. package/src/agents/coder/gpt-pro.ts +3 -2
  56. package/src/agents/coder/gpt.ts +2 -1
  57. package/src/agents/critic/agent.ts +1 -0
  58. package/src/agents/designer.ts +70 -0
  59. package/src/agents/dynamic-agent-category-skills-guide.ts +6 -0
  60. package/src/agents/guard/default.ts +1 -0
  61. package/src/agents/guard/gemini.ts +1 -0
  62. package/src/agents/guard/gpt.ts +1 -0
  63. package/src/agents/platform-manager.ts +17 -1
  64. package/src/agents/prompt-library/platform.ts +34 -0
  65. package/src/agents/researcher.ts +1 -0
  66. package/src/agents/strategist/gemini.ts +1 -0
  67. package/src/agents/strategist/gpt.ts +1 -0
  68. package/src/agents/types.ts +4 -1
  69. package/src/agents/ui.ts +1 -0
  70. package/src/config/defaults.ts +31 -12
  71. package/src/config/index.ts +0 -1
  72. package/src/config/model-slots-and-export.test.ts +22 -4
  73. package/src/config/platform-schema.ts +2 -0
  74. package/src/config/schema/hooks.ts +0 -2
  75. package/src/config/schema/index.ts +0 -2
  76. package/src/config/schema/oh-my-opencode-config.ts +0 -2
  77. package/src/config/types.ts +3 -1
  78. package/src/features/builtin-skills/skills/index.ts +1 -0
  79. package/src/features/builtin-skills/skills/website-copywriting.ts +41 -0
  80. package/src/features/builtin-skills/skills.test.ts +8 -0
  81. package/src/features/builtin-skills/skills.ts +2 -0
  82. package/src/features/skill-mcp-manager/AGENTS.md +1 -1
  83. package/src/hooks/agent-usage-reminder/constants.ts +4 -4
  84. package/src/hooks/index.ts +0 -2
  85. package/src/hooks/keyword-detector/ultrawork/default.ts +18 -18
  86. package/src/hooks/keyword-detector/ultrawork/gemini.ts +21 -21
  87. package/src/hooks/keyword-detector/ultrawork/gpt.ts +6 -8
  88. package/src/hooks/keyword-detector/ultrawork/planner.ts +5 -5
  89. package/src/index.ts +5 -3
  90. package/src/internals/plugins/subtask2/commands/manifest.ts +2 -6
  91. package/src/internals/plugins/subtask2/hooks/command-hooks.ts +2 -2
  92. package/src/internals/plugins/subtask2/hooks/message-hooks.ts +1 -1
  93. package/src/internals/plugins/subtask2/parsing/parallel.ts +13 -10
  94. package/src/mcp/index.ts +0 -1
  95. package/src/mcp/registry.ts +27 -0
  96. package/src/plugin/chat-message.ts +0 -2
  97. package/src/plugin/hooks/create-session-hooks.ts +0 -17
  98. package/src/plugin/startup-diagnostics.ts +27 -0
  99. package/src/plugin-handlers/agent-config-handler.ts +3 -2
  100. package/src/plugin-handlers/mcp-config-handler.test.ts +63 -0
  101. package/src/plugin-handlers/mcp-config-handler.ts +29 -14
  102. package/src/plugin-handlers/strategist-agent-config-builder.ts +1 -1
  103. package/src/shared/agent-display-names.test.ts +9 -0
  104. package/src/shared/agent-display-names.ts +5 -0
  105. package/src/shared/log-legacy-plugin-startup-warning.ts +6 -8
  106. package/src/shared/logger.ts +8 -0
  107. package/src/shared/mcp-static-export.ts +4 -6
  108. package/src/shared/migration/agent-names.ts +8 -0
  109. package/src/shared/migration/hook-names.ts +1 -1
  110. package/src/shared/mode-routing.test.ts +88 -0
  111. package/src/shared/mode-routing.ts +30 -0
  112. package/src/shared/startup-diagnostics.ts +6 -7
  113. package/src/tools/call-omo-agent/tools.ts +11 -4
  114. package/src/tools/delegate-task/anthropic-categories.ts +3 -3
  115. package/src/tools/delegate-task/builtin-categories.ts +2 -0
  116. package/src/tools/delegate-task/categories.test.ts +87 -0
  117. package/src/tools/delegate-task/category-resolver.ts +8 -9
  118. package/src/tools/delegate-task/git-categories.ts +30 -0
  119. package/src/tools/delegate-task/model-string-parser.test.ts +90 -0
  120. package/src/tools/delegate-task/openai-categories.ts +26 -22
  121. package/src/tools/delegate-task/sub-agent.ts +10 -0
  122. package/src/tools/delegate-task/subagent-discovery.test.ts +123 -0
  123. package/src/tools/delegate-task/subagent-resolver.ts +18 -1
  124. package/src/tools/skill-mcp/constants.ts +1 -1
  125. package/dist/config/schema/websearch.d.ts +0 -13
  126. package/dist/hooks/no-bob-gpt/hook.d.ts +0 -16
  127. package/dist/hooks/no-bob-gpt/index.d.ts +0 -1
  128. package/dist/hooks/no-coder-non-gpt/hook.d.ts +0 -20
  129. package/dist/hooks/no-coder-non-gpt/index.d.ts +0 -1
  130. package/dist/mcp/grep-app.d.ts +0 -6
  131. package/dist/mcp/omo-mcp-index.d.ts +0 -10
  132. package/dist/mcp/websearch.d.ts +0 -11
  133. package/src/config/schema/websearch.ts +0 -15
  134. package/src/hooks/no-bob-gpt/hook.ts +0 -56
  135. package/src/hooks/no-bob-gpt/index.ts +0 -1
  136. package/src/hooks/no-coder-non-gpt/hook.ts +0 -67
  137. package/src/hooks/no-coder-non-gpt/index.ts +0 -1
  138. package/src/mcp/grep-app.ts +0 -6
  139. package/src/mcp/omo-mcp-index.ts +0 -30
  140. package/src/mcp/websearch.ts +0 -44
@@ -1,29 +1,33 @@
1
1
  import type { BuiltinCategoryDefinition } from "./builtin-category-definition"
2
2
 
3
3
  const ULTRABRAIN_CATEGORY_PROMPT_APPEND = `<Category_Context>
4
- You are working on DEEP LOGICAL REASONING / COMPLEX ARCHITECTURE tasks.
4
+ You are working on DEEP LOGICAL REASONING / ARCHITECTURE / STRATEGY tasks.
5
5
 
6
6
  <Routing_Policy>
7
- Executor contour: coder (deep execution). Do not use this category for bounded quick edits.
7
+ Executor contour: strategist (plan + analysis, read-only). This category produces a structured plan, NOT implementation. The caller is responsible for routing the implementation step to \`deep\`, \`bounded\`, or \`cross-module\` mode based on your plan.
8
8
  </Routing_Policy>
9
9
 
10
- **CRITICAL - CODE STYLE REQUIREMENTS (NON-NEGOTIABLE)**:
11
- 1. BEFORE writing ANY code, SEARCH the existing codebase to find similar patterns/styles
12
- 2. Your code MUST match the project's existing conventions - blend in seamlessly
13
- 3. Write READABLE code that humans can easily understand - no clever tricks
14
- 4. If unsure about style, explore more files until you find the pattern
15
-
16
- Strategic advisor mindset:
17
- - Bias toward simplicity: least complex solution that fulfills requirements
18
- - Leverage existing code/patterns over new components
19
- - Prioritize developer experience and maintainability
20
- - One clear recommendation with effort estimate (Quick/Short/Medium/Large)
21
- - Signal when advanced approach warranted
22
-
23
- Response format:
24
- - Bottom line (2-3 sentences)
25
- - Action plan (numbered steps)
26
- - Risks and mitigations (if relevant)
10
+ <Output_Contract>
11
+ Your output MUST contain:
12
+ 1. **Decomposition** - concrete tasks with bounded scope (each task small enough that one executor agent can complete it without further planning).
13
+ 2. **Dependency graph** - which tasks block which; identify parallel-safe tasks explicitly.
14
+ 3. **Tradeoffs** - at least 2 alternative approaches with cost/risk for each; recommend one and justify briefly.
15
+ 4. **Recommended next step** - explicit \`task(category=...)\` call the caller should make to start implementation.
16
+ 5. **Open questions** - assumptions the caller should confirm before kickoff (if any).
17
+
18
+ You MUST NOT:
19
+ - Write or edit code (you have no write/edit tools).
20
+ - Mark task as done yourself; the caller verifies the plan and routes implementation.
21
+ - Defer planning by asking clarifying questions when assumptions can be stated and surfaced as Open questions.
22
+ </Output_Contract>
23
+
24
+ <Reasoning_Discipline>
25
+ - Bias toward simplicity: least complex solution that fulfills requirements.
26
+ - Leverage existing code/patterns over new components - reference concrete files when relevant.
27
+ - Surface assumptions explicitly as Open questions rather than guessing silently.
28
+ - For unfamiliar domains, recommend a \`task(subagent_type="researcher", run_in_background=true, ...)\` exploration step before planning.
29
+ - One clear recommendation with effort estimate (Quick / Short / Medium / Large).
30
+ </Reasoning_Discipline>
27
31
  </Category_Context>`
28
32
 
29
33
  const DEEP_CATEGORY_PROMPT_APPEND = `<Category_Context>
@@ -55,7 +59,7 @@ const QUICK_CATEGORY_PROMPT_APPEND = `<Category_Context>
55
59
  You are working on SMALL / QUICK tasks.
56
60
 
57
61
  <Routing_Policy>
58
- Executor contour: coder (fast bounded execution). Keep the implementation narrow and escalate only when the task truly becomes deep or cross-system.
62
+ Executor contour: sub (cheap fast-tier executor). Keep the implementation narrow and escalate only when the task truly becomes deep or cross-system.
59
63
  </Routing_Policy>
60
64
 
61
65
  Efficient execution mindset:
@@ -110,7 +114,7 @@ export const OPENAI_CATEGORIES: BuiltinCategoryDefinition[] = [
110
114
  {
111
115
  name: "ultrabrain",
112
116
  config: {},
113
- description: "Hard logic and architecture tasks. Uses coder execution contour; avoid for quick bounded edits.",
117
+ description: "Hard logic, architecture, strategy. Returns structured plan (decomposition + dependency graph + tradeoffs), NOT implementation. Follow up with deep/bounded/cross-module mode for build.",
114
118
  promptAppend: ULTRABRAIN_CATEGORY_PROMPT_APPEND,
115
119
  },
116
120
  {
@@ -122,7 +126,7 @@ export const OPENAI_CATEGORIES: BuiltinCategoryDefinition[] = [
122
126
  {
123
127
  name: "quick",
124
128
  config: {},
125
- description: "Fast bounded execution: single-file fixes, typos, and simple modifications. Uses coder execution contour.",
129
+ description: "Fast bounded execution: single-file fixes, typos, and simple modifications. Uses sub (cheap fast-tier) execution contour.",
126
130
  promptAppend: QUICK_CATEGORY_PROMPT_APPEND,
127
131
  },
128
132
  ]
@@ -21,6 +21,7 @@ export const CANONICAL_DELEGATE_AGENT_KEYS = [
21
21
  ] as const
22
22
 
23
23
  type CanonicalDelegateAgentKey = (typeof CANONICAL_DELEGATE_AGENT_KEYS)[number]
24
+ export type { CanonicalDelegateAgentKey }
24
25
  const CANONICAL_DELEGATE_AGENT_KEY_SET = new Set<string>(CANONICAL_DELEGATE_AGENT_KEYS)
25
26
 
26
27
  const LEGACY_DELEGATE_AGENT_ALIASES: Record<string, CanonicalDelegateAgentKey> = {
@@ -44,8 +45,17 @@ const LEGACY_DELEGATE_AGENT_ALIASES: Record<string, CanonicalDelegateAgentKey> =
44
45
  "project-initializer": "platform-manager",
45
46
  mindmodel: "platform-manager",
46
47
  ui: "multimodal",
48
+ writer: "brainstormer",
49
+ copywriter: "brainstormer",
50
+ "content-writer": "brainstormer",
47
51
  subagent: "sub",
48
52
  "bob-junior": "sub",
53
+ oracle: "strategist",
54
+ hephaestus: "coder",
55
+ metis: "strategist",
56
+ momus: "critic",
57
+ "sisyphus-junior": "sub",
58
+ "multimodal-looker": "multimodal",
49
59
  }
50
60
 
51
61
  export function resolveCanonicalDelegateAgentKey(agentName: string): string {
@@ -0,0 +1,123 @@
1
+ import { expect, test, describe } from "bun:test"
2
+ import { sanitizeSubagentType, findPrimaryAgentMatch, findCallableAgentMatch, listCallableAgentNames, isTaskCallableAgentMode } from "./subagent-discovery"
3
+
4
+ describe("sanitizeSubagentType", () => {
5
+ test("trims whitespace", () => {
6
+ expect(sanitizeSubagentType(" researcher ")).toBe("researcher")
7
+ })
8
+
9
+ test("removes surrounding quotes", () => {
10
+ expect(sanitizeSubagentType('"researcher"')).toBe("researcher")
11
+ expect(sanitizeSubagentType("'researcher'")).toBe("researcher")
12
+ })
13
+
14
+ test("removes surrounding slashes", () => {
15
+ expect(sanitizeSubagentType("/researcher/")).toBe("researcher")
16
+ })
17
+
18
+ test("removes backslashes", () => {
19
+ expect(sanitizeSubagentType("\\researcher\\")).toBe("researcher")
20
+ })
21
+
22
+ test("handles mixed characters", () => {
23
+ expect(sanitizeSubagentType(' /"researcher"\\ ')).toBe("researcher")
24
+ })
25
+ })
26
+
27
+ describe("isTaskCallableAgentMode", () => {
28
+ test("returns true for subagent mode", () => {
29
+ expect(isTaskCallableAgentMode("subagent")).toBe(true)
30
+ })
31
+
32
+ test("returns true for all mode", () => {
33
+ expect(isTaskCallableAgentMode("all")).toBe(true)
34
+ })
35
+
36
+ test("returns false for primary mode", () => {
37
+ expect(isTaskCallableAgentMode("primary")).toBe(false)
38
+ })
39
+
40
+ test("returns false for undefined", () => {
41
+ expect(isTaskCallableAgentMode(undefined)).toBe(false)
42
+ })
43
+ })
44
+
45
+ describe("findPrimaryAgentMatch", () => {
46
+ const agents = [
47
+ { name: "Bob", mode: "primary" as const, model: "openrouter/test/bob" },
48
+ { name: "Coder", mode: "primary" as const, model: "openrouter/test/coder" },
49
+ { name: "Researcher", mode: "primary" as const, model: "openrouter/test/researcher" },
50
+ { name: "SubAgent", mode: "subagent" as const, model: "openrouter/test/sub" },
51
+ ]
52
+
53
+ test("finds primary agent by exact name", () => {
54
+ const result = findPrimaryAgentMatch(agents, "Bob")
55
+ expect(result?.name).toBe("Bob")
56
+ })
57
+
58
+ test("finds primary agent by canonical key", () => {
59
+ const result = findPrimaryAgentMatch(agents, "researcher")
60
+ expect(result?.name).toBe("Researcher")
61
+ })
62
+
63
+ test("returns undefined for non-existent agent", () => {
64
+ const result = findPrimaryAgentMatch(agents, "Nonexistent")
65
+ expect(result).toBeUndefined()
66
+ })
67
+
68
+ test("returns undefined when searching for subagent mode", () => {
69
+ const result = findPrimaryAgentMatch(agents, "SubAgent")
70
+ expect(result).toBeUndefined()
71
+ })
72
+ })
73
+
74
+ describe("findCallableAgentMatch", () => {
75
+ const agents = [
76
+ { name: "Bob", mode: "primary" as const, model: "openrouter/test/bob" },
77
+ { name: "Coder", mode: "primary" as const, model: "openrouter/test/coder" },
78
+ { name: "Researcher", mode: "subagent" as const, model: "openrouter/test/researcher" },
79
+ { name: "SubAgent", mode: "subagent" as const, model: "openrouter/test/sub" },
80
+ { name: "Vision", mode: "all" as const, model: "openrouter/test/vision" },
81
+ ]
82
+
83
+ test("finds callable agent by subagent mode", () => {
84
+ const result = findCallableAgentMatch(agents, "SubAgent")
85
+ expect(result?.name).toBe("SubAgent")
86
+ })
87
+
88
+ test("finds callable agent by all mode", () => {
89
+ const result = findCallableAgentMatch(agents, "Vision")
90
+ expect(result?.name).toBe("Vision")
91
+ })
92
+
93
+ test("finds callable agent by canonical researcher (subagent mode)", () => {
94
+ const result = findCallableAgentMatch(agents, "researcher")
95
+ expect(result?.name).toBe("Researcher")
96
+ })
97
+
98
+ test("returns undefined for primary-only agent", () => {
99
+ const result = findCallableAgentMatch(agents, "Bob")
100
+ expect(result).toBeUndefined()
101
+ })
102
+ })
103
+
104
+ describe("listCallableAgentNames", () => {
105
+ test("lists callable agents sorted alphabetically (subagent and all modes)", () => {
106
+ const agents = [
107
+ { name: "Vision", mode: "all" as const },
108
+ { name: "Researcher", mode: "subagent" as const },
109
+ { name: "Bob", mode: "primary" as const },
110
+ ]
111
+ const result = listCallableAgentNames(agents)
112
+ expect(result).toBe("Researcher, Vision")
113
+ })
114
+
115
+ test("returns empty string for no callable agents", () => {
116
+ const agents = [
117
+ { name: "Bob", mode: "primary" as const },
118
+ { name: "Coder", mode: "primary" as const },
119
+ ]
120
+ const result = listCallableAgentNames(agents)
121
+ expect(result).toBe("")
122
+ })
123
+ })
@@ -73,6 +73,23 @@ Create the work plan directly - that's your job as the planning agent.`,
73
73
  }
74
74
  }
75
75
 
76
+ const ORCHESTRATOR_AGENTS: Set<string> = new Set(["bob"])
77
+ if (ORCHESTRATOR_AGENTS.has(requestedCanonicalAgentKey)) {
78
+ return {
79
+ agentToUse: "",
80
+ categoryModel: undefined,
81
+ error: `Cannot delegate to orchestrator agent "${requestedCanonicalAgentKey}". Bob is the entry-point orchestrator and is not callable as a subagent. Use a specific subagent (researcher/strategist/coder/...) or a mode (quick/deep/...).`,
82
+ }
83
+ }
84
+
85
+ if (canonicalParentAgent && canonicalParentAgent === requestedCanonicalAgentKey) {
86
+ return {
87
+ agentToUse: "",
88
+ categoryModel: undefined,
89
+ error: `Self-delegation not allowed: agent "${requestedCanonicalAgentKey}" cannot delegate to itself via task. Continue the work directly or use session_id=... to continue an existing subagent session.`,
90
+ }
91
+ }
92
+
76
93
  let agentToUse = requestedAgentName
77
94
  let categoryModel: DelegatedModelConfig | undefined
78
95
  let fallbackChain: FallbackEntry[] | undefined = undefined
@@ -84,8 +101,8 @@ Create the work plan directly - that's your job as the planning agent.`,
84
101
  })
85
102
 
86
103
  const mergedAgents = mergeWithClaudeCodeAgents(agents, executorCtx.directory)
87
- const matchedPrimaryAgent = findPrimaryAgentMatch(mergedAgents, agentToUse)
88
104
 
105
+ const matchedPrimaryAgent = findPrimaryAgentMatch(mergedAgents, agentToUse)
89
106
  if (matchedPrimaryAgent) {
90
107
  return {
91
108
  agentToUse: "",
@@ -1,6 +1,6 @@
1
1
  export const SKILL_MCP_TOOL_NAME = "skill_mcp"
2
2
 
3
- export const SKILL_MCP_DESCRIPTION = `Invoke MCP server operations from skill-embedded MCPs. Requires mcp_name plus exactly one of: tool_name, resource_name, or prompt_name.`
3
+ export const SKILL_MCP_DESCRIPTION = `Invoke MCP server operations from skill-embedded MCPs or enabled hiai-opencode builtin MCPs. Requires mcp_name plus exactly one of: tool_name, resource_name, or prompt_name.`
4
4
 
5
5
  export const BUILTIN_MCP_TOOL_HINTS: Record<string, string[]> = {
6
6
  context7: ["context7_resolve-library-id", "context7_query-docs"],
@@ -1,13 +0,0 @@
1
- import { z } from "zod";
2
- export declare const WebsearchProviderSchema: z.ZodEnum<{
3
- exa: "exa";
4
- tavily: "tavily";
5
- }>;
6
- export declare const WebsearchConfigSchema: z.ZodObject<{
7
- provider: z.ZodOptional<z.ZodEnum<{
8
- exa: "exa";
9
- tavily: "tavily";
10
- }>>;
11
- }, z.core.$strip>;
12
- export type WebsearchProvider = z.infer<typeof WebsearchProviderSchema>;
13
- export type WebsearchConfig = z.infer<typeof WebsearchConfigSchema>;
@@ -1,16 +0,0 @@
1
- import type { PluginInput } from "@opencode-ai/plugin";
2
- export declare function createNoBobGptHook(ctx: PluginInput): {
3
- "chat.message": (input: {
4
- sessionID: string;
5
- agent?: string;
6
- model?: {
7
- providerID: string;
8
- modelID: string;
9
- };
10
- }, output?: {
11
- message?: {
12
- agent?: string;
13
- [key: string]: unknown;
14
- };
15
- }) => Promise<void>;
16
- };
@@ -1 +0,0 @@
1
- export { createNoBobGptHook } from "./hook";
@@ -1,20 +0,0 @@
1
- import type { PluginInput } from "@opencode-ai/plugin";
2
- type NoCoderNonGptHookOptions = {
3
- allowNonGptModel?: boolean;
4
- };
5
- export declare function createNoCoderNonGptHook(ctx: PluginInput, options?: NoCoderNonGptHookOptions): {
6
- "chat.message": (input: {
7
- sessionID: string;
8
- agent?: string;
9
- model?: {
10
- providerID: string;
11
- modelID: string;
12
- };
13
- }, output?: {
14
- message?: {
15
- agent?: string;
16
- [key: string]: unknown;
17
- };
18
- }) => Promise<void>;
19
- };
20
- export {};
@@ -1 +0,0 @@
1
- export { createNoCoderNonGptHook } from "./hook";
@@ -1,6 +0,0 @@
1
- export declare const grep_app: {
2
- type: "remote";
3
- url: string;
4
- enabled: boolean;
5
- oauth: false;
6
- };
@@ -1,10 +0,0 @@
1
- import type { HiaiOpenCodeConfig } from "../config/schema";
2
- export { McpNameSchema, type McpName } from "./types";
3
- type RemoteMcpConfig = {
4
- type: "remote";
5
- url: string;
6
- enabled: boolean;
7
- headers?: Record<string, string>;
8
- oauth?: false;
9
- };
10
- export declare function createBuiltinMcps(disabledMcps?: string[], config?: HiaiOpenCodeConfig): Record<string, RemoteMcpConfig>;
@@ -1,11 +0,0 @@
1
- import type { WebsearchConfig } from "../config/schema";
2
- type RemoteMcpConfig = {
3
- type: "remote";
4
- url: string;
5
- enabled: boolean;
6
- headers?: Record<string, string>;
7
- oauth?: false;
8
- };
9
- export declare function createWebsearchConfig(config?: WebsearchConfig): RemoteMcpConfig | undefined;
10
- export declare const websearch: RemoteMcpConfig | undefined;
11
- export {};
@@ -1,15 +0,0 @@
1
- import { z } from "zod"
2
-
3
- export const WebsearchProviderSchema = z.enum(["exa", "tavily"])
4
-
5
- export const WebsearchConfigSchema = z.object({
6
- /**
7
- * Websearch provider to use.
8
- * - "exa": Uses Exa websearch (default, works without API key)
9
- * - "tavily": Uses Tavily websearch (requires TAVILY_API_KEY)
10
- */
11
- provider: WebsearchProviderSchema.optional(),
12
- })
13
-
14
- export type WebsearchProvider = z.infer<typeof WebsearchProviderSchema>
15
- export type WebsearchConfig = z.infer<typeof WebsearchConfigSchema>
@@ -1,56 +0,0 @@
1
- import type { PluginInput } from "@opencode-ai/plugin"
2
- import { isGptModel, isGptProModel } from "../../agents/types"
3
- import {
4
- getSessionAgent,
5
- resolveRegisteredAgentName,
6
- updateSessionAgent,
7
- } from "../../features/claude-code-session-state"
8
- import { log } from "../../shared"
9
- import { getAgentConfigKey } from "../../shared/agent-display-names"
10
-
11
- const TOAST_TITLE = "NEVER Use Bob with GPT"
12
- const TOAST_MESSAGE = [
13
- "Bob works best with Claude Opus, and works fine with Kimi/GLM models.",
14
- "Do NOT use Bob with GPT (except High-Tier Pro models which have specialized support).",
15
- "For GPT models (other than Pro), always use Coder.",
16
- ].join("\n")
17
- function showToast(ctx: PluginInput, sessionID: string): void {
18
- ctx.client.tui.showToast({
19
- body: {
20
- title: TOAST_TITLE,
21
- message: TOAST_MESSAGE,
22
- variant: "error",
23
- duration: 10000,
24
- },
25
- }).catch((error) => {
26
- log("[no-bob-gpt] Failed to show toast", {
27
- sessionID,
28
- error,
29
- })
30
- })
31
- }
32
-
33
- export function createNoBobGptHook(ctx: PluginInput) {
34
- return {
35
- "chat.message": async (input: {
36
- sessionID: string
37
- agent?: string
38
- model?: { providerID: string; modelID: string }
39
- }, output?: {
40
- message?: { agent?: string; [key: string]: unknown }
41
- }): Promise<void> => {
42
- const rawAgent = input.agent ?? getSessionAgent(input.sessionID) ?? ""
43
- const agentKey = getAgentConfigKey(rawAgent)
44
- const modelID = input.model?.modelID
45
-
46
- if (agentKey === "bob" && modelID && isGptModel(modelID) && !isGptProModel(modelID)) {
47
- showToast(ctx, input.sessionID)
48
- input.agent = resolveRegisteredAgentName("coder") ?? "coder"
49
- if (output?.message) {
50
- output.message.agent = resolveRegisteredAgentName("coder") ?? "coder"
51
- }
52
- updateSessionAgent(input.sessionID, "coder")
53
- }
54
- },
55
- }
56
- }
@@ -1 +0,0 @@
1
- export { createNoBobGptHook } from "./hook"
@@ -1,67 +0,0 @@
1
- import type { PluginInput } from "@opencode-ai/plugin"
2
- import { isGptModel } from "../../agents/types"
3
- import {
4
- getSessionAgent,
5
- resolveRegisteredAgentName,
6
- updateSessionAgent,
7
- } from "../../features/claude-code-session-state"
8
- import { log } from "../../shared"
9
- import { getAgentConfigKey } from "../../shared/agent-display-names"
10
-
11
- const TOAST_TITLE = "NEVER Use Coder with Non-GPT"
12
- const TOAST_MESSAGE = [
13
- "Coder is designed exclusively for GPT models.",
14
- "Coder is trash without GPT.",
15
- "For Claude/Kimi/GLM models, always use Bob.",
16
- ].join("\n")
17
- type NoCoderNonGptHookOptions = {
18
- allowNonGptModel?: boolean
19
- }
20
-
21
- function showToast(ctx: PluginInput, sessionID: string, variant: "error" | "warning"): void {
22
- ctx.client.tui.showToast({
23
- body: {
24
- title: TOAST_TITLE,
25
- message: TOAST_MESSAGE,
26
- variant,
27
- duration: 10000,
28
- },
29
- }).catch((error) => {
30
- log("[no-coder-non-gpt] Failed to show toast", {
31
- sessionID,
32
- error,
33
- })
34
- })
35
- }
36
-
37
- export function createNoCoderNonGptHook(
38
- ctx: PluginInput,
39
- options?: NoCoderNonGptHookOptions,
40
- ) {
41
- return {
42
- "chat.message": async (input: {
43
- sessionID: string
44
- agent?: string
45
- model?: { providerID: string; modelID: string }
46
- }, output?: {
47
- message?: { agent?: string; [key: string]: unknown }
48
- }): Promise<void> => {
49
- const rawAgent = input.agent ?? getSessionAgent(input.sessionID) ?? ""
50
- const agentKey = getAgentConfigKey(rawAgent)
51
- const modelID = input.model?.modelID
52
- const allowNonGptModel = options?.allowNonGptModel === true
53
-
54
- if (agentKey === "coder" && modelID && !isGptModel(modelID)) {
55
- showToast(ctx, input.sessionID, allowNonGptModel ? "warning" : "error")
56
- if (allowNonGptModel) {
57
- return
58
- }
59
- input.agent = resolveRegisteredAgentName("bob") ?? "bob"
60
- if (output?.message) {
61
- output.message.agent = resolveRegisteredAgentName("bob") ?? "bob"
62
- }
63
- updateSessionAgent(input.sessionID, "bob")
64
- }
65
- },
66
- }
67
- }
@@ -1 +0,0 @@
1
- export { createNoCoderNonGptHook } from "./hook"
@@ -1,6 +0,0 @@
1
- export const grep_app = {
2
- type: "remote" as const,
3
- url: "https://mcp.grep.app",
4
- enabled: true,
5
- oauth: false as const,
6
- }
@@ -1,30 +0,0 @@
1
- import { createWebsearchConfig } from "./websearch"
2
- import { grep_app } from "./grep-app"
3
- import type { HiaiOpenCodeConfig } from "../config/schema"
4
-
5
- export { McpNameSchema, type McpName } from "./types"
6
-
7
- type RemoteMcpConfig = {
8
- type: "remote"
9
- url: string
10
- enabled: boolean
11
- headers?: Record<string, string>
12
- oauth?: false
13
- }
14
-
15
- export function createBuiltinMcps(disabledMcps: string[] = [], config?: HiaiOpenCodeConfig) {
16
- const mcps: Record<string, RemoteMcpConfig> = {}
17
-
18
- if (!disabledMcps.includes("websearch")) {
19
- const websearchConfig = createWebsearchConfig(config?.websearch)
20
- if (websearchConfig) {
21
- mcps.websearch = websearchConfig
22
- }
23
- }
24
-
25
- if (!disabledMcps.includes("grep_app")) {
26
- mcps.grep_app = grep_app
27
- }
28
-
29
- return mcps
30
- }
@@ -1,44 +0,0 @@
1
- import type { WebsearchConfig } from "../config/schema"
2
- import { log } from "../shared/logger"
3
-
4
- type RemoteMcpConfig = {
5
- type: "remote"
6
- url: string
7
- enabled: boolean
8
- headers?: Record<string, string>
9
- oauth?: false
10
- }
11
-
12
- export function createWebsearchConfig(config?: WebsearchConfig): RemoteMcpConfig | undefined {
13
- const provider = config?.provider || "exa"
14
-
15
- if (provider === "tavily") {
16
- const tavilyKey = process.env.TAVILY_API_KEY
17
- if (!tavilyKey) {
18
- log("[websearch] Tavily API key not found, skipping websearch MCP")
19
- return undefined
20
- }
21
-
22
- return {
23
- type: "remote" as const,
24
- url: "https://mcp.tavily.com/mcp/",
25
- enabled: true,
26
- headers: {
27
- Authorization: `Bearer ${tavilyKey}`,
28
- },
29
- oauth: false as const,
30
- }
31
- }
32
-
33
- return {
34
- type: "remote" as const,
35
- url: process.env.EXA_API_KEY
36
- ? `https://mcp.exa.ai/mcp?tools=web_search_exa&exaApiKey=${encodeURIComponent(process.env.EXA_API_KEY)}`
37
- : "https://mcp.exa.ai/mcp?tools=web_search_exa",
38
- enabled: true,
39
- ...(process.env.EXA_API_KEY ? { headers: { "x-api-key": process.env.EXA_API_KEY } } : {}),
40
- oauth: false as const,
41
- }
42
- }
43
-
44
- export const websearch = createWebsearchConfig()