@hiai-gg/hiai-opencode 0.1.5 → 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 +21 -8
- package/AGENTS.md +60 -6
- package/ARCHITECTURE.md +6 -3
- package/LICENSE.md +0 -1
- package/README.md +113 -33
- package/assets/cli/hiai-opencode.mjs +668 -7
- package/assets/mcp/mempalace.mjs +159 -25
- package/config/hiai-opencode.schema.json +29 -3
- 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/dynamic-agent-core-sections.d.ts +4 -1
- package/dist/agents/dynamic-agent-prompt-builder.d.ts +1 -1
- 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 +34 -6
- package/dist/config/schema/commands.d.ts +1 -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 +1 -9
- package/dist/config/types.d.ts +4 -4
- package/dist/create-hooks.d.ts +0 -2
- package/dist/features/builtin-commands/templates/doctor.d.ts +1 -0
- package/dist/features/builtin-commands/types.d.ts +1 -1
- package/dist/features/builtin-skills/skills/hiai-opencode-setup.d.ts +2 -0
- package/dist/features/builtin-skills/skills/index.d.ts +2 -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 +7719 -153698
- 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/mcp-static-export.d.ts +22 -0
- package/dist/shared/mode-routing.d.ts +6 -0
- package/dist/tools/ast-grep/constants.d.ts +1 -1
- package/dist/tools/ast-grep/environment-check.d.ts +1 -5
- package/dist/tools/ast-grep/language-support.d.ts +0 -1
- package/dist/tools/ast-grep/types.d.ts +1 -2
- 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 +50 -19
- package/package.json +10 -5
- package/src/agents/agent-skills.ts +70 -0
- package/src/agents/bob/default.ts +7 -1
- package/src/agents/bob/gemini.ts +1 -0
- package/src/agents/bob/gpt-pro.ts +3 -1
- package/src/agents/bob.ts +3 -0
- package/src/agents/brainstormer.ts +72 -0
- package/src/agents/builtin-agents.ts +59 -3
- package/src/agents/coder/gpt-codex.ts +5 -3
- package/src/agents/coder/gpt-pro.ts +4 -2
- package/src/agents/coder/gpt.ts +3 -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/dynamic-agent-core-sections.ts +36 -0
- package/src/agents/dynamic-agent-prompt-builder.ts +1 -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 +45 -13
- package/src/config/index.ts +0 -1
- package/src/config/model-slots-and-export.test.ts +73 -0
- package/src/config/platform-schema.ts +3 -3
- package/src/config/schema/commands.ts +1 -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 -5
- package/src/config/types.ts +4 -4
- package/src/features/builtin-commands/commands.ts +7 -0
- package/src/features/builtin-commands/templates/doctor.ts +43 -0
- package/src/features/builtin-commands/types.ts +1 -1
- package/src/features/builtin-skills/skills/hiai-opencode-setup.ts +69 -0
- package/src/features/builtin-skills/skills/index.ts +2 -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 +12 -1
- 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 +8 -78
- 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 +119 -0
- 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/ast-grep/constants.ts +1 -1
- package/src/tools/ast-grep/environment-check.ts +2 -32
- package/src/tools/ast-grep/language-support.ts +0 -3
- package/src/tools/ast-grep/types.ts +1 -2
- 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/src/tools/skill-mcp/tools.test.ts +44 -0
- package/dist/ast-grep-napi.win32-x64-msvc-67c0y8nc.node +0 -0
- package/dist/config/loader.test.d.ts +0 -1
- package/dist/config/models.d.ts +0 -13
- 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/internals/plugins/websearch-cited/google.d.ts +0 -38
- package/dist/internals/plugins/websearch-cited/index.d.ts +0 -17
- package/dist/internals/plugins/websearch-cited/openai.d.ts +0 -9
- package/dist/internals/plugins/websearch-cited/openrouter.d.ts +0 -2
- package/dist/internals/plugins/websearch-cited/types.d.ts +0 -5
- 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/internals/plugins/websearch-cited/LICENSE +0 -214
- package/src/internals/plugins/websearch-cited/codex_prompt.txt +0 -79
- package/src/internals/plugins/websearch-cited/google.ts +0 -749
- package/src/internals/plugins/websearch-cited/index.ts +0 -306
- package/src/internals/plugins/websearch-cited/openai.ts +0 -407
- package/src/internals/plugins/websearch-cited/openrouter.ts +0 -190
- package/src/internals/plugins/websearch-cited/types.ts +0 -7
- package/src/mcp/grep-app.ts +0 -6
- package/src/mcp/omo-mcp-index.ts +0 -30
- package/src/mcp/websearch.ts +0 -44
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs"
|
|
2
|
+
import { dirname, join } from "node:path"
|
|
3
|
+
|
|
4
|
+
import type { HiaiOpencodeConfig, McpServerConfig } from "../config/types"
|
|
5
|
+
import { resolveEnvVars } from "../config/loader"
|
|
6
|
+
import { log, logWarn } from "./logger"
|
|
7
|
+
|
|
8
|
+
type StaticMcpServer = {
|
|
9
|
+
type?: "http" | "stdio"
|
|
10
|
+
url?: string
|
|
11
|
+
command?: string
|
|
12
|
+
args?: string[]
|
|
13
|
+
env?: Record<string, string>
|
|
14
|
+
headers?: Record<string, string>
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const MCP_EXPORT_MARKER = "hiai-opencode"
|
|
18
|
+
|
|
19
|
+
type StaticMcpJsonPayload = {
|
|
20
|
+
mcpServers: Record<string, StaticMcpServer>
|
|
21
|
+
_meta?: {
|
|
22
|
+
generatedBy: typeof MCP_EXPORT_MARKER
|
|
23
|
+
version: 1
|
|
24
|
+
generatedAt: string
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function toStaticMcpServer(config: McpServerConfig): StaticMcpServer | null {
|
|
29
|
+
if (config.enabled === false) return null
|
|
30
|
+
|
|
31
|
+
if (config.type === "remote") {
|
|
32
|
+
const headers = config.headers
|
|
33
|
+
? Object.fromEntries(Object.entries(config.headers).map(([key, value]) => [key, resolveEnvVars(value)]))
|
|
34
|
+
: undefined
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
type: "http",
|
|
38
|
+
url: config.url,
|
|
39
|
+
...(headers ? { headers } : {}),
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const [command, ...args] = config.command ?? []
|
|
44
|
+
if (!command) return null
|
|
45
|
+
|
|
46
|
+
const env = config.environment
|
|
47
|
+
? Object.fromEntries(Object.entries(config.environment).map(([key, value]) => [key, resolveEnvVars(value)]))
|
|
48
|
+
: undefined
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
command,
|
|
52
|
+
...(args.length > 0 ? { args } : {}),
|
|
53
|
+
...(env ? { env } : {}),
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function buildStaticMcpJson(config: HiaiOpencodeConfig): StaticMcpJsonPayload {
|
|
58
|
+
const mcpServers: Record<string, StaticMcpServer> = {}
|
|
59
|
+
|
|
60
|
+
for (const [name, serverConfig] of Object.entries(config.mcp ?? {})) {
|
|
61
|
+
const converted = toStaticMcpServer(serverConfig)
|
|
62
|
+
if (converted) {
|
|
63
|
+
mcpServers[name] = converted
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return {
|
|
68
|
+
_meta: {
|
|
69
|
+
generatedBy: MCP_EXPORT_MARKER,
|
|
70
|
+
version: 1,
|
|
71
|
+
generatedAt: new Date().toISOString(),
|
|
72
|
+
},
|
|
73
|
+
mcpServers,
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function isManagedStaticMcpFile(path: string): boolean {
|
|
78
|
+
if (!existsSync(path)) return false
|
|
79
|
+
|
|
80
|
+
try {
|
|
81
|
+
const raw = readFileSync(path, "utf-8")
|
|
82
|
+
const parsed = JSON.parse(raw) as StaticMcpJsonPayload
|
|
83
|
+
return parsed?._meta?.generatedBy === MCP_EXPORT_MARKER
|
|
84
|
+
} catch {
|
|
85
|
+
return false
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function autoExportStaticMcpJson(directory: string, config: HiaiOpencodeConfig): void {
|
|
90
|
+
const mode = process.env.HIAI_OPENCODE_AUTO_EXPORT_MCP?.trim().toLowerCase() || "if-missing"
|
|
91
|
+
if (mode === "0" || mode === "false" || mode === "no" || mode === "off") {
|
|
92
|
+
return
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const outputPath = process.env.HIAI_OPENCODE_MCP_EXPORT_PATH?.trim() || join(directory, ".mcp.json")
|
|
96
|
+
if (mode === "if-missing" && existsSync(outputPath)) {
|
|
97
|
+
return
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const isForceMode = mode === "force"
|
|
101
|
+
if (mode === "always" && existsSync(outputPath) && !isManagedStaticMcpFile(outputPath) && !isForceMode) {
|
|
102
|
+
logWarn(`refusing to overwrite non-managed static MCP config at ${outputPath}. `
|
|
103
|
+
+ "Set HIAI_OPENCODE_AUTO_EXPORT_MCP=force to override.")
|
|
104
|
+
return
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
try {
|
|
108
|
+
mkdirSync(dirname(outputPath), { recursive: true })
|
|
109
|
+
const payload = buildStaticMcpJson(config)
|
|
110
|
+
writeFileSync(outputPath, `${JSON.stringify(payload, null, 2)}\n`)
|
|
111
|
+
log("[hiai-opencode] exported static MCP config", {
|
|
112
|
+
outputPath,
|
|
113
|
+
servers: Object.keys(payload.mcpServers),
|
|
114
|
+
})
|
|
115
|
+
} catch (error) {
|
|
116
|
+
const message = error instanceof Error ? error.message : String(error)
|
|
117
|
+
logWarn(`failed to export static MCP config to ${outputPath}: ${message}`)
|
|
118
|
+
}
|
|
119
|
+
}
|
|
@@ -64,6 +64,13 @@ export const AGENT_NAME_MAP: Record<string, string> = {
|
|
|
64
64
|
// Designer
|
|
65
65
|
designer: "designer",
|
|
66
66
|
|
|
67
|
+
// Writer/copywriter aliases are handled by Brainstormer.
|
|
68
|
+
writer: "brainstormer",
|
|
69
|
+
copywriter: "brainstormer",
|
|
70
|
+
"content-writer": "brainstormer",
|
|
71
|
+
"content writer": "brainstormer",
|
|
72
|
+
"website-writer": "brainstormer",
|
|
73
|
+
|
|
67
74
|
// Multimodal / Vision
|
|
68
75
|
ui: "multimodal",
|
|
69
76
|
vision: "multimodal",
|
|
@@ -80,6 +87,7 @@ export const BUILTIN_AGENT_NAMES = new Set([
|
|
|
80
87
|
"multimodal",
|
|
81
88
|
"platform-manager",
|
|
82
89
|
"guard",
|
|
90
|
+
"brainstormer",
|
|
83
91
|
])
|
|
84
92
|
|
|
85
93
|
export function migrateAgentNames(
|
|
@@ -5,7 +5,7 @@ export const HOOK_NAME_MAP: Record<string, string | null> = {
|
|
|
5
5
|
"anthropic-auto-compact": "anthropic-context-window-limit-recovery",
|
|
6
6
|
"bob-orchestrator": "guard",
|
|
7
7
|
|
|
8
|
-
"bob-gpt-coder-reminder":
|
|
8
|
+
"bob-gpt-coder-reminder": null,
|
|
9
9
|
|
|
10
10
|
// Removed hooks (v3.0.0) - will be filtered out and user warned
|
|
11
11
|
"empty-message-sanitizer": null,
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { expect, test } from "bun:test"
|
|
2
|
+
import { normalizeMode, resolveModeAgent, MODE_TO_AGENT, MODE_NAMES } from "./mode-routing"
|
|
3
|
+
|
|
4
|
+
test("normalizeMode returns legacy aliases mapped to new names", () => {
|
|
5
|
+
expect(normalizeMode("unspecified-low")).toBe("bounded")
|
|
6
|
+
expect(normalizeMode("unspecified-high")).toBe("cross-module")
|
|
7
|
+
})
|
|
8
|
+
|
|
9
|
+
test("normalizeMode returns same name when not a legacy alias", () => {
|
|
10
|
+
expect(normalizeMode("quick")).toBe("quick")
|
|
11
|
+
expect(normalizeMode("deep")).toBe("deep")
|
|
12
|
+
expect(normalizeMode("writing")).toBe("writing")
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
test("resolveModeAgent maps quick to sub", () => {
|
|
16
|
+
expect(resolveModeAgent("quick")).toBe("sub")
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
test("resolveModeAgent maps writing to brainstormer", () => {
|
|
20
|
+
expect(resolveModeAgent("writing")).toBe("brainstormer")
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
test("resolveModeAgent maps deep to coder", () => {
|
|
24
|
+
expect(resolveModeAgent("deep")).toBe("coder")
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
test("resolveModeAgent maps ultrabrain to strategist", () => {
|
|
28
|
+
expect(resolveModeAgent("ultrabrain")).toBe("strategist")
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
test("resolveModeAgent maps visual-engineering to designer", () => {
|
|
32
|
+
expect(resolveModeAgent("visual-engineering")).toBe("designer")
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
test("resolveModeAgent maps artistry to designer", () => {
|
|
36
|
+
expect(resolveModeAgent("artistry")).toBe("designer")
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
test("resolveModeAgent maps git to platform-manager", () => {
|
|
40
|
+
expect(resolveModeAgent("git")).toBe("platform-manager")
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
test("resolveModeAgent maps git-ops to platform-manager", () => {
|
|
44
|
+
expect(resolveModeAgent("git-ops")).toBe("platform-manager")
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
test("resolveModeAgent maps bounded to sub", () => {
|
|
48
|
+
expect(resolveModeAgent("bounded")).toBe("sub")
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
test("resolveModeAgent maps cross-module to coder", () => {
|
|
52
|
+
expect(resolveModeAgent("cross-module")).toBe("coder")
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
test("resolveModeAgent falls back to coder for unknown mode", () => {
|
|
56
|
+
expect(resolveModeAgent("unknown-mode")).toBe("coder")
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
test("resolveModeAgent handles legacy aliases", () => {
|
|
60
|
+
expect(resolveModeAgent("unspecified-low")).toBe("sub")
|
|
61
|
+
expect(resolveModeAgent("unspecified-high")).toBe("coder")
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
test("MODE_NAMES contains all mode names", () => {
|
|
65
|
+
expect(MODE_NAMES).toContain("quick")
|
|
66
|
+
expect(MODE_NAMES).toContain("writing")
|
|
67
|
+
expect(MODE_NAMES).toContain("deep")
|
|
68
|
+
expect(MODE_NAMES).toContain("ultrabrain")
|
|
69
|
+
expect(MODE_NAMES).toContain("visual-engineering")
|
|
70
|
+
expect(MODE_NAMES).toContain("artistry")
|
|
71
|
+
expect(MODE_NAMES).toContain("git")
|
|
72
|
+
expect(MODE_NAMES).toContain("git-ops")
|
|
73
|
+
expect(MODE_NAMES).toContain("bounded")
|
|
74
|
+
expect(MODE_NAMES).toContain("cross-module")
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
test("MODE_TO_AGENT has correct mapping", () => {
|
|
78
|
+
expect(MODE_TO_AGENT.quick).toBe("sub")
|
|
79
|
+
expect(MODE_TO_AGENT.writing).toBe("brainstormer")
|
|
80
|
+
expect(MODE_TO_AGENT.deep).toBe("coder")
|
|
81
|
+
expect(MODE_TO_AGENT.ultrabrain).toBe("strategist")
|
|
82
|
+
expect(MODE_TO_AGENT["visual-engineering"]).toBe("designer")
|
|
83
|
+
expect(MODE_TO_AGENT.artistry).toBe("designer")
|
|
84
|
+
expect(MODE_TO_AGENT.git).toBe("platform-manager")
|
|
85
|
+
expect(MODE_TO_AGENT["git-ops"]).toBe("platform-manager")
|
|
86
|
+
expect(MODE_TO_AGENT.bounded).toBe("sub")
|
|
87
|
+
expect(MODE_TO_AGENT["cross-module"]).toBe("coder")
|
|
88
|
+
})
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { CanonicalDelegateAgentKey } from "../tools/delegate-task/sub-agent"
|
|
2
|
+
|
|
3
|
+
export const MODE_TO_AGENT: Record<string, CanonicalDelegateAgentKey> = {
|
|
4
|
+
quick: "sub",
|
|
5
|
+
writing: "brainstormer",
|
|
6
|
+
deep: "coder",
|
|
7
|
+
ultrabrain: "strategist",
|
|
8
|
+
"visual-engineering": "designer",
|
|
9
|
+
artistry: "designer",
|
|
10
|
+
git: "platform-manager",
|
|
11
|
+
"git-ops": "platform-manager",
|
|
12
|
+
bounded: "sub",
|
|
13
|
+
"cross-module": "coder",
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const LEGACY_MODE_ALIASES: Record<string, string> = {
|
|
17
|
+
"unspecified-low": "bounded",
|
|
18
|
+
"unspecified-high": "cross-module",
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export type ModeName = keyof typeof MODE_TO_AGENT
|
|
22
|
+
export const MODE_NAMES = Object.keys(MODE_TO_AGENT) as ModeName[]
|
|
23
|
+
|
|
24
|
+
export function normalizeMode(modeName: string): string {
|
|
25
|
+
return LEGACY_MODE_ALIASES[modeName] ?? modeName
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function resolveModeAgent(modeName: string): CanonicalDelegateAgentKey {
|
|
29
|
+
return MODE_TO_AGENT[normalizeMode(modeName)] ?? "coder"
|
|
30
|
+
}
|
|
@@ -5,6 +5,7 @@ import type { HiaiOpenCodeConfig, HiaiOpencodeConfig } from "../config"
|
|
|
5
5
|
import { HIAI_MCP_REGISTRY } from "../mcp/registry"
|
|
6
6
|
import { parseJsoncSafe } from "./jsonc-parser"
|
|
7
7
|
import { getOpenCodeConfigPaths } from "./opencode-config-dir"
|
|
8
|
+
import { logWarn } from "./logger"
|
|
8
9
|
import { PLUGIN_NAME } from "./plugin-identity"
|
|
9
10
|
|
|
10
11
|
interface OpenCodeConfig {
|
|
@@ -38,9 +39,9 @@ export function warnIfListPluginEntry(directory: string): void {
|
|
|
38
39
|
const plugins = readPlugins(configPath)
|
|
39
40
|
if (!plugins.includes("list")) continue
|
|
40
41
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
logWarn(`${configPath} contains plugin: ["list"].`)
|
|
43
|
+
logWarn("This can prevent hiai-opencode MCP servers from loading from that config scope.")
|
|
44
|
+
logWarn(`Update it to: plugin: ["${PLUGIN_NAME}"]`)
|
|
44
45
|
}
|
|
45
46
|
}
|
|
46
47
|
|
|
@@ -69,9 +70,7 @@ export function warnMissingRequiredMcpEnv(args: {
|
|
|
69
70
|
|
|
70
71
|
if (missing.length === 0) continue
|
|
71
72
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
+ " The plugin will continue to load; set the key or disable this MCP in hiai-opencode.json.",
|
|
75
|
-
)
|
|
73
|
+
logWarn(`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.")
|
|
76
75
|
}
|
|
77
76
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export type { EnvironmentCheckResult } from "./environment-check"
|
|
2
2
|
export { checkEnvironment, formatEnvironmentCheck } from "./environment-check"
|
|
3
|
-
export { CLI_LANGUAGES,
|
|
3
|
+
export { CLI_LANGUAGES, LANG_EXTENSIONS } from "./language-support"
|
|
4
4
|
export { DEFAULT_TIMEOUT_MS, DEFAULT_MAX_OUTPUT_BYTES, DEFAULT_MAX_MATCHES } from "./language-support"
|
|
5
5
|
export { findSgCliPathSync, getSgCliPath, setSgCliPath } from "./sg-cli-path"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { existsSync } from "fs"
|
|
2
2
|
|
|
3
|
-
import { CLI_LANGUAGES
|
|
3
|
+
import { CLI_LANGUAGES } from "./language-support"
|
|
4
4
|
import { getSgCliPath } from "./sg-cli-path"
|
|
5
5
|
|
|
6
6
|
export interface EnvironmentCheckResult {
|
|
@@ -9,14 +9,10 @@ export interface EnvironmentCheckResult {
|
|
|
9
9
|
path: string
|
|
10
10
|
error?: string
|
|
11
11
|
}
|
|
12
|
-
napi: {
|
|
13
|
-
available: boolean
|
|
14
|
-
error?: string
|
|
15
|
-
}
|
|
16
12
|
}
|
|
17
13
|
|
|
18
14
|
/**
|
|
19
|
-
* Check if ast-grep CLI
|
|
15
|
+
* Check if ast-grep CLI is available.
|
|
20
16
|
* Call this at startup to provide early feedback about missing dependencies.
|
|
21
17
|
*/
|
|
22
18
|
export function checkEnvironment(): EnvironmentCheckResult {
|
|
@@ -26,9 +22,6 @@ export function checkEnvironment(): EnvironmentCheckResult {
|
|
|
26
22
|
available: false,
|
|
27
23
|
path: cliPath ?? "not found",
|
|
28
24
|
},
|
|
29
|
-
napi: {
|
|
30
|
-
available: false,
|
|
31
|
-
},
|
|
32
25
|
}
|
|
33
26
|
|
|
34
27
|
if (cliPath && existsSync(cliPath)) {
|
|
@@ -39,17 +32,6 @@ export function checkEnvironment(): EnvironmentCheckResult {
|
|
|
39
32
|
result.cli.error = `Binary not found: ${cliPath}`
|
|
40
33
|
}
|
|
41
34
|
|
|
42
|
-
// Check NAPI availability
|
|
43
|
-
try {
|
|
44
|
-
require("@ast-grep/napi")
|
|
45
|
-
result.napi.available = true
|
|
46
|
-
} catch (error) {
|
|
47
|
-
result.napi.available = false
|
|
48
|
-
result.napi.error = `@ast-grep/napi not installed: ${
|
|
49
|
-
error instanceof Error ? error.message : String(error)
|
|
50
|
-
}`
|
|
51
|
-
}
|
|
52
|
-
|
|
53
35
|
return result
|
|
54
36
|
}
|
|
55
37
|
|
|
@@ -70,20 +52,8 @@ export function formatEnvironmentCheck(result: EnvironmentCheckResult): string {
|
|
|
70
52
|
lines.push(" Install: bun add -D @ast-grep/cli")
|
|
71
53
|
}
|
|
72
54
|
|
|
73
|
-
// NAPI status
|
|
74
|
-
if (result.napi.available) {
|
|
75
|
-
lines.push("[OK] NAPI: Available")
|
|
76
|
-
} else {
|
|
77
|
-
lines.push("[X] NAPI: Not available")
|
|
78
|
-
if (result.napi.error) {
|
|
79
|
-
lines.push(` Error: ${result.napi.error}`)
|
|
80
|
-
}
|
|
81
|
-
lines.push(" Install: bun add -D @ast-grep/napi")
|
|
82
|
-
}
|
|
83
|
-
|
|
84
55
|
lines.push("")
|
|
85
56
|
lines.push(`CLI supports ${CLI_LANGUAGES.length} languages`)
|
|
86
|
-
lines.push(`NAPI supports ${NAPI_LANGUAGES.length} languages: ${NAPI_LANGUAGES.join(", ")}`)
|
|
87
57
|
|
|
88
58
|
return lines.join("\n")
|
|
89
59
|
}
|
|
@@ -27,9 +27,6 @@ export const CLI_LANGUAGES = [
|
|
|
27
27
|
"yaml",
|
|
28
28
|
] as const
|
|
29
29
|
|
|
30
|
-
// NAPI supported languages (5 total - native bindings)
|
|
31
|
-
export const NAPI_LANGUAGES = ["html", "javascript", "tsx", "css", "typescript"] as const
|
|
32
|
-
|
|
33
30
|
export const DEFAULT_TIMEOUT_MS = 300_000
|
|
34
31
|
export const DEFAULT_MAX_OUTPUT_BYTES = 1 * 1024 * 1024
|
|
35
32
|
export const DEFAULT_MAX_MATCHES = 500
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import type { CLI_LANGUAGES
|
|
1
|
+
import type { CLI_LANGUAGES } from "./constants"
|
|
2
2
|
|
|
3
3
|
export type CliLanguage = (typeof CLI_LANGUAGES)[number]
|
|
4
|
-
export type NapiLanguage = (typeof NAPI_LANGUAGES)[number]
|
|
5
4
|
|
|
6
5
|
export interface Position {
|
|
7
6
|
line: number
|
|
@@ -10,6 +10,7 @@ import type { DelegatedModelConfig } from "../../shared/model-resolution-types"
|
|
|
10
10
|
import type { FallbackEntry } from "../../shared/model-requirements"
|
|
11
11
|
import { AGENT_MODEL_REQUIREMENTS } from "../../shared/model-requirements"
|
|
12
12
|
import { getAgentConfigKey, stripInvisibleAgentCharacters } from "../../shared/agent-display-names"
|
|
13
|
+
import { resolveCanonicalDelegateAgentKey } from "../../tools/delegate-task/sub-agent"
|
|
13
14
|
import { normalizeFallbackModels } from "../../shared/model-resolver"
|
|
14
15
|
import { buildFallbackChainFromModels } from "../../shared/fallback-chain-from-models"
|
|
15
16
|
import { log } from "../../shared"
|
|
@@ -139,22 +140,28 @@ export function createCallOmoAgent(
|
|
|
139
140
|
async execute(args: CallOmoAgentArgs, toolContext) {
|
|
140
141
|
const toolCtx = toolContext as ToolContextWithMetadata;
|
|
141
142
|
log(
|
|
142
|
-
`[call_omo_agent]
|
|
143
|
+
`[call_omo_agent] DEPRECATED: call_omo_agent is deprecated. Use task(agent="...", load_skills=[], run_in_background=...) instead.`,
|
|
143
144
|
);
|
|
144
145
|
|
|
145
146
|
const callableAgents = await resolveCallableAgents(ctx.client);
|
|
146
147
|
|
|
147
148
|
// Strip ZWSP and case-insensitive agent validation - allows canonical names and compatibility aliases.
|
|
148
149
|
const strippedAgentType = stripInvisibleAgentCharacters(args.subagent_type)
|
|
150
|
+
const preCanonicalAgentType = getAgentConfigKey(strippedAgentType)
|
|
151
|
+
const canonicalAgentType = resolveCanonicalDelegateAgentKey(preCanonicalAgentType)
|
|
152
|
+
const wasLegacyAlias = canonicalAgentType !== preCanonicalAgentType.toLowerCase()
|
|
153
|
+
if (wasLegacyAlias) {
|
|
154
|
+
log("[call_omo_agent] Legacy agent alias resolved", { from: preCanonicalAgentType, to: canonicalAgentType })
|
|
155
|
+
}
|
|
149
156
|
if (
|
|
150
157
|
!callableAgents.some(
|
|
151
|
-
(name) => name.toLowerCase() ===
|
|
158
|
+
(name) => name.toLowerCase() === canonicalAgentType.toLowerCase(),
|
|
152
159
|
)
|
|
153
160
|
) {
|
|
154
|
-
return `Error: Invalid agent type "${args.subagent_type}".
|
|
161
|
+
return `Error: Invalid agent type "${args.subagent_type}". Available agents: ${callableAgents.join(", ")}. Legacy aliases: oracle→strategist, hephaestus→coder, metis→strategist, momus→critic, sisyphus-junior→sub, multimodal-looker→multimodal, librarian→researcher, explore→researcher.`;
|
|
155
162
|
}
|
|
156
163
|
|
|
157
|
-
const normalizedAgent =
|
|
164
|
+
const normalizedAgent = canonicalAgentType.toLowerCase();
|
|
158
165
|
args = { ...args, subagent_type: normalizedAgent };
|
|
159
166
|
|
|
160
167
|
// Check if agent is disabled
|
|
@@ -4,7 +4,7 @@ const UNSPECIFIED_LOW_CATEGORY_PROMPT_APPEND = `<Category_Context>
|
|
|
4
4
|
You are working on tasks that don't fit specific categories but require moderate effort.
|
|
5
5
|
|
|
6
6
|
<Routing_Policy>
|
|
7
|
-
Executor contour:
|
|
7
|
+
Executor contour: sub (cheap fast-tier executor). Keep the scope bounded unless the task clearly requires deep or cross-system work; in that case escalate to unspecified-high or deep.
|
|
8
8
|
</Routing_Policy>
|
|
9
9
|
|
|
10
10
|
<Selection_Gate>
|
|
@@ -50,13 +50,13 @@ export const ANTHROPIC_CATEGORIES: BuiltinCategoryDefinition[] = [
|
|
|
50
50
|
{
|
|
51
51
|
name: "unspecified-low",
|
|
52
52
|
config: {},
|
|
53
|
-
description: "Unclassifiable moderate tasks with bounded scope. Uses
|
|
53
|
+
description: "Unclassifiable moderate tasks with bounded scope. Uses sub (cheap fast-tier) execution contour.",
|
|
54
54
|
promptAppend: UNSPECIFIED_LOW_CATEGORY_PROMPT_APPEND,
|
|
55
55
|
},
|
|
56
56
|
{
|
|
57
57
|
name: "unspecified-high",
|
|
58
58
|
config: {},
|
|
59
|
-
description: "Unclassifiable substantial tasks across modules. Uses coder execution
|
|
59
|
+
description: "Unclassifiable substantial tasks across modules. Uses coder (deep execution) with max-variant model.",
|
|
60
60
|
promptAppend: UNSPECIFIED_HIGH_CATEGORY_PROMPT_APPEND,
|
|
61
61
|
},
|
|
62
62
|
]
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { CategoryConfig } from "../../config/schema"
|
|
2
2
|
import { ANTHROPIC_CATEGORIES } from "./anthropic-categories"
|
|
3
3
|
import type { BuiltinCategoryDefinition } from "./builtin-category-definition"
|
|
4
|
+
import { GIT_CATEGORIES } from "./git-categories"
|
|
4
5
|
import { GOOGLE_CATEGORIES } from "./google-categories"
|
|
5
6
|
import { KIMI_CATEGORIES } from "./kimi-categories"
|
|
6
7
|
import { OPENAI_CATEGORIES } from "./openai-categories"
|
|
@@ -10,6 +11,7 @@ const BUILTIN_CATEGORIES: BuiltinCategoryDefinition[] = [
|
|
|
10
11
|
...OPENAI_CATEGORIES,
|
|
11
12
|
...ANTHROPIC_CATEGORIES,
|
|
12
13
|
...KIMI_CATEGORIES,
|
|
14
|
+
...GIT_CATEGORIES,
|
|
13
15
|
]
|
|
14
16
|
|
|
15
17
|
function buildCategoryRecord<TValue>(
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { expect, test } from "bun:test"
|
|
2
|
+
import { resolveCategoryConfig } from "./categories"
|
|
3
|
+
|
|
4
|
+
test("resolveCategoryConfig returns null for unknown category with no config", () => {
|
|
5
|
+
const result = resolveCategoryConfig("nonexistent-category", {
|
|
6
|
+
userCategories: {},
|
|
7
|
+
availableModels: new Set(["openrouter/minimax/minimax-m2.7"]),
|
|
8
|
+
})
|
|
9
|
+
expect(result).toBeNull()
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
test("resolveCategoryConfig returns null when category is disabled", () => {
|
|
13
|
+
const result = resolveCategoryConfig("quick", {
|
|
14
|
+
userCategories: { quick: { disable: true } },
|
|
15
|
+
})
|
|
16
|
+
expect(result).toBeNull()
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
test("resolveCategoryConfig merges default config with user config", () => {
|
|
20
|
+
const result = resolveCategoryConfig("quick", {
|
|
21
|
+
userCategories: { quick: { temperature: 0.9 } },
|
|
22
|
+
availableModels: new Set(["openrouter/minimax/minimax-m2.7"]),
|
|
23
|
+
})
|
|
24
|
+
expect(result).not.toBeNull()
|
|
25
|
+
expect(result!.config.temperature).toBe(0.9)
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
test("resolveCategoryConfig user model takes precedence over default", () => {
|
|
29
|
+
const result = resolveCategoryConfig("quick", {
|
|
30
|
+
userCategories: { quick: { model: "openrouter/deepseek/deepseek-v4-flash" } },
|
|
31
|
+
availableModels: new Set(["openrouter/deepseek/deepseek-v4-flash"]),
|
|
32
|
+
})
|
|
33
|
+
expect(result).not.toBeNull()
|
|
34
|
+
expect(result!.model).toBe("openrouter/deepseek/deepseek-v4-flash")
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
test("resolveCategoryConfig returns promptAppend for category", () => {
|
|
38
|
+
const result = resolveCategoryConfig("deep", {
|
|
39
|
+
userCategories: {},
|
|
40
|
+
availableModels: new Set(["openrouter/minimax/minimax-m2.7"]),
|
|
41
|
+
})
|
|
42
|
+
expect(result).not.toBeNull()
|
|
43
|
+
expect(result!.promptAppend).toBeDefined()
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
test("resolveCategoryConfig isUserConfiguredModel true when user provides model", () => {
|
|
47
|
+
const result = resolveCategoryConfig("quick", {
|
|
48
|
+
userCategories: { quick: { model: "openrouter/deepseek/deepseek-v4-flash" } },
|
|
49
|
+
availableModels: new Set(["openrouter/deepseek/deepseek-v4-flash"]),
|
|
50
|
+
})
|
|
51
|
+
expect(result).not.toBeNull()
|
|
52
|
+
expect(result!.isUserConfiguredModel).toBe(true)
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
test("resolveCategoryConfig isUserConfiguredModel false when no user model", () => {
|
|
56
|
+
const result = resolveCategoryConfig("quick", {
|
|
57
|
+
userCategories: { quick: {} },
|
|
58
|
+
availableModels: new Set(["openrouter/minimax/minimax-m2.7"]),
|
|
59
|
+
})
|
|
60
|
+
expect(result).not.toBeNull()
|
|
61
|
+
expect(result!.isUserConfiguredModel).toBe(false)
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
test("resolveCategoryConfig concatenates prompt_append with default", () => {
|
|
65
|
+
const result = resolveCategoryConfig("quick", {
|
|
66
|
+
userCategories: { quick: { prompt_append: "Extra instructions" } },
|
|
67
|
+
availableModels: new Set(["openrouter/minimax/minimax-m2.7"]),
|
|
68
|
+
})
|
|
69
|
+
expect(result).not.toBeNull()
|
|
70
|
+
expect(result!.promptAppend).toContain("Extra instructions")
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
test("resolveCategoryConfig returns null when category not in defaults or user config", () => {
|
|
74
|
+
const result = resolveCategoryConfig("nonexistent", {
|
|
75
|
+
userCategories: {},
|
|
76
|
+
})
|
|
77
|
+
expect(result).toBeNull()
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
test("resolveCategoryConfig applies user variant over default variant", () => {
|
|
81
|
+
const result = resolveCategoryConfig("deep", {
|
|
82
|
+
userCategories: { deep: { variant: "high" } },
|
|
83
|
+
availableModels: new Set(["openrouter/minimax/minimax-m2.7"]),
|
|
84
|
+
})
|
|
85
|
+
expect(result).not.toBeNull()
|
|
86
|
+
expect(result!.config.variant).toBe("high")
|
|
87
|
+
})
|
|
@@ -3,9 +3,6 @@ import type { DelegateTaskArgs } from "./types"
|
|
|
3
3
|
import type { ExecutorContext } from "./executor-types"
|
|
4
4
|
import type { FallbackEntry } from "../../shared/model-requirements"
|
|
5
5
|
import { mergeCategories } from "../../shared/merge-categories"
|
|
6
|
-
import {
|
|
7
|
-
CODER_AGENT_CONFIG_KEY,
|
|
8
|
-
} from "./sub-agent"
|
|
9
6
|
import { resolveCategoryConfig } from "./categories"
|
|
10
7
|
import { parseModelString } from "./model-string-parser"
|
|
11
8
|
import { CATEGORY_MODEL_REQUIREMENTS } from "../../shared/model-requirements"
|
|
@@ -15,6 +12,7 @@ import { CONFIG_BASENAME } from "../../shared/plugin-identity"
|
|
|
15
12
|
import { getAvailableModelsForDelegateTask } from "./available-models"
|
|
16
13
|
import { resolveModelForDelegateTask } from "./model-selection"
|
|
17
14
|
import { getAgentDisplayName } from "../../shared/agent-display-names"
|
|
15
|
+
import { resolveModeAgent } from "../../shared/mode-routing"
|
|
18
16
|
|
|
19
17
|
import type { CategoryConfig } from "../../config/schema"
|
|
20
18
|
import type { DelegatedModelConfig } from "./types"
|
|
@@ -42,11 +40,11 @@ export interface CategoryResolutionResult {
|
|
|
42
40
|
}
|
|
43
41
|
|
|
44
42
|
function resolveCategoryExecutorAgentKey(
|
|
45
|
-
|
|
43
|
+
categoryName: string,
|
|
46
44
|
config: CategoryConfig,
|
|
47
|
-
):
|
|
45
|
+
): string {
|
|
48
46
|
void config
|
|
49
|
-
return
|
|
47
|
+
return resolveModeAgent(categoryName)
|
|
50
48
|
}
|
|
51
49
|
|
|
52
50
|
export async function resolveCategoryExecution(
|
|
@@ -120,7 +118,7 @@ Available categories: ${allCategoryNames}`,
|
|
|
120
118
|
}
|
|
121
119
|
|
|
122
120
|
const categoryExecutorAgentKey = resolveCategoryExecutorAgentKey(categoryName, resolved.config)
|
|
123
|
-
const categoryExecutorAgentName = getAgentDisplayName(
|
|
121
|
+
const categoryExecutorAgentName = getAgentDisplayName(categoryExecutorAgentKey)
|
|
124
122
|
const categoryExecutorOverride = agentOverrides?.[categoryExecutorAgentKey as keyof typeof agentOverrides]
|
|
125
123
|
?? (agentOverrides
|
|
126
124
|
? Object.entries(agentOverrides).find(([key]) => key.toLowerCase() === categoryExecutorAgentKey)?.[1]
|
|
@@ -141,8 +139,9 @@ Available categories: ${allCategoryNames}`,
|
|
|
141
139
|
const explicitCategoryModel = userCategories?.[args.category!]?.model
|
|
142
140
|
|
|
143
141
|
if (!requirement) {
|
|
144
|
-
// Precedence: explicit category model > routed executor override model > category resolved model.
|
|
145
|
-
//
|
|
142
|
+
// Precedence: explicit category model > routed executor agent's override model > category resolved model.
|
|
143
|
+
// The routed executor agent is selected per-mode via resolveModeAgent (see shared/mode-routing.ts);
|
|
144
|
+
// its agentOverrides[<agent>].model serves as the fallback when no explicit category model is set.
|
|
146
145
|
actualModel = explicitCategoryModel ?? overrideModel ?? resolved.model
|
|
147
146
|
if (actualModel) {
|
|
148
147
|
modelInfo = explicitCategoryModel || overrideModel
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { BuiltinCategoryDefinition } from "./builtin-category-definition"
|
|
2
|
+
|
|
3
|
+
const GIT_CATEGORY_PROMPT_APPEND = `<Category_Context>
|
|
4
|
+
You are working on GIT / VERSION CONTROL tasks.
|
|
5
|
+
|
|
6
|
+
<Routing_Policy>
|
|
7
|
+
Executor contour: platform-manager (git operations specialist).
|
|
8
|
+
</Routing_Policy>
|
|
9
|
+
|
|
10
|
+
Git operations mindset:
|
|
11
|
+
- Atomic commits with clear messages
|
|
12
|
+
- Clean history: rebase/squash before push
|
|
13
|
+
- Safe operations: never force-push to main/master
|
|
14
|
+
- Clear handoff notes for session continuity
|
|
15
|
+
|
|
16
|
+
Approach:
|
|
17
|
+
- Understand the current branch state
|
|
18
|
+
- Plan atomic commit strategy
|
|
19
|
+
- Execute with minimal noise
|
|
20
|
+
- Verify state after operation
|
|
21
|
+
</Category_Context>`
|
|
22
|
+
|
|
23
|
+
export const GIT_CATEGORIES: BuiltinCategoryDefinition[] = [
|
|
24
|
+
{
|
|
25
|
+
name: "git",
|
|
26
|
+
config: {},
|
|
27
|
+
description: "Git operations: commits, branching, history, rebasing. Uses platform-manager execution contour.",
|
|
28
|
+
promptAppend: GIT_CATEGORY_PROMPT_APPEND,
|
|
29
|
+
},
|
|
30
|
+
]
|