synapsexcoder 6.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 (705) hide show
  1. package/.opencode/opencode.jsonc +102 -0
  2. package/README.md +353 -0
  3. package/dist/agents/agent-config-manager.d.ts +58 -0
  4. package/dist/agents/agent-config-manager.d.ts.map +1 -0
  5. package/dist/agents/agent-config-manager.js +313 -0
  6. package/dist/agents/agent-config-manager.js.map +1 -0
  7. package/dist/agents/base-agents.d.ts +352 -0
  8. package/dist/agents/base-agents.d.ts.map +1 -0
  9. package/dist/agents/base-agents.js +3961 -0
  10. package/dist/agents/base-agents.js.map +1 -0
  11. package/dist/agents/gated-subagent.d.ts +126 -0
  12. package/dist/agents/gated-subagent.d.ts.map +1 -0
  13. package/dist/agents/gated-subagent.js +591 -0
  14. package/dist/agents/gated-subagent.js.map +1 -0
  15. package/dist/agents/gated-subagents.d.ts +130 -0
  16. package/dist/agents/gated-subagents.d.ts.map +1 -0
  17. package/dist/agents/gated-subagents.js +2014 -0
  18. package/dist/agents/gated-subagents.js.map +1 -0
  19. package/dist/agents/internal-gatekeeper.d.ts +167 -0
  20. package/dist/agents/internal-gatekeeper.d.ts.map +1 -0
  21. package/dist/agents/internal-gatekeeper.js +1130 -0
  22. package/dist/agents/internal-gatekeeper.js.map +1 -0
  23. package/dist/agents/verification-agent.d.ts +86 -0
  24. package/dist/agents/verification-agent.d.ts.map +1 -0
  25. package/dist/agents/verification-agent.js +211 -0
  26. package/dist/agents/verification-agent.js.map +1 -0
  27. package/dist/analytics/analytics-types.d.ts +113 -0
  28. package/dist/analytics/analytics-types.d.ts.map +1 -0
  29. package/dist/analytics/analytics-types.js +8 -0
  30. package/dist/analytics/analytics-types.js.map +1 -0
  31. package/dist/analytics/dashboard-generator.d.ts +35 -0
  32. package/dist/analytics/dashboard-generator.d.ts.map +1 -0
  33. package/dist/analytics/dashboard-generator.js +365 -0
  34. package/dist/analytics/dashboard-generator.js.map +1 -0
  35. package/dist/analytics/index.d.ts +12 -0
  36. package/dist/analytics/index.d.ts.map +1 -0
  37. package/dist/analytics/index.js +12 -0
  38. package/dist/analytics/index.js.map +1 -0
  39. package/dist/analytics/metrics-aggregator.d.ts +88 -0
  40. package/dist/analytics/metrics-aggregator.d.ts.map +1 -0
  41. package/dist/analytics/metrics-aggregator.js +280 -0
  42. package/dist/analytics/metrics-aggregator.js.map +1 -0
  43. package/dist/cli/index.d.ts +36 -0
  44. package/dist/cli/index.d.ts.map +1 -0
  45. package/dist/cli/index.js +2677 -0
  46. package/dist/cli/index.js.map +1 -0
  47. package/dist/cli/normalize_patch.d.ts +3 -0
  48. package/dist/cli/normalize_patch.d.ts.map +1 -0
  49. package/dist/cli/normalize_patch.js +34 -0
  50. package/dist/cli/normalize_patch.js.map +1 -0
  51. package/dist/commands/command-processor.d.ts +58 -0
  52. package/dist/commands/command-processor.d.ts.map +1 -0
  53. package/dist/commands/command-processor.js +796 -0
  54. package/dist/commands/command-processor.js.map +1 -0
  55. package/dist/config/compliance-checker.d.ts +93 -0
  56. package/dist/config/compliance-checker.d.ts.map +1 -0
  57. package/dist/config/compliance-checker.js +424 -0
  58. package/dist/config/compliance-checker.js.map +1 -0
  59. package/dist/config/enterprise-config.d.ts +173 -0
  60. package/dist/config/enterprise-config.d.ts.map +1 -0
  61. package/dist/config/enterprise-config.js +190 -0
  62. package/dist/config/enterprise-config.js.map +1 -0
  63. package/dist/config/index.d.ts +13 -0
  64. package/dist/config/index.d.ts.map +1 -0
  65. package/dist/config/index.js +11 -0
  66. package/dist/config/index.js.map +1 -0
  67. package/dist/context/context-system.d.ts +97 -0
  68. package/dist/context/context-system.d.ts.map +1 -0
  69. package/dist/context/context-system.js +880 -0
  70. package/dist/context/context-system.js.map +1 -0
  71. package/dist/context/store.d.ts +123 -0
  72. package/dist/context/store.d.ts.map +1 -0
  73. package/dist/context/store.js +281 -0
  74. package/dist/context/store.js.map +1 -0
  75. package/dist/dasp/dasp-controller.d.ts +83 -0
  76. package/dist/dasp/dasp-controller.d.ts.map +1 -0
  77. package/dist/dasp/dasp-controller.js +190 -0
  78. package/dist/dasp/dasp-controller.js.map +1 -0
  79. package/dist/dasp/feedback-adapter.d.ts +56 -0
  80. package/dist/dasp/feedback-adapter.d.ts.map +1 -0
  81. package/dist/dasp/feedback-adapter.js +158 -0
  82. package/dist/dasp/feedback-adapter.js.map +1 -0
  83. package/dist/dasp/index.d.ts +14 -0
  84. package/dist/dasp/index.d.ts.map +1 -0
  85. package/dist/dasp/index.js +10 -0
  86. package/dist/dasp/index.js.map +1 -0
  87. package/dist/dasp/prompt-templates.d.ts +38 -0
  88. package/dist/dasp/prompt-templates.d.ts.map +1 -0
  89. package/dist/dasp/prompt-templates.js +406 -0
  90. package/dist/dasp/prompt-templates.js.map +1 -0
  91. package/dist/dasp/vault-rag-provider.d.ts +51 -0
  92. package/dist/dasp/vault-rag-provider.d.ts.map +1 -0
  93. package/dist/dasp/vault-rag-provider.js +125 -0
  94. package/dist/dasp/vault-rag-provider.js.map +1 -0
  95. package/dist/distribution/cli-distribution.d.ts +68 -0
  96. package/dist/distribution/cli-distribution.d.ts.map +1 -0
  97. package/dist/distribution/cli-distribution.js +941 -0
  98. package/dist/distribution/cli-distribution.js.map +1 -0
  99. package/dist/docs/doc-generator.d.ts +78 -0
  100. package/dist/docs/doc-generator.d.ts.map +1 -0
  101. package/dist/docs/doc-generator.js +297 -0
  102. package/dist/docs/doc-generator.js.map +1 -0
  103. package/dist/docs/index.d.ts +13 -0
  104. package/dist/docs/index.d.ts.map +1 -0
  105. package/dist/docs/index.js +11 -0
  106. package/dist/docs/index.js.map +1 -0
  107. package/dist/docs/site-builder.d.ts +58 -0
  108. package/dist/docs/site-builder.d.ts.map +1 -0
  109. package/dist/docs/site-builder.js +229 -0
  110. package/dist/docs/site-builder.js.map +1 -0
  111. package/dist/ecosystem/adapters/claude-adapter.d.ts +29 -0
  112. package/dist/ecosystem/adapters/claude-adapter.d.ts.map +1 -0
  113. package/dist/ecosystem/adapters/claude-adapter.js +116 -0
  114. package/dist/ecosystem/adapters/claude-adapter.js.map +1 -0
  115. package/dist/ecosystem/adapters/cursor-adapter.d.ts +27 -0
  116. package/dist/ecosystem/adapters/cursor-adapter.d.ts.map +1 -0
  117. package/dist/ecosystem/adapters/cursor-adapter.js +93 -0
  118. package/dist/ecosystem/adapters/cursor-adapter.js.map +1 -0
  119. package/dist/ecosystem/adapters/v0-adapter.d.ts +30 -0
  120. package/dist/ecosystem/adapters/v0-adapter.d.ts.map +1 -0
  121. package/dist/ecosystem/adapters/v0-adapter.js +112 -0
  122. package/dist/ecosystem/adapters/v0-adapter.js.map +1 -0
  123. package/dist/ecosystem/ecosystem-router.d.ts +80 -0
  124. package/dist/ecosystem/ecosystem-router.d.ts.map +1 -0
  125. package/dist/ecosystem/ecosystem-router.js +241 -0
  126. package/dist/ecosystem/ecosystem-router.js.map +1 -0
  127. package/dist/ecosystem/ecosystem-types.d.ts +94 -0
  128. package/dist/ecosystem/ecosystem-types.d.ts.map +1 -0
  129. package/dist/ecosystem/ecosystem-types.js +27 -0
  130. package/dist/ecosystem/ecosystem-types.js.map +1 -0
  131. package/dist/ecosystem/index.d.ts +10 -0
  132. package/dist/ecosystem/index.d.ts.map +1 -0
  133. package/dist/ecosystem/index.js +9 -0
  134. package/dist/ecosystem/index.js.map +1 -0
  135. package/dist/integration/agentic-integration.d.ts +73 -0
  136. package/dist/integration/agentic-integration.d.ts.map +1 -0
  137. package/dist/integration/agentic-integration.js +253 -0
  138. package/dist/integration/agentic-integration.js.map +1 -0
  139. package/dist/integration/background-agents-integration.d.ts +54 -0
  140. package/dist/integration/background-agents-integration.d.ts.map +1 -0
  141. package/dist/integration/background-agents-integration.js +225 -0
  142. package/dist/integration/background-agents-integration.js.map +1 -0
  143. package/dist/integration/dcp-integration.d.ts +81 -0
  144. package/dist/integration/dcp-integration.d.ts.map +1 -0
  145. package/dist/integration/dcp-integration.js +189 -0
  146. package/dist/integration/dcp-integration.js.map +1 -0
  147. package/dist/integration/firecrawl-integration.d.ts +61 -0
  148. package/dist/integration/firecrawl-integration.d.ts.map +1 -0
  149. package/dist/integration/firecrawl-integration.js +246 -0
  150. package/dist/integration/firecrawl-integration.js.map +1 -0
  151. package/dist/integration/index.d.ts +40 -0
  152. package/dist/integration/index.d.ts.map +1 -0
  153. package/dist/integration/index.js +43 -0
  154. package/dist/integration/index.js.map +1 -0
  155. package/dist/integration/integration-hub.d.ts +43 -0
  156. package/dist/integration/integration-hub.d.ts.map +1 -0
  157. package/dist/integration/integration-hub.js +507 -0
  158. package/dist/integration/integration-hub.js.map +1 -0
  159. package/dist/integration/integration-loader.d.ts +42 -0
  160. package/dist/integration/integration-loader.d.ts.map +1 -0
  161. package/dist/integration/integration-loader.js +240 -0
  162. package/dist/integration/integration-loader.js.map +1 -0
  163. package/dist/integration/md-table-formatter-integration.d.ts +41 -0
  164. package/dist/integration/md-table-formatter-integration.d.ts.map +1 -0
  165. package/dist/integration/md-table-formatter-integration.js +183 -0
  166. package/dist/integration/md-table-formatter-integration.js.map +1 -0
  167. package/dist/integration/native/agentic/agentic-engine.d.ts +52 -0
  168. package/dist/integration/native/agentic/agentic-engine.d.ts.map +1 -0
  169. package/dist/integration/native/agentic/agentic-engine.js +267 -0
  170. package/dist/integration/native/agentic/agentic-engine.js.map +1 -0
  171. package/dist/integration/native/background/background-engine.d.ts +62 -0
  172. package/dist/integration/native/background/background-engine.d.ts.map +1 -0
  173. package/dist/integration/native/background/background-engine.js +167 -0
  174. package/dist/integration/native/background/background-engine.js.map +1 -0
  175. package/dist/integration/native/dcp/dcp-engine.d.ts +55 -0
  176. package/dist/integration/native/dcp/dcp-engine.d.ts.map +1 -0
  177. package/dist/integration/native/dcp/dcp-engine.js +168 -0
  178. package/dist/integration/native/dcp/dcp-engine.js.map +1 -0
  179. package/dist/integration/native/firecrawl/firecrawl-engine.d.ts +66 -0
  180. package/dist/integration/native/firecrawl/firecrawl-engine.d.ts.map +1 -0
  181. package/dist/integration/native/firecrawl/firecrawl-engine.js +221 -0
  182. package/dist/integration/native/firecrawl/firecrawl-engine.js.map +1 -0
  183. package/dist/integration/native/formatter/formatter-engine.d.ts +47 -0
  184. package/dist/integration/native/formatter/formatter-engine.d.ts.map +1 -0
  185. package/dist/integration/native/formatter/formatter-engine.js +130 -0
  186. package/dist/integration/native/formatter/formatter-engine.js.map +1 -0
  187. package/dist/integration/native/index.d.ts +41 -0
  188. package/dist/integration/native/index.d.ts.map +1 -0
  189. package/dist/integration/native/index.js +80 -0
  190. package/dist/integration/native/index.js.map +1 -0
  191. package/dist/integration/native/orchestration/orchestration-engine.d.ts +62 -0
  192. package/dist/integration/native/orchestration/orchestration-engine.d.ts.map +1 -0
  193. package/dist/integration/native/orchestration/orchestration-engine.js +177 -0
  194. package/dist/integration/native/orchestration/orchestration-engine.js.map +1 -0
  195. package/dist/integration/native/pty/pty-engine.d.ts +45 -0
  196. package/dist/integration/native/pty/pty-engine.d.ts.map +1 -0
  197. package/dist/integration/native/pty/pty-engine.js +103 -0
  198. package/dist/integration/native/pty/pty-engine.js.map +1 -0
  199. package/dist/integration/native/shell-strategy.d.ts +60 -0
  200. package/dist/integration/native/shell-strategy.d.ts.map +1 -0
  201. package/dist/integration/native/shell-strategy.js +131 -0
  202. package/dist/integration/native/shell-strategy.js.map +1 -0
  203. package/dist/integration/native/skillful/skillful-engine.d.ts +53 -0
  204. package/dist/integration/native/skillful/skillful-engine.d.ts.map +1 -0
  205. package/dist/integration/native/skillful/skillful-engine.js +127 -0
  206. package/dist/integration/native/skillful/skillful-engine.js.map +1 -0
  207. package/dist/integration/native/subtask2/subtask2-engine.d.ts +50 -0
  208. package/dist/integration/native/subtask2/subtask2-engine.d.ts.map +1 -0
  209. package/dist/integration/native/subtask2/subtask2-engine.js +158 -0
  210. package/dist/integration/native/subtask2/subtask2-engine.js.map +1 -0
  211. package/dist/integration/native/supermemory/supermemory-engine.d.ts +63 -0
  212. package/dist/integration/native/supermemory/supermemory-engine.d.ts.map +1 -0
  213. package/dist/integration/native/supermemory/supermemory-engine.js +127 -0
  214. package/dist/integration/native/supermemory/supermemory-engine.js.map +1 -0
  215. package/dist/integration/native/workspace/workspace-engine.d.ts +75 -0
  216. package/dist/integration/native/workspace/workspace-engine.d.ts.map +1 -0
  217. package/dist/integration/native/workspace/workspace-engine.js +216 -0
  218. package/dist/integration/native/workspace/workspace-engine.js.map +1 -0
  219. package/dist/integration/oh-my-opencode-integration.d.ts +59 -0
  220. package/dist/integration/oh-my-opencode-integration.d.ts.map +1 -0
  221. package/dist/integration/oh-my-opencode-integration.js +180 -0
  222. package/dist/integration/oh-my-opencode-integration.js.map +1 -0
  223. package/dist/integration/openagents-control-integration.d.ts +81 -0
  224. package/dist/integration/openagents-control-integration.d.ts.map +1 -0
  225. package/dist/integration/openagents-control-integration.js +273 -0
  226. package/dist/integration/openagents-control-integration.js.map +1 -0
  227. package/dist/integration/pty-integration.d.ts +64 -0
  228. package/dist/integration/pty-integration.d.ts.map +1 -0
  229. package/dist/integration/pty-integration.js +180 -0
  230. package/dist/integration/pty-integration.js.map +1 -0
  231. package/dist/integration/shell-strategy-integration.d.ts +26 -0
  232. package/dist/integration/shell-strategy-integration.d.ts.map +1 -0
  233. package/dist/integration/shell-strategy-integration.js +110 -0
  234. package/dist/integration/shell-strategy-integration.js.map +1 -0
  235. package/dist/integration/skillful-integration.d.ts +74 -0
  236. package/dist/integration/skillful-integration.d.ts.map +1 -0
  237. package/dist/integration/skillful-integration.js +317 -0
  238. package/dist/integration/skillful-integration.js.map +1 -0
  239. package/dist/integration/subtask2-integration.d.ts +71 -0
  240. package/dist/integration/subtask2-integration.d.ts.map +1 -0
  241. package/dist/integration/subtask2-integration.js +240 -0
  242. package/dist/integration/subtask2-integration.js.map +1 -0
  243. package/dist/integration/supermemory-integration.d.ts +82 -0
  244. package/dist/integration/supermemory-integration.d.ts.map +1 -0
  245. package/dist/integration/supermemory-integration.js +252 -0
  246. package/dist/integration/supermemory-integration.js.map +1 -0
  247. package/dist/integration/types.d.ts +218 -0
  248. package/dist/integration/types.d.ts.map +1 -0
  249. package/dist/integration/types.js +5 -0
  250. package/dist/integration/types.js.map +1 -0
  251. package/dist/integration/workspace-integration.d.ts +46 -0
  252. package/dist/integration/workspace-integration.d.ts.map +1 -0
  253. package/dist/integration/workspace-integration.js +181 -0
  254. package/dist/integration/workspace-integration.js.map +1 -0
  255. package/dist/knowledge-vault/deepwiki-sync.d.ts +99 -0
  256. package/dist/knowledge-vault/deepwiki-sync.d.ts.map +1 -0
  257. package/dist/knowledge-vault/deepwiki-sync.js +381 -0
  258. package/dist/knowledge-vault/deepwiki-sync.js.map +1 -0
  259. package/dist/knowledge-vault/index.d.ts +11 -0
  260. package/dist/knowledge-vault/index.d.ts.map +1 -0
  261. package/dist/knowledge-vault/index.js +8 -0
  262. package/dist/knowledge-vault/index.js.map +1 -0
  263. package/dist/knowledge-vault/knowledge-vault.d.ts +38 -0
  264. package/dist/knowledge-vault/knowledge-vault.d.ts.map +1 -0
  265. package/dist/knowledge-vault/knowledge-vault.js +284 -0
  266. package/dist/knowledge-vault/knowledge-vault.js.map +1 -0
  267. package/dist/knowledge-vault/opencode-doc-ingest.d.ts +23 -0
  268. package/dist/knowledge-vault/opencode-doc-ingest.d.ts.map +1 -0
  269. package/dist/knowledge-vault/opencode-doc-ingest.js +48 -0
  270. package/dist/knowledge-vault/opencode-doc-ingest.js.map +1 -0
  271. package/dist/knowledge-vault/types.d.ts +61 -0
  272. package/dist/knowledge-vault/types.d.ts.map +1 -0
  273. package/dist/knowledge-vault/types.js +5 -0
  274. package/dist/knowledge-vault/types.js.map +1 -0
  275. package/dist/knowledge-vault/vault-config.d.ts +12 -0
  276. package/dist/knowledge-vault/vault-config.d.ts.map +1 -0
  277. package/dist/knowledge-vault/vault-config.js +24 -0
  278. package/dist/knowledge-vault/vault-config.js.map +1 -0
  279. package/dist/mcp/index.d.ts +28 -0
  280. package/dist/mcp/index.d.ts.map +1 -0
  281. package/dist/mcp/index.js +29 -0
  282. package/dist/mcp/index.js.map +1 -0
  283. package/dist/mcp/mcp-manager.d.ts +181 -0
  284. package/dist/mcp/mcp-manager.d.ts.map +1 -0
  285. package/dist/mcp/mcp-manager.js +621 -0
  286. package/dist/mcp/mcp-manager.js.map +1 -0
  287. package/dist/mcp/mcp-types.d.ts +199 -0
  288. package/dist/mcp/mcp-types.d.ts.map +1 -0
  289. package/dist/mcp/mcp-types.js +152 -0
  290. package/dist/mcp/mcp-types.js.map +1 -0
  291. package/dist/mcp/servers/mcp-server-bridge.d.ts +64 -0
  292. package/dist/mcp/servers/mcp-server-bridge.d.ts.map +1 -0
  293. package/dist/mcp/servers/mcp-server-bridge.js +189 -0
  294. package/dist/mcp/servers/mcp-server-bridge.js.map +1 -0
  295. package/dist/messages/message-bus.d.ts +148 -0
  296. package/dist/messages/message-bus.d.ts.map +1 -0
  297. package/dist/messages/message-bus.js +432 -0
  298. package/dist/messages/message-bus.js.map +1 -0
  299. package/dist/modes/custom-mode-registry.d.ts +121 -0
  300. package/dist/modes/custom-mode-registry.d.ts.map +1 -0
  301. package/dist/modes/custom-mode-registry.js +306 -0
  302. package/dist/modes/custom-mode-registry.js.map +1 -0
  303. package/dist/modes/index.d.ts +10 -0
  304. package/dist/modes/index.d.ts.map +1 -0
  305. package/dist/modes/index.js +8 -0
  306. package/dist/modes/index.js.map +1 -0
  307. package/dist/modes/mode-designer.d.ts +89 -0
  308. package/dist/modes/mode-designer.d.ts.map +1 -0
  309. package/dist/modes/mode-designer.js +485 -0
  310. package/dist/modes/mode-designer.js.map +1 -0
  311. package/dist/modes/mode-manager.d.ts +27 -0
  312. package/dist/modes/mode-manager.d.ts.map +1 -0
  313. package/dist/modes/mode-manager.js +68 -0
  314. package/dist/modes/mode-manager.js.map +1 -0
  315. package/dist/modes/synapse-modes.d.ts +26 -0
  316. package/dist/modes/synapse-modes.d.ts.map +1 -0
  317. package/dist/modes/synapse-modes.js +69 -0
  318. package/dist/modes/synapse-modes.js.map +1 -0
  319. package/dist/opencode/agent-delegate.d.ts +3 -0
  320. package/dist/opencode/agent-delegate.d.ts.map +1 -0
  321. package/dist/opencode/agent-delegate.js +3 -0
  322. package/dist/opencode/agent-delegate.js.map +1 -0
  323. package/dist/opencode/tool-bridge.d.ts +3 -0
  324. package/dist/opencode/tool-bridge.d.ts.map +1 -0
  325. package/dist/opencode/tool-bridge.js +3 -0
  326. package/dist/opencode/tool-bridge.js.map +1 -0
  327. package/dist/optimization/token-optimizer-v2.d.ts +18 -0
  328. package/dist/optimization/token-optimizer-v2.d.ts.map +1 -0
  329. package/dist/optimization/token-optimizer-v2.js +23 -0
  330. package/dist/optimization/token-optimizer-v2.js.map +1 -0
  331. package/dist/optimization/token-optimizer.d.ts +90 -0
  332. package/dist/optimization/token-optimizer.d.ts.map +1 -0
  333. package/dist/optimization/token-optimizer.js +399 -0
  334. package/dist/optimization/token-optimizer.js.map +1 -0
  335. package/dist/parallel/agent-farm.d.ts +123 -0
  336. package/dist/parallel/agent-farm.d.ts.map +1 -0
  337. package/dist/parallel/agent-farm.js +501 -0
  338. package/dist/parallel/agent-farm.js.map +1 -0
  339. package/dist/parallel/farm-scheduler.d.ts +115 -0
  340. package/dist/parallel/farm-scheduler.d.ts.map +1 -0
  341. package/dist/parallel/farm-scheduler.js +356 -0
  342. package/dist/parallel/farm-scheduler.js.map +1 -0
  343. package/dist/parallel/farm-types.d.ts +104 -0
  344. package/dist/parallel/farm-types.d.ts.map +1 -0
  345. package/dist/parallel/farm-types.js +9 -0
  346. package/dist/parallel/farm-types.js.map +1 -0
  347. package/dist/parallel/farm-worker.d.ts +62 -0
  348. package/dist/parallel/farm-worker.d.ts.map +1 -0
  349. package/dist/parallel/farm-worker.js +268 -0
  350. package/dist/parallel/farm-worker.js.map +1 -0
  351. package/dist/parallel/index.d.ts +14 -0
  352. package/dist/parallel/index.d.ts.map +1 -0
  353. package/dist/parallel/index.js +14 -0
  354. package/dist/parallel/index.js.map +1 -0
  355. package/dist/plugin/native-tools.d.ts +8 -0
  356. package/dist/plugin/native-tools.d.ts.map +1 -0
  357. package/dist/plugin/native-tools.js +147 -0
  358. package/dist/plugin/native-tools.js.map +1 -0
  359. package/dist/plugin/opencode-plugin.d.ts +32 -0
  360. package/dist/plugin/opencode-plugin.d.ts.map +1 -0
  361. package/dist/plugin/opencode-plugin.js +119 -0
  362. package/dist/plugin/opencode-plugin.js.map +1 -0
  363. package/dist/plugins/plugin-system.d.ts +108 -0
  364. package/dist/plugins/plugin-system.d.ts.map +1 -0
  365. package/dist/plugins/plugin-system.js +707 -0
  366. package/dist/plugins/plugin-system.js.map +1 -0
  367. package/dist/profiler/cpu-profiler.d.ts +53 -0
  368. package/dist/profiler/cpu-profiler.d.ts.map +1 -0
  369. package/dist/profiler/cpu-profiler.js +233 -0
  370. package/dist/profiler/cpu-profiler.js.map +1 -0
  371. package/dist/profiler/index.d.ts +36 -0
  372. package/dist/profiler/index.d.ts.map +1 -0
  373. package/dist/profiler/index.js +122 -0
  374. package/dist/profiler/index.js.map +1 -0
  375. package/dist/profiler/memory-profiler.d.ts +45 -0
  376. package/dist/profiler/memory-profiler.d.ts.map +1 -0
  377. package/dist/profiler/memory-profiler.js +211 -0
  378. package/dist/profiler/memory-profiler.js.map +1 -0
  379. package/dist/profiler/profiler-types.d.ts +234 -0
  380. package/dist/profiler/profiler-types.d.ts.map +1 -0
  381. package/dist/profiler/profiler-types.js +89 -0
  382. package/dist/profiler/profiler-types.js.map +1 -0
  383. package/dist/profiler/query-profiler.d.ts +48 -0
  384. package/dist/profiler/query-profiler.d.ts.map +1 -0
  385. package/dist/profiler/query-profiler.js +210 -0
  386. package/dist/profiler/query-profiler.js.map +1 -0
  387. package/dist/profiler/report-generator.d.ts +17 -0
  388. package/dist/profiler/report-generator.d.ts.map +1 -0
  389. package/dist/profiler/report-generator.js +329 -0
  390. package/dist/profiler/report-generator.js.map +1 -0
  391. package/dist/sdk/api.d.ts +85 -0
  392. package/dist/sdk/api.d.ts.map +1 -0
  393. package/dist/sdk/api.js +155 -0
  394. package/dist/sdk/api.js.map +1 -0
  395. package/dist/sdk/hooks.d.ts +58 -0
  396. package/dist/sdk/hooks.d.ts.map +1 -0
  397. package/dist/sdk/hooks.js +115 -0
  398. package/dist/sdk/hooks.js.map +1 -0
  399. package/dist/sdk/index.d.ts +17 -0
  400. package/dist/sdk/index.d.ts.map +1 -0
  401. package/dist/sdk/index.js +14 -0
  402. package/dist/sdk/index.js.map +1 -0
  403. package/dist/sdk/plugin-base.d.ts +198 -0
  404. package/dist/sdk/plugin-base.d.ts.map +1 -0
  405. package/dist/sdk/plugin-base.js +227 -0
  406. package/dist/sdk/plugin-base.js.map +1 -0
  407. package/dist/security/audit-rules.d.ts +12 -0
  408. package/dist/security/audit-rules.d.ts.map +1 -0
  409. package/dist/security/audit-rules.js +214 -0
  410. package/dist/security/audit-rules.js.map +1 -0
  411. package/dist/security/index.d.ts +13 -0
  412. package/dist/security/index.d.ts.map +1 -0
  413. package/dist/security/index.js +13 -0
  414. package/dist/security/index.js.map +1 -0
  415. package/dist/security/sarif-exporter.d.ts +36 -0
  416. package/dist/security/sarif-exporter.d.ts.map +1 -0
  417. package/dist/security/sarif-exporter.js +216 -0
  418. package/dist/security/sarif-exporter.js.map +1 -0
  419. package/dist/security/security-auditor.d.ts +30 -0
  420. package/dist/security/security-auditor.d.ts.map +1 -0
  421. package/dist/security/security-auditor.js +295 -0
  422. package/dist/security/security-auditor.js.map +1 -0
  423. package/dist/security/security-types.d.ts +132 -0
  424. package/dist/security/security-types.d.ts.map +1 -0
  425. package/dist/security/security-types.js +7 -0
  426. package/dist/security/security-types.js.map +1 -0
  427. package/dist/synapsexcoder/agent-delegate.d.ts +128 -0
  428. package/dist/synapsexcoder/agent-delegate.d.ts.map +1 -0
  429. package/dist/synapsexcoder/agent-delegate.js +837 -0
  430. package/dist/synapsexcoder/agent-delegate.js.map +1 -0
  431. package/dist/synapsexcoder/agent-mailbox.d.ts +26 -0
  432. package/dist/synapsexcoder/agent-mailbox.d.ts.map +1 -0
  433. package/dist/synapsexcoder/agent-mailbox.js +64 -0
  434. package/dist/synapsexcoder/agent-mailbox.js.map +1 -0
  435. package/dist/synapsexcoder/ast-grep/ast-grep-api.d.ts +62 -0
  436. package/dist/synapsexcoder/ast-grep/ast-grep-api.d.ts.map +1 -0
  437. package/dist/synapsexcoder/ast-grep/ast-grep-api.js +223 -0
  438. package/dist/synapsexcoder/ast-grep/ast-grep-api.js.map +1 -0
  439. package/dist/synapsexcoder/cited-search.d.ts +32 -0
  440. package/dist/synapsexcoder/cited-search.d.ts.map +1 -0
  441. package/dist/synapsexcoder/cited-search.js +141 -0
  442. package/dist/synapsexcoder/cited-search.js.map +1 -0
  443. package/dist/synapsexcoder/credential-store.d.ts +42 -0
  444. package/dist/synapsexcoder/credential-store.d.ts.map +1 -0
  445. package/dist/synapsexcoder/credential-store.js +165 -0
  446. package/dist/synapsexcoder/credential-store.js.map +1 -0
  447. package/dist/synapsexcoder/devcontainer-awareness.d.ts +35 -0
  448. package/dist/synapsexcoder/devcontainer-awareness.d.ts.map +1 -0
  449. package/dist/synapsexcoder/devcontainer-awareness.js +133 -0
  450. package/dist/synapsexcoder/devcontainer-awareness.js.map +1 -0
  451. package/dist/synapsexcoder/execution-loops/helix-loop.d.ts +31 -0
  452. package/dist/synapsexcoder/execution-loops/helix-loop.d.ts.map +1 -0
  453. package/dist/synapsexcoder/execution-loops/helix-loop.js +93 -0
  454. package/dist/synapsexcoder/execution-loops/helix-loop.js.map +1 -0
  455. package/dist/synapsexcoder/execution-loops/ralp-loop.d.ts +28 -0
  456. package/dist/synapsexcoder/execution-loops/ralp-loop.d.ts.map +1 -0
  457. package/dist/synapsexcoder/execution-loops/ralp-loop.js +77 -0
  458. package/dist/synapsexcoder/execution-loops/ralp-loop.js.map +1 -0
  459. package/dist/synapsexcoder/feature-names.d.ts +27 -0
  460. package/dist/synapsexcoder/feature-names.d.ts.map +1 -0
  461. package/dist/synapsexcoder/feature-names.js +40 -0
  462. package/dist/synapsexcoder/feature-names.js.map +1 -0
  463. package/dist/synapsexcoder/index.d.ts +39 -0
  464. package/dist/synapsexcoder/index.d.ts.map +1 -0
  465. package/dist/synapsexcoder/index.js +23 -0
  466. package/dist/synapsexcoder/index.js.map +1 -0
  467. package/dist/synapsexcoder/knowledge-provider.d.ts +44 -0
  468. package/dist/synapsexcoder/knowledge-provider.d.ts.map +1 -0
  469. package/dist/synapsexcoder/knowledge-provider.js +107 -0
  470. package/dist/synapsexcoder/knowledge-provider.js.map +1 -0
  471. package/dist/synapsexcoder/lsp/lsp-layer.d.ts +51 -0
  472. package/dist/synapsexcoder/lsp/lsp-layer.d.ts.map +1 -0
  473. package/dist/synapsexcoder/lsp/lsp-layer.js +302 -0
  474. package/dist/synapsexcoder/lsp/lsp-layer.js.map +1 -0
  475. package/dist/synapsexcoder/migration/opencode-config-migrator.d.ts +37 -0
  476. package/dist/synapsexcoder/migration/opencode-config-migrator.d.ts.map +1 -0
  477. package/dist/synapsexcoder/migration/opencode-config-migrator.js +100 -0
  478. package/dist/synapsexcoder/migration/opencode-config-migrator.js.map +1 -0
  479. package/dist/synapsexcoder/runtime-environment.d.ts +35 -0
  480. package/dist/synapsexcoder/runtime-environment.d.ts.map +1 -0
  481. package/dist/synapsexcoder/runtime-environment.js +108 -0
  482. package/dist/synapsexcoder/runtime-environment.js.map +1 -0
  483. package/dist/synapsexcoder/secret-guard.d.ts +18 -0
  484. package/dist/synapsexcoder/secret-guard.d.ts.map +1 -0
  485. package/dist/synapsexcoder/secret-guard.js +69 -0
  486. package/dist/synapsexcoder/secret-guard.js.map +1 -0
  487. package/dist/synapsexcoder/self-prompt-engine.d.ts +15 -0
  488. package/dist/synapsexcoder/self-prompt-engine.d.ts.map +1 -0
  489. package/dist/synapsexcoder/self-prompt-engine.js +11 -0
  490. package/dist/synapsexcoder/self-prompt-engine.js.map +1 -0
  491. package/dist/synapsexcoder/session-observability.d.ts +44 -0
  492. package/dist/synapsexcoder/session-observability.d.ts.map +1 -0
  493. package/dist/synapsexcoder/session-observability.js +115 -0
  494. package/dist/synapsexcoder/session-observability.js.map +1 -0
  495. package/dist/synapsexcoder/specialist-agents.d.ts +38 -0
  496. package/dist/synapsexcoder/specialist-agents.d.ts.map +1 -0
  497. package/dist/synapsexcoder/specialist-agents.js +192 -0
  498. package/dist/synapsexcoder/specialist-agents.js.map +1 -0
  499. package/dist/synapsexcoder/swarm/kanban-board.d.ts +34 -0
  500. package/dist/synapsexcoder/swarm/kanban-board.d.ts.map +1 -0
  501. package/dist/synapsexcoder/swarm/kanban-board.js +85 -0
  502. package/dist/synapsexcoder/swarm/kanban-board.js.map +1 -0
  503. package/dist/synapsexcoder/swarm/swarm-mailbox.d.ts +38 -0
  504. package/dist/synapsexcoder/swarm/swarm-mailbox.d.ts.map +1 -0
  505. package/dist/synapsexcoder/swarm/swarm-mailbox.js +93 -0
  506. package/dist/synapsexcoder/swarm/swarm-mailbox.js.map +1 -0
  507. package/dist/synapsexcoder/tool-bridge.d.ts +277 -0
  508. package/dist/synapsexcoder/tool-bridge.d.ts.map +1 -0
  509. package/dist/synapsexcoder/tool-bridge.js +1356 -0
  510. package/dist/synapsexcoder/tool-bridge.js.map +1 -0
  511. package/dist/synapsexcoder/tool-routing.d.ts +28 -0
  512. package/dist/synapsexcoder/tool-routing.d.ts.map +1 -0
  513. package/dist/synapsexcoder/tool-routing.js +79 -0
  514. package/dist/synapsexcoder/tool-routing.js.map +1 -0
  515. package/dist/synapsexcoder/type-injector.d.ts +26 -0
  516. package/dist/synapsexcoder/type-injector.d.ts.map +1 -0
  517. package/dist/synapsexcoder/type-injector.js +124 -0
  518. package/dist/synapsexcoder/type-injector.js.map +1 -0
  519. package/dist/synapsexcoder/utils/fuzzy-match.d.ts +25 -0
  520. package/dist/synapsexcoder/utils/fuzzy-match.d.ts.map +1 -0
  521. package/dist/synapsexcoder/utils/fuzzy-match.js +83 -0
  522. package/dist/synapsexcoder/utils/fuzzy-match.js.map +1 -0
  523. package/dist/synapsexcoder/vault-crawl.d.ts +29 -0
  524. package/dist/synapsexcoder/vault-crawl.d.ts.map +1 -0
  525. package/dist/synapsexcoder/vault-crawl.js +103 -0
  526. package/dist/synapsexcoder/vault-crawl.js.map +1 -0
  527. package/dist/synapsexcoder/verified-apply.d.ts +39 -0
  528. package/dist/synapsexcoder/verified-apply.d.ts.map +1 -0
  529. package/dist/synapsexcoder/verified-apply.js +81 -0
  530. package/dist/synapsexcoder/verified-apply.js.map +1 -0
  531. package/dist/synapsexcoder/verified-context-editing.d.ts +34 -0
  532. package/dist/synapsexcoder/verified-context-editing.d.ts.map +1 -0
  533. package/dist/synapsexcoder/verified-context-editing.js +80 -0
  534. package/dist/synapsexcoder/verified-context-editing.js.map +1 -0
  535. package/dist/synapsexcoder/worker-pool.d.ts +47 -0
  536. package/dist/synapsexcoder/worker-pool.d.ts.map +1 -0
  537. package/dist/synapsexcoder/worker-pool.js +120 -0
  538. package/dist/synapsexcoder/worker-pool.js.map +1 -0
  539. package/dist/synapsexcoder/workspace-intelligence.d.ts +35 -0
  540. package/dist/synapsexcoder/workspace-intelligence.d.ts.map +1 -0
  541. package/dist/synapsexcoder/workspace-intelligence.js +126 -0
  542. package/dist/synapsexcoder/workspace-intelligence.js.map +1 -0
  543. package/dist/synapsexcoder/worktree-manager.d.ts +31 -0
  544. package/dist/synapsexcoder/worktree-manager.d.ts.map +1 -0
  545. package/dist/synapsexcoder/worktree-manager.js +100 -0
  546. package/dist/synapsexcoder/worktree-manager.js.map +1 -0
  547. package/dist/team/index.d.ts +17 -0
  548. package/dist/team/index.d.ts.map +1 -0
  549. package/dist/team/index.js +13 -0
  550. package/dist/team/index.js.map +1 -0
  551. package/dist/team/team-audit.d.ts +120 -0
  552. package/dist/team/team-audit.d.ts.map +1 -0
  553. package/dist/team/team-audit.js +357 -0
  554. package/dist/team/team-audit.js.map +1 -0
  555. package/dist/team/team-collaboration.d.ts +150 -0
  556. package/dist/team/team-collaboration.d.ts.map +1 -0
  557. package/dist/team/team-collaboration.js +495 -0
  558. package/dist/team/team-collaboration.js.map +1 -0
  559. package/dist/team/team-rbac.d.ts +84 -0
  560. package/dist/team/team-rbac.d.ts.map +1 -0
  561. package/dist/team/team-rbac.js +259 -0
  562. package/dist/team/team-rbac.js.map +1 -0
  563. package/dist/team/team-session-manager.d.ts +100 -0
  564. package/dist/team/team-session-manager.d.ts.map +1 -0
  565. package/dist/team/team-session-manager.js +255 -0
  566. package/dist/team/team-session-manager.js.map +1 -0
  567. package/dist/thoughts/thoughts-manager.d.ts +52 -0
  568. package/dist/thoughts/thoughts-manager.d.ts.map +1 -0
  569. package/dist/thoughts/thoughts-manager.js +271 -0
  570. package/dist/thoughts/thoughts-manager.js.map +1 -0
  571. package/dist/tools/api-validator/index.d.ts +97 -0
  572. package/dist/tools/api-validator/index.d.ts.map +1 -0
  573. package/dist/tools/api-validator/index.js +312 -0
  574. package/dist/tools/api-validator/index.js.map +1 -0
  575. package/dist/tools/code-archaeology/index.d.ts +193 -0
  576. package/dist/tools/code-archaeology/index.d.ts.map +1 -0
  577. package/dist/tools/code-archaeology/index.js +468 -0
  578. package/dist/tools/code-archaeology/index.js.map +1 -0
  579. package/dist/tools/codebase-search/index.d.ts +126 -0
  580. package/dist/tools/codebase-search/index.d.ts.map +1 -0
  581. package/dist/tools/codebase-search/index.js +437 -0
  582. package/dist/tools/codebase-search/index.js.map +1 -0
  583. package/dist/tools/context/index.d.ts +162 -0
  584. package/dist/tools/context/index.d.ts.map +1 -0
  585. package/dist/tools/context/index.js +332 -0
  586. package/dist/tools/context/index.js.map +1 -0
  587. package/dist/tools/deepwiki/analyzer.d.ts +167 -0
  588. package/dist/tools/deepwiki/analyzer.d.ts.map +1 -0
  589. package/dist/tools/deepwiki/analyzer.js +925 -0
  590. package/dist/tools/deepwiki/analyzer.js.map +1 -0
  591. package/dist/tools/deepwiki/extractor.d.ts +151 -0
  592. package/dist/tools/deepwiki/extractor.d.ts.map +1 -0
  593. package/dist/tools/deepwiki/extractor.js +923 -0
  594. package/dist/tools/deepwiki/extractor.js.map +1 -0
  595. package/dist/tools/deepwiki/generator.d.ts +89 -0
  596. package/dist/tools/deepwiki/generator.d.ts.map +1 -0
  597. package/dist/tools/deepwiki/generator.js +638 -0
  598. package/dist/tools/deepwiki/generator.js.map +1 -0
  599. package/dist/tools/deepwiki/index.d.ts +96 -0
  600. package/dist/tools/deepwiki/index.d.ts.map +1 -0
  601. package/dist/tools/deepwiki/index.js +282 -0
  602. package/dist/tools/deepwiki/index.js.map +1 -0
  603. package/dist/tools/deepwiki/types.d.ts +370 -0
  604. package/dist/tools/deepwiki/types.d.ts.map +1 -0
  605. package/dist/tools/deepwiki/types.js +21 -0
  606. package/dist/tools/deepwiki/types.js.map +1 -0
  607. package/dist/tools/dependency-mapper/index.d.ts +212 -0
  608. package/dist/tools/dependency-mapper/index.d.ts.map +1 -0
  609. package/dist/tools/dependency-mapper/index.js +592 -0
  610. package/dist/tools/dependency-mapper/index.js.map +1 -0
  611. package/dist/tools/memory/index.d.ts +120 -0
  612. package/dist/tools/memory/index.d.ts.map +1 -0
  613. package/dist/tools/memory/index.js +275 -0
  614. package/dist/tools/memory/index.js.map +1 -0
  615. package/dist/tools/normalize_patch.d.ts +3 -0
  616. package/dist/tools/normalize_patch.d.ts.map +1 -0
  617. package/dist/tools/normalize_patch.js +22 -0
  618. package/dist/tools/normalize_patch.js.map +1 -0
  619. package/dist/tools/pattern-detector/index.d.ts +105 -0
  620. package/dist/tools/pattern-detector/index.d.ts.map +1 -0
  621. package/dist/tools/pattern-detector/index.js +526 -0
  622. package/dist/tools/pattern-detector/index.js.map +1 -0
  623. package/dist/tools/performance-profiler/index.d.ts +50 -0
  624. package/dist/tools/performance-profiler/index.d.ts.map +1 -0
  625. package/dist/tools/performance-profiler/index.js +164 -0
  626. package/dist/tools/performance-profiler/index.js.map +1 -0
  627. package/dist/tools/refactoring-engine/index.d.ts +63 -0
  628. package/dist/tools/refactoring-engine/index.d.ts.map +1 -0
  629. package/dist/tools/refactoring-engine/index.js +270 -0
  630. package/dist/tools/refactoring-engine/index.js.map +1 -0
  631. package/dist/tools/registry.d.ts +36 -0
  632. package/dist/tools/registry.d.ts.map +1 -0
  633. package/dist/tools/registry.js +499 -0
  634. package/dist/tools/registry.js.map +1 -0
  635. package/dist/tools/test-generator/index.d.ts +152 -0
  636. package/dist/tools/test-generator/index.d.ts.map +1 -0
  637. package/dist/tools/test-generator/index.js +448 -0
  638. package/dist/tools/test-generator/index.js.map +1 -0
  639. package/dist/types/index.d.ts +565 -0
  640. package/dist/types/index.d.ts.map +1 -0
  641. package/dist/types/index.js +7 -0
  642. package/dist/types/index.js.map +1 -0
  643. package/dist/utils/path-validator.d.ts +94 -0
  644. package/dist/utils/path-validator.d.ts.map +1 -0
  645. package/dist/utils/path-validator.js +192 -0
  646. package/dist/utils/path-validator.js.map +1 -0
  647. package/dist/utils/secure-exec.d.ts +92 -0
  648. package/dist/utils/secure-exec.d.ts.map +1 -0
  649. package/dist/utils/secure-exec.js +230 -0
  650. package/dist/utils/secure-exec.js.map +1 -0
  651. package/dist/verification/claim-parser.d.ts +37 -0
  652. package/dist/verification/claim-parser.d.ts.map +1 -0
  653. package/dist/verification/claim-parser.js +231 -0
  654. package/dist/verification/claim-parser.js.map +1 -0
  655. package/dist/verification/cross-reference-engine.d.ts +55 -0
  656. package/dist/verification/cross-reference-engine.d.ts.map +1 -0
  657. package/dist/verification/cross-reference-engine.js +149 -0
  658. package/dist/verification/cross-reference-engine.js.map +1 -0
  659. package/dist/verification/discrepancy-tracker.d.ts +73 -0
  660. package/dist/verification/discrepancy-tracker.d.ts.map +1 -0
  661. package/dist/verification/discrepancy-tracker.js +196 -0
  662. package/dist/verification/discrepancy-tracker.js.map +1 -0
  663. package/dist/verification/feature-reconciler.d.ts +90 -0
  664. package/dist/verification/feature-reconciler.d.ts.map +1 -0
  665. package/dist/verification/feature-reconciler.js +264 -0
  666. package/dist/verification/feature-reconciler.js.map +1 -0
  667. package/dist/verification/index.d.ts +16 -0
  668. package/dist/verification/index.d.ts.map +1 -0
  669. package/dist/verification/index.js +19 -0
  670. package/dist/verification/index.js.map +1 -0
  671. package/dist/verification/integration-verifier.d.ts +66 -0
  672. package/dist/verification/integration-verifier.d.ts.map +1 -0
  673. package/dist/verification/integration-verifier.js +210 -0
  674. package/dist/verification/integration-verifier.js.map +1 -0
  675. package/dist/verification/recursive-audit-loop.d.ts +56 -0
  676. package/dist/verification/recursive-audit-loop.d.ts.map +1 -0
  677. package/dist/verification/recursive-audit-loop.js +136 -0
  678. package/dist/verification/recursive-audit-loop.js.map +1 -0
  679. package/dist/verification/runtime-verifier.d.ts +37 -0
  680. package/dist/verification/runtime-verifier.d.ts.map +1 -0
  681. package/dist/verification/runtime-verifier.js +221 -0
  682. package/dist/verification/runtime-verifier.js.map +1 -0
  683. package/dist/verification/types.d.ts +88 -0
  684. package/dist/verification/types.d.ts.map +1 -0
  685. package/dist/verification/types.js +5 -0
  686. package/dist/verification/types.js.map +1 -0
  687. package/dist/verification/verification-engine.d.ts +48 -0
  688. package/dist/verification/verification-engine.d.ts.map +1 -0
  689. package/dist/verification/verification-engine.js +203 -0
  690. package/dist/verification/verification-engine.js.map +1 -0
  691. package/opencode-agents/synapse-analyzer.md +64 -0
  692. package/opencode-agents/synapse-debugger.md +43 -0
  693. package/opencode-agents/synapse-diff-validator.md +42 -0
  694. package/opencode-agents/synapse-gatekeeper.md +273 -0
  695. package/opencode-agents/synapse-master.md +167 -0
  696. package/opencode-agents/synapse-planner.md +55 -0
  697. package/opencode-agents/synapse-researcher.md +61 -0
  698. package/opencode-agents/synapse-reviewer.md +64 -0
  699. package/opencode-agents/synapse-rewriter.md +62 -0
  700. package/opencode-agents/synapse-strategist.md +50 -0
  701. package/opencode-agents/synapse-test-runner.md +43 -0
  702. package/opencode-agents/synapse-ui-designer.md +61 -0
  703. package/opencode-agents/synapse-verifier.md +544 -0
  704. package/package.json +108 -0
  705. package/src/plugin/opencode-plugin.ts +141 -0
