@hiai-gg/hiai-opencode 0.1.2 → 0.1.4
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 +28 -21
- package/AGENTS.md +183 -28
- package/ARCHITECTURE.md +17 -20
- package/LICENSE.md +1 -0
- package/README.md +269 -66
- package/assets/cli/hiai-opencode.mjs +276 -0
- package/assets/mcp/mempalace.mjs +47 -4
- package/assets/mcp/playwright.mjs +83 -0
- package/config/hiai-opencode.schema.json +113 -1
- package/dist/config/index.d.ts +0 -1
- package/dist/config/platform-schema.d.ts +72 -0
- package/dist/config/schema/agent-overrides.d.ts +256 -0
- package/dist/config/schema/categories.d.ts +2 -2
- package/dist/config/schema/commands.d.ts +1 -0
- package/dist/config/schema/index.d.ts +2 -0
- package/dist/config/schema/oh-my-opencode-config.d.ts +267 -0
- package/dist/config/schema/skill-discovery.d.ts +11 -0
- package/dist/config/types.d.ts +12 -1
- package/dist/features/builtin-commands/templates/mcp-status.d.ts +1 -0
- package/dist/features/builtin-commands/types.d.ts +1 -1
- package/dist/features/opencode-skill-loader/loader.d.ts +2 -0
- package/dist/index.js +617 -421
- package/dist/mcp/registry.d.ts +14 -0
- package/dist/mcp/types.d.ts +6 -0
- package/dist/plugin/skill-discovery-config.d.ts +4 -0
- package/dist/shared/startup-diagnostics.d.ts +6 -0
- package/hiai-opencode.json +192 -36
- package/package.json +4 -1
- package/src/agents/AGENTS.md +3 -4
- package/src/config/defaults.ts +55 -133
- package/src/config/index.ts +0 -1
- package/src/config/loader.ts +4 -1
- package/src/config/platform-schema.ts +18 -2
- package/src/config/schema/agent-overrides.ts +2 -0
- package/src/config/schema/commands.ts +1 -0
- package/src/config/schema/fast-apply.ts +4 -4
- package/src/config/schema/index.ts +2 -0
- package/src/config/schema/oh-my-opencode-config.ts +3 -0
- package/src/config/schema/skill-discovery.ts +25 -0
- package/src/config/types.ts +16 -0
- package/src/features/builtin-commands/commands.ts +7 -0
- package/src/features/builtin-commands/templates/mcp-status.ts +36 -0
- package/src/features/builtin-commands/types.ts +1 -1
- package/src/features/builtin-skills/skills/playwright.ts +24 -2
- package/src/features/opencode-skill-loader/loader.ts +11 -0
- package/src/index.ts +15 -13
- package/src/mcp/index.ts +0 -33
- package/src/mcp/omo-mcp-index.ts +0 -5
- package/src/mcp/registry.ts +132 -0
- package/src/mcp/types.ts +11 -1
- package/src/plugin/hooks/create-tool-guard-hooks.ts +1 -1
- package/src/plugin/skill-context.ts +31 -13
- package/src/plugin/skill-discovery-config.ts +32 -0
- package/src/plugin-handlers/agent-config-handler.ts +20 -13
- package/src/plugin-handlers/command-config-handler.ts +22 -12
- package/src/shared/migration/agent-names.ts +5 -5
- package/src/shared/startup-diagnostics.ts +77 -0
- package/src/config/models.ts +0 -32
|
@@ -3,10 +3,10 @@ import { z } from "zod"
|
|
|
3
3
|
export const FastApplyConfigSchema = z.object({
|
|
4
4
|
/** Enable fast-apply via Ollama (default: false) */
|
|
5
5
|
enabled: z.boolean().optional().default(false),
|
|
6
|
-
/** Ollama API URL
|
|
7
|
-
ollama_url: z.string().optional().default("
|
|
8
|
-
/** Model name for fast-apply
|
|
9
|
-
model: z.string().optional().default("
|
|
6
|
+
/** Ollama API URL. Configure in hiai-opencode.json. */
|
|
7
|
+
ollama_url: z.string().optional().default(""),
|
|
8
|
+
/** Model name for fast-apply. Configure in hiai-opencode.json. */
|
|
9
|
+
model: z.string().optional().default(""),
|
|
10
10
|
/** Timeout in milliseconds for Ollama request (default: 30000) */
|
|
11
11
|
timeout: z.number().int().positive().optional().default(30000),
|
|
12
12
|
})
|
|
@@ -44,6 +44,8 @@ export { BobAgentConfigSchema } from "./bob-agent"
|
|
|
44
44
|
export type { BobAgentConfig } from "./bob-agent"
|
|
45
45
|
export { SkillsConfigSchema } from "./skills"
|
|
46
46
|
export type { SkillsConfig, SkillDefinition } from "./skills"
|
|
47
|
+
export { SkillDiscoveryConfigSchema } from "./skill-discovery"
|
|
48
|
+
export type { SkillDiscoveryConfig } from "./skill-discovery"
|
|
47
49
|
export { StartWorkConfigSchema } from "./start-work"
|
|
48
50
|
export type { StartWorkConfig } from "./start-work"
|
|
49
51
|
export { TmuxConfigSchema, TmuxLayoutSchema, TmuxIsolationSchema } from "./tmux"
|
|
@@ -17,6 +17,7 @@ import { ModelCapabilitiesConfigSchema } from "./model-capabilities"
|
|
|
17
17
|
import { RalphLoopConfigSchema } from "./ralph-loop"
|
|
18
18
|
import { RuntimeFallbackConfigSchema } from "./runtime-fallback"
|
|
19
19
|
import { SkillsConfigSchema } from "./skills"
|
|
20
|
+
import { SkillDiscoveryConfigSchema } from "./skill-discovery"
|
|
20
21
|
import { BobConfigSchema } from "./bob"
|
|
21
22
|
import { BobAgentConfigSchema } from "./bob-agent"
|
|
22
23
|
import { TmuxConfigSchema } from "./tmux"
|
|
@@ -30,6 +31,7 @@ const AuthConfigSchema = z.object({
|
|
|
30
31
|
openrouter: z.string().optional(),
|
|
31
32
|
stitch: z.string().optional(),
|
|
32
33
|
firecrawl: z.string().optional(),
|
|
34
|
+
context7: z.string().optional(),
|
|
33
35
|
}).optional()
|
|
34
36
|
|
|
35
37
|
export const HiaiOpenCodeConfigSchema = z.object({
|
|
@@ -60,6 +62,7 @@ export const HiaiOpenCodeConfigSchema = z.object({
|
|
|
60
62
|
experimental: ExperimentalConfigSchema.optional(),
|
|
61
63
|
auto_update: z.boolean().optional(),
|
|
62
64
|
skills: SkillsConfigSchema.optional(),
|
|
65
|
+
skill_discovery: SkillDiscoveryConfigSchema.optional(),
|
|
63
66
|
ralph_loop: RalphLoopConfigSchema.optional(),
|
|
64
67
|
/**
|
|
65
68
|
* Enable runtime fallback (default: false)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { z } from "zod"
|
|
2
|
+
|
|
3
|
+
export const SkillDiscoveryConfigSchema = z.object({
|
|
4
|
+
/**
|
|
5
|
+
* Explicit skills.sources entries from hiai-opencode config.
|
|
6
|
+
* Safe by default because the user intentionally configured them.
|
|
7
|
+
*/
|
|
8
|
+
config_sources: z.boolean().default(true),
|
|
9
|
+
/**
|
|
10
|
+
* Project-local OpenCode skills: .opencode/skills and .opencode/skill.
|
|
11
|
+
* Kept on by default because they are part of the current project contract.
|
|
12
|
+
*/
|
|
13
|
+
project_opencode: z.boolean().default(true),
|
|
14
|
+
/**
|
|
15
|
+
* Global OpenCode skills from the user's OpenCode config directory.
|
|
16
|
+
* Off by default to keep clean installs deterministic.
|
|
17
|
+
*/
|
|
18
|
+
global_opencode: z.boolean().default(false),
|
|
19
|
+
project_claude: z.boolean().default(false),
|
|
20
|
+
global_claude: z.boolean().default(false),
|
|
21
|
+
project_agents: z.boolean().default(false),
|
|
22
|
+
global_agents: z.boolean().default(false),
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
export type SkillDiscoveryConfig = z.infer<typeof SkillDiscoveryConfigSchema>
|
package/src/config/types.ts
CHANGED
|
@@ -32,6 +32,8 @@ export const LEGACY_AGENT_ALIAS_NAMES = [
|
|
|
32
32
|
"zoe",
|
|
33
33
|
"build",
|
|
34
34
|
"pre-plan",
|
|
35
|
+
"manager",
|
|
36
|
+
"vision",
|
|
35
37
|
"logician",
|
|
36
38
|
"librarian",
|
|
37
39
|
"explore",
|
|
@@ -54,6 +56,8 @@ export const LEGACY_AGENT_ALIAS_TO_CANONICAL: Record<
|
|
|
54
56
|
zoe: "bob",
|
|
55
57
|
build: "bob",
|
|
56
58
|
"pre-plan": "strategist",
|
|
59
|
+
manager: "platform-manager",
|
|
60
|
+
vision: "multimodal",
|
|
57
61
|
logician: "strategist",
|
|
58
62
|
librarian: "researcher",
|
|
59
63
|
explore: "researcher",
|
|
@@ -135,6 +139,16 @@ export interface SkillsConfig {
|
|
|
135
139
|
disabled?: string[];
|
|
136
140
|
}
|
|
137
141
|
|
|
142
|
+
export interface SkillDiscoveryConfig {
|
|
143
|
+
config_sources?: boolean;
|
|
144
|
+
project_opencode?: boolean;
|
|
145
|
+
global_opencode?: boolean;
|
|
146
|
+
project_claude?: boolean;
|
|
147
|
+
global_claude?: boolean;
|
|
148
|
+
project_agents?: boolean;
|
|
149
|
+
global_agents?: boolean;
|
|
150
|
+
}
|
|
151
|
+
|
|
138
152
|
export interface PermissionsConfig {
|
|
139
153
|
read?: Record<string, string>;
|
|
140
154
|
edit?: Record<string, string>;
|
|
@@ -148,6 +162,7 @@ export interface AuthKeys {
|
|
|
148
162
|
openrouter?: string;
|
|
149
163
|
stitch?: string;
|
|
150
164
|
firecrawl?: string;
|
|
165
|
+
context7?: string;
|
|
151
166
|
}
|
|
152
167
|
|
|
153
168
|
export interface OllamaConfig {
|
|
@@ -168,6 +183,7 @@ export interface HiaiOpencodeConfig {
|
|
|
168
183
|
lsp?: Record<string, LspServerConfig>;
|
|
169
184
|
subtask2?: Subtask2Config;
|
|
170
185
|
skills?: SkillsConfig;
|
|
186
|
+
skill_discovery?: SkillDiscoveryConfig;
|
|
171
187
|
permissions?: PermissionsConfig;
|
|
172
188
|
auth?: AuthKeys;
|
|
173
189
|
ollama?: OllamaConfig;
|
|
@@ -8,6 +8,7 @@ import { REFACTOR_TEMPLATE } from "./templates/refactor"
|
|
|
8
8
|
import { START_WORK_TEMPLATE } from "./templates/start-work"
|
|
9
9
|
import { HANDOFF_TEMPLATE } from "./templates/handoff"
|
|
10
10
|
import { REMOVE_AI_SLOPS_TEMPLATE } from "./templates/remove-ai-slops"
|
|
11
|
+
import { MCP_STATUS_TEMPLATE } from "./templates/mcp-status"
|
|
11
12
|
|
|
12
13
|
interface LoadBuiltinCommandsOptions {
|
|
13
14
|
useRegisteredAgents?: boolean
|
|
@@ -121,6 +122,12 @@ $ARGUMENTS
|
|
|
121
122
|
</user-request>`,
|
|
122
123
|
argumentHint: "[goal]",
|
|
123
124
|
},
|
|
125
|
+
"mcp-status": {
|
|
126
|
+
description: "(builtin) Show hiai-opencode MCP server status, missing keys, and local runtime availability",
|
|
127
|
+
template: `<command-instruction>
|
|
128
|
+
${MCP_STATUS_TEMPLATE}
|
|
129
|
+
</command-instruction>`,
|
|
130
|
+
},
|
|
124
131
|
}
|
|
125
132
|
}
|
|
126
133
|
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export const MCP_STATUS_TEMPLATE = `# MCP Status Command
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
Use /mcp-status to show the effective hiai-opencode MCP setup without relying on OpenCode's mcp list output.
|
|
6
|
+
|
|
7
|
+
## Execute
|
|
8
|
+
|
|
9
|
+
Run:
|
|
10
|
+
|
|
11
|
+
\`\`\`bash
|
|
12
|
+
hiai-opencode mcp-status
|
|
13
|
+
\`\`\`
|
|
14
|
+
|
|
15
|
+
If the binary is not on PATH, try the package-local fallback:
|
|
16
|
+
|
|
17
|
+
\`\`\`bash
|
|
18
|
+
node ./node_modules/@hiai-gg/hiai-opencode/assets/cli/hiai-opencode.mjs mcp-status
|
|
19
|
+
\`\`\`
|
|
20
|
+
|
|
21
|
+
## Report
|
|
22
|
+
|
|
23
|
+
Summarize the output in a compact status table:
|
|
24
|
+
|
|
25
|
+
- MCP server name
|
|
26
|
+
- status: ok, warning, error, disabled
|
|
27
|
+
- cause or next action
|
|
28
|
+
|
|
29
|
+
Rules:
|
|
30
|
+
|
|
31
|
+
- Do not print API key values.
|
|
32
|
+
- If a key is missing, name the env var only.
|
|
33
|
+
- If a runtime is missing, give the exact install hint from the command output or the shortest safe next command.
|
|
34
|
+
- Do not edit config unless the user explicitly asks.
|
|
35
|
+
- Do not run package installs unless the user explicitly asks.
|
|
36
|
+
`
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { CommandDefinition } from "../claude-code-command-loader"
|
|
2
2
|
|
|
3
|
-
export type BuiltinCommandName = "init-deep" | "ralph-loop" | "cancel-ralph" | "ulw-loop" | "refactor" | "start-work" | "stop-continuation" | "handoff" | "remove-ai-slops"
|
|
3
|
+
export type BuiltinCommandName = "init-deep" | "ralph-loop" | "cancel-ralph" | "ulw-loop" | "refactor" | "start-work" | "stop-continuation" | "handoff" | "remove-ai-slops" | "mcp-status"
|
|
4
4
|
|
|
5
5
|
export interface BuiltinCommandConfig {
|
|
6
6
|
disabled_commands?: BuiltinCommandName[]
|
|
@@ -5,11 +5,33 @@ export const playwrightSkill: BuiltinSkill = {
|
|
|
5
5
|
description: "MUST USE for any browser-related tasks. Browser automation via Playwright MCP - verification, browsing, information gathering, web scraping, testing, screenshots, and all browser interactions.",
|
|
6
6
|
template: `# Playwright Browser Automation
|
|
7
7
|
|
|
8
|
-
This skill provides browser automation capabilities via the Playwright MCP server
|
|
8
|
+
This skill provides browser automation capabilities via the Playwright MCP server.
|
|
9
|
+
|
|
10
|
+
## Required workflow
|
|
11
|
+
|
|
12
|
+
1. Load this skill before calling \`skill_mcp\`.
|
|
13
|
+
2. Use \`skill_mcp\` with \`mcp_name="playwright"\` for browser navigation, interaction, screenshots, and visual verification.
|
|
14
|
+
3. If the host says \`MCP server "playwright" not found\`, do not conclude that Playwright is impossible. First report that the skill was not loaded or the Playwright MCP server was not registered in this session.
|
|
15
|
+
4. If Chromium starts but fails with missing Linux libraries such as \`libnspr4\`, \`libnss3\`, \`libatk-bridge\`, \`libgtk-3\`, or similar, distinguish browser OS dependencies from MCP availability.
|
|
16
|
+
|
|
17
|
+
## Linux dependency fallback
|
|
18
|
+
|
|
19
|
+
Playwright has two dependency layers:
|
|
20
|
+
|
|
21
|
+
- Browser binary: installable without sudo with \`npx playwright install chromium\` or by setting \`HIAI_PLAYWRIGHT_INSTALL_BROWSERS=1\` before OpenCode starts.
|
|
22
|
+
- System libraries: on minimal Linux images these usually require admin rights via \`sudo npx playwright install-deps chromium\` or OS package manager equivalents.
|
|
23
|
+
|
|
24
|
+
If sudo is unavailable, try these alternatives before falling back to curl-only checks:
|
|
25
|
+
|
|
26
|
+
- Use an already installed Chrome/Chromium/Edge by adding Playwright MCP args in \`hiai-opencode.json\`, for example \`--browser chrome\` or \`--browser msedge\`.
|
|
27
|
+
- Use a remote/browser service or CDP-backed browser when available.
|
|
28
|
+
- Switch the browser automation provider to \`agent-browser\` or \`playwright-cli\` if the workspace has those tools installed.
|
|
29
|
+
|
|
30
|
+
Only use \`curl\` as a final degraded check. Clearly say that HTTP checks do not replace interactive browser verification.`,
|
|
9
31
|
mcpConfig: {
|
|
10
32
|
playwright: {
|
|
11
33
|
command: "npx",
|
|
12
|
-
args: ["@playwright/mcp@latest"],
|
|
34
|
+
args: ["-y", "@playwright/mcp@latest"],
|
|
13
35
|
},
|
|
14
36
|
},
|
|
15
37
|
}
|
|
@@ -62,6 +62,12 @@ export async function loadGlobalAgentsSkills(): Promise<Record<string, CommandDe
|
|
|
62
62
|
return skillsToCommandDefinitionRecord(skills)
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
export async function loadManagedPluginSkills(): Promise<Record<string, CommandDefinition>> {
|
|
66
|
+
const skillsDir = join(getOpenCodeConfigDir({ binary: "opencode" }), ".hiai", "skills", "plugin")
|
|
67
|
+
const skills = await loadSkillsFromDir({ skillsDir, scope: "builtin" })
|
|
68
|
+
return skillsToCommandDefinitionRecord(skills)
|
|
69
|
+
}
|
|
70
|
+
|
|
65
71
|
export interface DiscoverSkillsOptions {
|
|
66
72
|
includeClaudeCodePaths?: boolean
|
|
67
73
|
directory?: string
|
|
@@ -170,3 +176,8 @@ export async function discoverGlobalAgentsSkills(): Promise<LoadedSkill[]> {
|
|
|
170
176
|
const agentsGlobalDir = join(getAgentsConfigDir(), "skills")
|
|
171
177
|
return loadSkillsFromDir({ skillsDir: agentsGlobalDir, scope: "user" })
|
|
172
178
|
}
|
|
179
|
+
|
|
180
|
+
export async function discoverManagedPluginSkills(): Promise<LoadedSkill[]> {
|
|
181
|
+
const skillsDir = join(getOpenCodeConfigDir({ binary: "opencode" }), ".hiai", "skills", "plugin")
|
|
182
|
+
return loadSkillsFromDir({ skillsDir, scope: "builtin" })
|
|
183
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -18,6 +18,7 @@ import { injectServerAuthIntoClient, log } from "./shared"
|
|
|
18
18
|
import { hydratePluginConfigWithPlatformDefaults } from "./shared/runtime-plugin-config"
|
|
19
19
|
import { detectExternalSkillPlugin, getSkillPluginConflictWarning } from "./shared/external-plugin-detector"
|
|
20
20
|
import { PLUGIN_NAME } from "./shared/plugin-identity"
|
|
21
|
+
import { warnIfListPluginEntry, warnMissingRequiredMcpEnv } from "./shared/startup-diagnostics"
|
|
21
22
|
import { startBackgroundCheck as startTmuxCheck } from "./tools/interactive-bash"
|
|
22
23
|
import { lspManager } from "./tools/lsp/client"
|
|
23
24
|
|
|
@@ -56,6 +57,7 @@ function configureBundledBunPtyLibrary(): void {
|
|
|
56
57
|
const candidates = [
|
|
57
58
|
join(import.meta.dirname, "..", "node_modules", "bun-pty", "rust-pty", "target", "release", libraryName),
|
|
58
59
|
join(import.meta.dirname, "..", "..", "bun-pty", "rust-pty", "target", "release", libraryName),
|
|
60
|
+
join(import.meta.dirname, "..", "..", "..", "bun-pty", "rust-pty", "target", "release", libraryName),
|
|
59
61
|
]
|
|
60
62
|
|
|
61
63
|
const resolved = candidates.find((candidate) => existsSync(candidate))
|
|
@@ -68,6 +70,7 @@ const HiaiOpenCodePlugin: Plugin = async (ctx) => {
|
|
|
68
70
|
log("[HiaiOpenCodePlugin] ENTRY - plugin loading", {
|
|
69
71
|
directory: ctx.directory,
|
|
70
72
|
})
|
|
73
|
+
warnIfListPluginEntry(ctx.directory)
|
|
71
74
|
|
|
72
75
|
const skillPluginCheck = detectExternalSkillPlugin(ctx.directory)
|
|
73
76
|
if (skillPluginCheck.detected && skillPluginCheck.pluginName) {
|
|
@@ -82,6 +85,10 @@ const HiaiOpenCodePlugin: Plugin = async (ctx) => {
|
|
|
82
85
|
loadPluginConfig(ctx.directory, ctx),
|
|
83
86
|
internalConfig,
|
|
84
87
|
)
|
|
88
|
+
warnMissingRequiredMcpEnv({
|
|
89
|
+
pluginConfig,
|
|
90
|
+
platformConfig: internalConfig,
|
|
91
|
+
})
|
|
85
92
|
|
|
86
93
|
materializeBuiltinSkills(
|
|
87
94
|
createBuiltinSkills({
|
|
@@ -219,29 +226,24 @@ const HiaiOpenCodePlugin: Plugin = async (ctx) => {
|
|
|
219
226
|
auth: {
|
|
220
227
|
provider: "hiai-opencode",
|
|
221
228
|
methods: [
|
|
222
|
-
{ type: "api" as const, label: "Google API key" },
|
|
223
|
-
{ type: "api" as const, label: "OpenAI API key" },
|
|
224
|
-
{ type: "api" as const, label: "OpenRouter API key" },
|
|
229
|
+
{ type: "api" as const, label: "Google Search API key" },
|
|
225
230
|
],
|
|
226
231
|
loader: async (getAuth: any) => {
|
|
227
232
|
const authData = await getAuth();
|
|
228
233
|
const { registerGetAuth, GOOGLE_PROVIDER_ID, OPENAI_PROVIDER_ID, OPENROUTER_PROVIDER_ID } = await import("./internals/plugins/websearch-cited/index");
|
|
229
234
|
|
|
230
|
-
|
|
231
|
-
const getKey = (label: string, configKey?: string) => {
|
|
232
|
-
const fromAuth = authData[label];
|
|
233
|
-
if (fromAuth) return fromAuth;
|
|
235
|
+
const getConfiguredKey = (configKey?: string) => {
|
|
234
236
|
if (configKey) return resolveEnvVars(configKey);
|
|
235
237
|
return undefined;
|
|
236
238
|
};
|
|
237
239
|
|
|
238
|
-
const googleKey =
|
|
239
|
-
const openaiKey =
|
|
240
|
-
const openRouterKey =
|
|
240
|
+
const googleKey = authData["Google Search API key"] || getConfiguredKey(internalConfig.auth?.googleSearch);
|
|
241
|
+
const openaiKey = getConfiguredKey(internalConfig.auth?.openai);
|
|
242
|
+
const openRouterKey = getConfiguredKey(internalConfig.auth?.openrouter);
|
|
241
243
|
|
|
242
|
-
if (googleKey) registerGetAuth(GOOGLE_PROVIDER_ID, () => Promise.resolve(googleKey));
|
|
243
|
-
if (openaiKey) registerGetAuth(OPENAI_PROVIDER_ID, () => Promise.resolve(openaiKey));
|
|
244
|
-
if (openRouterKey) registerGetAuth(OPENROUTER_PROVIDER_ID, () => Promise.resolve(openRouterKey));
|
|
244
|
+
if (googleKey) registerGetAuth(GOOGLE_PROVIDER_ID, () => Promise.resolve({ type: "api", key: googleKey }));
|
|
245
|
+
if (openaiKey) registerGetAuth(OPENAI_PROVIDER_ID, () => Promise.resolve({ type: "api", key: openaiKey }));
|
|
246
|
+
if (openRouterKey) registerGetAuth(OPENROUTER_PROVIDER_ID, () => Promise.resolve({ type: "api", key: openRouterKey }));
|
|
245
247
|
|
|
246
248
|
return {};
|
|
247
249
|
},
|
package/src/mcp/index.ts
CHANGED
|
@@ -1,45 +1,12 @@
|
|
|
1
1
|
export { createBuiltinMcps } from "./omo-mcp-index"
|
|
2
2
|
export { McpNameSchema, type McpName, type AnyMcpName } from "./types"
|
|
3
3
|
|
|
4
|
-
import { existsSync } from "node:fs";
|
|
5
|
-
import { join } from "node:path";
|
|
6
4
|
import type { McpServerConfig } from "../config/types.js";
|
|
7
5
|
import { resolveEnvVars } from "../config/loader.js";
|
|
8
6
|
|
|
9
|
-
const ASSETS_DIR = join(import.meta.dirname || __dirname, "..", "assets", "mcp");
|
|
10
|
-
|
|
11
|
-
function resolveBundledMcp(name: string): string {
|
|
12
|
-
const script = join(ASSETS_DIR, name);
|
|
13
|
-
if (existsSync(script)) return script;
|
|
14
|
-
return "";
|
|
15
|
-
}
|
|
16
|
-
|
|
17
7
|
export function buildMcpConfig(mcp: Record<string, McpServerConfig>): Record<string, unknown> {
|
|
18
8
|
const result: Record<string, unknown> = {};
|
|
19
9
|
|
|
20
|
-
const defaults: Record<string, McpServerConfig> = {
|
|
21
|
-
rag: {
|
|
22
|
-
enabled: true,
|
|
23
|
-
type: "local",
|
|
24
|
-
command: ["node", resolveBundledMcp("rag.mjs")].filter(Boolean),
|
|
25
|
-
environment: {
|
|
26
|
-
OPENCODE_RAG_URL: "http://localhost:9002/tools/search",
|
|
27
|
-
},
|
|
28
|
-
},
|
|
29
|
-
mempalace: {
|
|
30
|
-
enabled: true,
|
|
31
|
-
type: "local",
|
|
32
|
-
command: ["node", resolveBundledMcp("mempalace.mjs"), "--palace", "./.opencode/palace"].filter(Boolean),
|
|
33
|
-
timeout: 60000,
|
|
34
|
-
},
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
for (const [name, server] of Object.entries(defaults)) {
|
|
38
|
-
if (!(name in mcp) && server.command && server.command.length >= 2) {
|
|
39
|
-
mcp[name] = server;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
10
|
for (const [name, server] of Object.entries(mcp)) {
|
|
44
11
|
if (!server.enabled) continue;
|
|
45
12
|
|
package/src/mcp/omo-mcp-index.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { createWebsearchConfig } from "./websearch"
|
|
2
|
-
import { context7 } from "./context7"
|
|
3
2
|
import { grep_app } from "./grep-app"
|
|
4
3
|
import type { HiaiOpenCodeConfig } from "../config/schema"
|
|
5
4
|
|
|
@@ -23,10 +22,6 @@ export function createBuiltinMcps(disabledMcps: string[] = [], config?: HiaiOpen
|
|
|
23
22
|
}
|
|
24
23
|
}
|
|
25
24
|
|
|
26
|
-
if (!disabledMcps.includes("context7")) {
|
|
27
|
-
mcps.context7 = context7
|
|
28
|
-
}
|
|
29
|
-
|
|
30
25
|
if (!disabledMcps.includes("grep_app")) {
|
|
31
26
|
mcps.grep_app = grep_app
|
|
32
27
|
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { join } from "node:path"
|
|
2
|
+
import type { McpServerConfig } from "../config/types.js"
|
|
3
|
+
|
|
4
|
+
export type HiaiMcpName =
|
|
5
|
+
| "playwright"
|
|
6
|
+
| "stitch"
|
|
7
|
+
| "sequential-thinking"
|
|
8
|
+
| "firecrawl"
|
|
9
|
+
| "rag"
|
|
10
|
+
| "context7"
|
|
11
|
+
| "mempalace"
|
|
12
|
+
|
|
13
|
+
export type HiaiMcpInstallKind = "bundled" | "npm" | "python" | "remote" | "user-service"
|
|
14
|
+
|
|
15
|
+
export interface HiaiMcpRegistryEntry {
|
|
16
|
+
name: HiaiMcpName
|
|
17
|
+
enabledByDefault: boolean
|
|
18
|
+
install: HiaiMcpInstallKind
|
|
19
|
+
requiredEnv?: string[]
|
|
20
|
+
optionalEnv?: string[]
|
|
21
|
+
config: McpServerConfig
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function resolveAssetScript(...segments: string[]): string {
|
|
25
|
+
return join(import.meta.dirname, "..", "assets", ...segments)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function createNpmPackageCommand(pkg: string, ...args: string[]): string[] {
|
|
29
|
+
return ["node", resolveAssetScript("runtime", "npm-package-runner.mjs"), pkg, ...args]
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export const HIAI_MCP_REGISTRY: Record<HiaiMcpName, HiaiMcpRegistryEntry> = {
|
|
33
|
+
playwright: {
|
|
34
|
+
name: "playwright",
|
|
35
|
+
enabledByDefault: true,
|
|
36
|
+
install: "npm",
|
|
37
|
+
optionalEnv: ["HIAI_PLAYWRIGHT_INSTALL_BROWSERS"],
|
|
38
|
+
config: {
|
|
39
|
+
enabled: true,
|
|
40
|
+
command: ["node", resolveAssetScript("mcp", "playwright.mjs")],
|
|
41
|
+
timeout: 600000,
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
stitch: {
|
|
45
|
+
name: "stitch",
|
|
46
|
+
enabledByDefault: true,
|
|
47
|
+
install: "remote",
|
|
48
|
+
requiredEnv: ["STITCH_AI_API_KEY"],
|
|
49
|
+
config: {
|
|
50
|
+
enabled: true,
|
|
51
|
+
type: "remote",
|
|
52
|
+
url: "https://stitch.googleapis.com/mcp",
|
|
53
|
+
headers: { "X-Goog-Api-Key": "{env:STITCH_AI_API_KEY}" },
|
|
54
|
+
timeout: 600000,
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
"sequential-thinking": {
|
|
58
|
+
name: "sequential-thinking",
|
|
59
|
+
enabledByDefault: true,
|
|
60
|
+
install: "npm",
|
|
61
|
+
config: {
|
|
62
|
+
enabled: true,
|
|
63
|
+
command: createNpmPackageCommand("@modelcontextprotocol/server-sequential-thinking"),
|
|
64
|
+
timeout: 600000,
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
firecrawl: {
|
|
68
|
+
name: "firecrawl",
|
|
69
|
+
enabledByDefault: true,
|
|
70
|
+
install: "npm",
|
|
71
|
+
requiredEnv: ["FIRECRAWL_API_KEY"],
|
|
72
|
+
config: {
|
|
73
|
+
enabled: true,
|
|
74
|
+
command: createNpmPackageCommand("firecrawl-mcp"),
|
|
75
|
+
timeout: 600000,
|
|
76
|
+
environment: { FIRECRAWL_API_KEY: "{env:FIRECRAWL_API_KEY}" },
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
rag: {
|
|
80
|
+
name: "rag",
|
|
81
|
+
enabledByDefault: true,
|
|
82
|
+
install: "user-service",
|
|
83
|
+
optionalEnv: ["OPENCODE_RAG_URL"],
|
|
84
|
+
config: {
|
|
85
|
+
enabled: true,
|
|
86
|
+
type: "local",
|
|
87
|
+
command: ["node", resolveAssetScript("mcp", "rag.mjs")],
|
|
88
|
+
environment: {
|
|
89
|
+
OPENCODE_RAG_URL: "{env:OPENCODE_RAG_URL:-http://localhost:9002/tools/search}",
|
|
90
|
+
},
|
|
91
|
+
timeout: 600000,
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
context7: {
|
|
95
|
+
name: "context7",
|
|
96
|
+
enabledByDefault: true,
|
|
97
|
+
install: "remote",
|
|
98
|
+
optionalEnv: ["CONTEXT7_API_KEY"],
|
|
99
|
+
config: {
|
|
100
|
+
enabled: true,
|
|
101
|
+
type: "remote",
|
|
102
|
+
url: "https://mcp.context7.com/mcp",
|
|
103
|
+
headers: { "X-API-KEY": "{env:CONTEXT7_API_KEY}" },
|
|
104
|
+
timeout: 600000,
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
mempalace: {
|
|
108
|
+
name: "mempalace",
|
|
109
|
+
enabledByDefault: true,
|
|
110
|
+
install: "python",
|
|
111
|
+
optionalEnv: ["MEMPALACE_PYTHON", "MEMPALACE_PALACE_PATH", "HIAI_MCP_AUTO_INSTALL"],
|
|
112
|
+
config: {
|
|
113
|
+
enabled: true,
|
|
114
|
+
type: "local",
|
|
115
|
+
command: ["node", resolveAssetScript("mcp", "mempalace.mjs"), "--palace", "./.opencode/palace"],
|
|
116
|
+
timeout: 600000,
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export function createDefaultMcpConfig(): Record<HiaiMcpName, McpServerConfig> {
|
|
122
|
+
return Object.fromEntries(
|
|
123
|
+
Object.entries(HIAI_MCP_REGISTRY).map(([name, entry]) => [
|
|
124
|
+
name,
|
|
125
|
+
{ ...entry.config, enabled: entry.enabledByDefault },
|
|
126
|
+
]),
|
|
127
|
+
) as Record<HiaiMcpName, McpServerConfig>
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export function getKnownMcpNames(): HiaiMcpName[] {
|
|
131
|
+
return Object.keys(HIAI_MCP_REGISTRY) as HiaiMcpName[]
|
|
132
|
+
}
|
package/src/mcp/types.ts
CHANGED
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
import { z } from "zod"
|
|
2
2
|
|
|
3
|
-
export const McpNameSchema = z.enum([
|
|
3
|
+
export const McpNameSchema = z.enum([
|
|
4
|
+
"playwright",
|
|
5
|
+
"stitch",
|
|
6
|
+
"sequential-thinking",
|
|
7
|
+
"firecrawl",
|
|
8
|
+
"rag",
|
|
9
|
+
"context7",
|
|
10
|
+
"mempalace",
|
|
11
|
+
"websearch",
|
|
12
|
+
"grep_app",
|
|
13
|
+
])
|
|
4
14
|
|
|
5
15
|
export type McpName = z.infer<typeof McpNameSchema>
|
|
6
16
|
|
|
@@ -136,7 +136,7 @@ export function createToolGuardHooks(args: {
|
|
|
136
136
|
: null
|
|
137
137
|
|
|
138
138
|
const fastApply = isHookEnabled("fast-apply")
|
|
139
|
-
? safeHook("fast-apply", () => createFastApplyHook(pluginConfig.fast_apply ?? { enabled: false, ollama_url: "
|
|
139
|
+
? safeHook("fast-apply", () => createFastApplyHook(pluginConfig.fast_apply ?? { enabled: false, ollama_url: "", model: "", timeout: 30000 }))
|
|
140
140
|
: null
|
|
141
141
|
|
|
142
142
|
return {
|
|
@@ -8,6 +8,7 @@ import type {
|
|
|
8
8
|
|
|
9
9
|
import {
|
|
10
10
|
discoverConfigSourceSkills,
|
|
11
|
+
discoverManagedPluginSkills,
|
|
11
12
|
discoverUserClaudeSkills,
|
|
12
13
|
discoverProjectClaudeSkills,
|
|
13
14
|
discoverOpencodeGlobalSkills,
|
|
@@ -18,6 +19,7 @@ import {
|
|
|
18
19
|
} from "../features/opencode-skill-loader"
|
|
19
20
|
import { createBuiltinSkills } from "../features/builtin-skills"
|
|
20
21
|
import { getSystemMcpServerNames } from "../features/claude-code-mcp-loader"
|
|
22
|
+
import { resolveSkillDiscoveryConfig } from "./skill-discovery-config"
|
|
21
23
|
|
|
22
24
|
export type SkillContext = {
|
|
23
25
|
mergedSkills: LoadedSkill[]
|
|
@@ -71,21 +73,37 @@ export async function createSkillContext(args: {
|
|
|
71
73
|
return true
|
|
72
74
|
})
|
|
73
75
|
|
|
74
|
-
const
|
|
75
|
-
const [
|
|
76
|
+
const discovery = resolveSkillDiscoveryConfig(pluginConfig)
|
|
77
|
+
const [
|
|
78
|
+
managedPluginSkills,
|
|
79
|
+
configSourceSkills,
|
|
80
|
+
userSkills,
|
|
81
|
+
globalSkills,
|
|
82
|
+
projectSkills,
|
|
83
|
+
opencodeProjectSkills,
|
|
84
|
+
agentsProjectSkills,
|
|
85
|
+
agentsGlobalSkills,
|
|
86
|
+
] =
|
|
76
87
|
await Promise.all([
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
88
|
+
discoverManagedPluginSkills(),
|
|
89
|
+
discovery.config_sources
|
|
90
|
+
? discoverConfigSourceSkills({
|
|
91
|
+
config: pluginConfig.skills,
|
|
92
|
+
configDir: directory,
|
|
93
|
+
})
|
|
94
|
+
: Promise.resolve([]),
|
|
95
|
+
discovery.global_claude ? discoverUserClaudeSkills() : Promise.resolve([]),
|
|
96
|
+
discovery.global_opencode ? discoverOpencodeGlobalSkills() : Promise.resolve([]),
|
|
97
|
+
discovery.project_claude ? discoverProjectClaudeSkills(directory) : Promise.resolve([]),
|
|
98
|
+
discovery.project_opencode ? discoverOpencodeProjectSkills(directory) : Promise.resolve([]),
|
|
99
|
+
discovery.project_agents ? discoverProjectAgentsSkills(directory) : Promise.resolve([]),
|
|
100
|
+
discovery.global_agents ? discoverGlobalAgentsSkills() : Promise.resolve([]),
|
|
87
101
|
])
|
|
88
102
|
|
|
103
|
+
const filteredManagedPluginSkills = filterProviderGatedSkills(
|
|
104
|
+
managedPluginSkills,
|
|
105
|
+
browserProvider,
|
|
106
|
+
)
|
|
89
107
|
const filteredConfigSourceSkills = filterProviderGatedSkills(
|
|
90
108
|
configSourceSkills,
|
|
91
109
|
browserProvider,
|
|
@@ -109,7 +127,7 @@ export async function createSkillContext(args: {
|
|
|
109
127
|
const mergedSkills = mergeSkills(
|
|
110
128
|
builtinSkills,
|
|
111
129
|
pluginConfig.skills,
|
|
112
|
-
filteredConfigSourceSkills,
|
|
130
|
+
[...filteredManagedPluginSkills, ...filteredConfigSourceSkills],
|
|
113
131
|
[...filteredUserSkills, ...filteredAgentsGlobalSkills],
|
|
114
132
|
filteredGlobalSkills,
|
|
115
133
|
[...filteredProjectSkills, ...filteredAgentsProjectSkills],
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { HiaiOpenCodeConfig } from "../config"
|
|
2
|
+
import type { SkillDiscoveryConfig } from "../config/schema"
|
|
3
|
+
|
|
4
|
+
export type ResolvedSkillDiscoveryConfig = Required<SkillDiscoveryConfig>
|
|
5
|
+
|
|
6
|
+
const DEFAULT_SKILL_DISCOVERY: ResolvedSkillDiscoveryConfig = {
|
|
7
|
+
config_sources: true,
|
|
8
|
+
project_opencode: true,
|
|
9
|
+
global_opencode: false,
|
|
10
|
+
project_claude: false,
|
|
11
|
+
global_claude: false,
|
|
12
|
+
project_agents: false,
|
|
13
|
+
global_agents: false,
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function resolveSkillDiscoveryConfig(
|
|
17
|
+
pluginConfig: HiaiOpenCodeConfig,
|
|
18
|
+
): ResolvedSkillDiscoveryConfig {
|
|
19
|
+
const resolved: ResolvedSkillDiscoveryConfig = {
|
|
20
|
+
...DEFAULT_SKILL_DISCOVERY,
|
|
21
|
+
...(pluginConfig.skill_discovery ?? {}),
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Compatibility switch: historically this only controlled Claude-style skills.
|
|
25
|
+
// Keep that meaning, but make it stronger for those two sources.
|
|
26
|
+
if (pluginConfig.claude_code?.skills === false) {
|
|
27
|
+
resolved.project_claude = false
|
|
28
|
+
resolved.global_claude = false
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return resolved
|
|
32
|
+
}
|