@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.
- package/.env.example +7 -0
- package/AGENTS.md +46 -1
- package/ARCHITECTURE.md +6 -3
- package/README.md +71 -10
- package/assets/cli/hiai-opencode.mjs +78 -0
- package/config/hiai-opencode.schema.json +16 -1
- package/dist/agents/agent-skills.d.ts +7 -0
- package/dist/agents/bob/default.d.ts +1 -0
- package/dist/agents/bob/gemini.d.ts +1 -0
- package/dist/agents/bob/gpt-pro.d.ts +1 -0
- package/dist/agents/brainstormer.d.ts +7 -0
- package/dist/agents/coder/gpt-codex.d.ts +1 -1
- package/dist/agents/coder/gpt-pro.d.ts +1 -0
- package/dist/agents/coder/gpt.d.ts +2 -1
- package/dist/agents/designer.d.ts +7 -0
- package/dist/agents/strategist/gemini.d.ts +1 -0
- package/dist/agents/strategist/gpt.d.ts +1 -0
- package/dist/agents/types.d.ts +3 -1
- package/dist/config/index.d.ts +0 -1
- package/dist/config/platform-schema.d.ts +32 -0
- package/dist/config/schema/hooks.d.ts +0 -2
- package/dist/config/schema/index.d.ts +0 -2
- package/dist/config/schema/oh-my-opencode-config.d.ts +0 -6
- package/dist/config/types.d.ts +3 -1
- package/dist/create-hooks.d.ts +0 -2
- package/dist/features/builtin-skills/skills/index.d.ts +1 -0
- package/dist/features/builtin-skills/skills/website-copywriting.d.ts +2 -0
- package/dist/hooks/agent-usage-reminder/constants.d.ts +1 -1
- package/dist/hooks/index.d.ts +0 -2
- package/dist/hooks/keyword-detector/ultrawork/default.d.ts +1 -1
- package/dist/hooks/keyword-detector/ultrawork/gemini.d.ts +1 -1
- package/dist/hooks/keyword-detector/ultrawork/gpt.d.ts +1 -1
- package/dist/hooks/keyword-detector/ultrawork/planner.d.ts +1 -1
- package/dist/index.js +8963 -153866
- package/dist/mcp/index.d.ts +0 -1
- package/dist/mcp/registry.d.ts +1 -1
- package/dist/plugin/hooks/create-core-hooks.d.ts +0 -2
- package/dist/plugin/hooks/create-session-hooks.d.ts +1 -3
- package/dist/plugin/startup-diagnostics.d.ts +1 -0
- package/dist/shared/logger.d.ts +2 -0
- package/dist/shared/mode-routing.d.ts +6 -0
- package/dist/tools/delegate-task/git-categories.d.ts +2 -0
- package/dist/tools/delegate-task/sub-agent.d.ts +2 -0
- package/dist/tools/skill-mcp/constants.d.ts +1 -1
- package/hiai-opencode.json +48 -19
- package/package.json +6 -3
- package/src/agents/agent-skills.ts +70 -0
- package/src/agents/bob/default.ts +1 -0
- package/src/agents/bob/gemini.ts +1 -0
- package/src/agents/bob/gpt-pro.ts +2 -1
- package/src/agents/bob.ts +2 -0
- package/src/agents/brainstormer.ts +72 -0
- package/src/agents/builtin-agents.ts +59 -3
- package/src/agents/coder/gpt-codex.ts +4 -3
- package/src/agents/coder/gpt-pro.ts +3 -2
- package/src/agents/coder/gpt.ts +2 -1
- package/src/agents/critic/agent.ts +1 -0
- package/src/agents/designer.ts +70 -0
- package/src/agents/dynamic-agent-category-skills-guide.ts +6 -0
- package/src/agents/guard/default.ts +1 -0
- package/src/agents/guard/gemini.ts +1 -0
- package/src/agents/guard/gpt.ts +1 -0
- package/src/agents/platform-manager.ts +17 -1
- package/src/agents/prompt-library/platform.ts +34 -0
- package/src/agents/researcher.ts +1 -0
- package/src/agents/strategist/gemini.ts +1 -0
- package/src/agents/strategist/gpt.ts +1 -0
- package/src/agents/types.ts +4 -1
- package/src/agents/ui.ts +1 -0
- package/src/config/defaults.ts +31 -12
- package/src/config/index.ts +0 -1
- package/src/config/model-slots-and-export.test.ts +22 -4
- package/src/config/platform-schema.ts +2 -0
- package/src/config/schema/hooks.ts +0 -2
- package/src/config/schema/index.ts +0 -2
- package/src/config/schema/oh-my-opencode-config.ts +0 -2
- package/src/config/types.ts +3 -1
- package/src/features/builtin-skills/skills/index.ts +1 -0
- package/src/features/builtin-skills/skills/website-copywriting.ts +41 -0
- package/src/features/builtin-skills/skills.test.ts +8 -0
- package/src/features/builtin-skills/skills.ts +2 -0
- package/src/features/skill-mcp-manager/AGENTS.md +1 -1
- package/src/hooks/agent-usage-reminder/constants.ts +4 -4
- package/src/hooks/index.ts +0 -2
- package/src/hooks/keyword-detector/ultrawork/default.ts +18 -18
- package/src/hooks/keyword-detector/ultrawork/gemini.ts +21 -21
- package/src/hooks/keyword-detector/ultrawork/gpt.ts +6 -8
- package/src/hooks/keyword-detector/ultrawork/planner.ts +5 -5
- package/src/index.ts +5 -3
- package/src/internals/plugins/subtask2/commands/manifest.ts +2 -6
- package/src/internals/plugins/subtask2/hooks/command-hooks.ts +2 -2
- package/src/internals/plugins/subtask2/hooks/message-hooks.ts +1 -1
- package/src/internals/plugins/subtask2/parsing/parallel.ts +13 -10
- package/src/mcp/index.ts +0 -1
- package/src/mcp/registry.ts +27 -0
- package/src/plugin/chat-message.ts +0 -2
- package/src/plugin/hooks/create-session-hooks.ts +0 -17
- package/src/plugin/startup-diagnostics.ts +27 -0
- package/src/plugin-handlers/agent-config-handler.ts +3 -2
- package/src/plugin-handlers/mcp-config-handler.test.ts +63 -0
- package/src/plugin-handlers/mcp-config-handler.ts +29 -14
- package/src/plugin-handlers/strategist-agent-config-builder.ts +1 -1
- package/src/shared/agent-display-names.test.ts +9 -0
- package/src/shared/agent-display-names.ts +5 -0
- package/src/shared/log-legacy-plugin-startup-warning.ts +6 -8
- package/src/shared/logger.ts +8 -0
- package/src/shared/mcp-static-export.ts +4 -6
- package/src/shared/migration/agent-names.ts +8 -0
- package/src/shared/migration/hook-names.ts +1 -1
- package/src/shared/mode-routing.test.ts +88 -0
- package/src/shared/mode-routing.ts +30 -0
- package/src/shared/startup-diagnostics.ts +6 -7
- package/src/tools/call-omo-agent/tools.ts +11 -4
- package/src/tools/delegate-task/anthropic-categories.ts +3 -3
- package/src/tools/delegate-task/builtin-categories.ts +2 -0
- package/src/tools/delegate-task/categories.test.ts +87 -0
- package/src/tools/delegate-task/category-resolver.ts +8 -9
- package/src/tools/delegate-task/git-categories.ts +30 -0
- package/src/tools/delegate-task/model-string-parser.test.ts +90 -0
- package/src/tools/delegate-task/openai-categories.ts +26 -22
- package/src/tools/delegate-task/sub-agent.ts +10 -0
- package/src/tools/delegate-task/subagent-discovery.test.ts +123 -0
- package/src/tools/delegate-task/subagent-resolver.ts +18 -1
- package/src/tools/skill-mcp/constants.ts +1 -1
- package/dist/config/schema/websearch.d.ts +0 -13
- package/dist/hooks/no-bob-gpt/hook.d.ts +0 -16
- package/dist/hooks/no-bob-gpt/index.d.ts +0 -1
- package/dist/hooks/no-coder-non-gpt/hook.d.ts +0 -20
- package/dist/hooks/no-coder-non-gpt/index.d.ts +0 -1
- package/dist/mcp/grep-app.d.ts +0 -6
- package/dist/mcp/omo-mcp-index.d.ts +0 -10
- package/dist/mcp/websearch.d.ts +0 -11
- package/src/config/schema/websearch.ts +0 -15
- package/src/hooks/no-bob-gpt/hook.ts +0 -56
- package/src/hooks/no-bob-gpt/index.ts +0 -1
- package/src/hooks/no-coder-non-gpt/hook.ts +0 -67
- package/src/hooks/no-coder-non-gpt/index.ts +0 -1
- package/src/mcp/grep-app.ts +0 -6
- package/src/mcp/omo-mcp-index.ts +0 -30
- 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 /
|
|
4
|
+
You are working on DEEP LOGICAL REASONING / ARCHITECTURE / STRATEGY tasks.
|
|
5
5
|
|
|
6
6
|
<Routing_Policy>
|
|
7
|
-
Executor contour:
|
|
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
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
-
|
|
20
|
-
-
|
|
21
|
-
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
-
|
|
26
|
-
-
|
|
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:
|
|
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
|
|
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
|
|
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";
|
package/dist/mcp/grep-app.d.ts
DELETED
|
@@ -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>;
|
package/dist/mcp/websearch.d.ts
DELETED
|
@@ -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"
|
package/src/mcp/grep-app.ts
DELETED
package/src/mcp/omo-mcp-index.ts
DELETED
|
@@ -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
|
-
}
|
package/src/mcp/websearch.ts
DELETED
|
@@ -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()
|