@hiai-gg/hiai-opencode 0.1.6 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (140) hide show
  1. package/.env.example +7 -0
  2. package/AGENTS.md +46 -1
  3. package/ARCHITECTURE.md +6 -3
  4. package/README.md +71 -10
  5. package/assets/cli/hiai-opencode.mjs +78 -0
  6. package/config/hiai-opencode.schema.json +16 -1
  7. package/dist/agents/agent-skills.d.ts +7 -0
  8. package/dist/agents/bob/default.d.ts +1 -0
  9. package/dist/agents/bob/gemini.d.ts +1 -0
  10. package/dist/agents/bob/gpt-pro.d.ts +1 -0
  11. package/dist/agents/brainstormer.d.ts +7 -0
  12. package/dist/agents/coder/gpt-codex.d.ts +1 -1
  13. package/dist/agents/coder/gpt-pro.d.ts +1 -0
  14. package/dist/agents/coder/gpt.d.ts +2 -1
  15. package/dist/agents/designer.d.ts +7 -0
  16. package/dist/agents/strategist/gemini.d.ts +1 -0
  17. package/dist/agents/strategist/gpt.d.ts +1 -0
  18. package/dist/agents/types.d.ts +3 -1
  19. package/dist/config/index.d.ts +0 -1
  20. package/dist/config/platform-schema.d.ts +32 -0
  21. package/dist/config/schema/hooks.d.ts +0 -2
  22. package/dist/config/schema/index.d.ts +0 -2
  23. package/dist/config/schema/oh-my-opencode-config.d.ts +0 -6
  24. package/dist/config/types.d.ts +3 -1
  25. package/dist/create-hooks.d.ts +0 -2
  26. package/dist/features/builtin-skills/skills/index.d.ts +1 -0
  27. package/dist/features/builtin-skills/skills/website-copywriting.d.ts +2 -0
  28. package/dist/hooks/agent-usage-reminder/constants.d.ts +1 -1
  29. package/dist/hooks/index.d.ts +0 -2
  30. package/dist/hooks/keyword-detector/ultrawork/default.d.ts +1 -1
  31. package/dist/hooks/keyword-detector/ultrawork/gemini.d.ts +1 -1
  32. package/dist/hooks/keyword-detector/ultrawork/gpt.d.ts +1 -1
  33. package/dist/hooks/keyword-detector/ultrawork/planner.d.ts +1 -1
  34. package/dist/index.js +8963 -153866
  35. package/dist/mcp/index.d.ts +0 -1
  36. package/dist/mcp/registry.d.ts +1 -1
  37. package/dist/plugin/hooks/create-core-hooks.d.ts +0 -2
  38. package/dist/plugin/hooks/create-session-hooks.d.ts +1 -3
  39. package/dist/plugin/startup-diagnostics.d.ts +1 -0
  40. package/dist/shared/logger.d.ts +2 -0
  41. package/dist/shared/mode-routing.d.ts +6 -0
  42. package/dist/tools/delegate-task/git-categories.d.ts +2 -0
  43. package/dist/tools/delegate-task/sub-agent.d.ts +2 -0
  44. package/dist/tools/skill-mcp/constants.d.ts +1 -1
  45. package/hiai-opencode.json +48 -19
  46. package/package.json +6 -3
  47. package/src/agents/agent-skills.ts +70 -0
  48. package/src/agents/bob/default.ts +1 -0
  49. package/src/agents/bob/gemini.ts +1 -0
  50. package/src/agents/bob/gpt-pro.ts +2 -1
  51. package/src/agents/bob.ts +2 -0
  52. package/src/agents/brainstormer.ts +72 -0
  53. package/src/agents/builtin-agents.ts +59 -3
  54. package/src/agents/coder/gpt-codex.ts +4 -3
  55. package/src/agents/coder/gpt-pro.ts +3 -2
  56. package/src/agents/coder/gpt.ts +2 -1
  57. package/src/agents/critic/agent.ts +1 -0
  58. package/src/agents/designer.ts +70 -0
  59. package/src/agents/dynamic-agent-category-skills-guide.ts +6 -0
  60. package/src/agents/guard/default.ts +1 -0
  61. package/src/agents/guard/gemini.ts +1 -0
  62. package/src/agents/guard/gpt.ts +1 -0
  63. package/src/agents/platform-manager.ts +17 -1
  64. package/src/agents/prompt-library/platform.ts +34 -0
  65. package/src/agents/researcher.ts +1 -0
  66. package/src/agents/strategist/gemini.ts +1 -0
  67. package/src/agents/strategist/gpt.ts +1 -0
  68. package/src/agents/types.ts +4 -1
  69. package/src/agents/ui.ts +1 -0
  70. package/src/config/defaults.ts +31 -12
  71. package/src/config/index.ts +0 -1
  72. package/src/config/model-slots-and-export.test.ts +22 -4
  73. package/src/config/platform-schema.ts +2 -0
  74. package/src/config/schema/hooks.ts +0 -2
  75. package/src/config/schema/index.ts +0 -2
  76. package/src/config/schema/oh-my-opencode-config.ts +0 -2
  77. package/src/config/types.ts +3 -1
  78. package/src/features/builtin-skills/skills/index.ts +1 -0
  79. package/src/features/builtin-skills/skills/website-copywriting.ts +41 -0
  80. package/src/features/builtin-skills/skills.test.ts +8 -0
  81. package/src/features/builtin-skills/skills.ts +2 -0
  82. package/src/features/skill-mcp-manager/AGENTS.md +1 -1
  83. package/src/hooks/agent-usage-reminder/constants.ts +4 -4
  84. package/src/hooks/index.ts +0 -2
  85. package/src/hooks/keyword-detector/ultrawork/default.ts +18 -18
  86. package/src/hooks/keyword-detector/ultrawork/gemini.ts +21 -21
  87. package/src/hooks/keyword-detector/ultrawork/gpt.ts +6 -8
  88. package/src/hooks/keyword-detector/ultrawork/planner.ts +5 -5
  89. package/src/index.ts +5 -3
  90. package/src/internals/plugins/subtask2/commands/manifest.ts +2 -6
  91. package/src/internals/plugins/subtask2/hooks/command-hooks.ts +2 -2
  92. package/src/internals/plugins/subtask2/hooks/message-hooks.ts +1 -1
  93. package/src/internals/plugins/subtask2/parsing/parallel.ts +13 -10
  94. package/src/mcp/index.ts +0 -1
  95. package/src/mcp/registry.ts +27 -0
  96. package/src/plugin/chat-message.ts +0 -2
  97. package/src/plugin/hooks/create-session-hooks.ts +0 -17
  98. package/src/plugin/startup-diagnostics.ts +27 -0
  99. package/src/plugin-handlers/agent-config-handler.ts +3 -2
  100. package/src/plugin-handlers/mcp-config-handler.test.ts +63 -0
  101. package/src/plugin-handlers/mcp-config-handler.ts +29 -14
  102. package/src/plugin-handlers/strategist-agent-config-builder.ts +1 -1
  103. package/src/shared/agent-display-names.test.ts +9 -0
  104. package/src/shared/agent-display-names.ts +5 -0
  105. package/src/shared/log-legacy-plugin-startup-warning.ts +6 -8
  106. package/src/shared/logger.ts +8 -0
  107. package/src/shared/mcp-static-export.ts +4 -6
  108. package/src/shared/migration/agent-names.ts +8 -0
  109. package/src/shared/migration/hook-names.ts +1 -1
  110. package/src/shared/mode-routing.test.ts +88 -0
  111. package/src/shared/mode-routing.ts +30 -0
  112. package/src/shared/startup-diagnostics.ts +6 -7
  113. package/src/tools/call-omo-agent/tools.ts +11 -4
  114. package/src/tools/delegate-task/anthropic-categories.ts +3 -3
  115. package/src/tools/delegate-task/builtin-categories.ts +2 -0
  116. package/src/tools/delegate-task/categories.test.ts +87 -0
  117. package/src/tools/delegate-task/category-resolver.ts +8 -9
  118. package/src/tools/delegate-task/git-categories.ts +30 -0
  119. package/src/tools/delegate-task/model-string-parser.test.ts +90 -0
  120. package/src/tools/delegate-task/openai-categories.ts +26 -22
  121. package/src/tools/delegate-task/sub-agent.ts +10 -0
  122. package/src/tools/delegate-task/subagent-discovery.test.ts +123 -0
  123. package/src/tools/delegate-task/subagent-resolver.ts +18 -1
  124. package/src/tools/skill-mcp/constants.ts +1 -1
  125. package/dist/config/schema/websearch.d.ts +0 -13
  126. package/dist/hooks/no-bob-gpt/hook.d.ts +0 -16
  127. package/dist/hooks/no-bob-gpt/index.d.ts +0 -1
  128. package/dist/hooks/no-coder-non-gpt/hook.d.ts +0 -20
  129. package/dist/hooks/no-coder-non-gpt/index.d.ts +0 -1
  130. package/dist/mcp/grep-app.d.ts +0 -6
  131. package/dist/mcp/omo-mcp-index.d.ts +0 -10
  132. package/dist/mcp/websearch.d.ts +0 -11
  133. package/src/config/schema/websearch.ts +0 -15
  134. package/src/hooks/no-bob-gpt/hook.ts +0 -56
  135. package/src/hooks/no-bob-gpt/index.ts +0 -1
  136. package/src/hooks/no-coder-non-gpt/hook.ts +0 -67
  137. package/src/hooks/no-coder-non-gpt/index.ts +0 -1
  138. package/src/mcp/grep-app.ts +0 -6
  139. package/src/mcp/omo-mcp-index.ts +0 -30
  140. package/src/mcp/websearch.ts +0 -44
