@hiai-gg/hiai-opencode 0.1.6 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +7 -0
- package/AGENTS.md +46 -1
- package/ARCHITECTURE.md +6 -3
- package/README.md +71 -10
- package/assets/cli/hiai-opencode.mjs +78 -0
- package/config/hiai-opencode.schema.json +16 -1
- package/dist/agents/agent-skills.d.ts +7 -0
- package/dist/agents/bob/default.d.ts +1 -0
- package/dist/agents/bob/gemini.d.ts +1 -0
- package/dist/agents/bob/gpt-pro.d.ts +1 -0
- package/dist/agents/brainstormer.d.ts +7 -0
- package/dist/agents/coder/gpt-codex.d.ts +1 -1
- package/dist/agents/coder/gpt-pro.d.ts +1 -0
- package/dist/agents/coder/gpt.d.ts +2 -1
- package/dist/agents/designer.d.ts +7 -0
- package/dist/agents/strategist/gemini.d.ts +1 -0
- package/dist/agents/strategist/gpt.d.ts +1 -0
- package/dist/agents/types.d.ts +3 -1
- package/dist/config/index.d.ts +0 -1
- package/dist/config/platform-schema.d.ts +32 -0
- package/dist/config/schema/hooks.d.ts +0 -2
- package/dist/config/schema/index.d.ts +0 -2
- package/dist/config/schema/oh-my-opencode-config.d.ts +0 -6
- package/dist/config/types.d.ts +3 -1
- package/dist/create-hooks.d.ts +0 -2
- package/dist/features/builtin-skills/skills/index.d.ts +1 -0
- package/dist/features/builtin-skills/skills/website-copywriting.d.ts +2 -0
- package/dist/hooks/agent-usage-reminder/constants.d.ts +1 -1
- package/dist/hooks/index.d.ts +0 -2
- package/dist/hooks/keyword-detector/ultrawork/default.d.ts +1 -1
- package/dist/hooks/keyword-detector/ultrawork/gemini.d.ts +1 -1
- package/dist/hooks/keyword-detector/ultrawork/gpt.d.ts +1 -1
- package/dist/hooks/keyword-detector/ultrawork/planner.d.ts +1 -1
- package/dist/index.js +8963 -153866
- package/dist/mcp/index.d.ts +0 -1
- package/dist/mcp/registry.d.ts +1 -1
- package/dist/plugin/hooks/create-core-hooks.d.ts +0 -2
- package/dist/plugin/hooks/create-session-hooks.d.ts +1 -3
- package/dist/plugin/startup-diagnostics.d.ts +1 -0
- package/dist/shared/logger.d.ts +2 -0
- package/dist/shared/mode-routing.d.ts +6 -0
- package/dist/tools/delegate-task/git-categories.d.ts +2 -0
- package/dist/tools/delegate-task/sub-agent.d.ts +2 -0
- package/dist/tools/skill-mcp/constants.d.ts +1 -1
- package/hiai-opencode.json +48 -19
- package/package.json +6 -3
- package/src/agents/agent-skills.ts +70 -0
- package/src/agents/bob/default.ts +1 -0
- package/src/agents/bob/gemini.ts +1 -0
- package/src/agents/bob/gpt-pro.ts +2 -1
- package/src/agents/bob.ts +2 -0
- package/src/agents/brainstormer.ts +72 -0
- package/src/agents/builtin-agents.ts +59 -3
- package/src/agents/coder/gpt-codex.ts +4 -3
- package/src/agents/coder/gpt-pro.ts +3 -2
- package/src/agents/coder/gpt.ts +2 -1
- package/src/agents/critic/agent.ts +1 -0
- package/src/agents/designer.ts +70 -0
- package/src/agents/dynamic-agent-category-skills-guide.ts +6 -0
- package/src/agents/guard/default.ts +1 -0
- package/src/agents/guard/gemini.ts +1 -0
- package/src/agents/guard/gpt.ts +1 -0
- package/src/agents/platform-manager.ts +17 -1
- package/src/agents/prompt-library/platform.ts +34 -0
- package/src/agents/researcher.ts +1 -0
- package/src/agents/strategist/gemini.ts +1 -0
- package/src/agents/strategist/gpt.ts +1 -0
- package/src/agents/types.ts +4 -1
- package/src/agents/ui.ts +1 -0
- package/src/config/defaults.ts +31 -12
- package/src/config/index.ts +0 -1
- package/src/config/model-slots-and-export.test.ts +22 -4
- package/src/config/platform-schema.ts +2 -0
- package/src/config/schema/hooks.ts +0 -2
- package/src/config/schema/index.ts +0 -2
- package/src/config/schema/oh-my-opencode-config.ts +0 -2
- package/src/config/types.ts +3 -1
- package/src/features/builtin-skills/skills/index.ts +1 -0
- package/src/features/builtin-skills/skills/website-copywriting.ts +41 -0
- package/src/features/builtin-skills/skills.test.ts +8 -0
- package/src/features/builtin-skills/skills.ts +2 -0
- package/src/features/skill-mcp-manager/AGENTS.md +1 -1
- package/src/hooks/agent-usage-reminder/constants.ts +4 -4
- package/src/hooks/index.ts +0 -2
- package/src/hooks/keyword-detector/ultrawork/default.ts +18 -18
- package/src/hooks/keyword-detector/ultrawork/gemini.ts +21 -21
- package/src/hooks/keyword-detector/ultrawork/gpt.ts +6 -8
- package/src/hooks/keyword-detector/ultrawork/planner.ts +5 -5
- package/src/index.ts +5 -3
- package/src/internals/plugins/subtask2/commands/manifest.ts +2 -6
- package/src/internals/plugins/subtask2/hooks/command-hooks.ts +2 -2
- package/src/internals/plugins/subtask2/hooks/message-hooks.ts +1 -1
- package/src/internals/plugins/subtask2/parsing/parallel.ts +13 -10
- package/src/mcp/index.ts +0 -1
- package/src/mcp/registry.ts +27 -0
- package/src/plugin/chat-message.ts +0 -2
- package/src/plugin/hooks/create-session-hooks.ts +0 -17
- package/src/plugin/startup-diagnostics.ts +27 -0
- package/src/plugin-handlers/agent-config-handler.ts +3 -2
- package/src/plugin-handlers/mcp-config-handler.test.ts +63 -0
- package/src/plugin-handlers/mcp-config-handler.ts +29 -14
- package/src/plugin-handlers/strategist-agent-config-builder.ts +1 -1
- package/src/shared/agent-display-names.test.ts +9 -0
- package/src/shared/agent-display-names.ts +5 -0
- package/src/shared/log-legacy-plugin-startup-warning.ts +6 -8
- package/src/shared/logger.ts +8 -0
- package/src/shared/mcp-static-export.ts +4 -6
- package/src/shared/migration/agent-names.ts +8 -0
- package/src/shared/migration/hook-names.ts +1 -1
- package/src/shared/mode-routing.test.ts +88 -0
- package/src/shared/mode-routing.ts +30 -0
- package/src/shared/startup-diagnostics.ts +6 -7
- package/src/tools/call-omo-agent/tools.ts +11 -4
- package/src/tools/delegate-task/anthropic-categories.ts +3 -3
- package/src/tools/delegate-task/builtin-categories.ts +2 -0
- package/src/tools/delegate-task/categories.test.ts +87 -0
- package/src/tools/delegate-task/category-resolver.ts +8 -9
- package/src/tools/delegate-task/git-categories.ts +30 -0
- package/src/tools/delegate-task/model-string-parser.test.ts +90 -0
- package/src/tools/delegate-task/openai-categories.ts +26 -22
- package/src/tools/delegate-task/sub-agent.ts +10 -0
- package/src/tools/delegate-task/subagent-discovery.test.ts +123 -0
- package/src/tools/delegate-task/subagent-resolver.ts +18 -1
- package/src/tools/skill-mcp/constants.ts +1 -1
- package/dist/config/schema/websearch.d.ts +0 -13
- package/dist/hooks/no-bob-gpt/hook.d.ts +0 -16
- package/dist/hooks/no-bob-gpt/index.d.ts +0 -1
- package/dist/hooks/no-coder-non-gpt/hook.d.ts +0 -20
- package/dist/hooks/no-coder-non-gpt/index.d.ts +0 -1
- package/dist/mcp/grep-app.d.ts +0 -6
- package/dist/mcp/omo-mcp-index.d.ts +0 -10
- package/dist/mcp/websearch.d.ts +0 -11
- package/src/config/schema/websearch.ts +0 -15
- package/src/hooks/no-bob-gpt/hook.ts +0 -56
- package/src/hooks/no-bob-gpt/index.ts +0 -1
- package/src/hooks/no-coder-non-gpt/hook.ts +0 -67
- package/src/hooks/no-coder-non-gpt/index.ts +0 -1
- package/src/mcp/grep-app.ts +0 -6
- package/src/mcp/omo-mcp-index.ts +0 -30
- package/src/mcp/websearch.ts +0 -44
|
@@ -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
|
|
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
|
-
- **
|
|
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="
|
|
85
|
-
task(subagent_type="
|
|
86
|
-
task(subagent_type="
|
|
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 (
|
|
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
|
|
154
|
-
| Task scope unclear | MUST call
|
|
155
|
-
| Implementation required | MUST call
|
|
156
|
-
| Architecture decision needed | MUST call
|
|
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="
|
|
159
|
+
task(subagent_type="strategist", load_skills=[], run_in_background=false, prompt="<gathered context + user request>")
|
|
160
160
|
\`\`\`
|
|
161
161
|
|
|
162
|
-
### SESSION CONTINUITY WITH
|
|
162
|
+
### SESSION CONTINUITY WITH STRATEGIST AGENT (CRITICAL)
|
|
163
163
|
|
|
164
|
-
**
|
|
164
|
+
**Strategist agent returns a session_id. USE IT for follow-up interactions.**
|
|
165
165
|
|
|
166
166
|
| Scenario | Action |
|
|
167
167
|
|----------|--------|
|
|
168
|
-
|
|
|
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
|
-
|
|
|
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
|
|
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="
|
|
185
|
-
| Documentation lookup | task(subagent_type="
|
|
186
|
-
| Planning | task(subagent_type="
|
|
187
|
-
| Hard problem (conventional) | task(subagent_type="
|
|
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
|
|
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
|
-
|
|
|
73
|
-
|
|
|
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,
|
|
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** |
|
|
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="
|
|
97
|
-
task(subagent_type="
|
|
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
|
|
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="
|
|
42
|
-
task(subagent_type="
|
|
43
|
-
task(subagent_type="
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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 (
|
|
264
|
+
if ("as" in part && part.as) {
|
|
265
265
|
registerPendingResultCaptureByPrompt(
|
|
266
266
|
part.prompt,
|
|
267
267
|
input.sessionID,
|
|
268
|
-
(part
|
|
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 =
|
|
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 (
|
|
61
|
+
if (isParallelCommandLike(p)) {
|
|
59
62
|
return {
|
|
60
|
-
command: (p
|
|
61
|
-
arguments:
|
|
62
|
-
prompt:
|
|
63
|
-
model:
|
|
64
|
-
agent:
|
|
65
|
-
loop:
|
|
66
|
-
as:
|
|
67
|
-
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
package/src/mcp/registry.ts
CHANGED
|
@@ -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: "
|
|
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
|
-
|
|
18
|
-
|
|
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"
|
|
29
|
-
|
|
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
|
-
|
|
127
|
-
? { ...headerFallback, ...
|
|
128
|
-
:
|
|
140
|
+
usableHeaders && headerFallback
|
|
141
|
+
? { ...headerFallback, ...usableHeaders }
|
|
142
|
+
: usableHeaders ?? headerFallback;
|
|
129
143
|
const environmentWithFallback =
|
|
130
|
-
|
|
131
|
-
? { ...environmentFallback, ...
|
|
132
|
-
:
|
|
144
|
+
usableEnvironment && environmentFallback
|
|
145
|
+
? { ...environmentFallback, ...usableEnvironment }
|
|
146
|
+
: usableEnvironment ?? environmentFallback;
|
|
133
147
|
|
|
134
148
|
const missingResolvedValues =
|
|
135
|
-
|
|
136
|
-
|
|
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: "
|
|
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",
|