opencode-dux 1.0.0

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 (302) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +452 -0
  3. package/dist/agents/descriptions.d.ts +6 -0
  4. package/dist/agents/designer.d.ts +2 -0
  5. package/dist/agents/explorer.d.ts +2 -0
  6. package/dist/agents/fixer.d.ts +2 -0
  7. package/dist/agents/index.d.ts +22 -0
  8. package/dist/agents/interpreter.d.ts +2 -0
  9. package/dist/agents/librarian.d.ts +2 -0
  10. package/dist/agents/oracle.d.ts +2 -0
  11. package/dist/agents/orchestrator.d.ts +27 -0
  12. package/dist/agents/overrides.d.ts +18 -0
  13. package/dist/agents/prompt-blocks.d.ts +97 -0
  14. package/dist/agents/steward.d.ts +3 -0
  15. package/dist/cli/config-io.d.ts +24 -0
  16. package/dist/cli/config-manager.d.ts +4 -0
  17. package/dist/cli/index.d.ts +2 -0
  18. package/dist/cli/index.js +1006 -0
  19. package/dist/cli/install.d.ts +2 -0
  20. package/dist/cli/mcps.d.ts +13 -0
  21. package/dist/cli/model-key-normalization.d.ts +1 -0
  22. package/dist/cli/paths.d.ts +35 -0
  23. package/dist/cli/providers.d.ts +137 -0
  24. package/dist/cli/skills.d.ts +22 -0
  25. package/dist/cli/system.d.ts +5 -0
  26. package/dist/cli/types.d.ts +38 -0
  27. package/dist/config/constants.d.ts +12 -0
  28. package/dist/config/index.d.ts +4 -0
  29. package/dist/config/loader.d.ts +40 -0
  30. package/dist/config/runtime-preset.d.ts +12 -0
  31. package/dist/config/schema.d.ts +281 -0
  32. package/dist/config/utils.d.ts +10 -0
  33. package/dist/discovery/local/types.d.ts +79 -0
  34. package/dist/discovery/local.d.ts +73 -0
  35. package/dist/discovery/mcp-servers.d.ts +88 -0
  36. package/dist/discovery/skills.d.ts +94 -0
  37. package/dist/hooks/apply-patch/codec.d.ts +7 -0
  38. package/dist/hooks/apply-patch/errors.d.ts +25 -0
  39. package/dist/hooks/apply-patch/execution-context.d.ts +27 -0
  40. package/dist/hooks/apply-patch/index.d.ts +15 -0
  41. package/dist/hooks/apply-patch/matching.d.ts +26 -0
  42. package/dist/hooks/apply-patch/operations.d.ts +3 -0
  43. package/dist/hooks/apply-patch/patch.d.ts +2 -0
  44. package/dist/hooks/apply-patch/prepared-changes.d.ts +17 -0
  45. package/dist/hooks/apply-patch/resolution.d.ts +19 -0
  46. package/dist/hooks/apply-patch/rewrite.d.ts +7 -0
  47. package/dist/hooks/apply-patch/test-helpers.d.ts +6 -0
  48. package/dist/hooks/apply-patch/types.d.ts +80 -0
  49. package/dist/hooks/auto-update-checker/cache.d.ts +11 -0
  50. package/dist/hooks/auto-update-checker/checker.d.ts +32 -0
  51. package/dist/hooks/auto-update-checker/constants.d.ts +11 -0
  52. package/dist/hooks/auto-update-checker/index.d.ts +18 -0
  53. package/dist/hooks/auto-update-checker/types.d.ts +22 -0
  54. package/dist/hooks/chat-headers.d.ts +16 -0
  55. package/dist/hooks/context-pressure-reminder/index.d.ts +33 -0
  56. package/dist/hooks/delegate-task-retry/guidance.d.ts +2 -0
  57. package/dist/hooks/delegate-task-retry/hook.d.ts +8 -0
  58. package/dist/hooks/delegate-task-retry/index.d.ts +4 -0
  59. package/dist/hooks/delegate-task-retry/patterns.d.ts +11 -0
  60. package/dist/hooks/filter-available-skills/index.d.ts +32 -0
  61. package/dist/hooks/foreground-fallback/index.d.ts +72 -0
  62. package/dist/hooks/image-hook.d.ts +5 -0
  63. package/dist/hooks/index.d.ts +14 -0
  64. package/dist/hooks/json-error-recovery/hook.d.ts +18 -0
  65. package/dist/hooks/json-error-recovery/index.d.ts +1 -0
  66. package/dist/hooks/phase-reminder/index.d.ts +26 -0
  67. package/dist/hooks/post-file-tool-nudge/index.d.ts +19 -0
  68. package/dist/hooks/task-session-manager/index.d.ts +52 -0
  69. package/dist/hooks/todo-continuation/index.d.ts +53 -0
  70. package/dist/hooks/todo-continuation/todo-hygiene.d.ts +35 -0
  71. package/dist/index.d.ts +5 -0
  72. package/dist/index.js +31782 -0
  73. package/dist/mcp/context7.d.ts +6 -0
  74. package/dist/mcp/grep-app.d.ts +6 -0
  75. package/dist/mcp/index.d.ts +13 -0
  76. package/dist/mcp/types.d.ts +12 -0
  77. package/dist/mcp/websearch.d.ts +9 -0
  78. package/dist/skills/registry.d.ts +29 -0
  79. package/dist/subscriptions/accounts-store.d.ts +57 -0
  80. package/dist/subscriptions/index.d.ts +13 -0
  81. package/dist/subscriptions/neuralwatt-scraper.d.ts +14 -0
  82. package/dist/subscriptions/opencode-go-scraper.d.ts +27 -0
  83. package/dist/subscriptions/types.d.ts +115 -0
  84. package/dist/subscriptions/usage-service.d.ts +74 -0
  85. package/dist/tools/ast-grep/cli.d.ts +15 -0
  86. package/dist/tools/ast-grep/constants.d.ts +25 -0
  87. package/dist/tools/ast-grep/downloader.d.ts +5 -0
  88. package/dist/tools/ast-grep/index.d.ts +10 -0
  89. package/dist/tools/ast-grep/tools.d.ts +3 -0
  90. package/dist/tools/ast-grep/types.d.ts +30 -0
  91. package/dist/tools/ast-grep/utils.d.ts +4 -0
  92. package/dist/tools/delegate.d.ts +14 -0
  93. package/dist/tools/index.d.ts +5 -0
  94. package/dist/tools/preset-manager.d.ts +27 -0
  95. package/dist/tools/smartfetch/binary.d.ts +3 -0
  96. package/dist/tools/smartfetch/cache.d.ts +6 -0
  97. package/dist/tools/smartfetch/constants.d.ts +12 -0
  98. package/dist/tools/smartfetch/index.d.ts +3 -0
  99. package/dist/tools/smartfetch/network.d.ts +38 -0
  100. package/dist/tools/smartfetch/secondary-model.d.ts +28 -0
  101. package/dist/tools/smartfetch/tool.d.ts +3 -0
  102. package/dist/tools/smartfetch/types.d.ts +122 -0
  103. package/dist/tools/smartfetch/utils.d.ts +18 -0
  104. package/dist/tui-state.d.ts +168 -0
  105. package/dist/tui.d.ts +37 -0
  106. package/dist/tui.js +1896 -0
  107. package/dist/utils/agent-variant.d.ts +63 -0
  108. package/dist/utils/compat.d.ts +30 -0
  109. package/dist/utils/env.d.ts +1 -0
  110. package/dist/utils/index.d.ts +9 -0
  111. package/dist/utils/internal-initiator.d.ts +6 -0
  112. package/dist/utils/logger.d.ts +8 -0
  113. package/dist/utils/polling.d.ts +21 -0
  114. package/dist/utils/session-manager.d.ts +55 -0
  115. package/dist/utils/session.d.ts +90 -0
  116. package/dist/utils/subagent-depth.d.ts +35 -0
  117. package/dist/utils/system-collapse.d.ts +6 -0
  118. package/dist/utils/task.d.ts +4 -0
  119. package/dist/utils/zip-extractor.d.ts +1 -0
  120. package/index.ts +1 -0
  121. package/opencode-dux.schema.json +634 -0
  122. package/package.json +103 -0
  123. package/src/agents/descriptions.ts +55 -0
  124. package/src/agents/designer.test.ts +86 -0
  125. package/src/agents/designer.ts +154 -0
  126. package/src/agents/display-name.test.ts +186 -0
  127. package/src/agents/explorer.test.ts +79 -0
  128. package/src/agents/explorer.ts +144 -0
  129. package/src/agents/fixer.test.ts +79 -0
  130. package/src/agents/fixer.ts +145 -0
  131. package/src/agents/index.test.ts +472 -0
  132. package/src/agents/index.ts +248 -0
  133. package/src/agents/interpreter.ts +136 -0
  134. package/src/agents/librarian.test.ts +80 -0
  135. package/src/agents/librarian.ts +145 -0
  136. package/src/agents/oracle.test.ts +89 -0
  137. package/src/agents/oracle.ts +184 -0
  138. package/src/agents/orchestrator.test.ts +116 -0
  139. package/src/agents/orchestrator.ts +574 -0
  140. package/src/agents/overrides.ts +95 -0
  141. package/src/agents/prompt-blocks.test.ts +114 -0
  142. package/src/agents/prompt-blocks.ts +640 -0
  143. package/src/agents/steward.ts +146 -0
  144. package/src/cli/config-io.test.ts +536 -0
  145. package/src/cli/config-io.ts +473 -0
  146. package/src/cli/config-manager.test.ts +141 -0
  147. package/src/cli/config-manager.ts +4 -0
  148. package/src/cli/index.ts +88 -0
  149. package/src/cli/install.ts +282 -0
  150. package/src/cli/mcps.test.ts +62 -0
  151. package/src/cli/mcps.ts +39 -0
  152. package/src/cli/model-key-normalization.test.ts +21 -0
  153. package/src/cli/model-key-normalization.ts +60 -0
  154. package/src/cli/paths.test.ts +167 -0
  155. package/src/cli/paths.ts +144 -0
  156. package/src/cli/providers.test.ts +118 -0
  157. package/src/cli/providers.ts +141 -0
  158. package/src/cli/skills.test.ts +111 -0
  159. package/src/cli/skills.ts +103 -0
  160. package/src/cli/system.test.ts +91 -0
  161. package/src/cli/system.ts +180 -0
  162. package/src/cli/types.ts +43 -0
  163. package/src/config/constants.ts +58 -0
  164. package/src/config/index.ts +4 -0
  165. package/src/config/loader.test.ts +1194 -0
  166. package/src/config/loader.ts +269 -0
  167. package/src/config/model-resolution.test.ts +176 -0
  168. package/src/config/runtime-preset.test.ts +61 -0
  169. package/src/config/runtime-preset.ts +37 -0
  170. package/src/config/schema.ts +248 -0
  171. package/src/config/utils.test.ts +41 -0
  172. package/src/config/utils.ts +23 -0
  173. package/src/discovery/local/types.ts +85 -0
  174. package/src/discovery/local.ts +322 -0
  175. package/src/discovery/mcp-servers.ts +804 -0
  176. package/src/discovery/skills.ts +959 -0
  177. package/src/hooks/apply-patch/codec.test.ts +184 -0
  178. package/src/hooks/apply-patch/codec.ts +352 -0
  179. package/src/hooks/apply-patch/errors.ts +117 -0
  180. package/src/hooks/apply-patch/execution-context.ts +432 -0
  181. package/src/hooks/apply-patch/hook.test.ts +768 -0
  182. package/src/hooks/apply-patch/index.ts +126 -0
  183. package/src/hooks/apply-patch/matching.test.ts +215 -0
  184. package/src/hooks/apply-patch/matching.ts +586 -0
  185. package/src/hooks/apply-patch/operations.test.ts +1535 -0
  186. package/src/hooks/apply-patch/operations.ts +3 -0
  187. package/src/hooks/apply-patch/patch.ts +9 -0
  188. package/src/hooks/apply-patch/prepared-changes.ts +400 -0
  189. package/src/hooks/apply-patch/resolution.test.ts +420 -0
  190. package/src/hooks/apply-patch/resolution.ts +437 -0
  191. package/src/hooks/apply-patch/rewrite.ts +496 -0
  192. package/src/hooks/apply-patch/test-helpers.ts +52 -0
  193. package/src/hooks/apply-patch/types.ts +111 -0
  194. package/src/hooks/auto-update-checker/cache.test.ts +179 -0
  195. package/src/hooks/auto-update-checker/cache.ts +188 -0
  196. package/src/hooks/auto-update-checker/checker.test.ts +159 -0
  197. package/src/hooks/auto-update-checker/checker.ts +308 -0
  198. package/src/hooks/auto-update-checker/constants.ts +33 -0
  199. package/src/hooks/auto-update-checker/index.test.ts +282 -0
  200. package/src/hooks/auto-update-checker/index.ts +225 -0
  201. package/src/hooks/auto-update-checker/types.ts +26 -0
  202. package/src/hooks/chat-headers.test.ts +236 -0
  203. package/src/hooks/chat-headers.ts +97 -0
  204. package/src/hooks/context-pressure-reminder/index.test.ts +179 -0
  205. package/src/hooks/context-pressure-reminder/index.ts +137 -0
  206. package/src/hooks/delegate-task-retry/guidance.ts +41 -0
  207. package/src/hooks/delegate-task-retry/hook.ts +23 -0
  208. package/src/hooks/delegate-task-retry/index.test.ts +38 -0
  209. package/src/hooks/delegate-task-retry/index.ts +7 -0
  210. package/src/hooks/delegate-task-retry/patterns.ts +79 -0
  211. package/src/hooks/filter-available-skills/index.test.ts +297 -0
  212. package/src/hooks/filter-available-skills/index.ts +160 -0
  213. package/src/hooks/foreground-fallback/index.test.ts +624 -0
  214. package/src/hooks/foreground-fallback/index.ts +374 -0
  215. package/src/hooks/image-hook.ts +6 -0
  216. package/src/hooks/index.ts +17 -0
  217. package/src/hooks/json-error-recovery/hook.ts +73 -0
  218. package/src/hooks/json-error-recovery/index.test.ts +111 -0
  219. package/src/hooks/json-error-recovery/index.ts +6 -0
  220. package/src/hooks/phase-reminder/index.test.ts +74 -0
  221. package/src/hooks/phase-reminder/index.ts +85 -0
  222. package/src/hooks/post-file-tool-nudge/index.test.ts +94 -0
  223. package/src/hooks/post-file-tool-nudge/index.ts +63 -0
  224. package/src/hooks/task-session-manager/index.test.ts +833 -0
  225. package/src/hooks/task-session-manager/index.ts +434 -0
  226. package/src/hooks/todo-continuation/index.test.ts +3026 -0
  227. package/src/hooks/todo-continuation/index.ts +878 -0
  228. package/src/hooks/todo-continuation/todo-hygiene.test.ts +204 -0
  229. package/src/hooks/todo-continuation/todo-hygiene.ts +207 -0
  230. package/src/index.ts +1672 -0
  231. package/src/mcp/context7.ts +14 -0
  232. package/src/mcp/grep-app.ts +11 -0
  233. package/src/mcp/index.test.ts +96 -0
  234. package/src/mcp/index.ts +66 -0
  235. package/src/mcp/types.ts +16 -0
  236. package/src/mcp/websearch.ts +47 -0
  237. package/src/skills/codemap/README.md +60 -0
  238. package/src/skills/codemap/SKILL.md +174 -0
  239. package/src/skills/codemap/scripts/codemap.mjs +483 -0
  240. package/src/skills/codemap/scripts/codemap.test.ts +129 -0
  241. package/src/skills/registry.ts +218 -0
  242. package/src/skills/simplify/README.md +19 -0
  243. package/src/skills/simplify/SKILL.md +138 -0
  244. package/src/subscriptions/accounts-store.test.ts +236 -0
  245. package/src/subscriptions/accounts-store.ts +184 -0
  246. package/src/subscriptions/index.ts +30 -0
  247. package/src/subscriptions/neuralwatt-scraper.ts +108 -0
  248. package/src/subscriptions/opencode-go-scraper.ts +301 -0
  249. package/src/subscriptions/types.ts +145 -0
  250. package/src/subscriptions/usage-service.test.ts +202 -0
  251. package/src/subscriptions/usage-service.ts +651 -0
  252. package/src/tools/ast-grep/cli.ts +257 -0
  253. package/src/tools/ast-grep/constants.ts +214 -0
  254. package/src/tools/ast-grep/downloader.ts +131 -0
  255. package/src/tools/ast-grep/index.ts +24 -0
  256. package/src/tools/ast-grep/tools.ts +117 -0
  257. package/src/tools/ast-grep/types.ts +51 -0
  258. package/src/tools/ast-grep/utils.ts +126 -0
  259. package/src/tools/delegate-handoff.test.ts +18 -0
  260. package/src/tools/delegate.ts +508 -0
  261. package/src/tools/index.ts +8 -0
  262. package/src/tools/preset-manager.test.ts +795 -0
  263. package/src/tools/preset-manager.ts +332 -0
  264. package/src/tools/smartfetch/binary.ts +58 -0
  265. package/src/tools/smartfetch/cache.test.ts +34 -0
  266. package/src/tools/smartfetch/cache.ts +112 -0
  267. package/src/tools/smartfetch/constants.ts +29 -0
  268. package/src/tools/smartfetch/index.ts +8 -0
  269. package/src/tools/smartfetch/network.test.ts +178 -0
  270. package/src/tools/smartfetch/network.ts +614 -0
  271. package/src/tools/smartfetch/secondary-model.test.ts +85 -0
  272. package/src/tools/smartfetch/secondary-model.ts +276 -0
  273. package/src/tools/smartfetch/tool.test.ts +60 -0
  274. package/src/tools/smartfetch/tool.ts +832 -0
  275. package/src/tools/smartfetch/types.ts +135 -0
  276. package/src/tools/smartfetch/utils.test.ts +24 -0
  277. package/src/tools/smartfetch/utils.ts +456 -0
  278. package/src/tui-state.test.ts +867 -0
  279. package/src/tui-state.ts +1255 -0
  280. package/src/tui.test.ts +336 -0
  281. package/src/tui.ts +1539 -0
  282. package/src/utils/agent-variant.test.ts +244 -0
  283. package/src/utils/agent-variant.ts +187 -0
  284. package/src/utils/compat.ts +91 -0
  285. package/src/utils/env.ts +12 -0
  286. package/src/utils/index.ts +9 -0
  287. package/src/utils/internal-initiator.ts +28 -0
  288. package/src/utils/logger.test.ts +220 -0
  289. package/src/utils/logger.ts +136 -0
  290. package/src/utils/polling.test.ts +191 -0
  291. package/src/utils/polling.ts +67 -0
  292. package/src/utils/session-manager.test.ts +173 -0
  293. package/src/utils/session-manager.ts +356 -0
  294. package/src/utils/session.test.ts +110 -0
  295. package/src/utils/session.ts +389 -0
  296. package/src/utils/subagent-depth.test.ts +170 -0
  297. package/src/utils/subagent-depth.ts +75 -0
  298. package/src/utils/system-collapse.test.ts +86 -0
  299. package/src/utils/system-collapse.ts +24 -0
  300. package/src/utils/task.test.ts +24 -0
  301. package/src/utils/task.ts +20 -0
  302. package/src/utils/zip-extractor.ts +102 -0
