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
package/package.json ADDED
@@ -0,0 +1,103 @@
1
+ {
2
+ "name": "opencode-dux",
3
+ "version": "1.0.0",
4
+ "description": "Agent orchestration, management, and operations plugin for OpenCode",
5
+ "main": "index.ts",
6
+ "types": "src/index.ts",
7
+ "exports": {
8
+ ".": {
9
+ "import": "./index.ts",
10
+ "types": "./src/index.ts"
11
+ },
12
+ "./tui": {
13
+ "import": "./src/tui.ts",
14
+ "types": "./dist/tui.d.ts"
15
+ }
16
+ },
17
+ "bin": {
18
+ "opencode-dux": "./dist/cli/index.js"
19
+ },
20
+ "type": "module",
21
+ "license": "MIT",
22
+ "keywords": [
23
+ "opencode",
24
+ "opencode-plugin",
25
+ "ai",
26
+ "agents",
27
+ "orchestration",
28
+ "llm",
29
+ "claude",
30
+ "gpt",
31
+ "gemini"
32
+ ],
33
+ "repository": {
34
+ "type": "git",
35
+ "url": "https://github.com/bakhtiar-personal-work/opencode-dux"
36
+ },
37
+ "bugs": {
38
+ "url": "https://github.com/bakhtiar-personal-work/opencode-dux/issues"
39
+ },
40
+ "homepage": "https://github.com/bakhtiar-personal-work/opencode-dux#readme",
41
+ "files": [
42
+ "dist",
43
+ "src",
44
+ "index.ts",
45
+ "opencode-dux.schema.json",
46
+ "README.md",
47
+ "LICENSE"
48
+ ],
49
+ "scripts": {
50
+ "build:plugin": "bun build src/index.ts src/tui.ts --outdir dist --target node --format esm --external @ast-grep/napi --external @opencode-ai/plugin --external @opencode-ai/sdk --external @opentui/core --external @opentui/solid --external jsdom --external zod --external solid-js",
51
+ "build:cli": "bun build src/cli/index.ts --outdir dist/cli --target node --format esm --external @ast-grep/napi --external @opencode-ai/plugin --external @opencode-ai/sdk --external jsdom --external zod",
52
+ "clean": "bun -e \"import{rmSync}from'node:fs';rmSync('dist',{recursive:true,force:true});console.log('Cleaned dist/')\"",
53
+ "build": "bun run clean && bun run build:plugin && bun run build:cli && tsc --emitDeclarationOnly && bun run generate-schema",
54
+ "contributors:add": "all-contributors add",
55
+ "contributors:check": "all-contributors check",
56
+ "contributors:generate": "all-contributors generate",
57
+ "generate-schema": "bun run scripts/generate-schema.ts",
58
+ "verify:release": "bun run scripts/verify-release-artifact.ts",
59
+ "verify:host-smoke": "bun run scripts/verify-opencode-host-smoke.ts",
60
+ "typecheck": "tsc --noEmit",
61
+ "test": "bun test",
62
+ "lint": "biome lint .",
63
+ "format": "biome format . --write",
64
+ "check": "biome check --write .",
65
+ "check:ci": "biome check .",
66
+ "dev": "bun run build && opencode",
67
+ "prepublishOnly": "bun run build",
68
+ "release:patch": "npm version patch && git push --follow-tags && npm publish",
69
+ "release:minor": "npm version minor && git push --follow-tags && npm publish",
70
+ "release:major": "npm version major && git push --follow-tags && npm publish"
71
+ },
72
+ "dependencies": {
73
+ "@ast-grep/cli": "^0.42.1",
74
+ "@modelcontextprotocol/sdk": "^1.29.0",
75
+ "@mozilla/readability": "^0.6.0",
76
+ "@opencode-ai/plugin": "^1.3.17",
77
+ "@opencode-ai/sdk": "^1.3.17",
78
+ "jsdom": "^26.1.0",
79
+ "lru-cache": "^11.3.3",
80
+ "turndown": "^7.2.4"
81
+ },
82
+ "optionalDependencies": {
83
+ "@opentui/solid": "^0.1.97"
84
+ },
85
+ "devDependencies": {
86
+ "@biomejs/biome": "2.4.11",
87
+ "@types/bun": "latest",
88
+ "@types/jsdom": "^21.1.7",
89
+ "@types/node": "^24.12.4",
90
+ "@types/turndown": "^5.0.6",
91
+ "all-contributors-cli": "^6.26.1",
92
+ "bun-types": "1.3.12",
93
+ "typescript": "^5.9.3",
94
+ "zod": "^4.3.6"
95
+ },
96
+ "peerDependencies": {
97
+ "zod": "^4.0.0"
98
+ },
99
+ "trustedDependencies": [
100
+ "@ast-grep/cli"
101
+ ],
102
+ "private": false
103
+ }
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Centralized agent descriptions used by the orchestrator prompt,
3
+ * SDK registration, and sidebar UI labels.
4
+ */
5
+
6
+ // XML-formatted agent descriptions for the orchestrator delegation prompt
7
+ export const AGENT_DESCRIPTIONS: Record<string, string> = {
8
+ explorer: `<agent name="@explorer">
9
+ - Role: codebase search specialist
10
+ - Delegate when: locate files, usages, symbols, tests, config links
11
+ - Do not use when: exact file is already known and must be read in full
12
+ </agent>`,
13
+ librarian: `<agent name="@librarian">
14
+ - Role: external docs and API reference specialist
15
+ - Delegate when: library behavior, version details, official examples, upstream GitHub issues/PRs/releases
16
+ - Do not use when: pure language fundamentals or local code discovery
17
+ </agent>`,
18
+ oracle: `<agent name="@oracle">
19
+ - Role: technical analysis and code review; uses orchestrator \`model\` + \`variant\` matrix
20
+ - Delegate when: debugging, architecture, tradeoffs, risk, any review depth
21
+ - Do not use when: pure local discovery (@explorer) or docs-only (@librarian)
22
+ </agent>`,
23
+ designer: `<agent name="@designer">
24
+ - Role: UI/UX specialist for ALL user-facing UI — new pages, existing component changes, layout updates, styling, visual polish, a11y
25
+ - Delegate when: ANY change to TSX/JSX files, components, pages, layouts, styling, or user-facing visual elements — BEFORE @oracle or @fixer
26
+ - Do not use when: backend-only, non-visual work, or pure logic changes (hooks/utils) that don't affect rendering
27
+ </agent>`,
28
+ fixer: `<agent name="@fixer">
29
+ - Role: implementation specialist
30
+ - Delegate when: edits, tests, scoped commands-after gates in <first_gate> when applicable
31
+ - Do not use when: strategy/conventions/UI design still unresolved-delegate upward first
32
+ </agent>`,
33
+ steward: `<agent name="@steward">
34
+ - Role: rules citation from steward_paths — verbatim excerpts only; does NOT analyze, evaluate, or compare rules
35
+ - Delegate when: repo conventions needed before oracle/fixer (see <first_gate> 1)
36
+ - Do not use when: pure symbol search (@explorer); rules analysis (@oracle).
37
+ </agent>`,
38
+ interpreter: `<agent name="@interpreter">
39
+ - Role: screenshot / attached-image analyst
40
+ - Delegate when: user message has images and task is not redesign-only
41
+ - Do not use when: redesign-only-use @designer; text-only prompts
42
+ </agent>`,
43
+ };
44
+
45
+ // Compact sidebar labels for the TUI agent list
46
+ export const AGENT_SIDEBAR_DESCRIPTIONS: Record<string, string> = {
47
+ orchestrator: 'Orchestrates',
48
+ explorer: 'File Search',
49
+ librarian: 'Doc Search',
50
+ oracle: 'Architecture',
51
+ designer: 'Design',
52
+ fixer: 'Implement',
53
+ steward: 'Repo rules',
54
+ interpreter: 'Vision',
55
+ };
@@ -0,0 +1,86 @@
1
+ import { describe, expect, test } from 'bun:test';
2
+ import { createDesignerAgent } from './designer';
3
+
4
+ describe('createDesignerAgent', () => {
5
+ test('creates agent with correct name', () => {
6
+ const agent = createDesignerAgent('test/designer-model');
7
+ expect(agent.name).toBe('designer');
8
+ });
9
+
10
+ test('sets the provided model', () => {
11
+ const agent = createDesignerAgent('test/designer-model');
12
+ expect(agent.config.model).toBe('test/designer-model');
13
+ });
14
+
15
+ test('prompt contains expected sections', () => {
16
+ const agent = createDesignerAgent('test/designer-model');
17
+ const prompt = agent.config.prompt ?? '';
18
+ expect(prompt).toContain('<role>');
19
+ expect(prompt).toContain('<discovery_first>');
20
+ expect(prompt).toContain('<design_principles>');
21
+ expect(prompt).toContain('<vision_and_evidence>');
22
+ expect(prompt).toContain('<user_choice_policy>');
23
+ expect(prompt).toContain('<constraints>');
24
+ expect(prompt).toContain('<variant_policy>');
25
+ expect(prompt).toContain('<output_format>');
26
+ });
27
+
28
+ test('has temperature 0.3', () => {
29
+ const agent = createDesignerAgent('test/designer-model');
30
+ expect(agent.config.temperature).toBe(0.3);
31
+ });
32
+
33
+ test('custom prompt overrides the base prompt', () => {
34
+ const agent = createDesignerAgent(
35
+ 'test/designer-model',
36
+ 'Custom designer prompt',
37
+ );
38
+ expect(agent.config.prompt).toBe('Custom designer prompt');
39
+ });
40
+
41
+ test('custom append prompt is appended to base', () => {
42
+ const agent = createDesignerAgent(
43
+ 'test/designer-model',
44
+ undefined,
45
+ 'Extra instructions',
46
+ );
47
+ const prompt = agent.config.prompt ?? '';
48
+ expect(prompt).toContain('Extra instructions');
49
+ expect(prompt).toContain('<role>');
50
+ });
51
+
52
+ test('has description', () => {
53
+ const agent = createDesignerAgent('test/designer-model');
54
+ expect(agent.description).toBeTruthy();
55
+ expect(agent.description?.length).toBeGreaterThan(10);
56
+ });
57
+
58
+ test('prompt contains all required sections (complete check)', () => {
59
+ const agent = createDesignerAgent('test/designer-model');
60
+ const prompt = agent.config.prompt ?? '';
61
+ const requiredSections = [
62
+ '<role>',
63
+ '<discovery_first>',
64
+ '<design_principles>',
65
+ '<vision_and_evidence>',
66
+ '<user_choice_policy>',
67
+ '<constraints>',
68
+ '<variant_policy>',
69
+ '<output_format>',
70
+ '<design_plan>',
71
+ '<accessibility_check>',
72
+ '<implementation_notes>',
73
+ '<blocked>',
74
+ ];
75
+ for (const section of requiredSections) {
76
+ expect(prompt).toContain(section);
77
+ }
78
+ });
79
+
80
+ test('prompt does not contain resolver boilerplate', () => {
81
+ const agent = createDesignerAgent('test/designer-model');
82
+ const prompt = agent.config.prompt ?? '';
83
+ expect(prompt).not.toContain('if (customPrompt)');
84
+ expect(prompt).not.toContain('else if (customAppendPrompt)');
85
+ });
86
+ });
@@ -0,0 +1,154 @@
1
+ import type { AgentDefinition } from './orchestrator';
2
+ import { resolvePrompt } from './orchestrator';
3
+ import {
4
+ DESIGNER_VARIANT_SCOPE_LINES,
5
+ formatBlockedOutputBlock,
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 DESIGNER_CRITICAL_INVARIANTS = `<critical_invariants>
14
+ Violating any = failure mode.
15
+ 1) DEFAULT: design-review mode — produce plan + \`<implementation_notes>\` for ALL UI work routed to you (new pages, existing component changes, layout, styling, visual elements). Only implement yourself when the task explicitly orders implementation. If the task scope includes any user-facing UI, your review is mandatory — do not defer to @oracle or @fixer.
16
+ 2) NEVER assume a styling system without glob evidence. Undetectable → <blocked>.
17
+ 3) NEVER invent new design tokens when project tokens already fit.
18
+ 4) NEVER modify files or delegate to subagents.
19
+ </critical_invariants>`;
20
+
21
+ const DESIGNER_PROMPT = `<role>
22
+ You are Designer, the sole authority on ALL user-facing UI work. You handle new pages, existing component modifications, layout changes, styling updates, visual polish, and accessibility. No UI change — new or existing — should reach @fixer without your design review. The orchestrator MUST route all UI work to you before @oracle or @fixer.
23
+ </role>
24
+
25
+ ${DESIGNER_CRITICAL_INVARIANTS}
26
+
27
+ ${REPO_RULES_PRECEDENCE_BLOCK}
28
+
29
+ <discovery_first>
30
+ Detect styling system (skip if task prompt spec):
31
+ 1) Glob for styling evidence (in order):
32
+ - \`**/tailwind.config.*\` (Tailwind)
33
+ - \`**/unocss.config.*\` (UnoCSS)
34
+ - \`**/panda.config.*\` (Panda CSS)
35
+ - \`**/*.module.css\` (CSS Modules)
36
+ - \`**/package.json\` then grep for "styled-components", "emotion", "vanilla-extract"
37
+ - \`**/tokens.*\` or \`**/design-tokens/**\` (design token files)
38
+ 2) Read found configs → extract project breakpoints & component library (shadcn/Radix/Headless UI/MUI/Chakra/Mantine/custom)
39
+ 3) NEVER assume Tailwind without evidence; if system undetectable → <blocked>
40
+ </discovery_first>
41
+
42
+ <tool_routing>
43
+ - Detect styling system: glob for config files (\`tailwind.config.*\`, \`unocss.config.*\`, \`panda.config.*\`, etc.) first; use read only on the files actually found.
44
+ - Read component/page sources: prefer targeted reads of the specific component named in the task; use search tools to locate the file if not provided in context.
45
+ - Avoid bulk reads: do not read entire directories; locate the minimal set needed to detect styling idioms and implement the plan.
46
+ - If no styling system can be detected after reasonable glob/search attempts, report in \`<blocked>\`.
47
+ </tool_routing>
48
+
49
+ <design_principles>
50
+ - Maintain cohesive visual language using the project's existing tokens.
51
+ - Prefer strong intentional hierarchy, spacing, and contrast.
52
+ - Use the project's primary styling mechanism first; introduce alternatives only with explicit justification.
53
+ - Verify responsive behavior at the breakpoints the project actually defines (read them from config).
54
+ </design_principles>
55
+
56
+ <vision_and_evidence>
57
+ - With an image (screenshot, mock, error capture): describe layout and visible issues → propose prioritized UX improvements → map them to concrete implementation steps.
58
+ - Without an image: read component/page sources and infer likely UX issues-label inferences distinctly from visually confirmed findings.
59
+ - Direct implementation stays aligned with detected tokens/components; novelty is justified only when the task explicitly pushes new patterns.
60
+ </vision_and_evidence>
61
+
62
+ <constraints>
63
+ - DEFAULT: design-review mode - produce \`<implementation_notes>\` for @fixer. Only implement when task explicitly orders.
64
+ - If user-facing scope ambiguous → <needs_user> (per <user_choice_policy>). If tooling/styling undetectable → <blocked>.
65
+ - Only apply patches yourself when the task prompt explicitly instructs Designer to implement.
66
+ - Respect existing design system tokens and component patterns.
67
+ - Prioritize accessibility and keyboard navigation (WCAG AA contrast minimum).
68
+ - Avoid cosmetic changes that regress usability.
69
+ - Never invent new tokens when an existing one fits.
70
+ - NEVER propose deleting or restructuring code beyond the explicit scope of
71
+ the design request.
72
+ </constraints>
73
+
74
+ ${USER_CHOICE_POLICY_BLOCK}
75
+ <designer_choice_supplement>
76
+ - Layout & pattern forks (e.g. primary action left vs right, toolbar vs footer, modal vs inline, tabs vs stepper, dense vs spacious) when the task does not mandate one: <needs_user>-each option \`description\` states the UX consequence.
77
+ - User-visible copy or tone when multiple wordings change meaning or stakes and the brief is silent: <needs_user>.
78
+ </designer_choice_supplement>
79
+
80
+ <variant_policy>
81
+ ${DESIGNER_VARIANT_SCOPE_LINES.map((l) => `- ${l}`).join('\n')}
82
+ </variant_policy>
83
+
84
+ ${SUBAGENT_NEEDS_USER_FORMAT}
85
+
86
+ ${SELF_REVIEW_BLOCK}
87
+
88
+ <output_format>
89
+ <design_plan>
90
+ - List each proposed change as a concrete action: component name or file, what changes (token, spacing, color, layout, copy), and why (contrast, hierarchy, accessibility, usability)
91
+ - Prioritize by user impact: critical issues first, polish last
92
+ - Separate visual changes from interaction/behavior changes
93
+ </design_plan>
94
+ <accessibility_check>
95
+ - WCAG 2.1 AA minimum
96
+ - contrast
97
+ - focus order
98
+ - semantic labels/roles
99
+ - keyboard interaction
100
+ </accessibility_check>
101
+ <implementation_notes>
102
+ - concrete component or style targets
103
+ - if implementation is needed, include a handoff checklist for @fixer with file targets and acceptance criteria
104
+ </implementation_notes>
105
+ ${formatBlockedOutputBlock('the design system cannot be detected or styling context is missing')}
106
+ ${NEEDS_USER_OUTPUT_FORMAT_BLOCK}
107
+
108
+ Batch every UX pattern choice in one handoff — and include <user_choice_policy> context.
109
+ <good_example>
110
+ <needs_user>
111
+ <reason>Config panel entry point ambiguous: modal or inline?</reason>
112
+ <questions>[{"question": "Should the config panel be a modal overlay or an inline section?", "header": "Config panel style", "options": [{"label": "Modal", "description": "Overlay dialog; better focus, interrupts workflow"}, {"label": "Inline", "description": "Same-page section; non-disruptive, visible alongside content"}]}]</questions>
113
+ </needs_user>
114
+ </good_example>
115
+ <iteration>
116
+ If the orchestrator reports the plan was rejected or needs revision, adjust the plan in a follow-up - do not repeat unchanged sections, only emit deltas.
117
+ </iteration>
118
+
119
+ <good_example>
120
+ User: "Improve the login form UX"
121
+ Designer: Detects Tailwind + shadcn, proposes 3 changes (spacing, contrast, focus order).
122
+ Returns: <design_plan> with file targets + <implementation_notes> for @fixer.
123
+ </good_example>
124
+
125
+ <bad_example>
126
+ User: "Improve the login form UX"
127
+ Designer: Assumes React + Material UI (wrong), proposes changes using non-existent tokens.
128
+ Missing: discovery_first step, project evidence.
129
+ </bad_example>
130
+ </output_format>`;
131
+
132
+ export function createDesignerAgent(
133
+ model: string,
134
+ customPrompt?: string,
135
+ customAppendPrompt?: string,
136
+ ): AgentDefinition {
137
+ const prompt = resolvePrompt(
138
+ DESIGNER_PROMPT,
139
+ customPrompt,
140
+ customAppendPrompt,
141
+ );
142
+
143
+ return {
144
+ name: 'designer',
145
+ description:
146
+ 'UI/UX design, review, and implementation. Use for styling, responsive design, component architecture and visual polish.',
147
+ config: {
148
+ model,
149
+ // 0.3 provides enough variation for creative UI texture choices while staying deterministic enough for consistent design-system application
150
+ temperature: 0.3,
151
+ prompt,
152
+ },
153
+ };
154
+ }
@@ -0,0 +1,186 @@
1
+ import { describe, expect, test } from 'bun:test';
2
+ import type { PluginConfig } from '../config';
3
+ import { createAgents, getAgentConfigs } from './index';
4
+
5
+ describe('displayName', () => {
6
+ test('stores displayName on agent when configured', async () => {
7
+ const config: PluginConfig = {
8
+ agents: {
9
+ explorer: { displayName: 'researcher' },
10
+ },
11
+ };
12
+
13
+ const agents = await createAgents(config);
14
+ const explorer = agents.find((a) => a.name === 'explorer');
15
+ expect(explorer?.displayName).toBe('researcher');
16
+
17
+ const sdkConfigs = await getAgentConfigs(config);
18
+ expect((sdkConfigs.explorer as { displayName?: string }).displayName).toBe(
19
+ 'researcher',
20
+ );
21
+ });
22
+
23
+ test('injects configured displayName into orchestrator prompt mentions', async () => {
24
+ const config: PluginConfig = {
25
+ agents: {
26
+ explorer: { displayName: 'researcher' },
27
+ },
28
+ };
29
+
30
+ const agents = await createAgents(config);
31
+ const orchestrator = agents.find((a) => a.name === 'orchestrator');
32
+ const prompt = orchestrator?.config.prompt ?? '';
33
+
34
+ expect(prompt).toContain('@researcher');
35
+ expect(prompt).not.toMatch(/@explorer\b/);
36
+ });
37
+
38
+ test('normalizes @-prefixed displayName in prompt injection', async () => {
39
+ const config: PluginConfig = {
40
+ agents: {
41
+ explorer: { displayName: '@researcher' },
42
+ },
43
+ };
44
+
45
+ const agents = await createAgents(config);
46
+ const orchestrator = agents.find((a) => a.name === 'orchestrator');
47
+ const prompt = orchestrator?.config.prompt ?? '';
48
+
49
+ expect(prompt).toContain('@researcher');
50
+ expect(prompt).not.toContain('@@researcher');
51
+ expect(prompt).not.toMatch(/@explorer\b/);
52
+ });
53
+
54
+ test('normalizes whitespace-padded displayName in prompt injection', async () => {
55
+ const config: PluginConfig = {
56
+ agents: {
57
+ explorer: { displayName: ' researcher ' },
58
+ },
59
+ };
60
+
61
+ const agents = await createAgents(config);
62
+ const orchestrator = agents.find((a) => a.name === 'orchestrator');
63
+ const prompt = orchestrator?.config.prompt ?? '';
64
+
65
+ expect(prompt).toContain('@researcher');
66
+ expect(prompt).not.toContain('@ researcher ');
67
+ expect(prompt).not.toMatch(/@explorer\b/);
68
+ });
69
+
70
+ test('throws when duplicate displayName is assigned', async () => {
71
+ const config: PluginConfig = {
72
+ agents: {
73
+ explorer: { displayName: 'helper' },
74
+ librarian: { displayName: 'helper' },
75
+ },
76
+ };
77
+
78
+ await expect(createAgents(config)).rejects.toThrow(
79
+ "Duplicate displayName 'helper' assigned to multiple agents",
80
+ );
81
+ });
82
+
83
+ test('throws when normalized duplicate displayName is assigned', async () => {
84
+ const config: PluginConfig = {
85
+ agents: {
86
+ explorer: { displayName: 'advisor' },
87
+ librarian: { displayName: ' @advisor ' },
88
+ },
89
+ };
90
+
91
+ await expect(createAgents(config)).rejects.toThrow(
92
+ "Duplicate displayName 'advisor' assigned to multiple agents",
93
+ );
94
+ });
95
+
96
+ test('throws when displayName conflicts with internal agent name', async () => {
97
+ const config: PluginConfig = {
98
+ agents: {
99
+ explorer: { displayName: 'oracle' },
100
+ },
101
+ };
102
+
103
+ await expect(createAgents(config)).rejects.toThrow(
104
+ "displayName 'oracle' conflicts with an agent name",
105
+ );
106
+ });
107
+
108
+ test('throws when normalized displayName conflicts with internal agent name', async () => {
109
+ const config: PluginConfig = {
110
+ agents: {
111
+ explorer: { displayName: ' @oracle ' },
112
+ },
113
+ };
114
+
115
+ await expect(createAgents(config)).rejects.toThrow(
116
+ "displayName 'oracle' conflicts with an agent name",
117
+ );
118
+ });
119
+
120
+ test('throws when orchestrator displayName conflicts with internal agent name', async () => {
121
+ const config: PluginConfig = {
122
+ agents: {
123
+ orchestrator: { displayName: 'oracle' },
124
+ },
125
+ };
126
+
127
+ await expect(createAgents(config)).rejects.toThrow(
128
+ /displayName.*conflicts with an agent name/,
129
+ );
130
+ });
131
+
132
+ test('resolves legacy alias for explorer displayName override', async () => {
133
+ const config: PluginConfig = {
134
+ agents: {
135
+ explore: { displayName: 'researcher' },
136
+ },
137
+ };
138
+
139
+ const agents = await createAgents(config);
140
+ const explorer = agents.find((a) => a.name === 'explorer');
141
+
142
+ expect(explorer?.displayName).toBe('researcher');
143
+ });
144
+
145
+ test('uses displayName as host-facing registry key with hidden internal alias', async () => {
146
+ const config: PluginConfig = {
147
+ agents: {
148
+ oracle: { displayName: 'advisor' },
149
+ },
150
+ };
151
+
152
+ const sdkConfigs = (await getAgentConfigs(config)) as Record<
153
+ string,
154
+ { hidden?: boolean; mode?: string }
155
+ >;
156
+
157
+ expect(sdkConfigs.advisor).toBeDefined();
158
+ expect(sdkConfigs.advisor.mode).toBe('subagent');
159
+ expect(sdkConfigs.advisor.hidden).toBeUndefined();
160
+
161
+ expect(sdkConfigs.oracle).toBeDefined();
162
+ expect(sdkConfigs.oracle.mode).toBe('subagent');
163
+ expect(sdkConfigs.oracle.hidden).toBe(true);
164
+ });
165
+
166
+ test('uses orchestrator displayName as host-facing key with hidden internal alias', async () => {
167
+ const config: PluginConfig = {
168
+ agents: {
169
+ orchestrator: { displayName: 'engineer' },
170
+ },
171
+ };
172
+
173
+ const sdkConfigs = (await getAgentConfigs(config)) as Record<
174
+ string,
175
+ { hidden?: boolean; mode?: string }
176
+ >;
177
+
178
+ expect(sdkConfigs.engineer).toBeDefined();
179
+ expect(sdkConfigs.engineer.mode).toBe('primary');
180
+ expect(sdkConfigs.engineer.hidden).toBeUndefined();
181
+
182
+ expect(sdkConfigs.orchestrator).toBeDefined();
183
+ expect(sdkConfigs.orchestrator.mode).toBe('primary');
184
+ expect(sdkConfigs.orchestrator.hidden).toBe(true);
185
+ });
186
+ });
@@ -0,0 +1,79 @@
1
+ import { describe, expect, test } from 'bun:test';
2
+ import { createExplorerAgent } from './explorer';
3
+
4
+ describe('createExplorerAgent', () => {
5
+ test('creates agent with correct name', () => {
6
+ const agent = createExplorerAgent('test/explorer-model');
7
+ expect(agent.name).toBe('explorer');
8
+ });
9
+
10
+ test('sets the provided model', () => {
11
+ const agent = createExplorerAgent('test/explorer-model');
12
+ expect(agent.config.model).toBe('test/explorer-model');
13
+ });
14
+
15
+ test('prompt contains expected sections', () => {
16
+ const agent = createExplorerAgent('test/explorer-model');
17
+ const prompt = agent.config.prompt ?? '';
18
+ expect(prompt).toContain('<role>');
19
+ expect(prompt).toContain('<tool_routing>');
20
+ expect(prompt).toContain('<workflow>');
21
+ expect(prompt).toContain('<constraints>');
22
+ expect(prompt).toContain('<user_choice_policy>');
23
+ expect(prompt).toContain('<output_format>');
24
+ expect(prompt).toContain('<variant_policy>');
25
+ });
26
+
27
+ test('custom prompt overrides the base prompt', () => {
28
+ const agent = createExplorerAgent(
29
+ 'test/explorer-model',
30
+ 'Custom explorer prompt',
31
+ );
32
+ expect(agent.config.prompt).toBe('Custom explorer prompt');
33
+ });
34
+
35
+ test('custom append prompt is appended to base', () => {
36
+ const agent = createExplorerAgent(
37
+ 'test/explorer-model',
38
+ undefined,
39
+ 'Extra instructions',
40
+ );
41
+ const prompt = agent.config.prompt ?? '';
42
+ expect(prompt).toContain('Extra instructions');
43
+ expect(prompt).toContain('<role>');
44
+ });
45
+
46
+ test('has description', () => {
47
+ const agent = createExplorerAgent('test/explorer-model');
48
+ expect(agent.description).toBeTruthy();
49
+ expect(agent.description?.length).toBeGreaterThan(10);
50
+ });
51
+
52
+ test('prompt contains all required sections (complete check)', () => {
53
+ const agent = createExplorerAgent('test/explorer-model');
54
+ const prompt = agent.config.prompt ?? '';
55
+ const requiredSections = [
56
+ '<role>',
57
+ '<tool_routing>',
58
+ '<workflow>',
59
+ '<big_repo_strategy>',
60
+ '<constraints>',
61
+ '<user_choice_policy>',
62
+ '<variant_policy>',
63
+ '<stale_codemap>',
64
+ '<output_format>',
65
+ '<results>',
66
+ '<no_results>',
67
+ ];
68
+ for (const section of requiredSections) {
69
+ expect(prompt).toContain(section);
70
+ }
71
+ });
72
+
73
+ test('prompt does not contain resolver boilerplate', () => {
74
+ const agent = createExplorerAgent('test/explorer-model');
75
+ const prompt = agent.config.prompt ?? '';
76
+ expect(prompt).not.toContain('if (customPrompt)');
77
+ expect(prompt).not.toContain('else if (customAppendPrompt)');
78
+ });
79
+ });