@@ -0,0 +1,3961 @@
1
+ // @ts-nocheck
2
+ /**
3
+ * SynapseXcoder V6 - Base Agent Class
4
+ *
5
+ * All specialized agents (Planner, Analyzer, Reviewer, etc.) extend this base class.
6
+ * Provides common functionality: task execution, metrics tracking, subagent spawning,
7
+ * and communication with the OpenCode tool bridge.
8
+ */
9
+ import { EventEmitter } from "events";
10
+ import { v4 as uuidv4 } from "uuid";
11
+ import { ContextSystem } from "../context/context-system.js";
12
+ import { ThoughtsManager } from "../thoughts/thoughts-manager.js";
13
+ import { AgentConfigManager } from "./agent-config-manager.js";
14
+ import { CommandProcessor } from "../commands/command-processor.js";
15
+ import { TeamCollaborationSystem } from "../team/team-collaboration.js";
16
+ import { PluginManager } from "../plugins/plugin-system.js";
17
+ import { TokenOptimizer } from "../optimization/token-optimizer.js";
18
+ import { optimizePromptForModel } from "../optimization/token-optimizer.js";
19
+ import { createGatedSubAgent } from "./gated-subagents.js";
20
+ import { getDefaultController as getDefaultDasp } from "../dasp/index.js";
21
+ // ============================================================================
22
+ // Model Resolution Utilities
23
+ // ============================================================================
24
+ /**
25
+ * Resolve model with fallback candidates
26
+ */
27
+ export function resolveModelCandidates(userModel) {
28
+ const candidates = [];
29
+ // User-specified model first
30
+ if (userModel) {
31
+ candidates.push(userModel);
32
+ }
33
+ // Environment variable
34
+ const envModel = process.env.DUCKYCODER_MODEL;
35
+ if (envModel && !candidates.includes(envModel)) {
36
+ candidates.push(envModel);
37
+ }
38
+ // Provider-specific aliases
39
+ const aliases = {
40
+ "claude-sonnet": [
41
+ "opencode/claude-sonnet-4-5",
42
+ "github-copilot/claude-sonnet-4.5",
43
+ ],
44
+ "claude-opus": [
45
+ "opencode/claude-opus-4-6",
46
+ "github-copilot/claude-opus-4.6",
47
+ "opencode/claude-opus-4-5",
48
+ "github-copilot/claude-opus-4-5",
49
+ ],
50
+ "gpt-4": ["github-copilot/gpt-4o", "opencode/gpt-5"],
51
+ grok: ["github-copilot/grok-code-fast-1"],
52
+ };
53
+ for (const [alias, models] of Object.entries(aliases)) {
54
+ if (userModel?.includes(alias) || envModel?.includes(alias)) {
55
+ candidates.push(...models.filter((m) => !candidates.includes(m)));
56
+ }
57
+ }
58
+ // If no model specified via userModel or envVar, default to claude-opus-4-6
59
+ if (!userModel && !envModel) {
60
+ candidates.push("opencode/claude-opus-4-6");
61
+ candidates.push("github-copilot/claude-opus-4.6");
62
+ }
63
+ // Hard fallbacks - use models that are actually available
64
+ // OPTIMIZATION: Limit to 3 fallbacks to reduce retry attempts
65
+ const fallbacks = [
66
+ "opencode/claude-opus-4-6",
67
+ "github-copilot/claude-opus-4.6",
68
+ "github-copilot/grok-code-fast-1",
69
+ ];
70
+ candidates.push(...fallbacks.filter((m) => !candidates.includes(m)));
71
+ return candidates;
72
+ }
73
+ /**
74
+ * Check if error is ProviderModelNotFoundError
75
+ */
76
+ export function isProviderModelNotFoundError(error) {
77
+ const patterns = [
78
+ /ProviderModelNotFoundError/i,
79
+ /ModelNotFound/i,
80
+ /model not found/i,
81
+ /provider\/model/i,
82
+ /invalid model/i,
83
+ /model.*not.*available/i,
84
+ ];
85
+ return patterns.some((pattern) => pattern.test(error));
86
+ }
87
+ // ============================================================================
88
+ // Base Agent (Abstract)
89
+ // ============================================================================
90
+ export class BaseAgent extends EventEmitter {
91
+ id;
92
+ type;
93
+ config;
94
+ memory = new Map();
95
+ status;
96
+ messageBus;
97
+ toolBridge;
98
+ artifacts = [];
99
+ isRunning = false;
100
+ abortController = null;
101
+ constructor(type, config, messageBus, toolBridge) {
102
+ super();
103
+ this.id = uuidv4();
104
+ this.type = type;
105
+ this.config = {
106
+ type,
107
+ mode: config.mode || "subagent",
108
+ model: config.model || "github-copilot/grok-code-fast-1",
109
+ temperature: config.temperature ?? 0.2,
110
+ maxSteps: config.maxSteps ?? 50,
111
+ description: config.description,
112
+ color: config.color,
113
+ tools: config.tools,
114
+ hidden: config.hidden ?? false,
115
+ timeout: config.timeout ?? 300000,
116
+ };
117
+ this.messageBus = messageBus;
118
+ this.toolBridge = toolBridge;
119
+ this.status = this.createInitialStatus();
120
+ }
121
+ createInitialStatus() {
122
+ return {
123
+ id: this.id,
124
+ type: this.type,
125
+ status: "idle",
126
+ startedAt: 0,
127
+ lastActivity: Date.now(),
128
+ metrics: {
129
+ tasksCompleted: 0,
130
+ tasksFailed: 0,
131
+ tokensUsed: 0,
132
+ executionTime: 0,
133
+ toolsInvoked: 0,
134
+ },
135
+ };
136
+ }
137
+ async start() {
138
+ if (this.isRunning)
139
+ return;
140
+ this.isRunning = true;
141
+ this.status.status = "running";
142
+ this.status.startedAt = Date.now();
143
+ this.abortController = new AbortController();
144
+ this.emit("start", this.id);
145
+ }
146
+ async stop() {
147
+ if (!this.isRunning)
148
+ return;
149
+ this.isRunning = false;
150
+ this.status.status = "idle";
151
+ this.abortController?.abort();
152
+ this.emit("stop", this.id);
153
+ }
154
+ async abort(reason) {
155
+ this.status.status = "idle";
156
+ this.abortController?.abort(reason);
157
+ await this.sendStatusUpdate("aborted", { reason });
158
+ this.emit("abort", this.id, reason);
159
+ }
160
+ // ========================================================================
161
+ // Status & Metrics
162
+ // ========================================================================
163
+ getStatus() {
164
+ return {
165
+ ...this.status,
166
+ lastActivity: Date.now(),
167
+ };
168
+ }
169
+ updateStatus(status, details) {
170
+ this.status.status = status;
171
+ this.status.lastActivity = Date.now();
172
+ if (details) {
173
+ this.status.currentTask = details;
174
+ }
175
+ this.emit("statusChange", this.status);
176
+ }
177
+ recordTaskCompletion(tokensUsed, toolsInvoked, executionTime) {
178
+ this.status.metrics.tasksCompleted++;
179
+ this.status.metrics.tokensUsed += tokensUsed;
180
+ this.status.metrics.toolsInvoked += toolsInvoked;
181
+ this.status.metrics.executionTime += executionTime;
182
+ }
183
+ recordTaskFailure() {
184
+ this.status.metrics.tasksFailed++;
185
+ }
186
+ // ========================================================================
187
+ // Communication
188
+ // ========================================================================
189
+ async sendMessage(recipient, type, payload) {
190
+ return this.messageBus.send({
191
+ sender: this.type,
192
+ recipient,
193
+ type: type,
194
+ payload,
195
+ });
196
+ }
197
+ async sendTaskResult(recipient, result, correlationId) {
198
+ return this.messageBus.sendTaskResult(this.type, recipient, result, correlationId);
199
+ }
200
+ async sendTaskError(recipient, taskId, error, correlationId) {
201
+ return this.messageBus.sendTaskError(this.type, recipient, taskId, error, correlationId);
202
+ }
203
+ async sendStatusUpdate(status, details) {
204
+ return this.messageBus.sendStatusUpdate(this.type, status, details);
205
+ }
206
+ async sendApprovalRequest(request) {
207
+ return this.messageBus.sendApprovalRequest(this.type, request);
208
+ }
209
+ // ========================================================================
210
+ // Approval Workflow
211
+ // ========================================================================
212
+ async requestApproval(taskId, changes, explanation, riskLevel = "medium") {
213
+ this.updateStatus("waiting", "Requesting approval for changes");
214
+ const approvalId = await this.sendApprovalRequest({
215
+ taskId,
216
+ changes,
217
+ riskLevel,
218
+ explanation,
219
+ });
220
+ // Wait for approval response via message bus
221
+ const approved = await this.waitForApproval(approvalId);
222
+ if (approved) {
223
+ this.updateStatus("running", "Approval granted, proceeding with changes");
224
+ return true;
225
+ }
226
+ else {
227
+ this.updateStatus("error", "Approval denied, cancelling task");
228
+ return false;
229
+ }
230
+ }
231
+ async waitForApproval(_approvalId) {
232
+ // Listen for approval response via message bus
233
+ const response = await this.messageBus.receive("master", 30000); // Wait 30s for approval response
234
+ return (response?.type === "approval_response" &&
235
+ response.payload?.approved === true);
236
+ }
237
+ // ========================================================================
238
+ // File Operations
239
+ // ========================================================================
240
+ async readFile(path) {
241
+ const result = await this.toolBridge.readFileContent(path);
242
+ return result?.content ?? null;
243
+ }
244
+ async readFiles(paths) {
245
+ const results = new Map();
246
+ for (const path of paths) {
247
+ results.set(path, await this.readFile(path));
248
+ }
249
+ return results;
250
+ }
251
+ async writeFile(path, content) {
252
+ const result = await this.toolBridge.writeFile(path, content);
253
+ return result.success;
254
+ }
255
+ async editFile(path, oldString, newString) {
256
+ const result = await this.toolBridge.editFile(path, oldString, newString);
257
+ return result.success;
258
+ }
259
+ async listDir(path, recursive = false) {
260
+ const result = await this.toolBridge.listDir(path, { recursive });
261
+ return result.success ? result.output : null;
262
+ }
263
+ async searchFiles(pattern, options) {
264
+ const result = await this.toolBridge.searchFiles(pattern, options);
265
+ return result.success ? result.output : null;
266
+ }
267
+ // ========================================================================
268
+ // Bash Operations
269
+ // ========================================================================
270
+ async runBash(command, options) {
271
+ const result = await this.toolBridge.runBash(command, options);
272
+ return result.success ? result.output : null;
273
+ }
274
+ async runGit(args) {
275
+ return this.runBash(`git ${args.join(" ")}`);
276
+ }
277
+ // ========================================================================
278
+ // Web Operations
279
+ // ========================================================================
280
+ async webSearch(query, numResults = 5) {
281
+ const result = await this.toolBridge.webSearch(query, { numResults });
282
+ return result.success ? result.output : null;
283
+ }
284
+ async callModel(prompt, options = {}) {
285
+ const result = await this.toolBridge.callModel(prompt, {
286
+ model: this.config.model,
287
+ ...options,
288
+ });
289
+ return result.success ? result.output : null;
290
+ }
291
+ // ========================================================================
292
+ // Artifact Management
293
+ // ========================================================================
294
+ createArtifact(type, name, content) {
295
+ const artifact = {
296
+ id: uuidv4(),
297
+ type,
298
+ name,
299
+ content,
300
+ createdAt: Date.now(),
301
+ agentId: this.id,
302
+ };
303
+ this.artifacts.push(artifact);
304
+ this.emit("artifactCreated", artifact);
305
+ return artifact;
306
+ }
307
+ setMemory(key, value) {
308
+ this.memory.set(key, value);
309
+ }
310
+ getMemory(key) {
311
+ return this.memory.get(key);
312
+ }
313
+ clearMemory() {
314
+ this.memory.clear();
315
+ }
316
+ getArtifacts() {
317
+ return [...this.artifacts];
318
+ }
319
+ clearArtifacts() {
320
+ this.artifacts = [];
321
+ }
322
+ }
323
+ // ============================================================================
324
+ // Master Agent
325
+ // ============================================================================
326
+ // ============================================================================
327
+ // Master Agent
328
+ // ============================================================================
329
+ export class MasterAgent extends BaseAgent {
330
+ activeSubAgents = new Map();
331
+ // Using more generic type to support both BaseAgent and GatedSubAgent
332
+ subAgentFactory = new Map();
333
+ listenerStops = new Map();
334
+ contextSystem;
335
+ thoughtsManager;
336
+ agentConfigManager;
337
+ commandProcessor;
338
+ teamSystem;
339
+ pluginManager;
340
+ tokenOptimizer;
341
+ gatekeeperCoordinator = null;
342
+ enableGatekeeper = true;
343
+ gatekeeperReviewLevel = "standard";
344
+ gatekeeperMaxRevisions = 3;
345
+ daspController = null;
346
+ daspEnabled = true;
347
+ constructor(messageBus, toolBridge, config, projectRoot) {
348
+ // Resolve model with fallbacks
349
+ const resolvedModel = resolveModelCandidates(config?.model)[0];
350
+ super("master", {
351
+ mode: "primary",
352
+ model: resolvedModel,
353
+ ...config,
354
+ }, messageBus, toolBridge);
355
+ // Initialize context and knowledge systems
356
+ this.contextSystem = new ContextSystem(projectRoot);
357
+ this.thoughtsManager = new ThoughtsManager(projectRoot || process.cwd());
358
+ this.agentConfigManager = new AgentConfigManager(projectRoot || process.cwd());
359
+ this.teamSystem = new TeamCollaborationSystem(projectRoot || process.cwd());
360
+ this.pluginManager = new PluginManager(projectRoot || process.cwd(), this);
361
+ this.tokenOptimizer = new TokenOptimizer();
362
+ this.daspController = getDefaultDasp();
363
+ this.commandProcessor = new CommandProcessor(this);
364
+ // Register default sub-agent constructors
365
+ // IMPORTANT: Using GatedSubAgents which include internal GatekeeperAgent
366
+ // for final review at the very end before returning to Master
367
+ this.subAgentFactory.set("planner", () => createGatedSubAgent("planner", messageBus, toolBridge, {}));
368
+ this.subAgentFactory.set("analyzer", () => createGatedSubAgent("analyzer", messageBus, toolBridge, {}));
369
+ this.subAgentFactory.set("reviewer", () => createGatedSubAgent("reviewer", messageBus, toolBridge, {}));
370
+ this.subAgentFactory.set("rewriter", () => createGatedSubAgent("rewriter", messageBus, toolBridge, {}));
371
+ this.subAgentFactory.set("ui_designer", () => createGatedSubAgent("ui_designer", messageBus, toolBridge, {}));
372
+ this.subAgentFactory.set("researcher", () => createGatedSubAgent("researcher", messageBus, toolBridge, {}));
373
+ // Gatekeeper is NOT a GatedSubAgent (it reviews others, not itself)
374
+ this.subAgentFactory.set("gatekeeper", () => new GatekeeperAgent(messageBus, toolBridge));
375
+ // Verifier - autonomous verification agent
376
+ this.subAgentFactory.set("verifier", () => createGatedSubAgent("verifier", messageBus, toolBridge, {}));
377
+ // Strategist - long-horizon goal decomposition
378
+ this.subAgentFactory.set("strategist", () => createGatedSubAgent("strategist", messageBus, toolBridge, {}));
379
+ // Diff Validator - compares original vs modified code
380
+ this.subAgentFactory.set("diff_validator", () => createGatedSubAgent("diff_validator", messageBus, toolBridge, {}));
381
+ // Debugger - auto-fix agent triggered on failure
382
+ this.subAgentFactory.set("debugger", () => createGatedSubAgent("debugger", messageBus, toolBridge, {}));
383
+ // Test Runner - executes validation tests
384
+ this.subAgentFactory.set("test_runner", () => createGatedSubAgent("test_runner", messageBus, toolBridge, {}));
385
+ }
386
+ async execute(task) {
387
+ const startTime = Date.now();
388
+ await this.start();
389
+ this.updateStatus("running", "Creating execution plan");
390
+ // Initialize gatekeeper coordinator if enabled
391
+ if (this.enableGatekeeper) {
392
+ this.gatekeeperCoordinator = new GatekeeperCoordinator(this.messageBus, this.toolBridge, 4);
393
+ }
394
+ const sessionId = task.context.metadata["sessionId"];
395
+ if (sessionId) {
396
+ // Log session activity
397
+ try {
398
+ const session = this.teamSystem.getSession(sessionId);
399
+ if (session) {
400
+ this.teamSystem.logActivity({
401
+ sessionId,
402
+ userId: "master-agent", // System user for now
403
+ type: "create",
404
+ targetType: "task",
405
+ targetId: task.id,
406
+ description: `Started processing task: ${task.prompt.substring(0, 50)}...`,
407
+ timestamp: Date.now(),
408
+ });
409
+ }
410
+ }
411
+ catch (e) {
412
+ console.warn(`Failed to log session start activity: ${e}`);
413
+ }
414
+ }
415
+ try {
416
+ // 1. Create execution plan
417
+ const phases = this.buildPhases(task);
418
+ // 2. Execute phases sequentially, passing context forward
419
+ // Each phase output will be reviewed by gatekeeper before proceeding
420
+ const phaseResults = await this.executePhasesWithGatekeeper(phases, task.context);
421
+ // 3. Synthesize output from all phase results
422
+ const output = this.synthesizeOutput(phaseResults);
423
+ const executionTime = Date.now() - startTime;
424
+ this.recordTaskCompletion(0, phases.length, executionTime);
425
+ this.updateStatus("completed", "All phases complete with gatekeeper review");
426
+ // Final gatekeeper report
427
+ if (this.gatekeeperCoordinator) {
428
+ const gatekeeperReport = this.generateGatekeeperReport(phaseResults);
429
+ output.gatekeeperReport = gatekeeperReport;
430
+ }
431
+ if (sessionId) {
432
+ try {
433
+ this.teamSystem.logActivity({
434
+ sessionId,
435
+ userId: "master-agent",
436
+ type: "approve",
437
+ targetType: "task",
438
+ targetId: task.id,
439
+ description: "Task completed successfully with gatekeeper review",
440
+ timestamp: Date.now(),
441
+ metadata: {
442
+ executionTime,
443
+ phases: phases.length,
444
+ artifacts: this.artifacts.length,
445
+ },
446
+ });
447
+ }
448
+ catch (e) {
449
+ console.warn(`Failed to log session success activity: ${e}`);
450
+ }
451
+ }
452
+ return {
453
+ taskId: task.id,
454
+ success: true,
455
+ output,
456
+ metrics: { executionTime, tokensUsed: 0, toolsInvoked: phases.length },
457
+ artifacts: this.collectAllArtifacts(),
458
+ };
459
+ }
460
+ catch (error) {
461
+ this.recordTaskFailure();
462
+ this.updateStatus("error");
463
+ if (sessionId) {
464
+ try {
465
+ this.teamSystem.logActivity({
466
+ sessionId,
467
+ userId: "master-agent",
468
+ type: "reject",
469
+ targetType: "task",
470
+ targetId: task.id,
471
+ description: `Task failed: ${error instanceof Error ? error.message : String(error)}`,
472
+ timestamp: Date.now(),
473
+ });
474
+ }
475
+ catch (e) {
476
+ console.warn(`Failed to log session failure activity: ${e}`);
477
+ }
478
+ }
479
+ return {
480
+ taskId: task.id,
481
+ success: false,
482
+ output: undefined,
483
+ error: error instanceof Error ? error.message : String(error),
484
+ metrics: {
485
+ executionTime: Date.now() - startTime,
486
+ tokensUsed: 0,
487
+ toolsInvoked: 0,
488
+ },
489
+ artifacts: [],
490
+ };
491
+ }
492
+ finally {
493
+ await this.stop();
494
+ // Cleanup gatekeeper coordinator
495
+ if (this.gatekeeperCoordinator) {
496
+ await this.gatekeeperCoordinator.shutdown();
497
+ }
498
+ }
499
+ }
500
+ // -------------------------------------------------------------------------
501
+ // Phase Planning
502
+ // -------------------------------------------------------------------------
503
+ buildPhases(task) {
504
+ const mode = task.context.metadata["mode"] || "full_pipeline";
505
+ const hasUI = task.context.files.some((f) => /\.(tsx?|jsx?|html|svelte|vue)$/.test(f.path) ||
506
+ f.content.includes("React") ||
507
+ f.content.includes("<div"));
508
+ const needsResearch = task.context.metadata["needsResearch"] === true;
509
+ const generateDocs = task.context.metadata["generateDocs"] === true;
510
+ const phases = [];
511
+ switch (mode) {
512
+ case "full_pipeline":
513
+ // OPTIMIZATION: Research and Plan can run in parallel (independent phases)
514
+ if (needsResearch) {
515
+ phases.push({
516
+ id: "research",
517
+ agentType: "researcher",
518
+ prompt: `Research background for: ${task.prompt}`,
519
+ parallelGroup: "pre_analysis", // Group for parallel execution
520
+ });
521
+ }
522
+ phases.push({
523
+ id: "plan",
524
+ agentType: "planner",
525
+ prompt: `Create a plan for: ${task.prompt}`,
526
+ parallelGroup: needsResearch ? "pre_analysis" : undefined, // Run in parallel with research if both exist
527
+ }, {
528
+ id: "analyze",
529
+ agentType: "analyzer",
530
+ prompt: `Analyze code: ${task.prompt}`,
531
+ }, {
532
+ id: "review",
533
+ agentType: "reviewer",
534
+ prompt: `Review findings from analysis`,
535
+ }, {
536
+ id: "rewrite",
537
+ agentType: "rewriter",
538
+ prompt: `Apply fixes: ${task.prompt}`,
539
+ });
540
+ if (generateDocs) {
541
+ phases.push({
542
+ id: "docs",
543
+ agentType: "rewriter",
544
+ prompt: `Generate documentation (README.md, API docs) based on the analysis and changes: ${task.prompt}`,
545
+ });
546
+ }
547
+ if (hasUI) {
548
+ phases.push({
549
+ id: "ui",
550
+ agentType: "ui_designer",
551
+ prompt: `Generate UI mockups for detected UI code`,
552
+ });
553
+ }
554
+ break;
555
+ case "analyze_only":
556
+ phases.push({
557
+ id: "analyze",
558
+ agentType: "analyzer",
559
+ prompt: `Analyze: ${task.prompt}`,
560
+ });
561
+ break;
562
+ case "review_only":
563
+ phases.push({
564
+ id: "review",
565
+ agentType: "reviewer",
566
+ prompt: `Review: ${task.prompt}`,
567
+ });
568
+ break;
569
+ case "rewrite_only":
570
+ phases.push({
571
+ id: "rewrite",
572
+ agentType: "rewriter",
573
+ prompt: `Rewrite: ${task.prompt}`,
574
+ });
575
+ break;
576
+ case "ui_design":
577
+ phases.push({
578
+ id: "ui",
579
+ agentType: "ui_designer",
580
+ prompt: `Design UI for: ${task.prompt}`,
581
+ });
582
+ break;
583
+ case "merge_only":
584
+ phases.push({
585
+ id: "plan",
586
+ agentType: "planner",
587
+ prompt: `Plan merge for: ${task.prompt}`,
588
+ });
589
+ break;
590
+ case "dry_run":
591
+ phases.push({
592
+ id: "analyze",
593
+ agentType: "analyzer",
594
+ prompt: `Dry-run analyze: ${task.prompt} (simulation only — no changes applied)`,
595
+ }, {
596
+ id: "review",
597
+ agentType: "reviewer",
598
+ prompt: `Dry-run review findings for: ${task.prompt}`,
599
+ });
600
+ break;
601
+ case "security_scan":
602
+ phases.push({
603
+ id: "security",
604
+ agentType: "gatekeeper",
605
+ prompt: `Security vulnerability scan for: ${task.prompt}`,
606
+ }, {
607
+ id: "analyze",
608
+ agentType: "analyzer",
609
+ prompt: `Analyze security findings for: ${task.prompt}`,
610
+ });
611
+ break;
612
+ case "debug_assistant":
613
+ phases.push({
614
+ id: "debug",
615
+ agentType: "debugger",
616
+ prompt: `Debug stack trace / error: ${task.prompt}`,
617
+ }, {
618
+ id: "analyze",
619
+ agentType: "analyzer",
620
+ prompt: `Analyze root cause for: ${task.prompt}`,
621
+ });
622
+ break;
623
+ case "api_validation":
624
+ phases.push({
625
+ id: "api",
626
+ agentType: "analyzer",
627
+ prompt: `Validate API endpoints for: ${task.prompt}`,
628
+ });
629
+ break;
630
+ case "doc_generator":
631
+ phases.push({
632
+ id: "docs",
633
+ agentType: "rewriter",
634
+ prompt: `Generate documentation from: ${task.prompt}`,
635
+ });
636
+ break;
637
+ case "create_mode":
638
+ phases.push({
639
+ id: "plan",
640
+ agentType: "planner",
641
+ prompt: `Plan scaffolding for: ${task.prompt}`,
642
+ }, {
643
+ id: "scaffold",
644
+ agentType: "rewriter",
645
+ prompt: `Create new files for: ${task.prompt}`,
646
+ });
647
+ break;
648
+ case "debug_mode":
649
+ phases.push({
650
+ id: "debug",
651
+ agentType: "debugger",
652
+ prompt: `Debug: ${task.prompt}`,
653
+ });
654
+ break;
655
+ case "refactor_mode":
656
+ phases.push({
657
+ id: "analyze",
658
+ agentType: "analyzer",
659
+ prompt: `Analyze refactoring targets: ${task.prompt}`,
660
+ }, {
661
+ id: "rewrite",
662
+ agentType: "rewriter",
663
+ prompt: `Apply refactoring: ${task.prompt}`,
664
+ }, {
665
+ id: "review",
666
+ agentType: "reviewer",
667
+ prompt: `Review refactored code for: ${task.prompt}`,
668
+ });
669
+ break;
670
+ case "document_mode":
671
+ phases.push({
672
+ id: "research",
673
+ agentType: "researcher",
674
+ prompt: `Research context for documentation: ${task.prompt}`,
675
+ }, {
676
+ id: "docs",
677
+ agentType: "rewriter",
678
+ prompt: `Generate comprehensive documentation: ${task.prompt}`,
679
+ });
680
+ break;
681
+ case "security_mode":
682
+ phases.push({
683
+ id: "security",
684
+ agentType: "gatekeeper",
685
+ prompt: `Hardened security scan: ${task.prompt}`,
686
+ }, {
687
+ id: "analyze",
688
+ agentType: "analyzer",
689
+ prompt: `Analyze security posture for: ${task.prompt}`,
690
+ }, {
691
+ id: "review",
692
+ agentType: "reviewer",
693
+ prompt: `Review security findings for: ${task.prompt}`,
694
+ });
695
+ break;
696
+ default:
697
+ phases.push({
698
+ id: "analyze",
699
+ agentType: "analyzer",
700
+ prompt: task.prompt,
701
+ });
702
+ }
703
+ return phases;
704
+ }
705
+ // -------------------------------------------------------------------------
706
+ // Dynamic Task Division — parallel sub-agent execution for large workloads
707
+ // -------------------------------------------------------------------------
708
+ /**
709
+ * Check if a phase should be parallelized based on context
710
+ */
711
+ shouldParallelizePhase(phaseId, context) {
712
+ // Parallelize rewrite phase if there are many files or issues
713
+ if (phaseId === "rewrite") {
714
+ // If we have many files, parallelize
715
+ if (context.files.length > 3)
716
+ return true;
717
+ // Check analysis output for many issues
718
+ const analysis = context.metadata["analyze_output"];
719
+ if (analysis && typeof analysis === "object") {
720
+ const issues = analysis.issues || [];
721
+ if (issues.length > 5)
722
+ return true;
723
+ }
724
+ }
725
+ // Parallelize analysis phase if many files
726
+ if (phaseId === "analyze") {
727
+ if (context.files.length > 5)
728
+ return true;
729
+ }
730
+ return false;
731
+ }
732
+ /**
733
+ * Divide a large task into smaller batches for parallel execution
734
+ */
735
+ divideTaskIntoBatches(originalTask, batchSize = 3) {
736
+ const batches = [];
737
+ // Get the analysis output to understand what needs to be done
738
+ const analysisOutput = originalTask.context.metadata?.analyze_output;
739
+ if (!analysisOutput && originalTask.type === "rewriter") {
740
+ return [originalTask]; // Can't divide rewrite without analysis
741
+ }
742
+ // For rewrite tasks, divide by files
743
+ if (originalTask.type === "rewriter") {
744
+ const files = originalTask.context.files;
745
+ // Create batches of files
746
+ for (let i = 0; i < files.length; i += batchSize) {
747
+ const fileBatch = files.slice(i, i + batchSize);
748
+ batches.push({
749
+ ...originalTask,
750
+ id: `${originalTask.id}-batch-${i / batchSize + 1}`,
751
+ context: {
752
+ ...originalTask.context,
753
+ files: fileBatch,
754
+ },
755
+ prompt: `${originalTask.prompt} (files: ${fileBatch
756
+ .map((f) => f.path)
757
+ .join(", ")})`,
758
+ });
759
+ }
760
+ }
761
+ else {
762
+ // For other tasks, create logical batches
763
+ for (let i = 0; i < Math.ceil(batchSize); i++) {
764
+ batches.push({
765
+ ...originalTask,
766
+ id: `${originalTask.id}-batch-${i + 1}`,
767
+ prompt: `${originalTask.prompt} (batch ${i + 1})`,
768
+ });
769
+ }
770
+ }
771
+ return batches.length > 1 ? batches : [originalTask];
772
+ }
773
+ /**
774
+ * Execute multiple subagents in parallel for a single phase
775
+ */
776
+ async executePhaseInParallel(phase, rollingContext) {
777
+ this.updateStatus("running", `Phase [${phase.id}] → Parallel ${phase.agentType} execution`);
778
+ // Create the initial task
779
+ const enhancedPrompt = this.enhancePromptWithContext(phase.prompt, phase.agentType, rollingContext);
780
+ const initialTask = {
781
+ id: uuidv4(),
782
+ type: phase.agentType,
783
+ prompt: enhancedPrompt,
784
+ context: rollingContext,
785
+ successCriteria: `Complete phase: ${phase.id}`,
786
+ };
787
+ // Divide into batches
788
+ const taskBatches = this.divideTaskIntoBatches(initialTask);
789
+ if (taskBatches.length === 1) {
790
+ // No parallelization needed
791
+ const result = await this.executeSingleTask(taskBatches[0]);
792
+ return [result];
793
+ }
794
+ // Execute batches in parallel
795
+ console.log(`🚀 Parallel execution: ${taskBatches.length} ${phase.agentType} agents`);
796
+ const results = await Promise.all(taskBatches.map((task) => this.executeSingleTask(task)));
797
+ return results;
798
+ }
799
+ /**
800
+ * Execute a single task with model fallback
801
+ */
802
+ async executeSingleTask(task) {
803
+ let attempt = 0;
804
+ const modelCandidates = resolveModelCandidates(this.config.model);
805
+ while (attempt < modelCandidates.length) {
806
+ try {
807
+ const modelToUse = attempt === 0 ? this.config.model : modelCandidates[attempt];
808
+ const subAgent = this.getOrCreateSubAgent(task.type, modelToUse);
809
+ // Register sub-agent
810
+ this.registerSubAgent(subAgent.id, subAgent);
811
+ // Dispatch task
812
+ const result = await this.messageBus.sendTask(this.type, task.type, task);
813
+ // Unregister after completion
814
+ this.unregisterSubAgent(subAgent.id);
815
+ if (result.success) {
816
+ return result;
817
+ }
818
+ // Fallback logic for model errors
819
+ if (result.error && isProviderModelNotFoundError(result.error)) {
820
+ attempt++;
821
+ console.warn(`Task ${task.id}: Model ${modelCandidates[attempt - 1]} unavailable, trying ${modelCandidates[attempt]}`);
822
+ continue;
823
+ }
824
+ return result;
825
+ }
826
+ catch (error) {
827
+ const errorMsg = error instanceof Error ? error.message : String(error);
828
+ if (isProviderModelNotFoundError(errorMsg)) {
829
+ attempt++;
830
+ if (attempt < modelCandidates.length) {
831
+ console.warn(`Task ${task.id}: Model ${modelCandidates[attempt - 1]} failed, trying ${modelCandidates[attempt]}`);
832
+ continue;
833
+ }
834
+ }
835
+ return {
836
+ taskId: task.id,
837
+ success: false,
838
+ output: undefined,
839
+ error: errorMsg,
840
+ metrics: { executionTime: 0, tokensUsed: 0, toolsInvoked: 0 },
841
+ artifacts: [],
842
+ };
843
+ }
844
+ }
845
+ return {
846
+ taskId: task.id,
847
+ success: false,
848
+ output: undefined,
849
+ error: "All model attempts failed",
850
+ metrics: { executionTime: 0, tokensUsed: 0, toolsInvoked: 0 },
851
+ artifacts: [],
852
+ };
853
+ }
854
+ // -------------------------------------------------------------------------
855
+ // Phase Execution with Gatekeeper Review
856
+ // -------------------------------------------------------------------------
857
+ /**
858
+ * Execute phases with gatekeeper review after each phase.
859
+ * Gatekeeper reviews each phase output before proceeding to the next.
860
+ * If gatekeeper requests revision, the affected phase is re-executed.
861
+ * If gatekeeper requests restart, the entire pipeline restarts.
862
+ */
863
+ async executePhasesWithGatekeeper(phases, baseContext) {
864
+ const accumulated = [];
865
+ let rollingContext = { ...baseContext };
866
+ const maxRevisions = this.gatekeeperMaxRevisions;
867
+ // OPTIMIZATION: Group phases by parallelGroup for concurrent execution
868
+ const phaseGroups = [];
869
+ for (const phase of phases) {
870
+ if (phase.parallelGroup) {
871
+ // Check if we already have a group for this parallelGroup
872
+ const existingGroup = phaseGroups.find((g) => g.phases[0]?.parallelGroup === phase.parallelGroup);
873
+ if (existingGroup) {
874
+ existingGroup.phases.push(phase);
875
+ }
876
+ else {
877
+ phaseGroups.push({ phases: [phase], parallel: true });
878
+ }
879
+ }
880
+ else {
881
+ phaseGroups.push({ phases: [phase], parallel: false });
882
+ }
883
+ }
884
+ // Execute phase groups sequentially, but phases within a group in parallel
885
+ for (const group of phaseGroups) {
886
+ if (group.parallel && group.phases.length > 1) {
887
+ // Execute all phases in this group in parallel
888
+ this.updateStatus("running", `Parallel phases: ${group.phases.map((p) => p.id).join(", ")}`);
889
+ console.log(`🚀 Executing parallel group: ${group.phases.map((p) => p.id).join(", ")}`);
890
+ const parallelResults = await Promise.all(group.phases.map((phase) => this.executePhaseWithGatekeeper(phase, rollingContext, maxRevisions)));
891
+ // Merge all results into rolling context
892
+ for (const { phase, result, gatekeeperReview } of parallelResults) {
893
+ rollingContext = {
894
+ ...rollingContext,
895
+ metadata: {
896
+ ...rollingContext.metadata,
897
+ [`${phase.id}_output`]: result.output,
898
+ },
899
+ };
900
+ accumulated.push({ phaseId: phase.id, agentType: phase.agentType, result, gatekeeperReview });
901
+ }
902
+ }
903
+ else {
904
+ // Execute phases sequentially
905
+ for (const phase of group.phases) {
906
+ const result = await this.executePhaseWithGatekeeper(phase, rollingContext, maxRevisions);
907
+ rollingContext = {
908
+ ...rollingContext,
909
+ metadata: {
910
+ ...rollingContext.metadata,
911
+ [`${phase.id}_output`]: result.result.output,
912
+ },
913
+ };
914
+ accumulated.push({
915
+ phaseId: phase.id,
916
+ agentType: phase.agentType,
917
+ result: result.result,
918
+ gatekeeperReview: result.gatekeeperReview,
919
+ });
920
+ }
921
+ }
922
+ }
923
+ return accumulated;
924
+ }
925
+ /**
926
+ * Execute a single phase with gatekeeper review (extracted for reuse)
927
+ */
928
+ async executePhaseWithGatekeeper(phase, rollingContext, maxRevisions) {
929
+ let phaseApproved = false;
930
+ let currentAttempt = 0;
931
+ let mergedResult = {
932
+ taskId: "",
933
+ success: false,
934
+ output: undefined,
935
+ error: undefined,
936
+ metrics: { executionTime: 0, tokensUsed: 0, toolsInvoked: 0 },
937
+ artifacts: [],
938
+ };
939
+ let gatekeeperReview;
940
+ while (!phaseApproved && currentAttempt <= maxRevisions) {
941
+ if (currentAttempt > 0) {
942
+ console.log(`🔄 Phase [${phase.id}] re-execution attempt ${currentAttempt}/${maxRevisions}`);
943
+ this.updateStatus("running", `Phase [${phase.id}] re-execution attempt ${currentAttempt}`);
944
+ }
945
+ // Execute the phase
946
+ const phaseResults = await this.executeSinglePhase(phase, rollingContext);
947
+ mergedResult = this.mergeParallelResults(phaseResults);
948
+ // Gatekeeper review
949
+ if (this.enableGatekeeper && this.gatekeeperCoordinator) {
950
+ gatekeeperReview = await this.performGatekeeperReview(phase.id, phase.agentType, mergedResult, rollingContext);
951
+ console.log(`🔍 Gatekeeper decision for [${phase.id}]: ${gatekeeperReview.decision}`);
952
+ console.log(` Confidence: ${(gatekeeperReview.confidence * 100).toFixed(1)}%`);
953
+ console.log(` Issues: ${gatekeeperReview.issues.length}`);
954
+ console.log(` Suggestions: ${gatekeeperReview.suggestions.length}`);
955
+ // Handle gatekeeper decision
956
+ switch (gatekeeperReview.decision) {
957
+ case "approve":
958
+ phaseApproved = true;
959
+ break;
960
+ case "request_revision":
961
+ if (currentAttempt < maxRevisions) {
962
+ // Prepare modified prompt based on issues
963
+ const modifiedPrompt = this.prepareRevisionPrompt(phase.prompt, gatekeeperReview.issues, gatekeeperReview.suggestions);
964
+ phase.prompt = modifiedPrompt;
965
+ currentAttempt++;
966
+ // Continue to re-execute
967
+ }
968
+ else {
969
+ console.warn(`⚠️ Max revisions reached for phase [${phase.id}], proceeding anyway`);
970
+ phaseApproved = true;
971
+ }
972
+ break;
973
+ case "restart_from_scratch":
974
+ console.error(`🚨 Gatekeeper requested restart_from_scratch for phase [${phase.id}]`);
975
+ // Clear accumulated results and restart from beginning
976
+ phaseApproved = true; // Continue but mark for restart
977
+ break;
978
+ case "escalate":
979
+ console.error(`🚨 Gatekeeper escalated phase [${phase.id}]`);
980
+ // Store the review and continue with warning
981
+ phaseApproved = true;
982
+ break;
983
+ }
984
+ }
985
+ else {
986
+ // No gatekeeper, approve automatically
987
+ phaseApproved = true;
988
+ }
989
+ // Store execution knowledge
990
+ await this.storeExecutionKnowledge(phase.id, phase.agentType, mergedResult, {
991
+ id: uuidv4(),
992
+ type: phase.agentType,
993
+ prompt: phase.prompt,
994
+ context: rollingContext,
995
+ successCriteria: `Complete phase: ${phase.id}`,
996
+ });
997
+ // Merge phase artifacts
998
+ this.artifacts.push(...mergedResult.artifacts);
999
+ }
1000
+ return { phase, result: mergedResult, gatekeeperReview };
1001
+ }
1002
+ /**
1003
+ * Perform gatekeeper review for a phase
1004
+ */
1005
+ async performGatekeeperReview(phaseId, agentType, result, context) {
1006
+ this.updateStatus("running", `Gatekeeper reviewing phase [${phaseId}]`);
1007
+ if (!this.gatekeeperCoordinator) {
1008
+ throw new Error("Gatekeeper coordinator not initialized");
1009
+ }
1010
+ const task = {
1011
+ id: uuidv4(),
1012
+ type: "gatekeeper",
1013
+ prompt: JSON.stringify(result.output),
1014
+ context: {
1015
+ ...context,
1016
+ metadata: {
1017
+ ...context.metadata,
1018
+ phaseId,
1019
+ agentType,
1020
+ workOutput: result.output,
1021
+ },
1022
+ },
1023
+ successCriteria: `Complete gatekeeper review for ${phaseId}`,
1024
+ };
1025
+ return this.gatekeeperCoordinator.submitForReview(task);
1026
+ }
1027
+ /**
1028
+ * Prepare a revised prompt based on gatekeeper feedback
1029
+ */
1030
+ prepareRevisionPrompt(originalPrompt, issues, suggestions) {
1031
+ let revisionGuidance = "\n\n## Gatekeeper Revision Requirements\n";
1032
+ if (issues.length > 0) {
1033
+ revisionGuidance += "\n### Issues to Address:\n";
1034
+ issues.forEach((issue, idx) => {
1035
+ revisionGuidance += `${idx + 1}. [${issue.severity.toUpperCase()}] ${issue.description}\n`;
1036
+ if (issue.remediation) {
1037
+ revisionGuidance += ` Remediation: ${issue.remediation}\n`;
1038
+ }
1039
+ });
1040
+ }
1041
+ if (suggestions.length > 0) {
1042
+ const mustHave = suggestions.filter((s) => s.priority === "must");
1043
+ if (mustHave.length > 0) {
1044
+ revisionGuidance += "\n### Must-Have Improvements:\n";
1045
+ mustHave.forEach((sug, idx) => {
1046
+ revisionGuidance += `${idx + 1}. ${sug.description}\n`;
1047
+ if (sug.rationale) {
1048
+ revisionGuidance += ` Rationale: ${sug.rationale}\n`;
1049
+ }
1050
+ });
1051
+ }
1052
+ }
1053
+ return `${originalPrompt}${revisionGuidance}`;
1054
+ }
1055
+ /**
1056
+ * Generate final gatekeeper report
1057
+ */
1058
+ generateGatekeeperReport(phaseResults) {
1059
+ const phaseReviews = phaseResults
1060
+ .filter((r) => r.gatekeeperReview)
1061
+ .map((r) => r.gatekeeperReview);
1062
+ const passedCount = phaseReviews.filter((r) => r.decision === "approve").length;
1063
+ const needsRevisionCount = phaseReviews.filter((r) => r.decision === "request_revision").length;
1064
+ const restartedCount = phaseReviews.filter((r) => r.decision === "restart_from_scratch").length;
1065
+ const escalatedCount = phaseReviews.filter((r) => r.decision === "escalate").length;
1066
+ const avgConfidence = phaseReviews.length > 0
1067
+ ? phaseReviews.reduce((sum, r) => sum + r.confidence, 0) /
1068
+ phaseReviews.length
1069
+ : 1;
1070
+ let overallDecision = "approve";
1071
+ if (restartedCount > 0) {
1072
+ overallDecision = "restart_from_scratch";
1073
+ }
1074
+ else if (needsRevisionCount > 0) {
1075
+ overallDecision = "request_revision";
1076
+ }
1077
+ else if (escalatedCount > 0) {
1078
+ overallDecision = "escalate";
1079
+ }
1080
+ // Build master decision
1081
+ let masterAction = "proceed";
1082
+ let reasoning = "All phases passed gatekeeper review";
1083
+ if (restartedCount > 0) {
1084
+ masterAction = "restart";
1085
+ reasoning = `${restartedCount} phase(s) required restart`;
1086
+ }
1087
+ else if (needsRevisionCount > 0) {
1088
+ masterAction = "modify_plan";
1089
+ reasoning = `${needsRevisionCount} phase(s) had issues requiring revision`;
1090
+ }
1091
+ else if (escalatedCount > 0) {
1092
+ masterAction = "stop";
1093
+ reasoning = `${escalatedCount} phase(s) escalated for human review`;
1094
+ }
1095
+ const masterDecision = {
1096
+ action: masterAction,
1097
+ reasoning,
1098
+ affectedPhases: phaseResults
1099
+ .filter((r) => r.gatekeeperReview?.decision !== "approve")
1100
+ .map((r) => r.phaseId),
1101
+ };
1102
+ const nextSteps = [];
1103
+ if (masterAction === "proceed") {
1104
+ nextSteps.push("Proceed to output synthesis");
1105
+ }
1106
+ else if (masterAction === "modify_plan") {
1107
+ nextSteps.push("Apply revisions to affected phases");
1108
+ nextSteps.push("Re-run pipeline with corrections");
1109
+ }
1110
+ else if (masterAction === "restart") {
1111
+ nextSteps.push("Clear current state");
1112
+ nextSteps.push("Restart pipeline from beginning");
1113
+ }
1114
+ else {
1115
+ nextSteps.push("Request human intervention");
1116
+ nextSteps.push("Await user instructions");
1117
+ }
1118
+ return {
1119
+ overallDecision,
1120
+ confidence: avgConfidence,
1121
+ phaseReviews,
1122
+ masterDecision,
1123
+ summary: `Gatekeeper Report: ${passedCount}/${phaseReviews.length} phases passed. Decision: ${overallDecision}`,
1124
+ nextSteps,
1125
+ revisionHistory: [],
1126
+ };
1127
+ }
1128
+ /**
1129
+ * Execute a single phase (extracted from executePhases)
1130
+ */
1131
+ async executeSinglePhase(phase, rollingContext) {
1132
+ const shouldParallelize = this.shouldParallelizePhase(phase.id, rollingContext);
1133
+ if (shouldParallelize) {
1134
+ return this.executePhaseInParallel(phase, rollingContext);
1135
+ }
1136
+ // Execute phase with single subagent
1137
+ let result = {
1138
+ taskId: uuidv4(),
1139
+ success: false,
1140
+ output: undefined,
1141
+ error: "Phase execution failed",
1142
+ metrics: { executionTime: 0, tokensUsed: 0, toolsInvoked: 0 },
1143
+ artifacts: [],
1144
+ };
1145
+ let attempt = 0;
1146
+ const modelCandidates = resolveModelCandidates(this.config.model);
1147
+ while (attempt < modelCandidates.length) {
1148
+ try {
1149
+ const modelToUse = attempt === 0 ? this.config.model : modelCandidates[attempt];
1150
+ const subAgent = this.getOrCreateSubAgent(phase.agentType, modelToUse);
1151
+ const enhancedPrompt = this.enhancePromptWithContext(phase.prompt, phase.agentType, rollingContext);
1152
+ const subTask = {
1153
+ id: uuidv4(),
1154
+ type: phase.agentType,
1155
+ prompt: enhancedPrompt,
1156
+ context: rollingContext,
1157
+ successCriteria: `Complete phase: ${phase.id}`,
1158
+ };
1159
+ this.registerSubAgent(subAgent.id, subAgent);
1160
+ result = await this.messageBus.sendTask(this.type, phase.agentType, subTask);
1161
+ this.unregisterSubAgent(subAgent.id);
1162
+ if (result.success) {
1163
+ return [result];
1164
+ }
1165
+ if (result.error && isProviderModelNotFoundError(result.error)) {
1166
+ attempt++;
1167
+ continue;
1168
+ }
1169
+ return [result];
1170
+ }
1171
+ catch (error) {
1172
+ const errorMsg = error instanceof Error ? error.message : String(error);
1173
+ if (isProviderModelNotFoundError(errorMsg)) {
1174
+ attempt++;
1175
+ if (attempt < modelCandidates.length) {
1176
+ continue;
1177
+ }
1178
+ }
1179
+ return [
1180
+ {
1181
+ taskId: uuidv4(),
1182
+ success: false,
1183
+ output: undefined,
1184
+ error: errorMsg,
1185
+ metrics: { executionTime: 0, tokensUsed: 0, toolsInvoked: 0 },
1186
+ artifacts: [],
1187
+ },
1188
+ ];
1189
+ }
1190
+ }
1191
+ return [
1192
+ {
1193
+ taskId: uuidv4(),
1194
+ success: false,
1195
+ output: undefined,
1196
+ error: "All model attempts failed",
1197
+ metrics: { executionTime: 0, tokensUsed: 0, toolsInvoked: 0 },
1198
+ artifacts: [],
1199
+ },
1200
+ ];
1201
+ }
1202
+ // -------------------------------------------------------------------------
1203
+ // Parallel Execution Helpers
1204
+ // -------------------------------------------------------------------------
1205
+ /**
1206
+ * Merge results from multiple parallel subagents into a single result
1207
+ */
1208
+ mergeParallelResults(results) {
1209
+ if (results.length === 1) {
1210
+ return results[0];
1211
+ }
1212
+ // Combine all outputs, artifacts, and metrics
1213
+ const combinedOutputs = results
1214
+ .filter((r) => r.success && r.output)
1215
+ .map((r) => r.output);
1216
+ const combinedArtifacts = results.flatMap((r) => r.artifacts || []);
1217
+ const totalExecutionTime = results.reduce((sum, r) => sum + (r.metrics?.executionTime || 0), 0);
1218
+ const totalTokensUsed = results.reduce((sum, r) => sum + (r.metrics?.tokensUsed || 0), 0);
1219
+ const totalToolsInvoked = results.reduce((sum, r) => sum + (r.metrics?.toolsInvoked || 0), 0);
1220
+ // Determine overall success
1221
+ const allSuccessful = results.every((r) => r.success);
1222
+ const someSuccessful = results.some((r) => r.success);
1223
+ return {
1224
+ taskId: uuidv4(),
1225
+ success: allSuccessful,
1226
+ output: combinedOutputs.length === 1
1227
+ ? combinedOutputs[0]
1228
+ : {
1229
+ results: combinedOutputs,
1230
+ summary: `Parallel execution: ${results.length} agents, ${someSuccessful ? "partial" : "no"} success`,
1231
+ },
1232
+ error: allSuccessful
1233
+ ? undefined
1234
+ : `Parallel execution: ${results.filter((r) => !r.success).length} of ${results.length} agents failed`,
1235
+ metrics: {
1236
+ executionTime: totalExecutionTime,
1237
+ tokensUsed: totalTokensUsed,
1238
+ toolsInvoked: totalToolsInvoked,
1239
+ },
1240
+ artifacts: combinedArtifacts,
1241
+ };
1242
+ }
1243
+ // -------------------------------------------------------------------------
1244
+ // Context Enhancement
1245
+ // -------------------------------------------------------------------------
1246
+ enhancePromptWithContext(basePrompt, _agentType, context) {
1247
+ // Generate context-aware prompt using the context system
1248
+ const codeContext = context.files
1249
+ .map((f) => `File: ${f.path}\n${f.content.substring(0, 500)}...`)
1250
+ .slice(0, 3);
1251
+ const contextPrompt = this.contextSystem.generateContextPrompt(basePrompt, codeContext);
1252
+ // Add relevant thoughts/knowledge
1253
+ const relevantThoughts = this.thoughtsManager.findThoughts({
1254
+ keywords: this.extractKeywords(basePrompt),
1255
+ limit: 3,
1256
+ });
1257
+ let knowledgeContext = "";
1258
+ if (relevantThoughts.length > 0) {
1259
+ knowledgeContext = "\n\n## Relevant Knowledge\n";
1260
+ for (const thought of relevantThoughts) {
1261
+ knowledgeContext += `### ${thought.title}\n${thought.content.substring(0, 300)}...\n\n`;
1262
+ }
1263
+ }
1264
+ // Combine everything and optimize for token efficiency
1265
+ const fullPrompt = `${contextPrompt}\n\n## Task\n${basePrompt}${knowledgeContext}`;
1266
+ // Apply DASP enhancement if enabled
1267
+ if (this.daspEnabled && this.daspController) {
1268
+ const daspResult = this.daspController.enhance({
1269
+ basePrompt: fullPrompt,
1270
+ agentType: _agentType,
1271
+ processingMode: context.mode,
1272
+ executionContext: context,
1273
+ searchKeywords: this.extractKeywords(basePrompt),
1274
+ });
1275
+ return daspResult.enhancedPrompt;
1276
+ }
1277
+ // Use token optimizer to ensure we stay within limits
1278
+ const maxTokens = 6000; // Leave room for response
1279
+ return optimizePromptForModel(fullPrompt, this.config.model, maxTokens);
1280
+ }
1281
+ extractKeywords(text) {
1282
+ // Simple keyword extraction - split by spaces and filter common words
1283
+ const words = text.toLowerCase().split(/\W+/);
1284
+ const stopWords = new Set([
1285
+ "the",
1286
+ "a",
1287
+ "an",
1288
+ "and",
1289
+ "or",
1290
+ "but",
1291
+ "in",
1292
+ "on",
1293
+ "at",
1294
+ "to",
1295
+ "for",
1296
+ "of",
1297
+ "with",
1298
+ "by",
1299
+ "is",
1300
+ "are",
1301
+ "was",
1302
+ "were",
1303
+ "be",
1304
+ "been",
1305
+ "being",
1306
+ "have",
1307
+ "has",
1308
+ "had",
1309
+ "do",
1310
+ "does",
1311
+ "did",
1312
+ "will",
1313
+ "would",
1314
+ "could",
1315
+ "should",
1316
+ "may",
1317
+ "might",
1318
+ "must",
1319
+ "can",
1320
+ "this",
1321
+ "that",
1322
+ "these",
1323
+ "those",
1324
+ ]);
1325
+ return words.filter((word) => word.length > 2 && !stopWords.has(word));
1326
+ }
1327
+ // -------------------------------------------------------------------------
1328
+ // Sub-Agent Lifecycle
1329
+ // -------------------------------------------------------------------------
1330
+ /** Get a cached sub-agent or create a fresh one from the factory */
1331
+ getOrCreateSubAgent(type, model) {
1332
+ // Reuse an idle registered agent of this type if available
1333
+ for (const [, agent] of this.activeSubAgents) {
1334
+ if (agent.type === type &&
1335
+ agent.getStatus().status === "idle" &&
1336
+ agent instanceof SubAgent &&
1337
+ (!model || agent.config.model === model)) {
1338
+ return agent;
1339
+ }
1340
+ }
1341
+ const factory = this.subAgentFactory.get(type);
1342
+ if (!factory)
1343
+ throw new Error(`No factory registered for agent type: ${type}`);
1344
+ const agent = factory();
1345
+ if (!(agent instanceof SubAgent))
1346
+ throw new Error(`Factory for ${type} did not return a SubAgent`);
1347
+ // Set model dynamically for this execution
1348
+ if (model) {
1349
+ agent.config.model = model;
1350
+ }
1351
+ // Start the message loop so the agent can receive tasks from Master
1352
+ const stopFn = agent.startListening();
1353
+ this.listenerStops.set(agent.id, stopFn);
1354
+ return agent;
1355
+ }
1356
+ /** Collect artifacts from master and all registered sub-agents */
1357
+ collectAllArtifacts() {
1358
+ const all = [...this.artifacts];
1359
+ for (const [, agent] of this.activeSubAgents) {
1360
+ all.push(...agent.getArtifacts());
1361
+ }
1362
+ return all;
1363
+ }
1364
+ synthesizeOutput(phases) {
1365
+ const summary = {
1366
+ totalPhases: phases.length,
1367
+ successfulPhases: phases.filter((p) => p.result.success).length,
1368
+ failedPhases: phases.filter((p) => !p.result.success).length,
1369
+ phases: phases.map((p) => ({
1370
+ id: p.phaseId,
1371
+ agent: p.agentType,
1372
+ success: p.result.success,
1373
+ output: p.result.output,
1374
+ error: p.result.error,
1375
+ artifacts: p.result.artifacts.length,
1376
+ gatekeeperDecision: p.gatekeeperReview?.decision || "none",
1377
+ gatekeeperConfidence: p.gatekeeperReview?.confidence || 0,
1378
+ gatekeeperIssues: p.gatekeeperReview?.issues.length || 0,
1379
+ gatekeeperSuggestions: p.gatekeeperReview?.suggestions.length || 0,
1380
+ })),
1381
+ gatekeeper: {
1382
+ enabled: this.enableGatekeeper,
1383
+ totalReviews: phases.filter((p) => p.gatekeeperReview).length,
1384
+ passedReviews: phases.filter((p) => p.gatekeeperReview?.decision === "approve").length,
1385
+ revisionRequests: phases.filter((p) => p.gatekeeperReview?.decision === "request_revision").length,
1386
+ },
1387
+ };
1388
+ return summary;
1389
+ }
1390
+ // ========================================================================
1391
+ // Sub-Agent Registry
1392
+ // ========================================================================
1393
+ registerSubAgent(agentId, agent) {
1394
+ this.activeSubAgents.set(agentId, agent);
1395
+ agent.on("statusChange", (status) => {
1396
+ this.emit("subAgentStatusChange", agentId, status);
1397
+ });
1398
+ }
1399
+ unregisterSubAgent(agentId) {
1400
+ const agent = this.activeSubAgents.get(agentId);
1401
+ if (agent) {
1402
+ // Stop the message loop
1403
+ const stopFn = this.listenerStops.get(agentId);
1404
+ if (stopFn) {
1405
+ stopFn();
1406
+ this.listenerStops.delete(agentId);
1407
+ }
1408
+ agent.stop();
1409
+ this.activeSubAgents.delete(agentId);
1410
+ }
1411
+ }
1412
+ getActiveSubAgents() {
1413
+ return new Map(this.activeSubAgents);
1414
+ }
1415
+ // ========================================================================
1416
+ // Context and Knowledge Management
1417
+ // ========================================================================
1418
+ getContextSystem() {
1419
+ return this.contextSystem;
1420
+ }
1421
+ getThoughtsManager() {
1422
+ return this.thoughtsManager;
1423
+ }
1424
+ getAgentConfigManager() {
1425
+ return this.agentConfigManager;
1426
+ }
1427
+ getCommandProcessor() {
1428
+ return this.commandProcessor;
1429
+ }
1430
+ getTeamSystem() {
1431
+ return this.teamSystem;
1432
+ }
1433
+ getPluginManager() {
1434
+ return this.pluginManager;
1435
+ }
1436
+ getTokenOptimizer() {
1437
+ return this.tokenOptimizer;
1438
+ }
1439
+ // ========================================================================
1440
+ // Gatekeeper Configuration
1441
+ // ========================================================================
1442
+ /**
1443
+ * Enable or disable gatekeeper review
1444
+ */
1445
+ setGatekeeperEnabled(enabled) {
1446
+ this.enableGatekeeper = enabled;
1447
+ console.log(`Gatekeeper ${enabled ? "enabled" : "disabled"}`);
1448
+ }
1449
+ /**
1450
+ * Check if gatekeeper is enabled
1451
+ */
1452
+ isGatekeeperEnabled() {
1453
+ return this.enableGatekeeper;
1454
+ }
1455
+ /**
1456
+ * Get the gatekeeper coordinator instance
1457
+ */
1458
+ getGatekeeperCoordinator() {
1459
+ return this.gatekeeperCoordinator;
1460
+ }
1461
+ /**
1462
+ * Configure gatekeeper review level
1463
+ */
1464
+ setGatekeeperReviewLevel(level) {
1465
+ this.gatekeeperReviewLevel = level;
1466
+ if (this.gatekeeperCoordinator) {
1467
+ // Update all gatekeeper agents with new level
1468
+ const statuses = this.gatekeeperCoordinator.getGatekeeperStatuses();
1469
+ console.log(`Gatekeeper review level set to: ${level} (${statuses.size} gatekeepers available)`);
1470
+ }
1471
+ }
1472
+ /**
1473
+ * Set maximum number of revision attempts per phase
1474
+ */
1475
+ setGatekeeperMaxRevisions(max) {
1476
+ this.gatekeeperMaxRevisions = Math.max(1, Math.min(10, max));
1477
+ console.log(`Gatekeeper max revisions set to: ${this.gatekeeperMaxRevisions}`);
1478
+ }
1479
+ /**
1480
+ * Get current gatekeeper configuration
1481
+ */
1482
+ getGatekeeperConfig() {
1483
+ return {
1484
+ enabled: this.enableGatekeeper,
1485
+ reviewLevel: this.gatekeeperReviewLevel,
1486
+ maxRevisions: this.gatekeeperMaxRevisions,
1487
+ coordinatorStatus: this.gatekeeperCoordinator?.getGatekeeperStatuses() || null,
1488
+ };
1489
+ }
1490
+ // ========================================================================
1491
+ // Command Processing
1492
+ // ========================================================================
1493
+ async processCommand(command) {
1494
+ return this.commandProcessor.processCommand(command);
1495
+ }
1496
+ // Store execution results as knowledge
1497
+ async storeExecutionKnowledge(phaseId, agentType, result, task) {
1498
+ if (!result.success || !result.output)
1499
+ return;
1500
+ try {
1501
+ // Determine thought type based on phase
1502
+ let thoughtType;
1503
+ switch (phaseId) {
1504
+ case "plan":
1505
+ thoughtType = "plan";
1506
+ break;
1507
+ case "analyze":
1508
+ thoughtType = "analysis";
1509
+ break;
1510
+ case "review":
1511
+ thoughtType = "review";
1512
+ break;
1513
+ case "research":
1514
+ thoughtType = "research";
1515
+ break;
1516
+ default:
1517
+ thoughtType = "decision";
1518
+ }
1519
+ // Create thought from execution result
1520
+ this.thoughtsManager.createThought({
1521
+ title: `${phaseId.charAt(0).toUpperCase() + phaseId.slice(1)}: ${task.prompt.substring(0, 50)}...`,
1522
+ type: thoughtType,
1523
+ content: typeof result.output === "string"
1524
+ ? result.output
1525
+ : JSON.stringify(result.output, null, 2),
1526
+ tags: [agentType, phaseId, "execution"],
1527
+ relatedFiles: task.context.files.map((f) => f.path),
1528
+ });
1529
+ // Learn patterns from successful executions
1530
+ if (result.success && typeof result.output === "object") {
1531
+ this.contextSystem.addPattern({
1532
+ type: "workflow",
1533
+ name: `${agentType} ${phaseId} pattern`,
1534
+ description: `Successful ${phaseId} execution pattern for ${agentType}`,
1535
+ examples: [
1536
+ {
1537
+ input: task.prompt,
1538
+ output: JSON.stringify(result.output),
1539
+ explanation: `Generated by ${agentType} during ${phaseId} phase`,
1540
+ },
1541
+ ],
1542
+ tags: [agentType, phaseId, "pattern"],
1543
+ });
1544
+ }
1545
+ }
1546
+ catch (error) {
1547
+ console.warn("Failed to store execution knowledge:", error);
1548
+ }
1549
+ }
1550
+ }
1551
+ // ============================================================================
1552
+ // Sub Agent Base
1553
+ // ============================================================================
1554
+ export class SubAgent extends BaseAgent {
1555
+ masterAgent = null;
1556
+ constructor(type, config, messageBus, toolBridge) {
1557
+ super(type, { mode: "subagent", ...config }, messageBus, toolBridge);
1558
+ }
1559
+ setMasterAgent(master) {
1560
+ this.masterAgent = master;
1561
+ }
1562
+ /**
1563
+ * Start the sub-agent's message loop.
1564
+ * Listens for task_dispatch messages addressed to this agent type and processes them.
1565
+ * Returns a stop function to cancel the loop.
1566
+ */
1567
+ startListening() {
1568
+ let active = true;
1569
+ const loop = async () => {
1570
+ while (active) {
1571
+ const msg = await this.messageBus.receive(this.type, 500);
1572
+ if (!msg)
1573
+ continue;
1574
+ if (msg.type === "task_dispatch") {
1575
+ const task = msg.payload;
1576
+ const result = await this.execute(task);
1577
+ // Reply to Master with the result, correlating to the dispatch message
1578
+ await this.messageBus.sendTaskResult(this.type, msg.sender, result, msg.id);
1579
+ }
1580
+ else if (msg.type === "abort_signal") {
1581
+ await this.abort("abort_signal received");
1582
+ break;
1583
+ }
1584
+ }
1585
+ };
1586
+ // Run the loop in the background (fire-and-forget)
1587
+ loop().catch((err) => {
1588
+ console.error(`[${this.type}] Message loop error:`, err);
1589
+ });
1590
+ return () => {
1591
+ active = false;
1592
+ };
1593
+ }
1594
+ async execute(task) {
1595
+ const startTime = Date.now();
1596
+ await this.start();
1597
+ try {
1598
+ const result = await this.performTask(task);
1599
+ const executionTime = Date.now() - startTime;
1600
+ this.recordTaskCompletion(0, 0, executionTime);
1601
+ this.updateStatus("completed");
1602
+ return {
1603
+ taskId: task.id,
1604
+ success: true,
1605
+ output: result,
1606
+ metrics: { executionTime, tokensUsed: 0, toolsInvoked: 0 },
1607
+ artifacts: this.artifacts,
1608
+ };
1609
+ }
1610
+ catch (error) {
1611
+ this.recordTaskFailure();
1612
+ this.updateStatus("error");
1613
+ return {
1614
+ taskId: task.id,
1615
+ success: false,
1616
+ output: undefined,
1617
+ error: error instanceof Error ? error.message : String(error),
1618
+ metrics: {
1619
+ executionTime: Date.now() - startTime,
1620
+ tokensUsed: 0,
1621
+ toolsInvoked: 0,
1622
+ },
1623
+ artifacts: [],
1624
+ };
1625
+ }
1626
+ finally {
1627
+ await this.stop();
1628
+ }
1629
+ }
1630
+ }
1631
+ // ============================================================================
1632
+ // Specialized Sub-Agents
1633
+ // ============================================================================
1634
+ export class PlannerAgent extends SubAgent {
1635
+ async performTask(task) {
1636
+ this.updateStatus("running", "Creating execution plan");
1637
+ const prompt = `Create a detailed execution plan for the following task:
1638
+
1639
+ Task: ${task.prompt}
1640
+
1641
+ Context: ${task.context.files.length} files provided, mode: ${task.context.metadata?.mode || "full"}
1642
+
1643
+ Please provide a structured plan with:
1644
+ 1. Goal analysis
1645
+ 2. Step-by-step breakdown
1646
+ 3. Estimated complexity (low/medium/high)
1647
+ 4. Estimated time
1648
+ 5. Required tools/resources
1649
+ 6. Potential risks
1650
+
1651
+ Format as structured JSON.`;
1652
+ const response = await this.callModel(prompt);
1653
+ if (!response) {
1654
+ return this.localPlanFallback(task);
1655
+ }
1656
+ try {
1657
+ return JSON.parse(response);
1658
+ }
1659
+ catch {
1660
+ return {
1661
+ goal: task.prompt,
1662
+ plan: response,
1663
+ ...this.estimateComplexityFromText(task.prompt),
1664
+ };
1665
+ }
1666
+ }
1667
+ /**
1668
+ * Local fallback: parse task description to build a real plan without a model.
1669
+ */
1670
+ localPlanFallback(task) {
1671
+ const description = task.prompt || "";
1672
+ const lower = description.toLowerCase();
1673
+ const fileCount = task.context.files.length;
1674
+ // --- Extract action verbs and targets ---
1675
+ const actionPatterns = [
1676
+ {
1677
+ verb: "create",
1678
+ regex: /\b(?:create|build|generate|scaffold|add|implement|write)\s+(?:a\s+)?(.+?)(?:\s+(?:and|then|,|\.)|$)/gi,
1679
+ },
1680
+ {
1681
+ verb: "refactor",
1682
+ regex: /\b(?:refactor|restructure|reorganize|redesign)\s+(?:the\s+)?(.+?)(?:\s+(?:and|then|,|\.)|$)/gi,
1683
+ },
1684
+ {
1685
+ verb: "fix",
1686
+ regex: /\b(?:fix|repair|resolve|patch|debug)\s+(?:the\s+)?(.+?)(?:\s+(?:and|then|,|\.)|$)/gi,
1687
+ },
1688
+ {
1689
+ verb: "update",
1690
+ regex: /\b(?:update|modify|change|edit|adjust|improve)\s+(?:the\s+)?(.+?)(?:\s+(?:and|then|,|\.)|$)/gi,
1691
+ },
1692
+ {
1693
+ verb: "test",
1694
+ regex: /\b(?:test|verify|validate|check)\s+(?:the\s+)?(.+?)(?:\s+(?:and|then|,|\.)|$)/gi,
1695
+ },
1696
+ {
1697
+ verb: "delete",
1698
+ regex: /\b(?:delete|remove|drop|clean\s*up)\s+(?:the\s+)?(.+?)(?:\s+(?:and|then|,|\.)|$)/gi,
1699
+ },
1700
+ {
1701
+ verb: "migrate",
1702
+ regex: /\b(?:migrate|move|transfer|port)\s+(?:the\s+)?(.+?)(?:\s+(?:and|then|,|\.)|$)/gi,
1703
+ },
1704
+ {
1705
+ verb: "deploy",
1706
+ regex: /\b(?:deploy|publish|release|ship)\s+(?:the\s+)?(.+?)(?:\s+(?:and|then|,|\.)|$)/gi,
1707
+ },
1708
+ {
1709
+ verb: "analyze",
1710
+ regex: /\b(?:analyze|inspect|examine|review|audit)\s+(?:the\s+)?(.+?)(?:\s+(?:and|then|,|\.)|$)/gi,
1711
+ },
1712
+ {
1713
+ verb: "configure",
1714
+ regex: /\b(?:configure|setup|set\s*up|initialize|install)\s+(?:the\s+)?(.+?)(?:\s+(?:and|then|,|\.)|$)/gi,
1715
+ },
1716
+ ];
1717
+ const extractedActions = [];
1718
+ for (const { verb, regex } of actionPatterns) {
1719
+ let match;
1720
+ while ((match = regex.exec(description)) !== null) {
1721
+ const target = match[1]?.trim().substring(0, 80) || "the task";
1722
+ if (target.length > 2) {
1723
+ extractedActions.push({ verb, target });
1724
+ }
1725
+ }
1726
+ }
1727
+ // --- Build steps from extracted actions or sentence splitting ---
1728
+ let steps;
1729
+ if (extractedActions.length > 0) {
1730
+ steps = extractedActions.map((action, idx) => ({
1731
+ step: idx + 1,
1732
+ action: action.verb,
1733
+ description: `${action.verb.charAt(0).toUpperCase() + action.verb.slice(1)} ${action.target}`,
1734
+ dependsOn: idx > 0 ? [idx] : [],
1735
+ }));
1736
+ }
1737
+ else {
1738
+ // Split by conjunctions, commas, or sentence boundaries
1739
+ const segments = description
1740
+ .split(/(?:\s+and\s+|\s*,\s*|\.\s+|\s+then\s+)/i)
1741
+ .map((s) => s.trim())
1742
+ .filter((s) => s.length > 3);
1743
+ if (segments.length > 1) {
1744
+ steps = segments.map((seg, idx) => ({
1745
+ step: idx + 1,
1746
+ action: "execute",
1747
+ description: seg.charAt(0).toUpperCase() + seg.slice(1),
1748
+ dependsOn: idx > 0 ? [idx] : [],
1749
+ }));
1750
+ }
1751
+ else {
1752
+ // Single task — generate analysis → implementation → verification steps
1753
+ steps = [
1754
+ {
1755
+ step: 1,
1756
+ action: "analyze",
1757
+ description: `Analyze requirements: ${description.substring(0, 100)}`,
1758
+ dependsOn: [],
1759
+ },
1760
+ {
1761
+ step: 2,
1762
+ action: "implement",
1763
+ description: `Implement changes for: ${description.substring(0, 80)}`,
1764
+ dependsOn: [1],
1765
+ },
1766
+ {
1767
+ step: 3,
1768
+ action: "verify",
1769
+ description: "Verify implementation and check for regressions",
1770
+ dependsOn: [2],
1771
+ },
1772
+ ];
1773
+ }
1774
+ }
1775
+ // Add file-scope step if files are present
1776
+ if (fileCount > 0 && steps.length > 0) {
1777
+ steps.unshift({
1778
+ step: 0,
1779
+ action: "scan",
1780
+ description: `Scan ${fileCount} provided file(s) for context`,
1781
+ dependsOn: [],
1782
+ });
1783
+ // Re-number and adjust deps
1784
+ steps = steps.map((s, idx) => ({
1785
+ ...s,
1786
+ step: idx + 1,
1787
+ dependsOn: idx === 0 ? [] : s.dependsOn.map((d) => d + 1),
1788
+ }));
1789
+ }
1790
+ // --- Estimate complexity ---
1791
+ const complexity = this.estimateComplexityFromText(lower);
1792
+ // --- Required tools based on keywords ---
1793
+ const tools = [];
1794
+ if (/\b(file|read|write|edit|create)\b/i.test(lower))
1795
+ tools.push("file_operations");
1796
+ if (/\b(test|spec|jest|mocha|vitest)\b/i.test(lower))
1797
+ tools.push("testing_framework");
1798
+ if (/\b(git|commit|branch|merge|pr)\b/i.test(lower))
1799
+ tools.push("version_control");
1800
+ if (/\b(npm|yarn|pnpm|install|package)\b/i.test(lower))
1801
+ tools.push("package_manager");
1802
+ if (/\b(api|endpoint|http|fetch|request)\b/i.test(lower))
1803
+ tools.push("http_client");
1804
+ if (/\b(db|database|sql|query|schema|migration)\b/i.test(lower))
1805
+ tools.push("database");
1806
+ if (/\b(docker|container|deploy|ci|cd)\b/i.test(lower))
1807
+ tools.push("devops");
1808
+ if (tools.length === 0)
1809
+ tools.push("code_editor");
1810
+ // --- Risks ---
1811
+ const risks = [];
1812
+ if (/\b(refactor|migrate|restructure)\b/i.test(lower))
1813
+ risks.push("Breaking changes in dependent code");
1814
+ if (/\b(delete|remove|drop)\b/i.test(lower))
1815
+ risks.push("Data or code loss if not backed up");
1816
+ if (/\b(security|auth|password|token|secret)\b/i.test(lower))
1817
+ risks.push("Security-sensitive changes require careful review");
1818
+ if (fileCount > 10)
1819
+ risks.push(`Large scope: ${fileCount} files may have cascading impacts`);
1820
+ if (/\b(production|prod|live)\b/i.test(lower))
1821
+ risks.push("Changes target production environment");
1822
+ if (risks.length === 0)
1823
+ risks.push("Standard development risk — verify with tests");
1824
+ return {
1825
+ goal: task.prompt,
1826
+ steps,
1827
+ ...complexity,
1828
+ requiredTools: tools,
1829
+ risks,
1830
+ fileScope: fileCount,
1831
+ note: "Plan generated via local keyword analysis (no model available)",
1832
+ };
1833
+ }
1834
+ /**
1835
+ * Estimate complexity from task description text.
1836
+ */
1837
+ estimateComplexityFromText(text) {
1838
+ const lower = text.toLowerCase();
1839
+ const wordCount = text.split(/\s+/).length;
1840
+ // High complexity indicators
1841
+ const highKeywords = /\b(refactor|migrate|integrate|redesign|architect|overhaul|rewrite|restructure|distributed|concurrent|multi.?thread)\b/i;
1842
+ // Low complexity indicators
1843
+ const lowKeywords = /\b(rename|move|delete|remove|typo|comment|log|print|bump|update version|fix typo)\b/i;
1844
+ let complexity;
1845
+ let time;
1846
+ if (highKeywords.test(lower) || wordCount > 80) {
1847
+ complexity = "high";
1848
+ time = "15-30 minutes";
1849
+ }
1850
+ else if (lowKeywords.test(lower) && wordCount < 20) {
1851
+ complexity = "low";
1852
+ time = "1-3 minutes";
1853
+ }
1854
+ else {
1855
+ complexity = "medium";
1856
+ time = "5-10 minutes";
1857
+ }
1858
+ return { estimatedComplexity: complexity, estimatedTime: time };
1859
+ }
1860
+ }
1861
+ export class AnalyzerAgent extends SubAgent {
1862
+ async performTask(task) {
1863
+ this.updateStatus("running", "Analyzing code");
1864
+ const files = task.context.files;
1865
+ const fileContents = files
1866
+ .map((f) => `File: ${f.path}\n\n${f.content}`)
1867
+ .join("\n\n---\n\n");
1868
+ const prompt = `Analyze the following code files for issues, patterns, and improvements:
1869
+
1870
+ ${fileContents}
1871
+
1872
+ Please provide:
1873
+ 1. Code quality issues
1874
+ 2. Security vulnerabilities
1875
+ 3. Performance bottlenecks
1876
+ 4. Best practice violations
1877
+ 5. Complexity metrics
1878
+ 6. Maintainability assessment
1879
+
1880
+ Format as structured JSON.`;
1881
+ const response = await this.callModel(prompt);
1882
+ if (!response) {
1883
+ return this.localAnalysisFallback(files);
1884
+ }
1885
+ // Parse AI response
1886
+ try {
1887
+ const analysis = JSON.parse(response);
1888
+ return analysis;
1889
+ }
1890
+ catch {
1891
+ return {
1892
+ file: task.context.files[0]?.path || "unknown",
1893
+ analysis: response,
1894
+ issues: [],
1895
+ metrics: this.computeFileMetrics(files),
1896
+ };
1897
+ }
1898
+ }
1899
+ /**
1900
+ * Local fallback: perform real static analysis on provided files without a model.
1901
+ */
1902
+ localAnalysisFallback(files) {
1903
+ const allIssues = [];
1904
+ const perFileMetrics = [];
1905
+ for (const file of files) {
1906
+ const content = file.content || "";
1907
+ const lines = content.split("\n");
1908
+ const lineCount = lines.length;
1909
+ // --- Basic metrics ---
1910
+ let blankLines = 0;
1911
+ let commentLines = 0;
1912
+ let importCount = 0;
1913
+ let exportCount = 0;
1914
+ // --- Function/class detection ---
1915
+ const functionMatches = content.match(/\b(?:function\s+\w+|(?:const|let|var)\s+\w+\s*=\s*(?:async\s+)?(?:\([^)]*\)|[a-zA-Z_$]\w*)\s*=>|(?:async\s+)?(?:get|set|static\s+)?\w+\s*\([^)]*\)\s*\{)/g) || [];
1916
+ const classMatches = content.match(/\bclass\s+\w+/g) || [];
1917
+ // --- Cyclomatic complexity (count decision points) ---
1918
+ let cyclomaticComplexity = 1; // Base
1919
+ const decisionPatterns = /\b(?:if|else\s+if|case|for|while|do|catch|\?\?|&&|\|\||[?]:)/g;
1920
+ // @ts-ignore
1921
+ let _decMatch;
1922
+ while ((_decMatch = decisionPatterns.exec(content)) !== null) {
1923
+ cyclomaticComplexity++;
1924
+ }
1925
+ // --- Nesting depth analysis ---
1926
+ let maxNesting = 0;
1927
+ let currentNesting = 0;
1928
+ for (const ch of content) {
1929
+ if (ch === "{") {
1930
+ currentNesting++;
1931
+ if (currentNesting > maxNesting)
1932
+ maxNesting = currentNesting;
1933
+ }
1934
+ else if (ch === "}") {
1935
+ currentNesting = Math.max(0, currentNesting - 1);
1936
+ }
1937
+ }
1938
+ // --- Longest function length ---
1939
+ let longestFn = 0;
1940
+ let fnNesting = 0;
1941
+ let fnStartLine = -1;
1942
+ for (let i = 0; i < lines.length; i++) {
1943
+ const line = lines[i];
1944
+ const trimmed = line.trim();
1945
+ // Track blank/comment/import/export lines
1946
+ if (trimmed === "") {
1947
+ blankLines++;
1948
+ }
1949
+ else if (trimmed.startsWith("//") ||
1950
+ trimmed.startsWith("/*") ||
1951
+ trimmed.startsWith("*")) {
1952
+ commentLines++;
1953
+ }
1954
+ if (/^import\s/.test(trimmed))
1955
+ importCount++;
1956
+ if (/^export\s/.test(trimmed))
1957
+ exportCount++;
1958
+ // Simple function length tracking
1959
+ if (/(?:function\s+\w+|=>\s*\{|\)\s*\{)\s*$/.test(trimmed) &&
1960
+ fnStartLine === -1) {
1961
+ fnStartLine = i;
1962
+ fnNesting = 0;
1963
+ }
1964
+ if (fnStartLine !== -1) {
1965
+ for (const ch of line) {
1966
+ if (ch === "{")
1967
+ fnNesting++;
1968
+ if (ch === "}")
1969
+ fnNesting--;
1970
+ }
1971
+ if (fnNesting <= 0 && fnStartLine !== -1) {
1972
+ const fnLen = i - fnStartLine + 1;
1973
+ if (fnLen > longestFn)
1974
+ longestFn = fnLen;
1975
+ fnStartLine = -1;
1976
+ }
1977
+ }
1978
+ }
1979
+ perFileMetrics.push({
1980
+ file: file.path,
1981
+ linesOfCode: lineCount,
1982
+ blankLines,
1983
+ commentLines,
1984
+ functions: functionMatches.length,
1985
+ classes: classMatches.length,
1986
+ imports: importCount,
1987
+ exports: exportCount,
1988
+ cyclomaticComplexity,
1989
+ maxNestingDepth: maxNesting,
1990
+ longestFunctionLength: longestFn,
1991
+ });
1992
+ // --- Issue detection ---
1993
+ // Long functions (>50 lines)
1994
+ if (longestFn > 50) {
1995
+ allIssues.push({
1996
+ type: "maintainability",
1997
+ severity: longestFn > 100 ? "high" : "medium",
1998
+ file: file.path,
1999
+ description: `Longest function is ${longestFn} lines (threshold: 50)`,
2000
+ recommendation: "Break large functions into smaller, focused helper functions",
2001
+ });
2002
+ }
2003
+ // Deep nesting (>4 levels)
2004
+ if (maxNesting > 4) {
2005
+ allIssues.push({
2006
+ type: "complexity",
2007
+ severity: maxNesting > 6 ? "high" : "medium",
2008
+ file: file.path,
2009
+ description: `Maximum nesting depth is ${maxNesting} levels (threshold: 4)`,
2010
+ recommendation: "Use early returns, guard clauses, or extract nested logic into functions",
2011
+ });
2012
+ }
2013
+ // High cyclomatic complexity
2014
+ if (cyclomaticComplexity > 20) {
2015
+ allIssues.push({
2016
+ type: "complexity",
2017
+ severity: cyclomaticComplexity > 40 ? "high" : "medium",
2018
+ file: file.path,
2019
+ description: `Cyclomatic complexity is ${cyclomaticComplexity} (threshold: 20)`,
2020
+ recommendation: "Simplify control flow; consider strategy pattern or lookup tables",
2021
+ });
2022
+ }
2023
+ // TODO/FIXME comments
2024
+ for (let i = 0; i < lines.length; i++) {
2025
+ const line = lines[i];
2026
+ if (/\bTODO\b/i.test(line)) {
2027
+ allIssues.push({
2028
+ type: "best_practice",
2029
+ severity: "low",
2030
+ file: file.path,
2031
+ line: i + 1,
2032
+ description: `TODO comment: ${line.trim().substring(0, 100)}`,
2033
+ recommendation: "Address TODO items or create tracking issues",
2034
+ });
2035
+ }
2036
+ if (/\bFIXME\b/i.test(line)) {
2037
+ allIssues.push({
2038
+ type: "best_practice",
2039
+ severity: "medium",
2040
+ file: file.path,
2041
+ line: i + 1,
2042
+ description: `FIXME comment: ${line.trim().substring(0, 100)}`,
2043
+ recommendation: "FIXME indicates known bugs — prioritize fixing",
2044
+ });
2045
+ }
2046
+ }
2047
+ // console.log statements
2048
+ const consoleLogLines = [];
2049
+ for (let i = 0; i < lines.length; i++) {
2050
+ if (/\bconsole\.(log|debug|info)\b/.test(lines[i]) &&
2051
+ !/\/\//.test(lines[i].split("console")[0])) {
2052
+ consoleLogLines.push(i + 1);
2053
+ }
2054
+ }
2055
+ if (consoleLogLines.length > 0) {
2056
+ allIssues.push({
2057
+ type: "best_practice",
2058
+ severity: "low",
2059
+ file: file.path,
2060
+ line: consoleLogLines[0],
2061
+ description: `Found ${consoleLogLines.length} console.log/debug/info statement(s) at lines: ${consoleLogLines.slice(0, 5).join(", ")}${consoleLogLines.length > 5 ? "..." : ""}`,
2062
+ recommendation: "Remove debug logging or replace with a proper logging framework",
2063
+ });
2064
+ }
2065
+ // Unused imports detection (basic: imported names not found elsewhere in file)
2066
+ const importLines = lines.filter((l) => /^import\s/.test(l.trim()));
2067
+ for (const impLine of importLines) {
2068
+ const namedImports = impLine.match(/\{\s*([^}]+)\s*\}/);
2069
+ if (namedImports) {
2070
+ const names = namedImports[1]
2071
+ .split(",")
2072
+ .map((n) => n
2073
+ .trim()
2074
+ .split(/\s+as\s+/)
2075
+ .pop()
2076
+ ?.trim())
2077
+ .filter(Boolean);
2078
+ for (const name of names) {
2079
+ if (!name)
2080
+ continue;
2081
+ // Count occurrences outside import lines
2082
+ const restOfFile = lines
2083
+ .filter((l) => !/^import\s/.test(l.trim()))
2084
+ .join("\n");
2085
+ const nameRegex = new RegExp(`\\b${name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\b`, "g");
2086
+ const usageCount = (restOfFile.match(nameRegex) || []).length;
2087
+ if (usageCount === 0) {
2088
+ allIssues.push({
2089
+ type: "best_practice",
2090
+ severity: "low",
2091
+ file: file.path,
2092
+ description: `Potentially unused import: '${name}'`,
2093
+ recommendation: "Remove unused imports to keep the codebase clean",
2094
+ });
2095
+ }
2096
+ }
2097
+ }
2098
+ }
2099
+ // 'any' type usage in TypeScript files
2100
+ if (/\.tsx?$/.test(file.path)) {
2101
+ const anyMatches = [];
2102
+ for (let i = 0; i < lines.length; i++) {
2103
+ if (/:\s*any\b|as\s+any\b|<any>/.test(lines[i])) {
2104
+ anyMatches.push(i + 1);
2105
+ }
2106
+ }
2107
+ if (anyMatches.length > 0) {
2108
+ allIssues.push({
2109
+ type: "type_safety",
2110
+ severity: anyMatches.length > 5 ? "medium" : "low",
2111
+ file: file.path,
2112
+ line: anyMatches[0],
2113
+ description: `Found ${anyMatches.length} 'any' type usage(s) at lines: ${anyMatches.slice(0, 5).join(", ")}${anyMatches.length > 5 ? "..." : ""}`,
2114
+ recommendation: "Replace 'any' with specific types for better type safety",
2115
+ });
2116
+ }
2117
+ }
2118
+ // Missing error handling in async functions
2119
+ const asyncFnPattern = /\basync\s+(?:function\s+\w+|\w+\s*=\s*async|\([^)]*\)\s*=>)/g;
2120
+ const asyncCount = (content.match(asyncFnPattern) || []).length;
2121
+ const tryCatchCount = (content.match(/\btry\s*\{/g) || []).length;
2122
+ const catchCount = (content.match(/\.catch\s*\(/g) || []).length;
2123
+ if (asyncCount > 0 && tryCatchCount + catchCount < asyncCount * 0.5) {
2124
+ allIssues.push({
2125
+ type: "error_handling",
2126
+ severity: "medium",
2127
+ file: file.path,
2128
+ description: `${asyncCount} async function(s) but only ${tryCatchCount + catchCount} error handler(s) — potential unhandled rejections`,
2129
+ recommendation: "Wrap async operations in try/catch or add .catch() handlers",
2130
+ });
2131
+ }
2132
+ // Hardcoded strings that look like secrets
2133
+ for (let i = 0; i < lines.length; i++) {
2134
+ if (/(?:password|secret|apiKey|api_key|token|AUTH)\s*[:=]\s*["'][^"']{8,}["']/i.test(lines[i])) {
2135
+ allIssues.push({
2136
+ type: "security",
2137
+ severity: "high",
2138
+ file: file.path,
2139
+ line: i + 1,
2140
+ description: "Potential hardcoded secret or credential detected",
2141
+ recommendation: "Move secrets to environment variables or a secure vault",
2142
+ });
2143
+ }
2144
+ }
2145
+ // Very long lines (>120 chars)
2146
+ const longLines = lines.filter((l) => l.length > 120).length;
2147
+ if (longLines > 10) {
2148
+ allIssues.push({
2149
+ type: "style",
2150
+ severity: "low",
2151
+ file: file.path,
2152
+ description: `${longLines} lines exceed 120 characters`,
2153
+ recommendation: "Consider reformatting for readability (use prettier or similar)",
2154
+ });
2155
+ }
2156
+ }
2157
+ // --- Aggregate metrics ---
2158
+ const totalLOC = perFileMetrics.reduce((s, m) => s + m.linesOfCode, 0);
2159
+ const totalFunctions = perFileMetrics.reduce((s, m) => s + m.functions, 0);
2160
+ const totalClasses = perFileMetrics.reduce((s, m) => s + m.classes, 0);
2161
+ const avgComplexity = perFileMetrics.length > 0
2162
+ ? perFileMetrics.reduce((s, m) => s + m.cyclomaticComplexity, 0) /
2163
+ perFileMetrics.length
2164
+ : 0;
2165
+ // Maintainability index (simplified Halstead-based formula)
2166
+ // Higher is better. Penalize for high complexity, deep nesting, low comment ratio.
2167
+ const commentRatio = totalLOC > 0
2168
+ ? perFileMetrics.reduce((s, m) => s + m.commentLines, 0) / totalLOC
2169
+ : 0;
2170
+ const avgNesting = perFileMetrics.length > 0
2171
+ ? perFileMetrics.reduce((s, m) => s + m.maxNestingDepth, 0) /
2172
+ perFileMetrics.length
2173
+ : 0;
2174
+ const maintainabilityIndex = Math.max(0, Math.min(100, 100 - avgComplexity * 0.5 - avgNesting * 3 + commentRatio * 30));
2175
+ // Sort issues by severity
2176
+ const severityOrder = {
2177
+ high: 0,
2178
+ medium: 1,
2179
+ low: 2,
2180
+ };
2181
+ allIssues.sort((a, b) => (severityOrder[a.severity] ?? 3) - (severityOrder[b.severity] ?? 3));
2182
+ return {
2183
+ filesAnalyzed: files.map((f) => f.path),
2184
+ issues: allIssues,
2185
+ metrics: {
2186
+ totalLinesOfCode: totalLOC,
2187
+ totalFunctions,
2188
+ totalClasses,
2189
+ averageCyclomaticComplexity: Math.round(avgComplexity * 10) / 10,
2190
+ maintainabilityIndex: Math.round(maintainabilityIndex * 10) / 10,
2191
+ },
2192
+ perFileMetrics,
2193
+ summary: {
2194
+ totalIssues: allIssues.length,
2195
+ highSeverity: allIssues.filter((i) => i.severity === "high").length,
2196
+ mediumSeverity: allIssues.filter((i) => i.severity === "medium").length,
2197
+ lowSeverity: allIssues.filter((i) => i.severity === "low").length,
2198
+ issueTypes: [...new Set(allIssues.map((i) => i.type))],
2199
+ },
2200
+ note: "Analysis performed via local static analysis (no model available)",
2201
+ };
2202
+ }
2203
+ /**
2204
+ * Compute basic file metrics for JSON parse fallback.
2205
+ */
2206
+ computeFileMetrics(files) {
2207
+ const totalLOC = files.reduce((sum, f) => sum + (f.content || "").split("\n").length, 0);
2208
+ const totalFunctions = files.reduce((sum, f) => sum + ((f.content || "").match(/\bfunction\s+\w+/g) || []).length, 0);
2209
+ const avgLineLength = totalLOC > 0
2210
+ ? Math.round(files.reduce((sum, f) => sum + f.content.length, 0) / totalLOC)
2211
+ : 0;
2212
+ return {
2213
+ linesOfCode: totalLOC,
2214
+ functions: totalFunctions,
2215
+ averageLineLength: avgLineLength,
2216
+ };
2217
+ }
2218
+ }
2219
+ export class ReviewerAgent extends SubAgent {
2220
+ async performTask(task) {
2221
+ this.updateStatus("running", "Reviewing changes");
2222
+ const analysisOutput = task.context.metadata?.analyze_output;
2223
+ const prompt = `Review the following code analysis and provide feedback:
2224
+
2225
+ Analysis: ${JSON.stringify(analysisOutput, null, 2)}
2226
+
2227
+ Task: ${task.prompt}
2228
+
2229
+ Please provide:
2230
+ 1. Overall assessment
2231
+ 2. Fixes reviewed
2232
+ 3. Blocking issues (if any)
2233
+ 4. Recommendations for improvement
2234
+
2235
+ Format as structured JSON.`;
2236
+ const response = await this.callModel(prompt);
2237
+ if (!response) {
2238
+ return this.localReviewFallback(task);
2239
+ }
2240
+ try {
2241
+ return JSON.parse(response);
2242
+ }
2243
+ catch {
2244
+ return {
2245
+ reviewId: uuidv4(),
2246
+ review: response,
2247
+ // @ts-ignore
2248
+ ...this.localReviewFallback(task),
2249
+ };
2250
+ }
2251
+ }
2252
+ /**
2253
+ * Local fallback: review code quality based on analysis output and file content.
2254
+ */
2255
+ localReviewFallback(task) {
2256
+ const analysisOutput = task.context.metadata?.analyze_output;
2257
+ const files = task.context.files;
2258
+ const fixesReviewed = [];
2259
+ const blockingIssues = [];
2260
+ const recommendations = [];
2261
+ let assessmentPoints = 0;
2262
+ let maxPoints = 0;
2263
+ // --- Review analysis output if available ---
2264
+ if (analysisOutput && typeof analysisOutput === "object") {
2265
+ const analysis = analysisOutput;
2266
+ const issues = analysis.issues || [];
2267
+ // Check for blocking issues from analysis
2268
+ for (const issue of issues) {
2269
+ const severity = issue.severity || "low";
2270
+ if (severity === "high" || severity === "critical") {
2271
+ blockingIssues.push({
2272
+ severity,
2273
+ description: issue.description ||
2274
+ "High severity issue from analysis",
2275
+ file: issue.file,
2276
+ });
2277
+ }
2278
+ }
2279
+ if (issues.length > 0) {
2280
+ fixesReviewed.push({
2281
+ file: "analysis",
2282
+ status: blockingIssues.length > 0 ? "needs_attention" : "reviewed",
2283
+ notes: `Analysis found ${issues.length} issue(s), ${blockingIssues.length} blocking`,
2284
+ });
2285
+ }
2286
+ }
2287
+ // --- Review each file for common quality issues ---
2288
+ for (const file of files) {
2289
+ const content = file.content || "";
2290
+ const lines = content.split("\n");
2291
+ const fileIssues = [];
2292
+ maxPoints += 5; // 5 quality checks per file
2293
+ // 1. Error handling presence
2294
+ const hasAsync = /\basync\b/.test(content);
2295
+ const hasTryCatch = /\btry\s*\{/.test(content);
2296
+ const hasCatch = /\.catch\s*\(/.test(content);
2297
+ if (hasAsync && !hasTryCatch && !hasCatch) {
2298
+ fileIssues.push("Missing error handling for async operations");
2299
+ }
2300
+ else {
2301
+ assessmentPoints++;
2302
+ }
2303
+ // 2. Type safety (TS files)
2304
+ if (/\.tsx?$/.test(file.path)) {
2305
+ const anyCount = (content.match(/:\s*any\b|as\s+any\b/g) || []).length;
2306
+ if (anyCount > 3) {
2307
+ fileIssues.push(`Excessive 'any' types (${anyCount} occurrences) — weakens type safety`);
2308
+ }
2309
+ else {
2310
+ assessmentPoints++;
2311
+ }
2312
+ }
2313
+ else {
2314
+ assessmentPoints++; // Non-TS files get a pass on type safety
2315
+ }
2316
+ // 3. Naming conventions check
2317
+ const badNames = content.match(/\b(?:const|let|var)\s+(?:[a-z]|_\d)\s*=/g) || [];
2318
+ const singleCharVars = badNames.filter((m) => /\s[a-z_]\s*=/.test(m) && !/\b(i|j|k|x|y|z|e|_)\s*=/.test(m));
2319
+ if (singleCharVars.length > 3) {
2320
+ fileIssues.push(`Multiple single-character variable names — use descriptive names`);
2321
+ }
2322
+ else {
2323
+ assessmentPoints++;
2324
+ }
2325
+ // 4. Null/undefined safety
2326
+ const nullAccess = (content.match(/\.\w+\.\w+/g) || []).length; // chain access count
2327
+ const optionalChaining = (content.match(/\?\./g) || []).length;
2328
+ const nullChecks = (content.match(/!==?\s*(?:null|undefined)|typeof\s+\w+\s*!==?\s*["']undefined["']/g) || []).length;
2329
+ if (nullAccess > 10 && optionalChaining === 0 && nullChecks < 2) {
2330
+ fileIssues.push("Multiple property chains without null checks — consider optional chaining (?.)");
2331
+ }
2332
+ else {
2333
+ assessmentPoints++;
2334
+ }
2335
+ // 5. Hardcoded values
2336
+ const hardcoded = [];
2337
+ for (let i = 0; i < lines.length; i++) {
2338
+ // Magic numbers (excluding common ones like 0, 1, 2, -1, 100)
2339
+ if (/(?:timeout|delay|interval|limit|max|min|size|count|threshold)\s*[:=]\s*\d{3,}/i.test(lines[i])) {
2340
+ hardcoded.push(`line ${i + 1}: hardcoded numeric value`);
2341
+ }
2342
+ }
2343
+ if (hardcoded.length > 2) {
2344
+ fileIssues.push(`${hardcoded.length} hardcoded magic numbers — extract as named constants`);
2345
+ }
2346
+ else {
2347
+ assessmentPoints++;
2348
+ }
2349
+ if (fileIssues.length > 0) {
2350
+ fixesReviewed.push({
2351
+ file: file.path,
2352
+ status: fileIssues.some((i) => i.includes("error handling") || i.includes("type safety"))
2353
+ ? "needs_attention"
2354
+ : "minor_issues",
2355
+ notes: fileIssues.join("; "),
2356
+ });
2357
+ for (const issue of fileIssues) {
2358
+ recommendations.push(`[${file.path}] ${issue}`);
2359
+ }
2360
+ }
2361
+ else {
2362
+ fixesReviewed.push({
2363
+ file: file.path,
2364
+ status: "passed",
2365
+ notes: "No significant quality issues detected",
2366
+ });
2367
+ }
2368
+ }
2369
+ // --- Calculate confidence based on how much analysis was possible ---
2370
+ const confidence = maxPoints > 0
2371
+ ? Math.round((assessmentPoints / maxPoints) * 100) / 100
2372
+ : 0.3;
2373
+ // --- Build overall assessment ---
2374
+ let overallAssessment;
2375
+ if (blockingIssues.length > 0) {
2376
+ overallAssessment = `Blocking issues found: ${blockingIssues.length} high/critical issue(s) require attention before proceeding.`;
2377
+ }
2378
+ else if (recommendations.length > 3) {
2379
+ overallAssessment = `Code has ${recommendations.length} quality concerns that should be addressed. Review the recommendations for improvements.`;
2380
+ }
2381
+ else if (recommendations.length > 0) {
2382
+ overallAssessment = `Code is generally acceptable with ${recommendations.length} minor suggestion(s). Consider addressing them in a follow-up.`;
2383
+ }
2384
+ else if (files.length === 0) {
2385
+ overallAssessment =
2386
+ "No files provided for review. Unable to perform meaningful analysis.";
2387
+ }
2388
+ else {
2389
+ overallAssessment = `Code passed basic quality checks across ${files.length} file(s). No significant issues detected.`;
2390
+ }
2391
+ // --- Standard recommendations ---
2392
+ if (files.length > 0) {
2393
+ const totalLOC = files.reduce((s, f) => s + (f.content || "").split("\n").length, 0);
2394
+ if (totalLOC > 500 && !files.some((f) => /test|spec/i.test(f.path))) {
2395
+ recommendations.push("Consider adding unit tests — no test files detected for this codebase");
2396
+ }
2397
+ }
2398
+ return {
2399
+ reviewId: uuidv4(),
2400
+ fixesReviewed,
2401
+ overallAssessment,
2402
+ blockingIssues,
2403
+ recommendations,
2404
+ confidence,
2405
+ qualityScore: maxPoints > 0 ? `${assessmentPoints}/${maxPoints}` : "N/A",
2406
+ note: "Review performed via local static analysis (no model available)",
2407
+ };
2408
+ }
2409
+ }
2410
+ export class RewriterAgent extends SubAgent {
2411
+ async performTask(task) {
2412
+ this.updateStatus("running", "Applying fixes");
2413
+ const analysisOutput = task.context.metadata?.analyze_output;
2414
+ const reviewOutput = task.context.metadata?.review_output;
2415
+ const files = task.context.files;
2416
+ const fileContents = files
2417
+ .map((f) => `File: ${f.path}\n\n${f.content}`)
2418
+ .join("\n\n---\n\n");
2419
+ const prompt = `Apply fixes to the following code based on the analysis and review:
2420
+
2421
+ Analysis: ${JSON.stringify(analysisOutput, null, 2)}
2422
+
2423
+ Review: ${JSON.stringify(reviewOutput, null, 2)}
2424
+
2425
+ Task: ${task.prompt}
2426
+
2427
+ Code Files:
2428
+ ${fileContents}
2429
+
2430
+ Please provide:
2431
+ 1. Specific code changes needed
2432
+ 2. New files to create (if any)
2433
+ 3. Files to delete (if any)
2434
+ 4. Explanation of each change
2435
+
2436
+ Format as structured JSON with file paths as keys and change objects as values.`;
2437
+ const response = await this.callModel(prompt);
2438
+ if (!response) {
2439
+ return this.localRewriteFallback(files, analysisOutput, reviewOutput);
2440
+ }
2441
+ try {
2442
+ return JSON.parse(response);
2443
+ }
2444
+ catch {
2445
+ return {
2446
+ changes: [],
2447
+ newFiles: [],
2448
+ deletedFiles: [],
2449
+ explanation: response,
2450
+ };
2451
+ }
2452
+ }
2453
+ /**
2454
+ * Local fallback: apply basic code transformations without a model.
2455
+ */
2456
+ localRewriteFallback(
2457
+ // @ts-ignore
2458
+ _files, _analysisOutput, _reviewOutput) {
2459
+ const changes = [];
2460
+ for (const file of files) {
2461
+ const content = file.content || "";
2462
+ if (!content.trim())
2463
+ continue;
2464
+ const transformations = [];
2465
+ let result = content;
2466
+ // --- 1. Remove trailing whitespace ---
2467
+ const beforeTrailing = result;
2468
+ result = result.replace(/[ \t]+$/gm, "");
2469
+ if (result !== beforeTrailing) {
2470
+ transformations.push("Removed trailing whitespace");
2471
+ }
2472
+ // --- 2. Normalize line endings to LF ---
2473
+ if (result.includes("\r\n")) {
2474
+ result = result.replace(/\r\n/g, "\n");
2475
+ transformations.push("Normalized line endings (CRLF → LF)");
2476
+ }
2477
+ // --- 3. Normalize indentation (detect and standardize) ---
2478
+ const lines = result.split("\n");
2479
+ const tabLines = lines.filter((l) => l.startsWith("\t")).length;
2480
+ const spaceLines = lines.filter((l) => /^ {2,}/.test(l)).length;
2481
+ // Only normalize if there's a clear mix
2482
+ if (tabLines > 0 && spaceLines > tabLines * 2) {
2483
+ // Convert tabs to 2-space indent
2484
+ result = result.replace(/^\t+/gm, (match) => " ".repeat(match.length));
2485
+ transformations.push("Converted tab indentation to 2-space");
2486
+ }
2487
+ // --- 4. Add missing semicolons for JS/TS files ---
2488
+ if (/\.[jt]sx?$/.test(file.path)) {
2489
+ const resultLines = result.split("\n");
2490
+ const semiFixedLines = resultLines.map((line) => {
2491
+ const trimmed = line.trim();
2492
+ // Skip lines that don't need semicolons
2493
+ if (!trimmed ||
2494
+ trimmed.endsWith(";") ||
2495
+ trimmed.endsWith("{") ||
2496
+ trimmed.endsWith("}") ||
2497
+ trimmed.endsWith("(") ||
2498
+ trimmed.endsWith(",") ||
2499
+ trimmed.endsWith("*/") ||
2500
+ trimmed.startsWith("//") ||
2501
+ trimmed.startsWith("/*") ||
2502
+ trimmed.startsWith("*") ||
2503
+ trimmed.startsWith("import ") ||
2504
+ trimmed.startsWith("export ") ||
2505
+ /^(?:if|else|for|while|switch|case|default|try|catch|finally|do|class|interface|type|enum)\b/.test(trimmed) ||
2506
+ trimmed.endsWith("=>") ||
2507
+ trimmed.endsWith("?") ||
2508
+ trimmed.endsWith(":") ||
2509
+ trimmed.endsWith("||") ||
2510
+ trimmed.endsWith("&&")) {
2511
+ return line;
2512
+ }
2513
+ // Add semicolons to statement-like lines ending with closing paren, string, number, identifier
2514
+ if (/(?:\)|\]|['"`]|\w)\s*$/.test(trimmed)) {
2515
+ // Check if it's a standalone statement (assignment, return, throw, const/let/var)
2516
+ if (/^(?:return|throw|const|let|var|yield)\b/.test(trimmed) ||
2517
+ /^[\w$]+\s*[+\-*/]?=\s/.test(trimmed) ||
2518
+ /^(?:await\s+)?[\w$]+\s*\(/.test(trimmed)) {
2519
+ return line.replace(/\s*$/, ";");
2520
+ }
2521
+ }
2522
+ return line;
2523
+ });
2524
+ const semiResult = semiFixedLines.join("\n");
2525
+ if (semiResult !== result) {
2526
+ result = semiResult;
2527
+ transformations.push("Added missing semicolons to statement lines");
2528
+ }
2529
+ }
2530
+ // --- 5. Sort imports alphabetically ---
2531
+ if (/\.[jt]sx?$/.test(file.path)) {
2532
+ const importResult = this.sortImports(result);
2533
+ if (importResult !== result) {
2534
+ result = importResult;
2535
+ transformations.push("Sorted import statements alphabetically");
2536
+ }
2537
+ }
2538
+ // --- 6. Remove consecutive blank lines (keep max 1) ---
2539
+ const beforeBlank = result;
2540
+ result = result.replace(/\n{3,}/g, "\n\n");
2541
+ if (result !== beforeBlank) {
2542
+ transformations.push("Collapsed excessive blank lines");
2543
+ }
2544
+ // --- 7. Ensure file ends with newline ---
2545
+ if (!result.endsWith("\n")) {
2546
+ result += "\n";
2547
+ transformations.push("Added trailing newline");
2548
+ }
2549
+ if (transformations.length > 0) {
2550
+ changes.push({
2551
+ file: file.path,
2552
+ original: content,
2553
+ rewritten: result,
2554
+ transformations,
2555
+ });
2556
+ }
2557
+ }
2558
+ return {
2559
+ changes,
2560
+ newFiles: [],
2561
+ deletedFiles: [],
2562
+ summary: changes.length > 0
2563
+ ? `Applied basic formatting transformations to ${changes.length} file(s): ${changes.map((c) => c.transformations.join(", ")).join("; ")}`
2564
+ : "No transformations needed — files already well-formatted",
2565
+ note: "Only basic code formatting applied (no model available for semantic rewrites)",
2566
+ };
2567
+ }
2568
+ /**
2569
+ * Sort import statements alphabetically while preserving groups.
2570
+ */
2571
+ sortImports(content) {
2572
+ const lines = content.split("\n");
2573
+ const importGroups = [];
2574
+ let groupStart = -1;
2575
+ let currentImports = [];
2576
+ for (let i = 0; i < lines.length; i++) {
2577
+ const trimmed = lines[i].trim();
2578
+ if (/^import\s/.test(trimmed)) {
2579
+ if (groupStart === -1)
2580
+ groupStart = i;
2581
+ currentImports.push(lines[i]);
2582
+ }
2583
+ else {
2584
+ if (currentImports.length > 1) {
2585
+ importGroups.push({
2586
+ start: groupStart,
2587
+ end: groupStart + currentImports.length - 1,
2588
+ imports: [...currentImports],
2589
+ });
2590
+ }
2591
+ groupStart = -1;
2592
+ currentImports = [];
2593
+ }
2594
+ }
2595
+ // Handle trailing import group
2596
+ if (currentImports.length > 1) {
2597
+ importGroups.push({
2598
+ start: groupStart,
2599
+ end: groupStart + currentImports.length - 1,
2600
+ imports: [...currentImports],
2601
+ });
2602
+ }
2603
+ if (importGroups.length === 0)
2604
+ return content;
2605
+ // Sort each group and rebuild
2606
+ const resultLines = [...lines];
2607
+ let changed = false;
2608
+ for (const group of importGroups) {
2609
+ const sorted = [...group.imports].sort((a, b) => {
2610
+ // Extract the source module for comparison
2611
+ const srcA = (a.match(/from\s+["']([^"']+)["']/) || [])[1] || a;
2612
+ const srcB = (b.match(/from\s+["']([^"']+)["']/) || [])[1] || b;
2613
+ return srcA.localeCompare(srcB);
2614
+ });
2615
+ const isAlreadySorted = sorted.every((line, idx) => line === group.imports[idx]);
2616
+ if (!isAlreadySorted) {
2617
+ for (let i = 0; i < sorted.length; i++) {
2618
+ resultLines[group.start + i] = sorted[i];
2619
+ }
2620
+ changed = true;
2621
+ }
2622
+ }
2623
+ return changed ? resultLines.join("\n") : content;
2624
+ }
2625
+ }
2626
+ export class UiDesignerAgent extends SubAgent {
2627
+ async performTask(task) {
2628
+ this.updateStatus("running", "Generating UI mockups");
2629
+ const files = task.context.files;
2630
+ const uiFiles = files.filter((f) => f.content.includes("div") ||
2631
+ f.content.includes("button") ||
2632
+ f.content.includes("<") ||
2633
+ /\.(tsx?|jsx?|html|svelte|vue)$/.test(f.path));
2634
+ if (uiFiles.length === 0) {
2635
+ // No UI files found - still analyze the task prompt for UI generation
2636
+ return this.localUiDesignFallback([], task);
2637
+ }
2638
+ const uiContent = uiFiles
2639
+ .map((f) => `File: ${f.path}\n\n${f.content}`)
2640
+ .join("\n\n---\n\n");
2641
+ const prompt = `Design UI improvements for the following code:
2642
+
2643
+ ${uiContent}
2644
+
2645
+ Task: ${task.prompt}
2646
+
2647
+ Please provide:
2648
+ 1. Detected UI frameworks
2649
+ 2. ASCII mockups for new UI components
2650
+ 3. Accessibility issues
2651
+ 4. Design suggestions
2652
+
2653
+ Format as structured JSON.`;
2654
+ const response = await this.callModel(prompt);
2655
+ if (!response) {
2656
+ return this.localUiDesignFallback(uiFiles, task);
2657
+ }
2658
+ try {
2659
+ return JSON.parse(response);
2660
+ }
2661
+ catch {
2662
+ return {
2663
+ detectedFrameworks: this.detectFrameworks(uiFiles),
2664
+ mockups: [
2665
+ {
2666
+ type: "ascii",
2667
+ content: response,
2668
+ viewport: "desktop",
2669
+ },
2670
+ ],
2671
+ accessibilityIssues: this.detectA11yIssues(uiFiles),
2672
+ designSuggestions: [],
2673
+ };
2674
+ }
2675
+ }
2676
+ /**
2677
+ * Local fallback: generate a real UI analysis and skeletal component.
2678
+ */
2679
+ localUiDesignFallback(uiFiles, task) {
2680
+ const prompt = task.prompt || "";
2681
+ const lower = prompt.toLowerCase();
2682
+ // --- Detect frameworks from file content ---
2683
+ const frameworks = this.detectFrameworks(uiFiles);
2684
+ // --- Detect UI keywords to determine component type ---
2685
+ const uiKeywords = {
2686
+ form: [
2687
+ "form",
2688
+ "input",
2689
+ "submit",
2690
+ "field",
2691
+ "validation",
2692
+ "textarea",
2693
+ "select",
2694
+ ],
2695
+ button: ["button", "btn", "click", "action", "cta"],
2696
+ layout: [
2697
+ "layout",
2698
+ "grid",
2699
+ "flex",
2700
+ "container",
2701
+ "wrapper",
2702
+ "sidebar",
2703
+ "header",
2704
+ "footer",
2705
+ ],
2706
+ page: ["page", "view", "screen", "route", "dashboard"],
2707
+ modal: ["modal", "dialog", "popup", "overlay"],
2708
+ list: ["list", "table", "grid", "card", "item", "row"],
2709
+ nav: ["nav", "navigation", "menu", "breadcrumb", "tab", "sidebar"],
2710
+ };
2711
+ const detectedTypes = [];
2712
+ for (const [type, keywords] of Object.entries(uiKeywords)) {
2713
+ if (keywords.some((kw) => lower.includes(kw))) {
2714
+ detectedTypes.push(type);
2715
+ }
2716
+ }
2717
+ if (detectedTypes.length === 0)
2718
+ detectedTypes.push("component");
2719
+ // --- Detect accessibility issues in existing files ---
2720
+ const accessibilityIssues = this.detectA11yIssues(uiFiles);
2721
+ // --- Generate skeletal component based on detected type ---
2722
+ const primaryType = detectedTypes[0];
2723
+ const componentName = this.inferComponentName(prompt);
2724
+ const isReact = frameworks.includes("react") || frameworks.includes("next.js");
2725
+ let componentCode;
2726
+ let asciiMockup;
2727
+ switch (primaryType) {
2728
+ case "form":
2729
+ asciiMockup = [
2730
+ `+${"─".repeat(40)}+`,
2731
+ `│ ${componentName.padEnd(38)}│`,
2732
+ `│${"─".repeat(40)}│`,
2733
+ `│ Label: │`,
2734
+ `│ ┌──────────────────────────────────┐ │`,
2735
+ `│ │ Input field │ │`,
2736
+ `│ └──────────────────────────────────┘ │`,
2737
+ `│ Label: │`,
2738
+ `│ ┌──────────────────────────────────┐ │`,
2739
+ `│ │ Input field │ │`,
2740
+ `│ └──────────────────────────────────┘ │`,
2741
+ `│ │`,
2742
+ `│ ┌──────────────────────┐ │`,
2743
+ `│ │ Submit │ │`,
2744
+ `│ └──────────────────────┘ │`,
2745
+ `+${"─".repeat(40)}+`,
2746
+ ].join("\n");
2747
+ componentCode = isReact
2748
+ ? this.generateReactForm(componentName)
2749
+ : this.generateHtmlForm(componentName);
2750
+ break;
2751
+ case "modal":
2752
+ asciiMockup = [
2753
+ ` ┌${"─".repeat(36)}┐`,
2754
+ ` │ ${componentName.padEnd(30)} [X] │`,
2755
+ ` │${"─".repeat(36)}│`,
2756
+ ` │ │`,
2757
+ ` │ Content area │`,
2758
+ ` │ │`,
2759
+ ` │${"─".repeat(36)}│`,
2760
+ ` │ [Cancel] [Confirm] │`,
2761
+ ` └${"─".repeat(36)}┘`,
2762
+ ].join("\n");
2763
+ componentCode = isReact
2764
+ ? this.generateReactModal(componentName)
2765
+ : this.generateHtmlModal(componentName);
2766
+ break;
2767
+ case "nav":
2768
+ asciiMockup = [
2769
+ `+${"─".repeat(50)}+`,
2770
+ `│ Logo │ Home │ About │ Contact │ ☰ │`,
2771
+ `+${"─".repeat(50)}+`,
2772
+ ].join("\n");
2773
+ componentCode = isReact
2774
+ ? this.generateReactNav(componentName)
2775
+ : `<nav role="navigation" aria-label="Main navigation">\n <ul>\n <li><a href="/">Home</a></li>\n <li><a href="/about">About</a></li>\n <li><a href="/contact">Contact</a></li>\n </ul>\n</nav>`;
2776
+ break;
2777
+ case "list":
2778
+ asciiMockup = [
2779
+ `+${"─".repeat(42)}+`,
2780
+ `│ ${componentName.padEnd(40)}│`,
2781
+ `│${"─".repeat(42)}│`,
2782
+ `│ ┌──────────────────────────────────┐ │`,
2783
+ `│ │ Item 1 [Action] │ │`,
2784
+ `│ ├──────────────────────────────────┤ │`,
2785
+ `│ │ Item 2 [Action] │ │`,
2786
+ `│ ├──────────────────────────────────┤ │`,
2787
+ `│ │ Item 3 [Action] │ │`,
2788
+ `│ └──────────────────────────────────┘ │`,
2789
+ `+${"─".repeat(42)}+`,
2790
+ ].join("\n");
2791
+ componentCode = isReact
2792
+ ? this.generateReactList(componentName)
2793
+ : `<section aria-label="${componentName}">\n <h2>${componentName}</h2>\n <ul role="list">\n <li role="listitem">Item 1</li>\n <li role="listitem">Item 2</li>\n </ul>\n</section>`;
2794
+ break;
2795
+ default: // generic component / page / layout / button
2796
+ asciiMockup = [
2797
+ `+${"─".repeat(44)}+`,
2798
+ `│ ${componentName.padEnd(42)}│`,
2799
+ `│${"─".repeat(44)}│`,
2800
+ `│ │`,
2801
+ `│ ┌────────────────────────────────────────┐│`,
2802
+ `│ │ ││`,
2803
+ `│ │ Main Content Area ││`,
2804
+ `│ │ ││`,
2805
+ `│ └────────────────────────────────────────┘│`,
2806
+ `│ │`,
2807
+ `│ [Primary Action] [Secondary Action] │`,
2808
+ `+${"─".repeat(44)}+`,
2809
+ ].join("\n");
2810
+ componentCode = isReact
2811
+ ? this.generateReactComponent(componentName)
2812
+ : `<section aria-label="${componentName}">\n <h2>${componentName}</h2>\n <div class="content">\n <p>Main content area</p>\n </div>\n <div class="actions">\n <button type="button">Primary Action</button>\n </div>\n</section>`;
2813
+ }
2814
+ // --- Design suggestions based on analysis ---
2815
+ const designSuggestions = [];
2816
+ if (!uiFiles.some((f) => f.content.includes("@media") || f.content.includes("responsive"))) {
2817
+ designSuggestions.push("Add responsive breakpoints for mobile/tablet viewports");
2818
+ }
2819
+ if (!uiFiles.some((f) => f.content.includes("dark") || f.content.includes("theme"))) {
2820
+ designSuggestions.push("Consider implementing dark mode / theme support");
2821
+ }
2822
+ if (!uiFiles.some((f) => /loading|spinner|skeleton/i.test(f.content))) {
2823
+ designSuggestions.push("Add loading states (skeleton screens or spinners) for async content");
2824
+ }
2825
+ if (!uiFiles.some((f) => /error|fallback/i.test(f.content))) {
2826
+ designSuggestions.push("Add error boundary and fallback UI for graceful error handling");
2827
+ }
2828
+ designSuggestions.push("Ensure consistent spacing and typography using design tokens");
2829
+ return {
2830
+ detectedFrameworks: frameworks,
2831
+ detectedComponentTypes: detectedTypes,
2832
+ mockups: [
2833
+ {
2834
+ type: "ascii",
2835
+ content: asciiMockup,
2836
+ viewport: "desktop",
2837
+ componentName,
2838
+ },
2839
+ ],
2840
+ generatedComponent: {
2841
+ name: componentName,
2842
+ code: componentCode,
2843
+ framework: isReact ? "React/TSX" : "HTML",
2844
+ },
2845
+ accessibilityIssues,
2846
+ designSuggestions,
2847
+ note: "UI design generated via local keyword analysis (no model available)",
2848
+ };
2849
+ }
2850
+ /**
2851
+ * Detect UI frameworks from file contents.
2852
+ */
2853
+ detectFrameworks(files) {
2854
+ const frameworks = new Set();
2855
+ for (const file of files) {
2856
+ const c = file.content || "";
2857
+ if (/\bimport\s.*from\s+["']react["']|React\.createElement|jsx/i.test(c))
2858
+ frameworks.add("react");
2859
+ if (/\bimport\s.*from\s+["']next/i.test(c) ||
2860
+ /\bgetServerSideProps|getStaticProps\b/.test(c))
2861
+ frameworks.add("next.js");
2862
+ if (/\bimport\s.*from\s+["']vue["']|createApp|defineComponent/.test(c))
2863
+ frameworks.add("vue");
2864
+ if (/\bimport\s.*from\s+["']svelte["']|<script\s+lang=["']ts["']/.test(c) &&
2865
+ file.path.endsWith(".svelte"))
2866
+ frameworks.add("svelte");
2867
+ if (/tailwind|className=["'][^"']*(?:flex|grid|p-|m-|text-)/.test(c))
2868
+ frameworks.add("tailwind");
2869
+ if (/\bimport\s.*from\s+["']@radix-ui/.test(c))
2870
+ frameworks.add("radix-ui");
2871
+ if (/\bimport\s.*from\s+["']@shadcn/.test(c) ||
2872
+ /\bimport\s.*from\s+["']@\/components\/ui/.test(c))
2873
+ frameworks.add("shadcn/ui");
2874
+ if (/<html|<body|<head/i.test(c))
2875
+ frameworks.add("html");
2876
+ }
2877
+ return [...frameworks];
2878
+ }
2879
+ /**
2880
+ * Detect accessibility issues in UI files.
2881
+ */
2882
+ detectA11yIssues(files) {
2883
+ const issues = [];
2884
+ for (const file of files) {
2885
+ const c = file.content || "";
2886
+ // Images without alt
2887
+ if (/<img\b(?![^>]*\balt\b)/i.test(c)) {
2888
+ issues.push({
2889
+ file: file.path,
2890
+ issue: "Image element missing 'alt' attribute",
2891
+ severity: "high",
2892
+ });
2893
+ }
2894
+ // Buttons without text content or aria-label
2895
+ if (/<button[^>]*>\s*<(?:img|svg|icon)/i.test(c) &&
2896
+ !/<button[^>]*aria-label/i.test(c)) {
2897
+ issues.push({
2898
+ file: file.path,
2899
+ issue: "Icon-only button missing aria-label",
2900
+ severity: "high",
2901
+ });
2902
+ }
2903
+ // onClick on non-interactive elements
2904
+ if (/\bonClick\b.*<(?:div|span|p)\b/i.test(c) &&
2905
+ !/role=["']button["']/i.test(c)) {
2906
+ issues.push({
2907
+ file: file.path,
2908
+ issue: "Click handler on non-interactive element without role='button'",
2909
+ severity: "medium",
2910
+ });
2911
+ }
2912
+ // Missing form labels
2913
+ if (/<input\b(?![^>]*aria-label)(?![^>]*id=["'](\w+)["'])/i.test(c)) {
2914
+ issues.push({
2915
+ file: file.path,
2916
+ issue: "Form input potentially missing associated label",
2917
+ severity: "medium",
2918
+ });
2919
+ }
2920
+ // Missing lang attribute
2921
+ if (/<html\b(?![^>]*\blang\b)/i.test(c)) {
2922
+ issues.push({
2923
+ file: file.path,
2924
+ issue: "HTML element missing 'lang' attribute",
2925
+ severity: "medium",
2926
+ });
2927
+ }
2928
+ // Color contrast (heuristic: very light text colors)
2929
+ if (/color:\s*#(?:eee|ddd|ccc|fff|fafafa)/i.test(c)) {
2930
+ issues.push({
2931
+ file: file.path,
2932
+ issue: "Potentially low color contrast text detected",
2933
+ severity: "low",
2934
+ });
2935
+ }
2936
+ }
2937
+ return issues;
2938
+ }
2939
+ /**
2940
+ * Infer component name from task prompt.
2941
+ */
2942
+ inferComponentName(prompt) {
2943
+ // Try to extract a meaningful name from the prompt
2944
+ const match = prompt.match(/(?:create|build|generate|design|add)\s+(?:a\s+)?(\w[\w\s]{1,30}?)(?:\s+(?:component|page|form|modal|dialog|view|section|panel|widget))?$/i);
2945
+ if (match) {
2946
+ return match[1]
2947
+ .trim()
2948
+ .split(/\s+/)
2949
+ .map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase())
2950
+ .join("");
2951
+ }
2952
+ return "Component";
2953
+ }
2954
+ generateReactForm(name) {
2955
+ return `import React, { FormEvent, useState } from "react";
2956
+
2957
+ interface ${name}Props {
2958
+ onSubmit?: (data: Record<string, string>) => void;
2959
+ }
2960
+
2961
+ export function ${name}({ onSubmit }: ${name}Props) {
2962
+ const [formData, setFormData] = useState<Record<string, string>>({});
2963
+
2964
+ const handleSubmit = (e: FormEvent) => {
2965
+ e.preventDefault();
2966
+ onSubmit?.(formData);
2967
+ };
2968
+
2969
+ return (
2970
+ <form onSubmit={handleSubmit} role="form" aria-label="${name}">
2971
+ <fieldset>
2972
+ <legend>${name}</legend>
2973
+ <div>
2974
+ <label htmlFor="${name.toLowerCase()}-field-1">Field 1</label>
2975
+ <input
2976
+ id="${name.toLowerCase()}-field-1"
2977
+ type="text"
2978
+ required
2979
+ aria-required="true"
2980
+ onChange={(e) => setFormData(prev => ({ ...prev, field1: e.target.value }))}
2981
+ />
2982
+ </div>
2983
+ <div>
2984
+ <label htmlFor="${name.toLowerCase()}-field-2">Field 2</label>
2985
+ <input
2986
+ id="${name.toLowerCase()}-field-2"
2987
+ type="text"
2988
+ onChange={(e) => setFormData(prev => ({ ...prev, field2: e.target.value }))}
2989
+ />
2990
+ </div>
2991
+ <button type="submit">Submit</button>
2992
+ </fieldset>
2993
+ </form>
2994
+ );
2995
+ }`;
2996
+ }
2997
+ generateHtmlForm(name) {
2998
+ return `<form role="form" aria-label="${name}">
2999
+ <fieldset>
3000
+ <legend>${name}</legend>
3001
+ <div>
3002
+ <label for="${name.toLowerCase()}-field-1">Field 1</label>
3003
+ <input id="${name.toLowerCase()}-field-1" type="text" required aria-required="true" />
3004
+ </div>
3005
+ <div>
3006
+ <label for="${name.toLowerCase()}-field-2">Field 2</label>
3007
+ <input id="${name.toLowerCase()}-field-2" type="text" />
3008
+ </div>
3009
+ <button type="submit">Submit</button>
3010
+ </fieldset>
3011
+ </form>`;
3012
+ }
3013
+ generateReactModal(name) {
3014
+ return `import React, { useEffect, useRef } from "react";
3015
+
3016
+ interface ${name}Props {
3017
+ isOpen: boolean;
3018
+ onClose: () => void;
3019
+ title: string;
3020
+ children: React.ReactNode;
3021
+ }
3022
+
3023
+ export function ${name}({ isOpen, onClose, title, children }: ${name}Props) {
3024
+ const dialogRef = useRef<HTMLDialogElement>(null);
3025
+
3026
+ useEffect(() => {
3027
+ if (isOpen) {
3028
+ dialogRef.current?.showModal();
3029
+ } else {
3030
+ dialogRef.current?.close();
3031
+ }
3032
+ }, [isOpen]);
3033
+
3034
+ return (
3035
+ <dialog
3036
+ ref={dialogRef}
3037
+ role="dialog"
3038
+ aria-modal="true"
3039
+ aria-label={title}
3040
+ onClose={onClose}
3041
+ >
3042
+ <header>
3043
+ <h2>{title}</h2>
3044
+ <button type="button" onClick={onClose} aria-label="Close dialog">✕</button>
3045
+ </header>
3046
+ <div role="document">{children}</div>
3047
+ <footer>
3048
+ <button type="button" onClick={onClose}>Cancel</button>
3049
+ <button type="button">Confirm</button>
3050
+ </footer>
3051
+ </dialog>
3052
+ );
3053
+ }`;
3054
+ }
3055
+ generateHtmlModal(name) {
3056
+ return `<dialog role="dialog" aria-modal="true" aria-label="${name}">
3057
+ <header>
3058
+ <h2>${name}</h2>
3059
+ <button type="button" aria-label="Close dialog">✕</button>
3060
+ </header>
3061
+ <div role="document">
3062
+ <p>Modal content goes here</p>
3063
+ </div>
3064
+ <footer>
3065
+ <button type="button">Cancel</button>
3066
+ <button type="button">Confirm</button>
3067
+ </footer>
3068
+ </dialog>`;
3069
+ }
3070
+ generateReactNav(name) {
3071
+ return `import React from "react";
3072
+
3073
+ interface NavItem {
3074
+ label: string;
3075
+ href: string;
3076
+ active?: boolean;
3077
+ }
3078
+
3079
+ interface ${name}Props {
3080
+ items: NavItem[];
3081
+ logo?: React.ReactNode;
3082
+ }
3083
+
3084
+ export function ${name}({ items, logo }: ${name}Props) {
3085
+ return (
3086
+ <nav role="navigation" aria-label="Main navigation">
3087
+ {logo && <div aria-hidden="true">{logo}</div>}
3088
+ <ul role="menubar">
3089
+ {items.map((item) => (
3090
+ <li key={item.href} role="none">
3091
+ <a
3092
+ href={item.href}
3093
+ role="menuitem"
3094
+ aria-current={item.active ? "page" : undefined}
3095
+ >
3096
+ {item.label}
3097
+ </a>
3098
+ </li>
3099
+ ))}
3100
+ </ul>
3101
+ </nav>
3102
+ );
3103
+ }`;
3104
+ }
3105
+ generateReactList(name) {
3106
+ return `import React from "react";
3107
+
3108
+ interface ListItem {
3109
+ id: string;
3110
+ title: string;
3111
+ description?: string;
3112
+ }
3113
+
3114
+ interface ${name}Props {
3115
+ items: ListItem[];
3116
+ onAction?: (id: string) => void;
3117
+ emptyMessage?: string;
3118
+ }
3119
+
3120
+ export function ${name}({ items, onAction, emptyMessage = "No items found" }: ${name}Props) {
3121
+ if (items.length === 0) {
3122
+ return (
3123
+ <section aria-label="${name}">
3124
+ <p role="status">{emptyMessage}</p>
3125
+ </section>
3126
+ );
3127
+ }
3128
+
3129
+ return (
3130
+ <section aria-label="${name}">
3131
+ <h2>${name}</h2>
3132
+ <ul role="list">
3133
+ {items.map((item) => (
3134
+ <li key={item.id} role="listitem">
3135
+ <div>
3136
+ <h3>{item.title}</h3>
3137
+ {item.description && <p>{item.description}</p>}
3138
+ </div>
3139
+ {onAction && (
3140
+ <button
3141
+ type="button"
3142
+ onClick={() => onAction(item.id)}
3143
+ aria-label={\`Action for \${item.title}\`}
3144
+ >
3145
+ Action
3146
+ </button>
3147
+ )}
3148
+ </li>
3149
+ ))}
3150
+ </ul>
3151
+ </section>
3152
+ );
3153
+ }`;
3154
+ }
3155
+ generateReactComponent(name) {
3156
+ return `import React from "react";
3157
+
3158
+ interface ${name}Props {
3159
+ title?: string;
3160
+ children?: React.ReactNode;
3161
+ }
3162
+
3163
+ export function ${name}({ title = "${name}", children }: ${name}Props) {
3164
+ return (
3165
+ <section aria-label={title}>
3166
+ <header>
3167
+ <h2>{title}</h2>
3168
+ </header>
3169
+ <div role="region" aria-label="Content">
3170
+ {children || <p>Main content area</p>}
3171
+ </div>
3172
+ <footer>
3173
+ <button type="button">Primary Action</button>
3174
+ </footer>
3175
+ </section>
3176
+ );
3177
+ }`;
3178
+ }
3179
+ }
3180
+ export class ResearcherAgent extends SubAgent {
3181
+ async performTask(task) {
3182
+ this.updateStatus("running", "Researching information");
3183
+ const query = task.prompt;
3184
+ let webResult = null;
3185
+ try {
3186
+ webResult = await this.webSearch(query, 3);
3187
+ }
3188
+ catch (error) {
3189
+ console.warn(`Web search failed for query "${query}": ${error}`);
3190
+ // Continue without web results
3191
+ }
3192
+ if (webResult) {
3193
+ const analysisPrompt = `Analyze the following web search results for the query "${query}":
3194
+
3195
+ ${webResult}
3196
+
3197
+ Please extract:
3198
+ 1. Key findings
3199
+ 2. Verified facts
3200
+ 3. Relevant sources
3201
+ 4. Summary
3202
+
3203
+ Format as structured JSON.`;
3204
+ const analysisResponse = await this.callModel(analysisPrompt);
3205
+ if (analysisResponse) {
3206
+ try {
3207
+ return JSON.parse(analysisResponse);
3208
+ }
3209
+ catch {
3210
+ // Fall through to local analysis with web results
3211
+ }
3212
+ }
3213
+ }
3214
+ return this.localResearchFallback(query, webResult, task);
3215
+ }
3216
+ /**
3217
+ * Local fallback: extract topics, search local files, and return findings.
3218
+ */
3219
+ localResearchFallback(_query, _webResult, _task) {
3220
+ const lower = query.toLowerCase();
3221
+ // --- Extract key topics from the query ---
3222
+ const stopWords = new Set([
3223
+ "the",
3224
+ "a",
3225
+ "an",
3226
+ "and",
3227
+ "or",
3228
+ "but",
3229
+ "in",
3230
+ "on",
3231
+ "at",
3232
+ "to",
3233
+ "for",
3234
+ "of",
3235
+ "with",
3236
+ "by",
3237
+ "from",
3238
+ "is",
3239
+ "are",
3240
+ "was",
3241
+ "were",
3242
+ "be",
3243
+ "been",
3244
+ "being",
3245
+ "have",
3246
+ "has",
3247
+ "had",
3248
+ "do",
3249
+ "does",
3250
+ "did",
3251
+ "will",
3252
+ "would",
3253
+ "could",
3254
+ "should",
3255
+ "may",
3256
+ "might",
3257
+ "must",
3258
+ "can",
3259
+ "this",
3260
+ "that",
3261
+ "these",
3262
+ "those",
3263
+ "what",
3264
+ "how",
3265
+ "why",
3266
+ "when",
3267
+ "where",
3268
+ "which",
3269
+ "who",
3270
+ "about",
3271
+ "into",
3272
+ "through",
3273
+ "during",
3274
+ "before",
3275
+ "after",
3276
+ "it",
3277
+ "its",
3278
+ "my",
3279
+ "your",
3280
+ "we",
3281
+ "they",
3282
+ "them",
3283
+ "our",
3284
+ "not",
3285
+ "no",
3286
+ "if",
3287
+ ]);
3288
+ const words = query
3289
+ .split(/\W+/)
3290
+ .filter((w) => w.length > 2 && !stopWords.has(w.toLowerCase()));
3291
+ const topics = [...new Set(words.map((w) => w.toLowerCase()))];
3292
+ // --- Identify technical concepts ---
3293
+ const technicalPatterns = {
3294
+ programming_language: /\b(typescript|javascript|python|rust|go|java|c\+\+|ruby|php|swift|kotlin)\b/i,
3295
+ framework: /\b(react|next\.?js|vue|angular|svelte|express|fastify|django|flask|spring)\b/i,
3296
+ tool: /\b(webpack|vite|esbuild|rollup|babel|eslint|prettier|jest|vitest|docker|kubernetes)\b/i,
3297
+ concept: /\b(api|rest|graphql|websocket|auth|oauth|jwt|database|sql|nosql|cache|queue|cicd|devops)\b/i,
3298
+ pattern: /\b(singleton|factory|observer|strategy|decorator|middleware|hook|context|provider|reducer)\b/i,
3299
+ };
3300
+ const identifiedConcepts = {};
3301
+ for (const [category, pattern] of Object.entries(technicalPatterns)) {
3302
+ const matches = query.match(pattern);
3303
+ if (matches) {
3304
+ identifiedConcepts[category] = [
3305
+ ...new Set(matches.map((m) => m.toLowerCase())),
3306
+ ];
3307
+ }
3308
+ }
3309
+ // --- Scan local context files for relevant content ---
3310
+ const localFindings = [];
3311
+ const contextFiles = task.context.files;
3312
+ for (const file of contextFiles) {
3313
+ const content = (file.content || "").toLowerCase();
3314
+ const matchedTopics = topics.filter((topic) => content.includes(topic));
3315
+ if (matchedTopics.length > 0) {
3316
+ // Determine relevance based on match density
3317
+ const matchRatio = matchedTopics.length / Math.max(topics.length, 1);
3318
+ const relevance = matchRatio > 0.5 ? "high" : matchRatio > 0.2 ? "medium" : "low";
3319
+ localFindings.push({
3320
+ file: file.path,
3321
+ relevance,
3322
+ matchedTopics,
3323
+ });
3324
+ }
3325
+ }
3326
+ // Sort by relevance
3327
+ const relevanceOrder = {
3328
+ high: 0,
3329
+ medium: 1,
3330
+ low: 2,
3331
+ };
3332
+ localFindings.sort((a, b) => (relevanceOrder[a.relevance] ?? 3) - (relevanceOrder[b.relevance] ?? 3));
3333
+ // --- Build results ---
3334
+ const results = [];
3335
+ // Add web results if available
3336
+ if (webResult) {
3337
+ results.push({
3338
+ title: "Web Search Results",
3339
+ url: "",
3340
+ snippet: webResult.substring(0, 500),
3341
+ relevance: 0.7,
3342
+ source: "web_search",
3343
+ });
3344
+ }
3345
+ // Add local file findings
3346
+ for (const finding of localFindings.slice(0, 10)) {
3347
+ const file = contextFiles.find((f) => f.path === finding.file);
3348
+ const snippet = file
3349
+ ? this.extractRelevantSnippet(file.content, finding.matchedTopics)
3350
+ : "";
3351
+ results.push({
3352
+ title: `Local: ${finding.file}`,
3353
+ url: finding.file,
3354
+ snippet,
3355
+ relevance: finding.relevance === "high"
3356
+ ? 0.9
3357
+ : finding.relevance === "medium"
3358
+ ? 0.6
3359
+ : 0.3,
3360
+ source: "local_file",
3361
+ });
3362
+ }
3363
+ // --- Key findings ---
3364
+ const keyFindings = [];
3365
+ if (localFindings.length > 0) {
3366
+ keyFindings.push(`Found ${localFindings.length} relevant local file(s) matching query topics`);
3367
+ const highRelevance = localFindings.filter((f) => f.relevance === "high");
3368
+ if (highRelevance.length > 0) {
3369
+ keyFindings.push(`High relevance files: ${highRelevance.map((f) => f.file).join(", ")}`);
3370
+ }
3371
+ }
3372
+ if (Object.keys(identifiedConcepts).length > 0) {
3373
+ for (const [category, concepts] of Object.entries(identifiedConcepts)) {
3374
+ keyFindings.push(`Identified ${category}: ${concepts.join(", ")}`);
3375
+ }
3376
+ }
3377
+ if (webResult) {
3378
+ keyFindings.push("Web search results available but model-based analysis unavailable");
3379
+ }
3380
+ if (keyFindings.length === 0) {
3381
+ keyFindings.push("No matching content found in local project files");
3382
+ keyFindings.push("External research requires a model connection for deeper analysis");
3383
+ }
3384
+ return {
3385
+ query,
3386
+ topics,
3387
+ identifiedConcepts,
3388
+ results,
3389
+ localFindings,
3390
+ keyFindings,
3391
+ verifiedFacts: [],
3392
+ limitations: [
3393
+ webResult
3394
+ ? "Web results retrieved but could not be analyzed without a model"
3395
+ : "Web search unavailable — results limited to local file scanning",
3396
+ "Deep semantic analysis requires model availability",
3397
+ ],
3398
+ note: "Research performed via local keyword matching and file scanning (no model available)",
3399
+ };
3400
+ }
3401
+ /**
3402
+ * Extract a relevant snippet from file content based on matched topics.
3403
+ */
3404
+ extractRelevantSnippet(content, topics) {
3405
+ const lines = content.split("\n");
3406
+ for (let i = 0; i < lines.length; i++) {
3407
+ const lower = lines[i].toLowerCase();
3408
+ if (topics.some((t) => lower.includes(t))) {
3409
+ // Return surrounding context (3 lines before and after)
3410
+ const start = Math.max(0, i - 2);
3411
+ const end = Math.min(lines.length, i + 3);
3412
+ return lines.slice(start, end).join("\n").substring(0, 300);
3413
+ }
3414
+ }
3415
+ return content.substring(0, 200);
3416
+ }
3417
+ }
3418
+ // ============================================================================
3419
+ // Gatekeeper Agent
3420
+ // ============================================================================
3421
+ export class GatekeeperAgent extends SubAgent {
3422
+ reviewConfig;
3423
+ revisionHistory = [];
3424
+ constructor(messageBus, toolBridge, config) {
3425
+ super("gatekeeper", { mode: "subagent", ...config }, messageBus, toolBridge);
3426
+ this.reviewConfig = {
3427
+ reviewLevel: config?.reviewConfig?.reviewLevel || "standard",
3428
+ criteria: config?.reviewConfig?.criteria || this.getDefaultCriteria(),
3429
+ autoApproveThreshold: config?.reviewConfig?.autoApproveThreshold || 0.8,
3430
+ maxRevisions: config?.reviewConfig?.maxRevisions || 3,
3431
+ enableParallelReview: config?.reviewConfig?.enableParallelReview || true,
3432
+ strictMode: config?.reviewConfig?.strictMode || false,
3433
+ };
3434
+ }
3435
+ getDefaultCriteria() {
3436
+ return {
3437
+ correctness: { enabled: true, weight: 0.25 },
3438
+ completeness: { enabled: true, weight: 0.2 },
3439
+ codeQuality: { enabled: true, weight: 0.15 },
3440
+ security: { enabled: true, weight: 0.2 },
3441
+ performance: { enabled: true, weight: 0.1 },
3442
+ documentation: { enabled: true, weight: 0.1 },
3443
+ };
3444
+ }
3445
+ async performTask(task) {
3446
+ const startTime = Date.now();
3447
+ this.updateStatus("running", "Reviewing agent work");
3448
+ const phaseId = task.context.metadata?.phaseId || "unknown";
3449
+ const agentType = task.context.metadata?.agentType || "analyzer";
3450
+ const workOutput = task.context.metadata?.workOutput || task.prompt;
3451
+ try {
3452
+ // Perform comprehensive review based on criteria
3453
+ const review = await this.performComprehensiveReview(workOutput, task.context);
3454
+ const reviewDuration = Date.now() - startTime;
3455
+ return {
3456
+ ...review,
3457
+ reviewId: uuidv4(),
3458
+ phaseId,
3459
+ agentType,
3460
+ workOutput,
3461
+ metadata: {
3462
+ reviewDuration,
3463
+ criteriaChecked: this.getCriteriaCheckList(),
3464
+ warnings: this.generateWarnings(review),
3465
+ },
3466
+ };
3467
+ }
3468
+ catch (error) {
3469
+ this.updateStatus("error", "Gatekeeper review failed");
3470
+ throw error;
3471
+ }
3472
+ }
3473
+ async performComprehensiveReview(workOutput, context) {
3474
+ // Build review prompt based on criteria
3475
+ const prompt = this.buildReviewPrompt(workOutput, context);
3476
+ const response = await this.callModel(prompt);
3477
+ // Parse the review response
3478
+ const parsedReview = this.parseReviewResponse(response);
3479
+ // Calculate overall decision
3480
+ const decision = this.calculateDecision(parsedReview.issues, parsedReview.suggestions);
3481
+ // Calculate confidence score
3482
+ const confidence = this.calculateConfidence(parsedReview);
3483
+ return {
3484
+ decision,
3485
+ reasoning: parsedReview.reasoning ||
3486
+ "Review completed based on criteria evaluation",
3487
+ issues: parsedReview.issues || [],
3488
+ suggestions: parsedReview.suggestions || [],
3489
+ confidence,
3490
+ };
3491
+ }
3492
+ buildReviewPrompt(workOutput, context) {
3493
+ const criteria = this.reviewConfig.criteria;
3494
+ const outputStr = typeof workOutput === "string"
3495
+ ? workOutput
3496
+ : JSON.stringify(workOutput, null, 2);
3497
+ let criteriaStr = "Review criteria:\n";
3498
+ if (criteria.correctness.enabled)
3499
+ criteriaStr += "- Correctness: Code functions as intended\n";
3500
+ if (criteria.completeness.enabled)
3501
+ criteriaStr += "- Completeness: All requirements addressed\n";
3502
+ if (criteria.codeQuality.enabled)
3503
+ criteriaStr += "- Code Quality: Clean, maintainable code\n";
3504
+ if (criteria.security.enabled)
3505
+ criteriaStr += "- Security: No vulnerabilities\n";
3506
+ if (criteria.performance.enabled)
3507
+ criteriaStr += "- Performance: Efficient implementation\n";
3508
+ if (criteria.documentation.enabled)
3509
+ criteriaStr += "- Documentation: Clear docs/comments\n";
3510
+ return `You are a senior code reviewer acting as a gatekeeper for a multi-agent coding system.
3511
+
3512
+ Agent Type: Gatekeeper Review
3513
+ Phase: Reviewing work output
3514
+
3515
+ Output to Review:
3516
+ ${outputStr.substring(0, 8000)}
3517
+
3518
+ ${criteriaStr}
3519
+
3520
+ Context Files: ${context.files.map((f) => f.path).join(", ")}
3521
+
3522
+ Provide your review in JSON format:
3523
+ {
3524
+ "decision": "approve|request_revision|restart_from_scratch|escalate",
3525
+ "reasoning": "Brief explanation of the decision",
3526
+ "issues": [
3527
+ {
3528
+ "id": "issue-1",
3529
+ "severity": "critical|high|medium|low",
3530
+ "category": "correctness|completeness|code_quality|security|performance|style|best_practice|consistency",
3531
+ "description": "Issue description",
3532
+ "location": { "file": "...", "line": ... },
3533
+ "evidence": "Why this is an issue",
3534
+ "remediation": "How to fix"
3535
+ }
3536
+ ],
3537
+ "suggestions": [
3538
+ {
3539
+ "id": "suggest-1",
3540
+ "priority": "must|should|could",
3541
+ "category": "...",
3542
+ "description": "Suggestion description",
3543
+ "rationale": "Why this helps",
3544
+ "example": "Optional code example"
3545
+ }
3546
+ ]
3547
+ }
3548
+
3549
+ Only include issues and suggestions that are relevant and actionable.`;
3550
+ }
3551
+ parseReviewResponse(response) {
3552
+ const defaultResult = {
3553
+ decision: "approve",
3554
+ reasoning: "Review completed, no critical issues found",
3555
+ issues: [],
3556
+ suggestions: [],
3557
+ };
3558
+ if (!response) {
3559
+ return defaultResult;
3560
+ }
3561
+ try {
3562
+ // Try to parse as JSON
3563
+ const parsed = JSON.parse(response);
3564
+ return {
3565
+ decision: parsed.decision || "approve",
3566
+ reasoning: parsed.reasoning || defaultResult.reasoning,
3567
+ issues: (parsed.issues || []).map((issue, idx) => ({
3568
+ id: issue.id || `issue-${idx}`,
3569
+ severity: issue.severity || "medium",
3570
+ category: issue.category || "code_quality",
3571
+ description: issue.description || "Issue identified",
3572
+ location: issue.location,
3573
+ evidence: issue.evidence || "",
3574
+ remediation: issue.remediation || "",
3575
+ })),
3576
+ suggestions: (parsed.suggestions || []).map((sug, idx) => ({
3577
+ id: sug.id || `suggest-${idx}`,
3578
+ priority: sug.priority || "should",
3579
+ category: sug.category || "code_quality",
3580
+ description: sug.description || "Suggestion",
3581
+ rationale: sug.rationale || "",
3582
+ example: sug.example,
3583
+ })),
3584
+ };
3585
+ }
3586
+ catch {
3587
+ // Fallback: analyze response text
3588
+ return this.analyzeResponseText(response);
3589
+ }
3590
+ }
3591
+ analyzeResponseText(response) {
3592
+ const lowerResponse = response.toLowerCase();
3593
+ // Check for negative indicators
3594
+ const hasCritical = lowerResponse.includes("critical") || lowerResponse.includes("error");
3595
+ const hasMajorIssues = lowerResponse.includes("major") || lowerResponse.includes("serious");
3596
+ const needsWork = lowerResponse.includes("needs work") ||
3597
+ lowerResponse.includes("should fix");
3598
+ let decision = "approve";
3599
+ if (hasCritical) {
3600
+ decision = "request_revision";
3601
+ }
3602
+ else if (hasMajorIssues) {
3603
+ decision = "request_revision";
3604
+ }
3605
+ else if (needsWork) {
3606
+ decision = "request_revision";
3607
+ }
3608
+ return {
3609
+ decision,
3610
+ reasoning: response.substring(0, 500),
3611
+ issues: [],
3612
+ suggestions: [],
3613
+ };
3614
+ }
3615
+ calculateDecision(issues, suggestions) {
3616
+ // Critical issues = restart or escalate
3617
+ const criticalCount = issues.filter((i) => i.severity === "critical").length;
3618
+ if (criticalCount > 0) {
3619
+ return this.reviewConfig.strictMode ? "restart_from_scratch" : "escalate";
3620
+ }
3621
+ // High severity issues = request revision
3622
+ const highCount = issues.filter((i) => i.severity === "high").length;
3623
+ if (highCount > 0) {
3624
+ return "request_revision";
3625
+ }
3626
+ // Must-have suggestions = request revision
3627
+ const mustHave = suggestions.filter((s) => s.priority === "must").length;
3628
+ if (mustHave > 0) {
3629
+ return "request_revision";
3630
+ }
3631
+ // Medium issues = request revision if strict mode
3632
+ const mediumCount = issues.filter((i) => i.severity === "medium").length;
3633
+ if (mediumCount > 2 && this.reviewConfig.strictMode) {
3634
+ return "request_revision";
3635
+ }
3636
+ return "approve";
3637
+ }
3638
+ calculateConfidence(parsedReview) {
3639
+ let confidence = 1.0;
3640
+ // Reduce confidence based on issue count
3641
+ confidence -= parsedReview.issues.length * 0.05;
3642
+ // Reduce confidence based on critical/high issues
3643
+ const criticalHighCount = parsedReview.issues.filter((i) => i.severity === "critical" || i.severity === "high").length;
3644
+ confidence -= criticalHighCount * 0.15;
3645
+ // Boost confidence for thorough reviews
3646
+ if (this.reviewConfig.reviewLevel === "thorough") {
3647
+ confidence += 0.1;
3648
+ }
3649
+ return Math.max(0, Math.min(1, confidence));
3650
+ }
3651
+ getCriteriaCheckList() {
3652
+ const criteria = this.reviewConfig.criteria;
3653
+ const checks = [];
3654
+ if (criteria.correctness.enabled)
3655
+ checks.push("correctness");
3656
+ if (criteria.completeness.enabled)
3657
+ checks.push("completeness");
3658
+ if (criteria.codeQuality.enabled)
3659
+ checks.push("codeQuality");
3660
+ if (criteria.security.enabled)
3661
+ checks.push("security");
3662
+ if (criteria.performance.enabled)
3663
+ checks.push("performance");
3664
+ if (criteria.documentation.enabled)
3665
+ checks.push("documentation");
3666
+ return checks;
3667
+ }
3668
+ generateWarnings(review) {
3669
+ const warnings = [];
3670
+ if (review.decision === "request_revision") {
3671
+ warnings.push("Review flagged for revision");
3672
+ }
3673
+ if (review.decision === "restart_from_scratch") {
3674
+ warnings.push("Major issues detected, restart recommended");
3675
+ }
3676
+ if (review.issues.length > 5) {
3677
+ warnings.push("High number of issues detected");
3678
+ }
3679
+ return warnings;
3680
+ }
3681
+ // Add revision to history
3682
+ addRevision(iteration, phaseId, originalIssue, revision) {
3683
+ this.revisionHistory.push({
3684
+ iteration,
3685
+ phaseId,
3686
+ originalIssue,
3687
+ revision,
3688
+ outcome: "accepted",
3689
+ });
3690
+ }
3691
+ // Get revision history
3692
+ getRevisionHistory() {
3693
+ return [...this.revisionHistory];
3694
+ }
3695
+ // Get current review config
3696
+ getConfig() {
3697
+ return { ...this.reviewConfig };
3698
+ }
3699
+ // Update review config
3700
+ updateConfig(config) {
3701
+ this.reviewConfig = { ...this.reviewConfig, ...config };
3702
+ }
3703
+ }
3704
+ // ============================================================================
3705
+ // Gatekeeper Coordinator - Orchestrates multiple gatekeeper reviews
3706
+ // ============================================================================
3707
+ export class GatekeeperCoordinator {
3708
+ gatekeepers = new Map();
3709
+ reviewQueue = [];
3710
+ isProcessing = false;
3711
+ constructor(messageBus, toolBridge, maxConcurrentReviews = 4) {
3712
+ // Initialize gatekeepers as per spec
3713
+ for (let i = 0; i < maxConcurrentReviews; i++) {
3714
+ const gatekeeper = new GatekeeperAgent(messageBus, toolBridge);
3715
+ this.gatekeepers.set(`gatekeeper-${i}`, gatekeeper);
3716
+ }
3717
+ }
3718
+ // Get an available gatekeeper
3719
+ getAvailableGatekeeper() {
3720
+ for (const [, gatekeeper] of this.gatekeepers) {
3721
+ const status = gatekeeper.getStatus();
3722
+ if (status.status === "idle" || status.status === "completed") {
3723
+ return gatekeeper;
3724
+ }
3725
+ }
3726
+ return null;
3727
+ }
3728
+ // Submit work for gatekeeper review
3729
+ async submitForReview(task) {
3730
+ const gatekeeper = this.getAvailableGatekeeper();
3731
+ if (!gatekeeper) {
3732
+ // Queue the review
3733
+ return new Promise((resolve, reject) => {
3734
+ this.reviewQueue.push({
3735
+ task,
3736
+ gatekeeper: this.gatekeepers.values().next().value,
3737
+ resolve,
3738
+ reject,
3739
+ });
3740
+ this.processQueue();
3741
+ });
3742
+ }
3743
+ return this.performReview(gatekeeper, task);
3744
+ }
3745
+ // Perform review with a specific gatekeeper
3746
+ async performReview(gatekeeper, task) {
3747
+ try {
3748
+ const result = await gatekeeper.execute(task);
3749
+ this.processQueue(); // Try to process next in queue
3750
+ return result;
3751
+ }
3752
+ catch (error) {
3753
+ this.processQueue();
3754
+ throw error;
3755
+ }
3756
+ }
3757
+ // Process queued reviews
3758
+ async processQueue() {
3759
+ if (this.isProcessing || this.reviewQueue.length === 0) {
3760
+ return;
3761
+ }
3762
+ this.isProcessing = true;
3763
+ while (this.reviewQueue.length > 0) {
3764
+ const gatekeeper = this.getAvailableGatekeeper();
3765
+ if (!gatekeeper) {
3766
+ break;
3767
+ }
3768
+ const queueItem = this.reviewQueue.shift();
3769
+ this.performReview(gatekeeper, queueItem.task)
3770
+ .then(queueItem.resolve)
3771
+ .catch(queueItem.reject);
3772
+ }
3773
+ this.isProcessing = false;
3774
+ }
3775
+ // Review all phase outputs
3776
+ async reviewAllPhases(phaseOutputs) {
3777
+ const phaseReviews = [];
3778
+ const revisionHistory = [];
3779
+ let totalConfidence = 0;
3780
+ // Review each phase in parallel (if enabled)
3781
+ const reviewPromises = phaseOutputs.map(async (phase) => {
3782
+ const gatekeeper = this.getAvailableGatekeeper();
3783
+ if (!gatekeeper) {
3784
+ throw new Error("No gatekeeper available for review");
3785
+ }
3786
+ const task = {
3787
+ id: uuidv4(),
3788
+ type: "gatekeeper",
3789
+ prompt: JSON.stringify(phase.output),
3790
+ context: {
3791
+ ...phase.context,
3792
+ metadata: {
3793
+ ...phase.context.metadata,
3794
+ phaseId: phase.phaseId,
3795
+ agentType: phase.agentType,
3796
+ workOutput: phase.output,
3797
+ },
3798
+ },
3799
+ successCriteria: "Complete gatekeeper review",
3800
+ };
3801
+ const review = await this.performReview(gatekeeper, task);
3802
+ phaseReviews.push(review);
3803
+ totalConfidence += review.confidence;
3804
+ return review;
3805
+ });
3806
+ await Promise.all(reviewPromises);
3807
+ // Calculate overall decision
3808
+ const avgConfidence = phaseReviews.length > 0 ? totalConfidence / phaseReviews.length : 0;
3809
+ const needsRevision = phaseReviews.some((r) => r.decision === "request_revision");
3810
+ const needsRestart = phaseReviews.some((r) => r.decision === "restart_from_scratch");
3811
+ const hasEscalation = phaseReviews.some((r) => r.decision === "escalate");
3812
+ let overallDecision = "approve";
3813
+ if (needsRestart) {
3814
+ overallDecision = "restart_from_scratch";
3815
+ }
3816
+ else if (needsRevision) {
3817
+ overallDecision = "request_revision";
3818
+ }
3819
+ else if (hasEscalation) {
3820
+ overallDecision = "escalate";
3821
+ }
3822
+ // Collect all issues and suggestions
3823
+ const allIssues = phaseReviews.flatMap((r) => r.issues);
3824
+ const allSuggestions = phaseReviews.flatMap((r) => r.suggestions);
3825
+ // Build master decision
3826
+ const masterDecision = this.buildMasterDecision(overallDecision, allIssues, allSuggestions, phaseReviews);
3827
+ return {
3828
+ overallDecision,
3829
+ confidence: avgConfidence,
3830
+ phaseReviews,
3831
+ masterDecision,
3832
+ summary: this.generateSummary(phaseReviews, overallDecision, avgConfidence),
3833
+ nextSteps: this.generateNextSteps(masterDecision, allIssues),
3834
+ revisionHistory,
3835
+ };
3836
+ }
3837
+ buildMasterDecision(overallDecision, allIssues, allSuggestions, phaseReviews) {
3838
+ switch (overallDecision) {
3839
+ case "approve":
3840
+ return {
3841
+ action: "proceed",
3842
+ reasoning: "All phases passed gatekeeper review",
3843
+ affectedPhases: [],
3844
+ };
3845
+ case "request_revision":
3846
+ return {
3847
+ action: "modify_plan",
3848
+ reasoning: `Revision needed: ${allIssues.length} issues, ${allSuggestions.length} suggestions`,
3849
+ affectedPhases: phaseReviews
3850
+ .filter((r) => r.decision === "request_revision")
3851
+ .map((r) => r.phaseId),
3852
+ };
3853
+ case "restart_from_scratch":
3854
+ return {
3855
+ action: "restart",
3856
+ reasoning: `Critical issues require restart: ${allIssues.filter((i) => i.severity === "critical").length} critical issues`,
3857
+ affectedPhases: phaseReviews.map((r) => r.phaseId),
3858
+ };
3859
+ case "escalate":
3860
+ return {
3861
+ action: "stop",
3862
+ reasoning: "Escalation required due to unresolvable issues",
3863
+ affectedPhases: phaseReviews
3864
+ .filter((r) => r.decision === "escalate")
3865
+ .map((r) => r.phaseId),
3866
+ };
3867
+ default:
3868
+ return {
3869
+ action: "proceed",
3870
+ reasoning: "Default decision: proceed",
3871
+ affectedPhases: [],
3872
+ };
3873
+ }
3874
+ }
3875
+ generateSummary(phaseReviews, decision, confidence) {
3876
+ const passed = phaseReviews.filter((r) => r.decision === "approve").length;
3877
+ const revised = phaseReviews.filter((r) => r.decision === "request_revision").length;
3878
+ const restarted = phaseReviews.filter((r) => r.decision === "restart_from_scratch").length;
3879
+ const escalated = phaseReviews.filter((r) => r.decision === "escalate").length;
3880
+ return `Gatekeeper Review Summary:
3881
+ - Total Phases Reviewed: ${phaseReviews.length}
3882
+ - Passed: ${passed}
3883
+ - Requested Revision: ${revised}
3884
+ - Restarted: ${restarted}
3885
+ - Escalated: ${escalated}
3886
+ - Overall Decision: ${decision}
3887
+ - Confidence: ${(confidence * 100).toFixed(1)}%
3888
+ - Total Issues Found: ${phaseReviews.reduce((sum, r) => sum + r.issues.length, 0)}
3889
+ - Total Suggestions: ${phaseReviews.reduce((sum, r) => sum + r.suggestions.length, 0)}`;
3890
+ }
3891
+ generateNextSteps(decision, issues) {
3892
+ const steps = [];
3893
+ switch (decision.action) {
3894
+ case "proceed":
3895
+ steps.push("Proceed to next pipeline phase");
3896
+ steps.push("Synthesize final output");
3897
+ break;
3898
+ case "modify_plan":
3899
+ steps.push("Collect revision requirements from gatekeeper");
3900
+ steps.push("Send modified plan back to affected agents");
3901
+ steps.push("Re-run affected phases with corrections");
3902
+ break;
3903
+ case "restart":
3904
+ steps.push("Clear current phase outputs");
3905
+ steps.push("Restart pipeline from beginning");
3906
+ steps.push("Apply learnings from previous attempts");
3907
+ break;
3908
+ case "stop":
3909
+ steps.push("Report critical issues to user");
3910
+ steps.push("Request human intervention");
3911
+ steps.push("Await further instructions");
3912
+ break;
3913
+ }
3914
+ // Add issue-specific steps
3915
+ if (issues.length > 0) {
3916
+ steps.push(`Address ${issues.length} identified issues`);
3917
+ }
3918
+ return steps;
3919
+ }
3920
+ // Get all gatekeepers status
3921
+ getGatekeeperStatuses() {
3922
+ const statuses = new Map();
3923
+ for (const [id, gatekeeper] of this.gatekeepers) {
3924
+ statuses.set(id, gatekeeper.getStatus());
3925
+ }
3926
+ return statuses;
3927
+ }
3928
+ // Shutdown all gatekeepers
3929
+ async shutdown() {
3930
+ for (const [, gatekeeper] of this.gatekeepers) {
3931
+ await gatekeeper.stop();
3932
+ }
3933
+ this.reviewQueue = [];
3934
+ }
3935
+ }
3936
+ // ============================================================================
3937
+ // Factory Function
3938
+ // ============================================================================
3939
+ export function createAgent(type, messageBus, toolBridge, config, projectRoot) {
3940
+ switch (type) {
3941
+ case "master":
3942
+ return new MasterAgent(messageBus, toolBridge, config, projectRoot);
3943
+ case "planner":
3944
+ return new PlannerAgent(type, config || {}, messageBus, toolBridge);
3945
+ case "analyzer":
3946
+ return new AnalyzerAgent(type, config || {}, messageBus, toolBridge);
3947
+ case "reviewer":
3948
+ return new ReviewerAgent(type, config || {}, messageBus, toolBridge);
3949
+ case "rewriter":
3950
+ return new RewriterAgent(type, config || {}, messageBus, toolBridge);
3951
+ case "ui_designer":
3952
+ return new UiDesignerAgent(type, config || {}, messageBus, toolBridge);
3953
+ case "researcher":
3954
+ return new ResearcherAgent(type, config || {}, messageBus, toolBridge);
3955
+ case "gatekeeper":
3956
+ return new GatekeeperAgent(messageBus, toolBridge, config);
3957
+ default:
3958
+ throw new Error(`Unknown agent type: ${type}`);
3959
+ }
3960
+ }
3961
+ //# sourceMappingURL=base-agents.js.map