@@ -0,0 +1,248 @@
1
+ import type { AgentConfig as SDKAgentConfig } from '@opencode-ai/sdk/v2';
2
+ import {
3
+ ALL_AGENT_NAMES,
4
+ DEFAULT_MODELS,
5
+ getAgentOverride,
6
+ loadAgentPrompt,
7
+ type PluginConfig,
8
+ SUBAGENT_NAMES,
9
+ } from '../config';
10
+ import { createDesignerAgent } from './designer';
11
+ import { createExplorerAgent } from './explorer';
12
+ import { createFixerAgent } from './fixer';
13
+ import { createInterpreterAgent } from './interpreter';
14
+ import { createLibrarianAgent } from './librarian';
15
+ import { createOracleAgent } from './oracle';
16
+ import { type AgentDefinition, createOrchestratorAgent } from './orchestrator';
17
+ import { applyDefaultPermissions, applyOverrides } from './overrides';
18
+ import { createStewardAgent } from './steward';
19
+
20
+ export type { AgentDefinition } from './orchestrator';
21
+
22
+ type AgentFactory = (
23
+ model: string,
24
+ customPrompt?: string,
25
+ customAppendPrompt?: string,
26
+ ) => AgentDefinition;
27
+
28
+ function normalizeDisplayName(displayName: string): string {
29
+ const trimmed = displayName.trim();
30
+ return trimmed.startsWith('@') ? trimmed.slice(1) : trimmed;
31
+ }
32
+
33
+ function escapeRegExp(value: string): string {
34
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
35
+ }
36
+
37
+ function injectDisplayNames(
38
+ orchestrator: AgentDefinition,
39
+ nameMap: Map<string, string>,
40
+ ): void {
41
+ if (nameMap.size === 0) return;
42
+ let prompt = orchestrator.config.prompt;
43
+ if (!prompt) return;
44
+
45
+ for (const [internalName, displayName] of nameMap) {
46
+ prompt = prompt.replace(
47
+ new RegExp(`@${escapeRegExp(internalName)}\\b`, 'g'),
48
+ `@${normalizeDisplayName(displayName)}`,
49
+ );
50
+ }
51
+
52
+ orchestrator.config.prompt = prompt;
53
+ }
54
+
55
+ // Agent Classification
56
+
57
+ export type SubagentName = (typeof SUBAGENT_NAMES)[number];
58
+
59
+ export function isSubagent(name: string): name is SubagentName {
60
+ return (SUBAGENT_NAMES as readonly string[]).includes(name);
61
+ }
62
+
63
+ // Agent Factories
64
+
65
+ const SUBAGENT_FACTORIES = {
66
+ explorer: createExplorerAgent,
67
+ librarian: createLibrarianAgent,
68
+ oracle: createOracleAgent,
69
+ designer: createDesignerAgent,
70
+ fixer: createFixerAgent,
71
+ steward: createStewardAgent,
72
+ interpreter: createInterpreterAgent,
73
+ } satisfies Record<SubagentName, AgentFactory>;
74
+
75
+ // Public API
76
+
77
+ /**
78
+ * Create all agent definitions with optional configuration overrides.
79
+ * Instantiates the orchestrator and all subagents, applying user config and defaults.
80
+ *
81
+ * @param config - Optional plugin configuration with agent overrides
82
+ * @returns Array of agent definitions (orchestrator first, then subagents)
83
+ */
84
+ export async function createAgents(
85
+ config?: PluginConfig,
86
+ ): Promise<AgentDefinition[]> {
87
+ // 1. Gather all sub-agent definitions with custom prompts
88
+ const protoSubAgents = (
89
+ Object.entries(SUBAGENT_FACTORIES) as [SubagentName, AgentFactory][]
90
+ ).map(([name, factory]) => {
91
+ const customPrompts = loadAgentPrompt(name, config?.preset);
92
+ return factory(
93
+ DEFAULT_MODELS[name] as string,
94
+ customPrompts.prompt,
95
+ customPrompts.appendPrompt,
96
+ );
97
+ });
98
+
99
+ // 2. Apply overrides and default permissions to built-in subagents
100
+ const builtInSubAgents = await Promise.all(
101
+ protoSubAgents.map(async (agent) => {
102
+ const override = getAgentOverride(config, agent.name);
103
+ if (override) {
104
+ applyOverrides(agent, override);
105
+ }
106
+ // Pass both skills AND mcps from the override
107
+ await applyDefaultPermissions(agent, override?.skills, override?.mcps);
108
+ return agent;
109
+ }),
110
+ );
111
+
112
+ const allSubAgents = [...builtInSubAgents];
113
+
114
+ // 3. Create Orchestrator (with its own overrides and custom prompts)
115
+ // Model is resolved from DEFAULT_MODELS.orchestrator (or user override).
116
+ // TUI /model selector overrides at runtime regardless.
117
+ const orchestratorOverride = getAgentOverride(config, 'orchestrator');
118
+ const orchestratorModel =
119
+ orchestratorOverride?.model ?? DEFAULT_MODELS.orchestrator;
120
+ const orchestratorPrompts = loadAgentPrompt('orchestrator', config?.preset);
121
+
122
+ // 3a. Resolve oracle model names for prompt injection
123
+ // (avoids hardcoding model IDs in the prompt text)
124
+ const oracleOverride = getAgentOverride(config, 'oracle');
125
+ const oracleDefaultModel =
126
+ typeof oracleOverride?.model === 'string'
127
+ ? oracleOverride.model
128
+ : DEFAULT_MODELS.oracle;
129
+ const oracleOptions = oracleOverride?.options as
130
+ | Record<string, unknown>
131
+ | undefined;
132
+ const oracleSmartModel =
133
+ typeof oracleOptions?.smart === 'string' ? oracleOptions.smart : '';
134
+ const oracleSmartModelOrFallback =
135
+ oracleSmartModel.length > 0 ? oracleSmartModel : (oracleDefaultModel ?? '');
136
+
137
+ const orchestrator = createOrchestratorAgent(
138
+ orchestratorModel,
139
+ orchestratorPrompts.prompt,
140
+ orchestratorPrompts.appendPrompt,
141
+ oracleDefaultModel as string | undefined,
142
+ oracleSmartModelOrFallback,
143
+ );
144
+ if (orchestratorOverride) {
145
+ applyOverrides(orchestrator, orchestratorOverride);
146
+ }
147
+ await applyDefaultPermissions(
148
+ orchestrator,
149
+ orchestratorOverride?.skills,
150
+ orchestratorOverride?.mcps,
151
+ );
152
+
153
+ // Collect all display names from orchestrator and all subagents
154
+ const displayNameMap = new Map<string, string>();
155
+ if (orchestrator.displayName) {
156
+ displayNameMap.set('orchestrator', orchestrator.displayName);
157
+ }
158
+ for (const agent of allSubAgents) {
159
+ if (agent.displayName) {
160
+ displayNameMap.set(agent.name, agent.displayName);
161
+ }
162
+ }
163
+
164
+ // Validate display names
165
+ const usedDisplayNames = new Set<string>();
166
+ for (const [, displayName] of displayNameMap) {
167
+ const normalizedDisplayName = normalizeDisplayName(displayName);
168
+ if (usedDisplayNames.has(normalizedDisplayName)) {
169
+ throw new Error(
170
+ `Duplicate displayName '${normalizedDisplayName}' assigned to multiple agents`,
171
+ );
172
+ }
173
+ usedDisplayNames.add(normalizedDisplayName);
174
+ }
175
+ for (const displayName of usedDisplayNames) {
176
+ if ((ALL_AGENT_NAMES as readonly string[]).includes(displayName)) {
177
+ throw new Error(
178
+ `displayName '${displayName}' conflicts with an agent name`,
179
+ );
180
+ }
181
+ }
182
+
183
+ // Inject display names into orchestrator prompt (complete map)
184
+ injectDisplayNames(orchestrator, displayNameMap);
185
+
186
+ return [orchestrator, ...allSubAgents];
187
+ }
188
+
189
+ /**
190
+ * Get agent configurations formatted for the OpenCode SDK.
191
+ * Converts agent definitions to SDK config format and applies classification metadata.
192
+ *
193
+ * @param config - Optional plugin configuration with agent overrides
194
+ * @returns Record mapping agent names to their SDK configurations
195
+ */
196
+ export async function getAgentConfigs(
197
+ config?: PluginConfig,
198
+ ): Promise<Record<string, SDKAgentConfig>> {
199
+ const agents = await createAgents(config);
200
+
201
+ const applyClassification = (
202
+ name: string,
203
+ sdkConfig: SDKAgentConfig & {
204
+ displayName?: string;
205
+ hidden?: boolean;
206
+ },
207
+ ): void => {
208
+ if (isSubagent(name)) {
209
+ sdkConfig.mode = 'subagent';
210
+ } else if (name === 'orchestrator') {
211
+ sdkConfig.mode = 'primary';
212
+ } else {
213
+ sdkConfig.mode = 'subagent';
214
+ }
215
+ };
216
+
217
+ const entries: Array<[string, SDKAgentConfig]> = [];
218
+
219
+ for (const a of agents) {
220
+ const sdkConfig: SDKAgentConfig & {
221
+ displayName?: string;
222
+ hidden?: boolean;
223
+ } = {
224
+ ...a.config,
225
+ description: a.description,
226
+ };
227
+
228
+ if (a.displayName) {
229
+ sdkConfig.displayName = a.displayName;
230
+ }
231
+
232
+ applyClassification(a.name, sdkConfig);
233
+
234
+ const normalizedDisplayName = a.displayName
235
+ ? normalizeDisplayName(a.displayName)
236
+ : undefined;
237
+
238
+ if (normalizedDisplayName) {
239
+ entries.push([normalizedDisplayName, sdkConfig]);
240
+ entries.push([a.name, { ...sdkConfig, hidden: true }]);
241
+ continue;
242
+ }
243
+
244
+ entries.push([a.name, sdkConfig]);
245
+ }
246
+
247
+ return Object.fromEntries(entries);
248
+ }
@@ -0,0 +1,136 @@
1
+ import type { AgentDefinition } from './orchestrator';
2
+ import { resolvePrompt } from './orchestrator';
3
+ import {
4
+ formatBlockedOutputBlock,
5
+ INTERPRETER_VARIANT_SCOPE_LINES,
6
+ NEEDS_USER_OUTPUT_FORMAT_BLOCK,
7
+ REPO_RULES_PRECEDENCE_BLOCK,
8
+ SELF_REVIEW_BLOCK,
9
+ SUBAGENT_NEEDS_USER_FORMAT,
10
+ USER_CHOICE_POLICY_BLOCK,
11
+ } from './prompt-blocks';
12
+
13
+ const INTERPRETER_CRITICAL_INVARIANTS = `<critical_invariants>
14
+ Violating any = failure mode.
15
+ 1) ALWAYS describe what IS visible - never skip reporting on partial/degraded images.
16
+ 2) NEVER assume UI redesign intent - neutral description first.
17
+ 3) NEVER substitute for @designer - describe pixels, not design opinions.
18
+ 4) NEVER modify files or delegate to subagents.
19
+ </critical_invariants>`;
20
+
21
+ const INTERPRETER_PROMPT = `<role>
22
+ You are Interpreter, a visual-context specialist. Your job is to translate
23
+ screenshots, diagrams, error captures, and UI images into structured,
24
+ actionable intelligence. You describe WHAT is visible - never HOW to fix it
25
+ (@fixer), never HOW to redesign it (@designer), never WHY it broke (@oracle).
26
+ Your output is the foundation other agents build on.
27
+ </role>
28
+
29
+ ${INTERPRETER_CRITICAL_INVARIANTS}
30
+
31
+ ${REPO_RULES_PRECEDENCE_BLOCK}
32
+
33
+ <capabilities>
34
+ - Describe visible layout, components, errors, and diagrams
35
+ - Transcribe readable on-screen text, labels, and error codes
36
+ - Infer likely user intent from visual context
37
+ - Suggest routing for the orchestrator (@explorer / @oracle / @designer / @fixer)
38
+ - Handle partially corrupted or blurred images with reduced confidence
39
+ </capabilities>
40
+
41
+ <tool_routing>
42
+ - Vision-only specialist - no tool calls required in most sessions.
43
+ - If host-injected context is insufficient AND image-reading tool available → use it.
44
+ - NEVER use search/glob/read tools - belongs to @explorer.
45
+ - If vision-capable tooling unavailable → report in <blocked>.
46
+ </tool_routing>
47
+
48
+ <workflow>
49
+ 1) Describe all visible elements: layout, components, error messages, diagrams.
50
+ 2) Transcribe readable text (labels, error codes, stack traces, form values).
51
+ 3) Infer the user's most likely intent from the visual context.
52
+ 4) Suggest the appropriate next agent(s) with one-line rationale each.
53
+ 5) Rate your confidence and note any regions that were unreadable or ambiguous.
54
+ </workflow>
55
+
56
+ ${USER_CHOICE_POLICY_BLOCK}
57
+
58
+ <constraints>
59
+ - Default analysis-only; code changes belong in @fixer. Never patch files yourself — invariant #4 prohibits it regardless of orchestrator instruction.
60
+ - NEVER assume UI redesign unless the user asked for design polish; neutral description first.
61
+ - Separate confirmed visually vs inferred claims.
62
+ - Host-injected "does not support image input" → vision-incapable model. Report that; do not claim "no image attached."
63
+ - Partial/corrupted images: describe what IS visible, label unreadable regions, lower <confidence>. NEVER skip reporting.
64
+ </constraints>
65
+
66
+ <variant_policy>
67
+ ${INTERPRETER_VARIANT_SCOPE_LINES.map((l) => `- ${l}`).join('\n')}
68
+ - max: not supported - interpreter provides context that the orchestrator then routes to @oracle for in-depth analysis. The expected flow is @interpreter first (describe), then @oracle (analyze).
69
+ </variant_policy>
70
+
71
+ ${SUBAGENT_NEEDS_USER_FORMAT}
72
+
73
+ ${SELF_REVIEW_BLOCK}
74
+
75
+ <output_format>
76
+ <visible>
77
+ What the image shows (layout, components, errors, diagrams).
78
+ </visible>
79
+ <text_detected>
80
+ Bullets of readable strings (approximate if partially blurred).
81
+ </text_detected>
82
+ <intent>
83
+ Likely user goal in one short paragraph.
84
+ </intent>
85
+ <routing_hint>
86
+ Suggested next agent(s) with one-line rationale each.
87
+ </routing_hint>
88
+ <confidence>
89
+ [high/medium/low] and why.
90
+ </confidence>
91
+ ${formatBlockedOutputBlock('image analysis cannot be completed due to missing image data or tools')}
92
+ ${NEEDS_USER_OUTPUT_FORMAT_BLOCK}
93
+
94
+ <good_example>
95
+ <needs_user>
96
+ <reason>Screenshot shows both form UI and API response-unclear if task is frontend or backend.</reason>
97
+ <questions>[{"question": "What is the primary focus of this task?", "header": "Task focus", "options": [{"label": "Frontend form", "description": "Build/redesign the UI form shown-route to @designer then @fixer"}, {"label": "Backend API", "description": "Implement/fix the API endpoint returning this data-route to @fixer"}]}]</questions>
98
+ </needs_user>
99
+ </good_example>
100
+
101
+ <good_example>
102
+ User: [screenshot of error]
103
+ Interpreter: Describes visible layout, transcribes error code "500 Internal Server Error",
104
+ infers intent (debugging), suggests @oracle for root cause analysis.
105
+ Returns: <visible> + <text_detected> + <intent> + <routing_hint> + <confidence: high>.
106
+ </good_example>
107
+
108
+ <bad_example>
109
+ User: [screenshot of error]
110
+ Interpreter: Claims "no image attached" when model is vision-incapable.
111
+ Missing: explicit "vision-incapable model" report.
112
+ </bad_example>
113
+ </output_format>`;
114
+
115
+ export function createInterpreterAgent(
116
+ model: string,
117
+ customPrompt?: string,
118
+ customAppendPrompt?: string,
119
+ ): AgentDefinition {
120
+ const prompt = resolvePrompt(
121
+ INTERPRETER_PROMPT,
122
+ customPrompt,
123
+ customAppendPrompt,
124
+ );
125
+
126
+ return {
127
+ name: 'interpreter',
128
+ description:
129
+ 'Screenshot and image understanding (errors, diagrams, repro captures). Routes context to other specialists; not a substitute for @designer UX reviews.',
130
+ config: {
131
+ model,
132
+ temperature: 0.15,
133
+ prompt,
134
+ },
135
+ };
136
+ }
@@ -0,0 +1,80 @@
1
+ import { describe, expect, test } from 'bun:test';
2
+ import { createLibrarianAgent } from './librarian';
3
+
4
+ describe('createLibrarianAgent', () => {
5
+ test('creates agent with correct name', () => {
6
+ const agent = createLibrarianAgent('test/librarian-model');
7
+ expect(agent.name).toBe('librarian');
8
+ });
9
+
10
+ test('sets the provided model', () => {
11
+ const agent = createLibrarianAgent('test/librarian-model');
12
+ expect(agent.config.model).toBe('test/librarian-model');
13
+ });
14
+
15
+ test('prompt contains expected sections', () => {
16
+ const agent = createLibrarianAgent('test/librarian-model');
17
+ const prompt = agent.config.prompt ?? '';
18
+ expect(prompt).toContain('<role>');
19
+ expect(prompt).toContain('<tool_and_mcp_routing>');
20
+ expect(prompt).toContain('<workflow>');
21
+ expect(prompt).toContain('<conflict_resolution>');
22
+ expect(prompt).toContain('Competing libraries');
23
+ expect(prompt).toContain('<variant_policy>');
24
+ expect(prompt).toContain('<constraints>');
25
+ expect(prompt).toContain('<output_format>');
26
+ });
27
+
28
+ test('custom prompt overrides the base prompt', () => {
29
+ const agent = createLibrarianAgent(
30
+ 'test/librarian-model',
31
+ 'Custom librarian prompt',
32
+ );
33
+ expect(agent.config.prompt).toBe('Custom librarian prompt');
34
+ });
35
+
36
+ test('custom append prompt is appended to base', () => {
37
+ const agent = createLibrarianAgent(
38
+ 'test/librarian-model',
39
+ undefined,
40
+ 'Extra instructions',
41
+ );
42
+ const prompt = agent.config.prompt ?? '';
43
+ expect(prompt).toContain('Extra instructions');
44
+ expect(prompt).toContain('<role>');
45
+ });
46
+
47
+ test('has description', () => {
48
+ const agent = createLibrarianAgent('test/librarian-model');
49
+ expect(agent.description).toBeTruthy();
50
+ expect(agent.description?.length).toBeGreaterThan(10);
51
+ });
52
+
53
+ test('prompt contains all required sections (complete check)', () => {
54
+ const agent = createLibrarianAgent('test/librarian-model');
55
+ const prompt = agent.config.prompt ?? '';
56
+ const requiredSections = [
57
+ '<role>',
58
+ '<tool_and_mcp_routing>',
59
+ '<workflow>',
60
+ '<conflict_resolution>',
61
+ '<variant_policy>',
62
+ '<constraints>',
63
+ '<output_format>',
64
+ '<answer>',
65
+ '<sources>',
66
+ '<notes>',
67
+ '<blocked>',
68
+ ];
69
+ for (const section of requiredSections) {
70
+ expect(prompt).toContain(section);
71
+ }
72
+ });
73
+
74
+ test('prompt does not contain resolver boilerplate', () => {
75
+ const agent = createLibrarianAgent('test/librarian-model');
76
+ const prompt = agent.config.prompt ?? '';
77
+ expect(prompt).not.toContain('if (customPrompt)');
78
+ expect(prompt).not.toContain('else if (customAppendPrompt)');
79
+ });
80
+ });
@@ -0,0 +1,145 @@
1
+ import type { AgentDefinition } from './orchestrator';
2
+ import { resolvePrompt } from './orchestrator';
3
+ import {
4
+ formatBlockedOutputBlock,
5
+ LIBRARIAN_VARIANT_SCOPE_LINES,
6
+ NEEDS_USER_OUTPUT_FORMAT_BLOCK,
7
+ REPO_RULES_PRECEDENCE_BLOCK,
8
+ SELF_REVIEW_BLOCK,
9
+ SUBAGENT_NEEDS_USER_FORMAT,
10
+ USER_CHOICE_POLICY_BLOCK,
11
+ } from './prompt-blocks';
12
+
13
+ const LIBRARIAN_CRITICAL_INVARIANTS = `<critical_invariants>
14
+ Violating any = failure mode.
15
+ 1) NEVER guess APIs - cite sources only.
16
+ 2) GitHub URLs → github MCP only, no substitutions.
17
+ 3) ALWAYS label versions when sources span multiple releases.
18
+ 4) NEVER modify files or delegate to subagents.
19
+ </critical_invariants>`;
20
+
21
+ const LIBRARIAN_PROMPT = `<role>
22
+ You are Librarian, a documentation and external research specialist.
23
+ </role>
24
+
25
+ ${LIBRARIAN_CRITICAL_INVARIANTS}
26
+
27
+ ${REPO_RULES_PRECEDENCE_BLOCK}
28
+
29
+ <capabilities>
30
+ - External API and library documentation lookup
31
+ - Version-specific behavior and changelog analysis
32
+ - Official examples and best practices from authoritative sources
33
+ - GitHub repository exploration (issues, PRs, releases)
34
+ - Conflict resolution across multiple documentation sources
35
+ </capabilities>
36
+
37
+ <workflow>
38
+ 1) GitHub first: requests with a GitHub URL or explicit repo target → github MCP immediately (all asset types-not only issues/PRs/releases).
39
+ 2) Gather official sources in this priority order:
40
+ a) GitHub repository (issues, PRs, releases, source code)
41
+ b) Context7 library documentation
42
+ c) Implementation examples from GitHub search
43
+ d) Websearch for recent blog posts or announcements (if version recency matters)
44
+ 3) Corroborate with implementation examples when helpful.
45
+ 4) Add websearch-driven recency when step 1 does not apply and freshness matters.
46
+ 5) Report concise findings with citations naming the tool actually used when non-obvious.
47
+ </workflow>
48
+
49
+ ${USER_CHOICE_POLICY_BLOCK}
50
+
51
+ <constraints>
52
+ - NEVER guess APIs or version behavior.
53
+ - NEVER omit source citations.
54
+ - NEVER mix versions without explicitly labeling them.
55
+ - NEVER treat forum chatter as canonical when official docs or repository metadata exists.
56
+ - NEVER modify files or delegate.
57
+ - GitHub URLs → github MCP only. If github MCP fails → <blocked> with URL + error. NEVER substitute webfetch/websearch for GitHub-hosted content.
58
+ - If github, context7, or websearch tools are missing from your callable tools, include that in \`<blocked>\` with what would be needed-do not compensate with guesses.
59
+ - Stay evidence-focused.
60
+ </constraints>
61
+
62
+ <tool_and_mcp_routing>
63
+ | Need | Tool/MCP | Usage |
64
+ |---|---|---|
65
+ | any GitHub URL, repository content, or GitHub-hosted resource | github | ALWAYS use GitHub MCP first for ANY GitHub URL (before websearch/other fetch tools) |
66
+ | official API behavior and version details | context7 | First choice for library docs when URL is not the primary source |
67
+ | real-world code examples from repos | github | Implementation patterns from repository source |
68
+ | recent ecosystem changes or release notes | websearch MCP | When no GitHub URL applies; use the configured websearch tools |
69
+ | upstream GitHub issues, PRs, and release metadata | github | Repository-native source of truth |
70
+ | arbitrary non-GitHub web URL | websearch MCP or webfetch | General web content; never substitute for github MCP on GitHub URLs |
71
+ </tool_and_mcp_routing>
72
+
73
+ <conflict_resolution>
74
+ - When sources disagree, prefer (in order): official changelog/release notes → official docs → repository source code → high-signal blog/forum posts.
75
+ - Always label the version each source pertains to.
76
+ - If sources span multiple major versions, report each version's behavior separately rather than averaging.
77
+ - If context7 returns nothing, fall back to GitHub repository source and tools from the websearch MCP - never invent.
78
+ - Competing libraries/versions when user did not specify → <needs_user>. Each option \`description\` must cover tradeoffs from docs (maintenance, bundle size, API style, ecosystem fit).
79
+ - NEVER crown a winner when the choice depends on user preference or constraints unknown to you.
80
+ </conflict_resolution>
81
+
82
+ <variant_policy>
83
+ ${LIBRARIAN_VARIANT_SCOPE_LINES.map((l) => `- ${l}`).join('\n')}
84
+ </variant_policy>
85
+
86
+ ${SUBAGENT_NEEDS_USER_FORMAT}
87
+
88
+ ${SELF_REVIEW_BLOCK}
89
+
90
+ <output_format>
91
+ <answer>
92
+ Short, evidence-based recommendation.
93
+ </answer>
94
+ <sources>
95
+ - <source>official-doc-url-or-id</source>
96
+ - <source>repo-url-or-path</source>
97
+ </sources>
98
+ <notes>
99
+ - version caveats or uncertainty, if any
100
+ </notes>
101
+ ${formatBlockedOutputBlock('no sources could be found or the query requires unavailable APIs')}
102
+ ${NEEDS_USER_OUTPUT_FORMAT_BLOCK}
103
+
104
+ <good_example>
105
+ <needs_user>
106
+ <reason>Library has multiple major versions with breaking changes.</reason>
107
+ <questions>[{"question": "Which version should I reference for this API?", "header": "Library version", "options": [{"label": "v14.x (stable)", "description": "Current LTS, most docs and examples target this"}, {"label": "v15.x (canary)", "description": "Latest features, may have breaking changes, fewer examples"}]}]</questions>
108
+ </needs_user>
109
+ </good_example>
110
+
111
+ <good_example>
112
+ User: "How to use Context7 MCP for Next.js docs?"
113
+ Librarian: Queries context7, cross-references GitHub repo, cites v14.2.3 docs.
114
+ Returns: <answer> with versioned recommendation + <sources> with URLs.
115
+ </good_example>
116
+
117
+ <bad_example>
118
+ User: "How to use Context7 MCP for Next.js docs?"
119
+ Librarian: Returns generic Next.js advice without version labels or source URLs.
120
+ Missing: version context, source URLs, conflict resolution.
121
+ </bad_example>
122
+ </output_format>`;
123
+
124
+ export function createLibrarianAgent(
125
+ model: string,
126
+ customPrompt?: string,
127
+ customAppendPrompt?: string,
128
+ ): AgentDefinition {
129
+ const prompt = resolvePrompt(
130
+ LIBRARIAN_PROMPT,
131
+ customPrompt,
132
+ customAppendPrompt,
133
+ );
134
+
135
+ return {
136
+ name: 'librarian',
137
+ description:
138
+ 'External documentation and library research. Use for official docs lookup, GitHub examples, and understanding library internals.',
139
+ config: {
140
+ model,
141
+ temperature: 0.1,
142
+ prompt,
143
+ },
144
+ };
145
+ }
@@ -0,0 +1,89 @@
1
+ import { describe, expect, test } from 'bun:test';
2
+ import { createOracleAgent } from './oracle';
3
+
4
+ describe('createOracleAgent', () => {
5
+ test('creates agent with correct name', () => {
6
+ const agent = createOracleAgent('test/oracle-model');
7
+ expect(agent.name).toBe('oracle');
8
+ });
9
+
10
+ test('sets the provided model', () => {
11
+ const agent = createOracleAgent('test/oracle-model');
12
+ expect(agent.config.model).toBe('test/oracle-model');
13
+ });
14
+
15
+ test('prompt contains expected sections', () => {
16
+ const agent = createOracleAgent('test/oracle-model');
17
+ const prompt = agent.config.prompt ?? '';
18
+ expect(prompt).toContain('<role>');
19
+ expect(prompt).toContain('<capabilities>');
20
+ expect(prompt).toContain('<tool_routing>');
21
+ expect(prompt).toContain('<constraints>');
22
+ expect(prompt).toContain('<user_choice_policy>');
23
+ expect(prompt).toContain('<variant_policy>');
24
+ expect(prompt).toContain('<output_format>');
25
+ });
26
+
27
+ test('has temperature 0.15', () => {
28
+ const agent = createOracleAgent('test/oracle-model');
29
+ expect(agent.config.temperature).toBe(0.15);
30
+ });
31
+
32
+ test('custom prompt overrides the base prompt', () => {
33
+ const agent = createOracleAgent(
34
+ 'test/oracle-model',
35
+ 'Custom oracle prompt',
36
+ );
37
+ expect(agent.config.prompt).toBe('Custom oracle prompt');
38
+ });
39
+
40
+ test('custom append prompt is appended to base', () => {
41
+ const agent = createOracleAgent(
42
+ 'test/oracle-model',
43
+ undefined,
44
+ 'Extra instructions',
45
+ );
46
+ const prompt = agent.config.prompt ?? '';
47
+ expect(prompt).toContain('Extra instructions');
48
+ expect(prompt).toContain('<role>');
49
+ });
50
+
51
+ test('has description', () => {
52
+ const agent = createOracleAgent('test/oracle-model');
53
+ expect(agent.description).toBeTruthy();
54
+ expect(agent.description?.length).toBeGreaterThan(10);
55
+ });
56
+
57
+ test('prompt contains all required sections (complete check)', () => {
58
+ const agent = createOracleAgent('test/oracle-model');
59
+ const prompt = agent.config.prompt ?? '';
60
+ const requiredSections = [
61
+ '<role>',
62
+ '<capabilities>',
63
+ '<tool_routing>',
64
+ '<constraints>',
65
+ '<user_choice_policy>',
66
+ '<variant_policy>',
67
+ '<output_format>',
68
+ '<diagnosis>',
69
+ '<recommendation>',
70
+ '<tradeoffs>',
71
+ '<risks>',
72
+ '<confidence>',
73
+ '<action_items>',
74
+ '<blocked>',
75
+ '<good_example>',
76
+ '<bad_example>',
77
+ ];
78
+ for (const section of requiredSections) {
79
+ expect(prompt).toContain(section);
80
+ }
81
+ });
82
+
83
+ test('prompt does not contain resolver boilerplate', () => {
84
+ const agent = createOracleAgent('test/oracle-model');
85
+ const prompt = agent.config.prompt ?? '';
86
+ expect(prompt).not.toContain('if (customPrompt)');
87
+ expect(prompt).not.toContain('else if (customAppendPrompt)');
88
+ });
89
+ });