@@ -66,9 +66,9 @@ Where TYPE is one of: research | implementation | investigation | evaluation | f
66
66
  **IF YOU ARE NOT 100% CERTAIN:**
67
67
 
68
68
  1. **THINK DEEPLY** - What is the user's TRUE intent? What problem are they REALLY trying to solve?
69
- 2. **EXPLORE THOROUGHLY** - Fire explore/librarian agents to gather ALL relevant context
69
+ 2. **EXPLORE THOROUGHLY** - Fire researcher agents (multiple parallel) to gather ALL relevant context
70
70
  3. **CONSULT SPECIALISTS** - For hard/complex tasks, DO NOT struggle alone. Delegate:
71
- - **Logician**: Conventional problems - architecture, debugging, complex logic
71
+ - **Strategist**: Conventional problems - architecture, debugging, complex logic
72
72
  - **Artistry**: Non-conventional problems - different approach needed, unusual constraints
73
73
  4. **ASK THE USER** - If ambiguity remains after exploration, ASK. Don't guess.
74
74
 
@@ -81,9 +81,9 @@ Where TYPE is one of: research | implementation | investigation | evaluation | f
81
81
 
82
82
  **WHEN IN DOUBT:**
83
83
  \`\`\`
84
- task(subagent_type="explore", load_skills=[], prompt="I'm implementing [TASK DESCRIPTION] and need to understand [SPECIFIC KNOWLEDGE GAP]. Find [X] patterns in the codebase - show file paths, implementation approach, and conventions used. I'll use this to [HOW RESULTS WILL BE USED]. Focus on src/ directories, skip test files unless test patterns are specifically needed. Return concrete file paths with brief descriptions of what each file does.", run_in_background=true)
85
- task(subagent_type="librarian", load_skills=[], prompt="I'm working with [LIBRARY/TECHNOLOGY] and need [SPECIFIC INFORMATION]. Find official documentation and production-quality examples for [Y] - specifically: API reference, configuration options, recommended patterns, and common pitfalls. Skip beginner tutorials. I'll use this to [DECISION THIS WILL INFORM].", run_in_background=true)
86
- task(subagent_type="logician", load_skills=[], prompt="I need architectural review of my approach to [TASK]. Here's my plan: [DESCRIBE PLAN WITH SPECIFIC FILES AND CHANGES]. My concerns are: [LIST SPECIFIC UNCERTAINTIES]. Please evaluate: correctness of approach, potential issues I'm missing, and whether a better alternative exists.", run_in_background=false)
84
+ task(subagent_type="researcher", load_skills=[], prompt="I'm implementing [TASK DESCRIPTION] and need to understand [SPECIFIC KNOWLEDGE GAP]. Find [X] patterns in the codebase - show file paths, implementation approach, and conventions used. I'll use this to [HOW RESULTS WILL BE USED]. Focus on src/ directories, skip test files unless test patterns are specifically needed. Return concrete file paths with brief descriptions of what each file does.", run_in_background=true)
85
+ task(subagent_type="researcher", load_skills=[], prompt="I'm working with [LIBRARY/TECHNOLOGY] and need [SPECIFIC INFORMATION]. Find official documentation and production-quality examples for [Y] - specifically: API reference, configuration options, recommended patterns, and common pitfalls. Skip beginner tutorials. I'll use this to [DECISION THIS WILL INFORM].", run_in_background=true)
86
+ task(subagent_type="strategist", load_skills=[], prompt="I need architectural review of my approach to [TASK]. Here's my plan: [DESCRIBE PLAN WITH SPECIFIC FILES AND CHANGES]. My concerns are: [LIST SPECIFIC UNCERTAINTIES]. Please evaluate: correctness of approach, potential issues I'm missing, and whether a better alternative exists.", run_in_background=false)
87
87
  \`\`\`
88
88
 
89
89
  **ONLY AFTER YOU HAVE:**
@@ -118,7 +118,7 @@ task(subagent_type="logician", load_skills=[], prompt="I need architectural revi
118
118
  **IF YOU ENCOUNTER A BLOCKER:**
119
119
  1. **DO NOT** give up
120
120
  2. **DO NOT** deliver a compromised version
121
- 3. **DO** consult specialists (logician for conventional, artistry for non-conventional)
121
+ 3. **DO** consult specialists (strategist for conventional, artistry for non-conventional)
122
122
  4. **DO** ask the user for guidance
123
123
  5. **DO** explore alternative approaches
124
124
 
@@ -150,26 +150,26 @@ TELL THE USER WHAT AGENTS YOU WILL LEVERAGE NOW TO SATISFY USER'S REQUEST.
150
150
 
151
151
  | Condition | Action |
152
152
  |-----------|--------|
153
- | Task has 2+ steps | MUST call plan agent |
154
- | Task scope unclear | MUST call plan agent |
155
- | Implementation required | MUST call plan agent |
156
- | Architecture decision needed | MUST call plan agent |
153
+ | Task has 2+ steps | MUST call strategist agent |
154
+ | Task scope unclear | MUST call strategist agent |
155
+ | Implementation required | MUST call strategist agent |
156
+ | Architecture decision needed | MUST call strategist agent |
157
157
 
158
158
  \`\`\`
159
- task(subagent_type="plan", load_skills=[], run_in_background=false, prompt="<gathered context + user request>")
159
+ task(subagent_type="strategist", load_skills=[], run_in_background=false, prompt="<gathered context + user request>")
160
160
  \`\`\`
161
161
 
162
- ### SESSION CONTINUITY WITH PLAN AGENT (CRITICAL)
162
+ ### SESSION CONTINUITY WITH STRATEGIST AGENT (CRITICAL)
163
163
 
164
- **Plan agent returns a session_id. USE IT for follow-up interactions.**
164
+ **Strategist agent returns a session_id. USE IT for follow-up interactions.**
165
165
 
166
166
  | Scenario | Action |
167
167
  |----------|--------|
168
- | Plan agent asks clarifying questions | \`task(session_id="{returned_session_id}", load_skills=[], run_in_background=false, prompt="<your answer>")\` |
168
+ | Strategist asks clarifying questions | \`task(session_id="{returned_session_id}", load_skills=[], run_in_background=false, prompt="<your answer>")\` |
169
169
  | Need to refine the plan | \`task(session_id="{returned_session_id}", load_skills=[], run_in_background=false, prompt="Please adjust: <feedback>")\` |
170
- | Plan needs more detail | \`task(session_id="{returned_session_id}", load_skills=[], run_in_background=false, prompt="Add more detail to Task N")\` |
170
+ | Strategist needs more detail | \`task(session_id="{returned_session_id}", load_skills=[], run_in_background=false, prompt="Add more detail to Task N")\` |
171
171
 
172
- **FAILURE TO CALL PLAN AGENT = INCOMPLETE WORK.**
172
+ **FAILURE TO CALL STRATEGIST AGENT = INCOMPLETE WORK.**
173
173
 
174
174
  ---
175
175
 
@@ -181,10 +181,10 @@ task(subagent_type="plan", load_skills=[], run_in_background=false, prompt="<gat
181
181
 
182
182
  | Task Type | Action | Why |
183
183
  |-----------|--------|-----|
184
- | Codebase exploration | task(subagent_type="explore", load_skills=[], run_in_background=true) | Parallel, context-efficient |
185
- | Documentation lookup | task(subagent_type="librarian", load_skills=[], run_in_background=true) | Specialized knowledge |
186
- | Planning | task(subagent_type="plan", load_skills=[], run_in_background=false) | Parallel task graph + structured TODO list |
187
- | Hard problem (conventional) | task(subagent_type="logician", load_skills=[], run_in_background=false) | Architecture, debugging, complex logic |
184
+ | Codebase exploration | task(subagent_type="researcher", load_skills=[], run_in_background=true) | Parallel, context-efficient |
185
+ | Documentation lookup | task(subagent_type="researcher", load_skills=[], run_in_background=true) | Specialized knowledge |
186
+ | Planning | task(subagent_type="strategist", load_skills=[], run_in_background=false) | Parallel task graph + structured TODO list |
187
+ | Hard problem (conventional) | task(subagent_type="strategist", load_skills=[], run_in_background=false) | Architecture, debugging, complex logic |
188
188
  | Hard problem (non-conventional) | task(category="artistry", load_skills=[...], run_in_background=true) | Different approach needed |
189
189
  | Implementation | task(category="...", load_skills=[...], run_in_background=true) | Domain-optimized models |
190
190
 
@@ -206,7 +206,7 @@ task(subagent_type="plan", load_skills=[], run_in_background=false, prompt="<gat
206
206
 
207
207
  ## WORKFLOW
208
208
  1. **CLASSIFY INTENT** (MANDATORY - see GEMINI_INTENT_GATE above)
209
- 2. Spawn exploration/librarian agents via task(run_in_background=true) in PARALLEL
209
+ 2. Spawn researcher agents via task(run_in_background=true) in PARALLEL
210
210
  3. Use Plan agent with gathered context to create detailed work breakdown
211
211
  4. Execute with continuous verification against original requirements
212
212
 
@@ -69,15 +69,13 @@ Use these when they provide clear value based on the decision framework above:
69
69
 
70
70
  | Resource | When to Use | How to Use |
71
71
  |----------|-------------|------------|
72
- | explore agent | Need codebase patterns you don't have | \`task(subagent_type="explore", load_skills=[], run_in_background=true, ...)\` |
73
- | librarian agent | External library docs, OSS examples | \`task(subagent_type="librarian", load_skills=[], run_in_background=true, ...)\` |
74
- | logician agent | Stuck on architecture/debugging after 2+ attempts | \`task(subagent_type="logician", load_skills=[], run_in_background=false, ...)\` |
75
- | plan agent | Complex multi-step with dependencies (5+ steps) | \`task(subagent_type="plan", load_skills=[], run_in_background=false, ...)\` |
72
+ | researcher agent | Need codebase patterns or external docs | \`task(subagent_type="researcher", load_skills=[], run_in_background=true, ...)\` |
73
+ | strategist agent | Complex multi-step (5+ steps) or architecture decisions | \`task(subagent_type="strategist", load_skills=[], run_in_background=false, ...)\` |
76
74
  | task category | Specialized work matching a category | \`task(category="...", load_skills=[...], run_in_background=true)\` |
77
75
 
78
76
  <tool_usage_rules>
79
77
  - Prefer tools over internal knowledge for fresh or user-specific data
80
- - Parallelize independent reads (read_file, grep, explore, librarian) to reduce latency
78
+ - Parallelize independent reads (read_file, grep, researcher) to reduce latency
81
79
  - After any write/update, briefly restate: What changed, Where (path), Follow-up needed
82
80
  </tool_usage_rules>
83
81
 
@@ -88,13 +86,13 @@ Use these when they provide clear value based on the decision framework above:
88
86
  | Track | Tools | Speed | Purpose |
89
87
  |-------|-------|-------|---------|
90
88
  | **Direct** | Grep, Read, LSP, AST-grep | Instant | Quick wins, known locations |
91
- | **Background** | explore, librarian agents | Async | Deep search, external docs |
89
+ | **Background** | researcher agents | Async | Deep search, external docs |
92
90
 
93
91
  **ALWAYS run both tracks in parallel:**
94
92
  \`\`\`
95
93
  // Fire background agents for deep exploration
96
- task(subagent_type="explore", load_skills=[], prompt="I'm implementing [TASK] and need to understand [KNOWLEDGE GAP]. Find [X] patterns in the codebase - file paths, implementation approach, conventions used, and how modules connect. I'll use this to [DOWNSTREAM DECISION]. Focus on production code in src/. Return file paths with brief descriptions.", run_in_background=true)
97
- task(subagent_type="librarian", load_skills=[], prompt="I'm working with [TECHNOLOGY] and need [SPECIFIC INFO]. Find official docs and production examples for [Y] - API reference, configuration, recommended patterns, and pitfalls. Skip tutorials. I'll use this to [DECISION THIS INFORMS].", run_in_background=true)
94
+ task(subagent_type="researcher", load_skills=[], prompt="I'm implementing [TASK] and need to understand [KNOWLEDGE GAP]. Find [X] patterns in the codebase - file paths, implementation approach, conventions used, and how modules connect. I'll use this to [DOWNSTREAM DECISION]. Focus on production code in src/. Return file paths with brief descriptions.", run_in_background=true)
95
+ task(subagent_type="researcher", load_skills=[], prompt="I'm working with [TECHNOLOGY] and need [SPECIFIC INFO]. Find official docs and production examples for [Y] - API reference, configuration, recommended patterns, and pitfalls. Skip tutorials. I'll use this to [DECISION THIS INFORMS].", run_in_background=true)
98
96
 
99
97
  // WHILE THEY RUN - use direct tools for immediate context
100
98
  grep(pattern="relevant_pattern", path="src/")
@@ -33,14 +33,14 @@ REFUSE. Say: "I'm a planner. I create work plans, not implementations. Run \`/st
33
33
  ## CONTEXT GATHERING (MANDATORY BEFORE PLANNING)
34
34
 
35
35
  You ARE the planner. Your job: create bulletproof work plans.
36
- **Before drafting ANY plan, gather context via explore/librarian agents.**
36
+ **Before drafting ANY plan, gather context via researcher agents.**
37
37
 
38
38
  ### Research Protocol
39
39
  1. **Fire parallel background agents** for comprehensive context:
40
40
  \`\`\`
41
- task(subagent_type="explore", load_skills=[], prompt="Find existing patterns for [topic] in codebase", run_in_background=true)
42
- task(subagent_type="explore", load_skills=[], prompt="Find test infrastructure and conventions", run_in_background=true)
43
- task(subagent_type="librarian", load_skills=[], prompt="Find official docs and best practices for [technology]", run_in_background=true)
41
+ task(subagent_type="researcher", load_skills=[], prompt="Find existing patterns for [topic] in codebase", run_in_background=true)
42
+ task(subagent_type="researcher", load_skills=[], prompt="Find test infrastructure and conventions", run_in_background=true)
43
+ task(subagent_type="researcher", load_skills=[], prompt="Find official docs and best practices for [technology]", run_in_background=true)
44
44
  \`\`\`
45
45
  2. **Wait for results** before planning - rushed plans fail
46
46
  3. **Synthesize findings** into informed requirements
@@ -49,7 +49,7 @@ You ARE the planner. Your job: create bulletproof work plans.
49
49
  - Existing codebase patterns and conventions
50
50
  - Test infrastructure (TDD possible?)
51
51
  - External library APIs and constraints
52
- - Similar implementations in OSS (via librarian)
52
+ - Similar implementations in OSS (via researcher)
53
53
 
54
54
  **NEVER plan blind. Context first, plan second.**
55
55
 
package/src/index.ts CHANGED
@@ -14,12 +14,13 @@ import { createPluginDispose, type PluginDispose } from "./plugin-dispose"
14
14
  import { loadPluginConfig } from "./plugin-config"
15
15
  import { createModelCacheState } from "./plugin-state"
16
16
  import { createFirstMessageVariantGate } from "./shared/first-message-variant"
17
- import { injectServerAuthIntoClient, log } from "./shared"
17
+ import { injectServerAuthIntoClient, log, logWarn, logError } 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
21
  import { autoExportStaticMcpJson } from "./shared/mcp-static-export"
22
22
  import { warnIfListPluginEntry, warnMissingRequiredMcpEnv } from "./shared/startup-diagnostics"
23
+ import { lintModeAgentCapabilities } from "./plugin/startup-diagnostics"
23
24
  import { startBackgroundCheck as startTmuxCheck } from "./tools/interactive-bash"
24
25
  import { lspManager } from "./tools/lsp/client"
25
26
 
@@ -71,7 +72,7 @@ const HiaiOpenCodePlugin: Plugin = async (ctx) => {
71
72
 
72
73
  const skillPluginCheck = detectExternalSkillPlugin(ctx.directory)
73
74
  if (skillPluginCheck.detected && skillPluginCheck.pluginName) {
74
- console.warn(getSkillPluginConflictWarning(skillPluginCheck.pluginName))
75
+ logWarn(getSkillPluginConflictWarning(skillPluginCheck.pluginName))
75
76
  }
76
77
 
77
78
  injectServerAuthIntoClient(ctx.client)
@@ -86,6 +87,7 @@ const HiaiOpenCodePlugin: Plugin = async (ctx) => {
86
87
  pluginConfig,
87
88
  platformConfig: internalConfig,
88
89
  })
90
+ lintModeAgentCapabilities()
89
91
  autoExportStaticMcpJson(ctx.directory, internalConfig)
90
92
 
91
93
  materializeBuiltinSkills(
@@ -166,7 +168,7 @@ const HiaiOpenCodePlugin: Plugin = async (ctx) => {
166
168
  const mod = await import("./internals/plugins/pty/plugin");
167
169
  ptyResult = await mod.PTYPlugin(ctx);
168
170
  } catch (err) {
169
- console.error("[hiai-opencode] PTYPlugin failed to load:", err);
171
+ logError("PTYPlugin failed to load:", err)
170
172
  }
171
173
  const combinedResult = {
172
174
  name: PLUGIN_NAME,
@@ -3,11 +3,7 @@
3
3
  import { join } from "path";
4
4
  import { getOpenCodeConfigDir } from "../../../../shared/opencode-config-dir";
5
5
  import type { CommandConfig } from "../types";
6
- import {
7
- parseFrontmatter,
8
- getTemplateBody,
9
- parseParallelConfig,
10
- } from "../parsing";
6
+ import { parseFrontmatter, getTemplateBody, parseParallelConfig, parseLoopConfig } from "../parsing";
11
7
 
12
8
  /**
13
9
  * Commands: Manifest building
@@ -43,7 +39,7 @@ export async function buildManifest(): Promise<Record<string, CommandConfig>> {
43
39
  agent: fm.agent as string | undefined,
44
40
  description: fm.description as string | undefined,
45
41
  template: getTemplateBody(content),
46
- loop: fm.loop as any,
42
+ loop: parseLoopConfig(fm.loop),
47
43
  model: fm.model as string | undefined,
48
44
  auto: fm.subtask2 === "auto",
49
45
  };
@@ -261,11 +261,11 @@ export async function commandExecuteBefore(
261
261
  log(
262
262
  `cmd.before: registered parent for prompt (${part.prompt.length} chars)`
263
263
  );
264
- if ((part as any).as) {
264
+ if ("as" in part && part.as) {
265
265
  registerPendingResultCaptureByPrompt(
266
266
  part.prompt,
267
267
  input.sessionID,
268
- (part as any).as
268
+ String(part.as)
269
269
  );
270
270
  }
271
271
  }
@@ -177,7 +177,7 @@ export async function chatMessagesTransform(input: any, output: any) {
177
177
  if (msg.info?.role !== "user") continue;
178
178
 
179
179
  // Track processed messages by ID to avoid infinite loop
180
- const msgId = (msg.info as any)?.id;
180
+ const msgId = msg.info?.id;
181
181
  if (msgId && hasProcessedS2Message(msgId)) continue;
182
182
 
183
183
  for (const part of msg.parts) {
@@ -27,11 +27,14 @@ export function parseLoopConfig(loop: unknown): LoopConfig | undefined {
27
27
  }
28
28
 
29
29
  // Parse a parallel item - handles "/cmd {model:...} args" syntax, plain "cmd", or {command, arguments} object
30
+ function isParallelCommandLike(p: unknown): p is { command: unknown; arguments?: unknown; prompt?: unknown; model?: unknown; agent?: unknown; loop?: unknown; as?: unknown; inline?: unknown } {
31
+ return typeof p === "object" && p !== null && "command" in p
32
+ }
33
+
30
34
  export function parseParallelItem(p: unknown): ParallelCommand | null {
31
35
  if (typeof p === "string") {
32
36
  const trimmed = p.trim();
33
37
  if (trimmed.startsWith("/")) {
34
- // Parse /command {overrides} args syntax
35
38
  const parsed = parseCommandWithOverrides(trimmed);
36
39
  if (parsed.isInlineSubtask) {
37
40
  return {
@@ -55,16 +58,16 @@ export function parseParallelItem(p: unknown): ParallelCommand | null {
55
58
  }
56
59
  return { command: trimmed };
57
60
  }
58
- if (typeof p === "object" && p !== null && (p as any).command) {
61
+ if (isParallelCommandLike(p)) {
59
62
  return {
60
- command: (p as any).command,
61
- arguments: (p as any).arguments,
62
- prompt: (p as any).prompt,
63
- model: (p as any).model,
64
- agent: (p as any).agent,
65
- loop: (p as any).loop,
66
- as: (p as any).as,
67
- inline: (p as any).inline,
63
+ command: String(p.command),
64
+ arguments: p.arguments !== undefined ? String(p.arguments) : undefined,
65
+ prompt: p.prompt !== undefined ? String(p.prompt) : undefined,
66
+ model: p.model !== undefined ? String(p.model) : undefined,
67
+ agent: p.agent !== undefined ? String(p.agent) : undefined,
68
+ loop: p.loop !== undefined ? parseLoopConfig(p.loop) : undefined,
69
+ as: p.as !== undefined ? String(p.as) : undefined,
70
+ inline: p.inline !== undefined ? Boolean(p.inline) : undefined,
68
71
  };
69
72
  }
70
73
  return null;
package/src/mcp/index.ts CHANGED
@@ -1,4 +1,3 @@
1
- export { createBuiltinMcps } from "./omo-mcp-index"
2
1
  export { McpNameSchema, type McpName, type AnyMcpName } from "./types"
3
2
 
4
3
  import type { McpServerConfig } from "../config/types.js";
@@ -10,6 +10,8 @@ export type HiaiMcpName =
10
10
  | "rag"
11
11
  | "context7"
12
12
  | "mempalace"
13
+ | "websearch"
14
+ | "grep_app"
13
15
 
14
16
  export type HiaiMcpInstallKind = "bundled" | "npm" | "python" | "remote" | "user-service"
15
17
 
@@ -121,6 +123,31 @@ export const HIAI_MCP_REGISTRY: Record<HiaiMcpName, HiaiMcpRegistryEntry> = {
121
123
  timeout: 600000,
122
124
  },
123
125
  },
126
+ websearch: {
127
+ name: "websearch",
128
+ enabledByDefault: true,
129
+ install: "remote",
130
+ optionalEnv: ["EXA_API_KEY", "TAVILY_API_KEY"],
131
+ config: {
132
+ enabled: true,
133
+ type: "remote",
134
+ url: "https://mcp.exa.ai/mcp?tools=web_search_exa",
135
+ headers: { "x-api-key": "{env:EXA_API_KEY}" },
136
+ timeout: 600000,
137
+ provider: "exa",
138
+ },
139
+ },
140
+ grep_app: {
141
+ name: "grep_app",
142
+ enabledByDefault: true,
143
+ install: "remote",
144
+ config: {
145
+ enabled: true,
146
+ type: "remote",
147
+ url: "https://mcp.grep.app",
148
+ timeout: 600000,
149
+ },
150
+ },
124
151
  }
125
152
 
126
153
  export function createDefaultMcpConfig(): Record<HiaiMcpName, McpServerConfig> {
@@ -236,8 +236,6 @@ export function createChatMessageHandler(args: {
236
236
  await hooks.thinkMode?.["chat.message"]?.(input, output)
237
237
  await hooks.claudeCodeHooks?.["chat.message"]?.(input, output)
238
238
  await hooks.autoSlashCommand?.["chat.message"]?.(input, output)
239
- await hooks.noBobGpt?.["chat.message"]?.(input, output)
240
- await hooks.noCoderNonGpt?.["chat.message"]?.(input, output)
241
239
  if (hooks.startWork && isStartWorkHookOutput(output)) {
242
240
  const promptText = extractPromptText(output.parts)
243
241
  if (isStartWorkFallbackTemplate(promptText)) {
@@ -19,8 +19,6 @@ import {
19
19
  createStartWorkHook,
20
20
  createStrategistMdOnlyHook,
21
21
  createBobJuniorNotepadHook,
22
- createNoBobGptHook,
23
- createNoCoderNonGptHook,
24
22
  createQuestionLabelTruncatorHook,
25
23
  createPreemptiveCompactionHook,
26
24
  createRuntimeFallbackHook,
@@ -54,8 +52,6 @@ export type SessionHooks = {
54
52
  startWork: ReturnType<typeof createStartWorkHook> | null
55
53
  strategistMdOnly: ReturnType<typeof createStrategistMdOnlyHook> | null
56
54
  bobJuniorNotepad: ReturnType<typeof createBobJuniorNotepadHook> | null
57
- noBobGpt: ReturnType<typeof createNoBobGptHook> | null
58
- noCoderNonGpt: ReturnType<typeof createNoCoderNonGptHook> | null
59
55
  questionLabelTruncator: ReturnType<typeof createQuestionLabelTruncatorHook> | null
60
56
  taskResumeInfo: ReturnType<typeof createTaskResumeInfoHook> | null
61
57
  anthropicEffort: ReturnType<typeof createAnthropicEffortHook> | null
@@ -219,17 +215,6 @@ export function createSessionHooks(args: {
219
215
  ? safeHook("sub-notepad", () => createBobJuniorNotepadHook(ctx))
220
216
  : null
221
217
 
222
- const noBobGpt = isHookEnabled("no-bob-gpt")
223
- ? safeHook("no-bob-gpt", () => createNoBobGptHook(ctx))
224
- : null
225
-
226
- const noCoderNonGpt = isHookEnabled("no-coder-non-gpt")
227
- ? safeHook("no-coder-non-gpt", () =>
228
- createNoCoderNonGptHook(ctx, {
229
- allowNonGptModel: pluginConfig.agents?.coder?.allow_non_gpt_model,
230
- }))
231
- : null
232
-
233
218
  const questionLabelTruncator = isHookEnabled("question-label-truncator")
234
219
  ? safeHook("question-label-truncator", () => createQuestionLabelTruncatorHook())
235
220
  : null
@@ -275,8 +260,6 @@ export function createSessionHooks(args: {
275
260
  startWork,
276
261
  strategistMdOnly,
277
262
  bobJuniorNotepad,
278
- noBobGpt,
279
- noCoderNonGpt,
280
263
  questionLabelTruncator,
281
264
  taskResumeInfo,
282
265
  anthropicEffort,
@@ -0,0 +1,27 @@
1
+ import { MODE_TO_AGENT } from "../shared/mode-routing"
2
+
3
+ const WRITE_CAPABLE_AGENTS = new Set(["coder", "sub", "designer", "brainstormer"])
4
+
5
+ const WRITE_MODES = new Set([
6
+ "quick",
7
+ "bounded",
8
+ "deep",
9
+ "cross-module",
10
+ "visual-engineering",
11
+ "artistry",
12
+ "writing",
13
+ ])
14
+
15
+ const READONLY_MODES = new Set(["ultrabrain", "git", "git-ops"])
16
+
17
+ export function lintModeAgentCapabilities(): void {
18
+ for (const [mode, agent] of Object.entries(MODE_TO_AGENT)) {
19
+ if (WRITE_MODES.has(mode) && !WRITE_CAPABLE_AGENTS.has(agent)) {
20
+ console.warn(`[startup-lint] Mode "${mode}" targets agent "${agent}" which cannot write/edit. Tasks using this mode may fail when attempting file operations.`)
21
+ }
22
+
23
+ if (READONLY_MODES.has(mode) && WRITE_CAPABLE_AGENTS.has(agent)) {
24
+ console.warn(`[startup-lint] Mode "${mode}" targets agent "${agent}" which can write. Consider verifying this is intentional.`)
25
+ }
26
+ }
27
+ }
@@ -81,7 +81,7 @@ const RUNTIME_AGENT_DESCRIPTIONS: Partial<Record<string, string>> = {
81
81
  "Compatibility wrapper for review functions now folded into Critic. (Quality Guardian - HiaiOpenCode)",
82
82
  };
83
83
 
84
- function forceVisiblePrimaryAgent(agent: unknown, name: string): unknown {
84
+ function forceVisiblePrimaryAgent(agent: unknown, name: string, forceMode?: "primary" | "all"): unknown {
85
85
  if (typeof agent !== "object" || agent === null) {
86
86
  return agent;
87
87
  }
@@ -91,7 +91,7 @@ function forceVisiblePrimaryAgent(agent: unknown, name: string): unknown {
91
91
  ...base,
92
92
  name,
93
93
  hidden: false,
94
- mode: "primary",
94
+ mode: forceMode ?? "all",
95
95
  ...(typeof base.description === "string" && base.description.trim().length > 0
96
96
  ? {}
97
97
  : { description: RUNTIME_AGENT_DESCRIPTIONS[name] }),
@@ -475,6 +475,7 @@ export async function applyAgentConfig(params: {
475
475
  normalizedAgents[name] = forceVisiblePrimaryAgent(
476
476
  normalizedAgents[name],
477
477
  name,
478
+ name === "Bob" ? "primary" : "all",
478
479
  );
479
480
  }
480
481
  }
@@ -0,0 +1,63 @@
1
+ import { expect, test } from "bun:test"
2
+
3
+ import { applyMcpConfig } from "./mcp-config-handler"
4
+
5
+ const emptyPluginComponents = {
6
+ commands: {},
7
+ skills: {},
8
+ agents: {},
9
+ mcpServers: {},
10
+ hooksConfigs: [],
11
+ plugins: [],
12
+ errors: [],
13
+ }
14
+
15
+ test("context7 auth fallback from hiai-opencode config is used when env placeholder is empty", async () => {
16
+ const config: Record<string, unknown> = {}
17
+
18
+ await applyMcpConfig({
19
+ config,
20
+ pluginConfig: {
21
+ auth: { context7: "ctx-test-key" },
22
+ claude_code: { mcp: false },
23
+ __platformDefaults: {
24
+ mcp: {
25
+ context7: {
26
+ enabled: true,
27
+ type: "remote",
28
+ url: "https://mcp.context7.com/mcp",
29
+ headers: { "X-API-KEY": "{env:CONTEXT7_API_KEY}" },
30
+ },
31
+ },
32
+ },
33
+ } as any,
34
+ pluginComponents: emptyPluginComponents,
35
+ })
36
+
37
+ expect((config.mcp as any).context7.headers).toEqual({ "X-API-KEY": "ctx-test-key" })
38
+ })
39
+
40
+ test("firecrawl auth fallback from hiai-opencode config is used when env placeholder is empty", async () => {
41
+ const config: Record<string, unknown> = {}
42
+
43
+ await applyMcpConfig({
44
+ config,
45
+ pluginConfig: {
46
+ auth: { firecrawl: "fc-test-key" },
47
+ claude_code: { mcp: false },
48
+ __platformDefaults: {
49
+ mcp: {
50
+ firecrawl: {
51
+ enabled: true,
52
+ type: "remote",
53
+ url: "http://localhost/firecrawl",
54
+ environment: { FIRECRAWL_API_KEY: "{env:FIRECRAWL_API_KEY}" },
55
+ },
56
+ },
57
+ },
58
+ } as any,
59
+ pluginComponents: emptyPluginComponents,
60
+ })
61
+
62
+ expect((config.mcp as any).firecrawl.environment).toEqual({ FIRECRAWL_API_KEY: "fc-test-key" })
63
+ })
@@ -2,7 +2,6 @@ import type { HiaiOpenCodeConfig } from "../config";
2
2
  import { spawnSync } from "node:child_process";
3
3
  import { existsSync } from "node:fs";
4
4
  import { loadMcpConfigs } from "../features/claude-code-mcp-loader";
5
- import { createBuiltinMcps } from "../mcp";
6
5
  import type { PluginComponents } from "./plugin-components-loader";
7
6
  import { log } from "../shared";
8
7
  import { getPlatformMcpDefaults } from "../shared/runtime-plugin-config";
@@ -14,8 +13,20 @@ function resolveHeaderAuthFallback(
14
13
  pluginConfig: HiaiOpenCodeConfig,
15
14
  name: string,
16
15
  ): Record<string, string> | undefined {
17
- if (name === "stitch" && pluginConfig.auth?.stitch?.trim()) {
18
- return { "X-Goog-Api-Key": pluginConfig.auth.stitch.trim() };
16
+ const resolveAuth = (value: string | undefined): string | undefined => {
17
+ if (!value?.trim()) return undefined;
18
+ const resolved = resolveEnvVars(value).trim();
19
+ return resolved.length > 0 ? resolved : undefined;
20
+ };
21
+
22
+ if (name === "stitch") {
23
+ const key = resolveAuth(pluginConfig.auth?.stitch);
24
+ return key ? { "X-Goog-Api-Key": key } : undefined;
25
+ }
26
+
27
+ if (name === "context7") {
28
+ const key = resolveAuth(pluginConfig.auth?.context7);
29
+ return key ? { "X-API-KEY": key } : undefined;
19
30
  }
20
31
 
21
32
  return undefined;
@@ -25,8 +36,9 @@ function resolveEnvironmentAuthFallback(
25
36
  pluginConfig: HiaiOpenCodeConfig,
26
37
  name: string,
27
38
  ): Record<string, string> | undefined {
28
- if (name === "firecrawl" && pluginConfig.auth?.firecrawl?.trim()) {
29
- return { FIRECRAWL_API_KEY: pluginConfig.auth.firecrawl.trim() };
39
+ if (name === "firecrawl") {
40
+ const key = pluginConfig.auth?.firecrawl ? resolveEnvVars(pluginConfig.auth.firecrawl).trim() : "";
41
+ return key ? { FIRECRAWL_API_KEY: key } : undefined;
30
42
  }
31
43
 
32
44
  return undefined;
@@ -122,18 +134,20 @@ function normalizePlatformMcpDefaults(
122
134
  );
123
135
  const headerFallback = resolveHeaderAuthFallback(pluginConfig, name);
124
136
  const environmentFallback = resolveEnvironmentAuthFallback(pluginConfig, name);
137
+ const usableHeaders = headers && !hasMissingResolvedValue(headers) ? headers : undefined;
138
+ const usableEnvironment = environment && !hasMissingResolvedValue(environment) ? environment : undefined;
125
139
  const headersWithFallback =
126
- headers && headerFallback
127
- ? { ...headerFallback, ...headers }
128
- : headers ?? headerFallback;
140
+ usableHeaders && headerFallback
141
+ ? { ...headerFallback, ...usableHeaders }
142
+ : usableHeaders ?? headerFallback;
129
143
  const environmentWithFallback =
130
- environment && environmentFallback
131
- ? { ...environmentFallback, ...environment }
132
- : environment ?? environmentFallback;
144
+ usableEnvironment && environmentFallback
145
+ ? { ...environmentFallback, ...usableEnvironment }
146
+ : usableEnvironment ?? environmentFallback;
133
147
 
134
148
  const missingResolvedValues =
135
- hasMissingResolvedValue(headersWithFallback) ||
136
- hasMissingResolvedValue(environmentWithFallback);
149
+ (!!headers && !usableHeaders && !headerFallback) ||
150
+ (!!environment && !usableEnvironment && !environmentFallback);
137
151
 
138
152
  if (missingResolvedValues) {
139
153
  log(`MCP server "${name}" is missing environment-backed auth; keeping it visible in config`);
@@ -152,6 +166,8 @@ function normalizePlatformMcpDefaults(
152
166
  nextEntry.environment = environmentWithFallback;
153
167
  }
154
168
 
169
+ delete nextEntry.provider;
170
+
155
171
  normalized[name] = nextEntry;
156
172
  }
157
173
 
@@ -184,7 +200,6 @@ export async function applyMcpConfig(params: {
184
200
  getPlatformMcpDefaults(params.pluginConfig) as unknown as Record<string, unknown>,
185
201
  params.pluginConfig,
186
202
  ),
187
- ...createBuiltinMcps(disabledMcps, params.pluginConfig),
188
203
  ...mcpResult.servers,
189
204
  ...(userMcp ?? {}),
190
205
  ...params.pluginComponents.mcpServers,
@@ -98,7 +98,7 @@ export async function buildStrategistAgentConfig(params: {
98
98
  const base: Record<string, unknown> = {
99
99
  ...(resolvedModel ? { model: resolvedModel } : {}),
100
100
  ...(variantToUse ? { variant: variantToUse } : {}),
101
- mode: "primary",
101
+ mode: "all",
102
102
  prompt: getStrategistPrompt(resolvedModel, params.disabledTools) + "\n\n" + CLOSURE_SCHEMA_PROMPT,
103
103
  permission: PROMETHEUS_PERMISSION,
104
104
  description: `${(params.configAgentPlan?.description as string) ?? "Plan agent"} (Strategist - HiaiOpenCode)`,
@@ -0,0 +1,9 @@
1
+ import { expect, test } from "bun:test"
2
+
3
+ import { getAgentConfigKey } from "./agent-display-names"
4
+
5
+ test("writer aliases resolve to brainstormer", () => {
6
+ expect(getAgentConfigKey("writer")).toBe("brainstormer")
7
+ expect(getAgentConfigKey("copywriter")).toBe("brainstormer")
8
+ expect(getAgentConfigKey("content-writer")).toBe("brainstormer")
9
+ })
@@ -110,6 +110,11 @@ const LEGACY_DISPLAY_NAMES: Record<string, string> = {
110
110
  "platform manager (utility)": "platform-manager",
111
111
  "platform manager - utility": "platform-manager",
112
112
  "brainstormer - idea explorer": "brainstormer",
113
+ "writer": "brainstormer",
114
+ "copywriter": "brainstormer",
115
+ "content-writer": "brainstormer",
116
+ "content writer": "brainstormer",
117
+ "website-writer": "brainstormer",
113
118
  "agent skills - skill composer": "agent-skills",
114
119
  "subagent": "sub",
115
120
  "ui": "multimodal",