@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
|
@@ -7,6 +7,7 @@ import { AGENT_NAME_MAP } from "../shared/migration";
|
|
|
7
7
|
import { registerAgentName } from "../features/claude-code-session-state";
|
|
8
8
|
import {
|
|
9
9
|
discoverConfigSourceSkills,
|
|
10
|
+
discoverManagedPluginSkills,
|
|
10
11
|
deduplicateSkillsByName,
|
|
11
12
|
discoverGlobalAgentsSkills,
|
|
12
13
|
discoverOpencodeGlobalSkills,
|
|
@@ -31,6 +32,7 @@ import {
|
|
|
31
32
|
filterProtectedAgentOverrides,
|
|
32
33
|
} from "./agent-override-protection";
|
|
33
34
|
import { buildStrategistAgentConfig } from "./strategist-agent-config-builder";
|
|
35
|
+
import { resolveSkillDiscoveryConfig } from "../plugin/skill-discovery-config";
|
|
34
36
|
|
|
35
37
|
type AgentConfigRecord = Record<string, Record<string, unknown> | undefined> & {
|
|
36
38
|
build?: Record<string, unknown>;
|
|
@@ -130,8 +132,9 @@ export async function applyAgentConfig(params: {
|
|
|
130
132
|
},
|
|
131
133
|
) as typeof params.pluginConfig.disabled_agents;
|
|
132
134
|
|
|
133
|
-
const
|
|
135
|
+
const discovery = resolveSkillDiscoveryConfig(params.pluginConfig);
|
|
134
136
|
const [
|
|
137
|
+
discoveredManagedPluginSkills,
|
|
135
138
|
discoveredConfigSourceSkills,
|
|
136
139
|
discoveredUserSkills,
|
|
137
140
|
discoveredProjectSkills,
|
|
@@ -140,23 +143,27 @@ export async function applyAgentConfig(params: {
|
|
|
140
143
|
discoveredOpencodeProjectSkills,
|
|
141
144
|
discoveredGlobalAgentsSkills,
|
|
142
145
|
] = await Promise.all([
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
146
|
+
discoverManagedPluginSkills(),
|
|
147
|
+
discovery.config_sources
|
|
148
|
+
? discoverConfigSourceSkills({
|
|
149
|
+
config: params.pluginConfig.skills,
|
|
150
|
+
configDir: params.ctx.directory,
|
|
151
|
+
})
|
|
152
|
+
: Promise.resolve([]),
|
|
153
|
+
discovery.global_claude ? discoverUserClaudeSkills() : Promise.resolve([]),
|
|
154
|
+
discovery.project_claude
|
|
155
|
+
? discoverProjectClaudeSkills(params.ctx.directory)
|
|
156
|
+
: Promise.resolve([]),
|
|
157
|
+
discovery.project_agents
|
|
152
158
|
? discoverProjectAgentsSkills(params.ctx.directory)
|
|
153
159
|
: Promise.resolve([]),
|
|
154
|
-
discoverOpencodeGlobalSkills(),
|
|
155
|
-
discoverOpencodeProjectSkills(params.ctx.directory),
|
|
156
|
-
|
|
160
|
+
discovery.global_opencode ? discoverOpencodeGlobalSkills() : Promise.resolve([]),
|
|
161
|
+
discovery.project_opencode ? discoverOpencodeProjectSkills(params.ctx.directory) : Promise.resolve([]),
|
|
162
|
+
discovery.global_agents ? discoverGlobalAgentsSkills() : Promise.resolve([]),
|
|
157
163
|
]);
|
|
158
164
|
|
|
159
165
|
const allDiscoveredSkills = [
|
|
166
|
+
...discoveredManagedPluginSkills,
|
|
160
167
|
...discoveredConfigSourceSkills,
|
|
161
168
|
...discoveredOpencodeProjectSkills,
|
|
162
169
|
...discoveredProjectSkills,
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
import { loadBuiltinCommands } from "../features/builtin-commands";
|
|
13
13
|
import {
|
|
14
14
|
discoverConfigSourceSkills,
|
|
15
|
+
loadManagedPluginSkills,
|
|
15
16
|
loadGlobalAgentsSkills,
|
|
16
17
|
loadProjectAgentsSkills,
|
|
17
18
|
loadUserSkills,
|
|
@@ -26,6 +27,7 @@ import {
|
|
|
26
27
|
log,
|
|
27
28
|
} from "../shared";
|
|
28
29
|
import type { PluginComponents } from "./plugin-components-loader";
|
|
30
|
+
import { resolveSkillDiscoveryConfig } from "../plugin/skill-discovery-config";
|
|
29
31
|
|
|
30
32
|
export async function applyCommandConfig(params: {
|
|
31
33
|
config: Record<string, unknown>;
|
|
@@ -39,10 +41,13 @@ export async function applyCommandConfig(params: {
|
|
|
39
41
|
const systemCommands = (params.config.command as Record<string, unknown>) ?? {};
|
|
40
42
|
|
|
41
43
|
const includeClaudeCommands = params.pluginConfig.claude_code?.commands ?? true;
|
|
42
|
-
const
|
|
44
|
+
const discovery = resolveSkillDiscoveryConfig(params.pluginConfig);
|
|
43
45
|
|
|
44
46
|
const externalSkillPlugin = detectExternalSkillPlugin(params.ctx.directory);
|
|
45
|
-
if (
|
|
47
|
+
if (
|
|
48
|
+
(discovery.project_claude || discovery.global_claude || discovery.global_opencode) &&
|
|
49
|
+
externalSkillPlugin.detected
|
|
50
|
+
) {
|
|
46
51
|
log(getSkillPluginConflictWarning(externalSkillPlugin.pluginName!));
|
|
47
52
|
}
|
|
48
53
|
|
|
@@ -52,6 +57,7 @@ export async function applyCommandConfig(params: {
|
|
|
52
57
|
projectCommands,
|
|
53
58
|
opencodeGlobalCommands,
|
|
54
59
|
opencodeProjectCommands,
|
|
60
|
+
managedPluginSkills,
|
|
55
61
|
userSkills,
|
|
56
62
|
globalAgentsSkills,
|
|
57
63
|
projectSkills,
|
|
@@ -59,24 +65,28 @@ export async function applyCommandConfig(params: {
|
|
|
59
65
|
opencodeGlobalSkills,
|
|
60
66
|
opencodeProjectSkills,
|
|
61
67
|
] = await Promise.all([
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
68
|
+
discovery.config_sources
|
|
69
|
+
? discoverConfigSourceSkills({
|
|
70
|
+
config: params.pluginConfig.skills,
|
|
71
|
+
configDir: params.ctx.directory,
|
|
72
|
+
})
|
|
73
|
+
: Promise.resolve([]),
|
|
66
74
|
includeClaudeCommands ? loadUserCommands() : Promise.resolve({}),
|
|
67
75
|
includeClaudeCommands ? loadProjectCommands(params.ctx.directory) : Promise.resolve({}),
|
|
68
76
|
loadOpencodeGlobalCommands(),
|
|
69
77
|
loadOpencodeProjectCommands(params.ctx.directory),
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
78
|
+
loadManagedPluginSkills(),
|
|
79
|
+
discovery.global_claude ? loadUserSkills() : Promise.resolve({}),
|
|
80
|
+
discovery.global_agents ? loadGlobalAgentsSkills() : Promise.resolve({}),
|
|
81
|
+
discovery.project_claude ? loadProjectSkills(params.ctx.directory) : Promise.resolve({}),
|
|
82
|
+
discovery.project_agents ? loadProjectAgentsSkills(params.ctx.directory) : Promise.resolve({}),
|
|
83
|
+
discovery.global_opencode ? loadOpencodeGlobalSkills() : Promise.resolve({}),
|
|
84
|
+
discovery.project_opencode ? loadOpencodeProjectSkills(params.ctx.directory) : Promise.resolve({}),
|
|
76
85
|
]);
|
|
77
86
|
|
|
78
87
|
params.config.command = {
|
|
79
88
|
...builtinCommands,
|
|
89
|
+
...managedPluginSkills,
|
|
80
90
|
...skillsToCommandDefinitionRecord(configSourceSkills),
|
|
81
91
|
...userCommands,
|
|
82
92
|
...userSkills,
|
|
@@ -64,10 +64,10 @@ export const AGENT_NAME_MAP: Record<string, string> = {
|
|
|
64
64
|
// Designer
|
|
65
65
|
designer: "designer",
|
|
66
66
|
|
|
67
|
-
// Multimodal
|
|
68
|
-
ui: "
|
|
69
|
-
vision: "
|
|
70
|
-
multimodal: "
|
|
67
|
+
// Multimodal / Vision
|
|
68
|
+
ui: "multimodal",
|
|
69
|
+
vision: "multimodal",
|
|
70
|
+
multimodal: "multimodal",
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
export const BUILTIN_AGENT_NAMES = new Set([
|
|
@@ -77,7 +77,7 @@ export const BUILTIN_AGENT_NAMES = new Set([
|
|
|
77
77
|
"critic",
|
|
78
78
|
"designer",
|
|
79
79
|
"researcher",
|
|
80
|
-
"
|
|
80
|
+
"multimodal",
|
|
81
81
|
"platform-manager",
|
|
82
82
|
"guard",
|
|
83
83
|
])
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs"
|
|
2
|
+
import { join } from "node:path"
|
|
3
|
+
|
|
4
|
+
import type { HiaiOpenCodeConfig, HiaiOpencodeConfig } from "../config"
|
|
5
|
+
import { HIAI_MCP_REGISTRY } from "../mcp/registry"
|
|
6
|
+
import { parseJsoncSafe } from "./jsonc-parser"
|
|
7
|
+
import { getOpenCodeConfigPaths } from "./opencode-config-dir"
|
|
8
|
+
import { PLUGIN_NAME } from "./plugin-identity"
|
|
9
|
+
|
|
10
|
+
interface OpenCodeConfig {
|
|
11
|
+
plugin?: Array<string | [string, ...unknown[]]>
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function readPlugins(configPath: string): string[] {
|
|
15
|
+
if (!existsSync(configPath)) return []
|
|
16
|
+
|
|
17
|
+
try {
|
|
18
|
+
const content = readFileSync(configPath, "utf-8")
|
|
19
|
+
const parsed = parseJsoncSafe<OpenCodeConfig>(content)
|
|
20
|
+
return (parsed.data?.plugin ?? [])
|
|
21
|
+
.map((entry) => typeof entry === "string" ? entry : Array.isArray(entry) ? entry[0] : "")
|
|
22
|
+
.filter((entry): entry is string => typeof entry === "string" && entry.trim().length > 0)
|
|
23
|
+
} catch {
|
|
24
|
+
return []
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function warnIfListPluginEntry(directory: string): void {
|
|
29
|
+
const globalPaths = getOpenCodeConfigPaths({ binary: "opencode", version: null })
|
|
30
|
+
const candidates = [
|
|
31
|
+
join(directory, ".opencode", "opencode.json"),
|
|
32
|
+
join(directory, ".opencode", "opencode.jsonc"),
|
|
33
|
+
globalPaths.configJson,
|
|
34
|
+
globalPaths.configJsonc,
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
for (const configPath of candidates) {
|
|
38
|
+
const plugins = readPlugins(configPath)
|
|
39
|
+
if (!plugins.includes("list")) continue
|
|
40
|
+
|
|
41
|
+
console.warn(`[hiai-opencode] WARNING: ${configPath} contains plugin: ["list"].`)
|
|
42
|
+
console.warn("[hiai-opencode] This can prevent hiai-opencode MCP servers from loading from that config scope.")
|
|
43
|
+
console.warn(`[hiai-opencode] Update it to: plugin: ["${PLUGIN_NAME}"]`)
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function hasConfigAuthFallback(pluginConfig: HiaiOpenCodeConfig, envName: string): boolean {
|
|
48
|
+
if (envName === "FIRECRAWL_API_KEY") return !!pluginConfig.auth?.firecrawl?.trim()
|
|
49
|
+
if (envName === "STITCH_AI_API_KEY") return !!pluginConfig.auth?.stitch?.trim()
|
|
50
|
+
if (envName === "CONTEXT7_API_KEY") return !!pluginConfig.auth?.context7?.trim()
|
|
51
|
+
return false
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function warnMissingRequiredMcpEnv(args: {
|
|
55
|
+
pluginConfig: HiaiOpenCodeConfig
|
|
56
|
+
platformConfig: HiaiOpencodeConfig
|
|
57
|
+
}): void {
|
|
58
|
+
const disabled = new Set(args.pluginConfig.disabled_mcps ?? [])
|
|
59
|
+
const mcpConfig = args.platformConfig.mcp ?? {}
|
|
60
|
+
|
|
61
|
+
for (const [name, entry] of Object.entries(HIAI_MCP_REGISTRY)) {
|
|
62
|
+
if (disabled.has(name)) continue
|
|
63
|
+
if (mcpConfig[name]?.enabled === false) continue
|
|
64
|
+
if (!entry.requiredEnv || entry.requiredEnv.length === 0) continue
|
|
65
|
+
|
|
66
|
+
const missing = entry.requiredEnv.filter((envName) =>
|
|
67
|
+
!process.env[envName]?.trim() && !hasConfigAuthFallback(args.pluginConfig, envName)
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
if (missing.length === 0) continue
|
|
71
|
+
|
|
72
|
+
console.warn(
|
|
73
|
+
`[hiai-opencode] MCP "${name}" is enabled but missing required env: ${missing.join(", ")}.`
|
|
74
|
+
+ " The plugin will continue to load; set the key or disable this MCP in hiai-opencode.json.",
|
|
75
|
+
)
|
|
76
|
+
}
|
|
77
|
+
}
|
package/src/config/models.ts
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
export const MODEL_ROLE_GUIDE = [
|
|
2
|
-
"fast: cheap/default for bounded helpers, researcher-style scans, platform chores",
|
|
3
|
-
"mid: balanced default for steady execution/review work",
|
|
4
|
-
"high: stronger general-purpose model for primary implementation and planning",
|
|
5
|
-
"ultrahigh: highest-cost/high-accuracy slot for hard architecture or critical decisions",
|
|
6
|
-
"vision: preferred for UI/media/multimodal interpretation and visual work",
|
|
7
|
-
"reasoning: preferred for deeper multi-step reasoning when latency/cost are acceptable",
|
|
8
|
-
] as const;
|
|
9
|
-
|
|
10
|
-
export const PROVIDER_MODEL_RULES = [
|
|
11
|
-
"openai: use `openai/<model>` for direct OpenAI calls, e.g. `openai/gpt-5` or `openai/o1`",
|
|
12
|
-
"anthropic: use `anthropic/<model>`, e.g. `anthropic/claude-3.5-sonnet`",
|
|
13
|
-
"deepseek: use `deepseek/<model>` when connected directly",
|
|
14
|
-
"glm: use `z-ai/<model>` or the provider id exposed by your gateway/client",
|
|
15
|
-
"minimax: use `minimax/<model>`",
|
|
16
|
-
"qwen: use `qwen/<model>`",
|
|
17
|
-
"ollama: use the native local model id in Ollama config, e.g. `qwen3.5:4b`",
|
|
18
|
-
"openrouter: use `openrouter/<vendor>/<model>`, e.g. `openrouter/anthropic/claude-3.5-sonnet`",
|
|
19
|
-
"rule: store fully qualified model ids in config; avoid local aliases like `fast`, `sonnet`, or provider-less ids",
|
|
20
|
-
] as const;
|
|
21
|
-
|
|
22
|
-
export const MODEL_PRESETS = {
|
|
23
|
-
fast: "openrouter/google/gemini-2.0-flash",
|
|
24
|
-
mid: "openrouter/anthropic/claude-3.5-sonnet",
|
|
25
|
-
high: "openrouter/anthropic/claude-3.5-opus",
|
|
26
|
-
ultrahigh: "openrouter/openai/gpt-4o",
|
|
27
|
-
vision: "openrouter/google/gemini-2.0-pro-exp-02-05",
|
|
28
|
-
reasoning: "openrouter/openai/o1",
|
|
29
|
-
strategist: "openrouter/z-ai/glm-5.1",
|
|
30
|
-
critic: "openrouter/qwen/qwen2.5-72b-instruct",
|
|
31
|
-
writing: "openrouter/kimi/kimi-latest",
|
|
32
|
-
} as const;
|