@tyvm/knowhow 0.0.89 → 0.0.91

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 (344) hide show
  1. package/.depcheckrc +31 -0
  2. package/CONFIG.md +52 -0
  3. package/README.md +344 -29
  4. package/WORKER.md +169 -334
  5. package/autodoc/chat-guide.md +540 -0
  6. package/autodoc/cli-reference.md +765 -0
  7. package/autodoc/config-reference.md +541 -0
  8. package/autodoc/embeddings-guide.md +566 -0
  9. package/autodoc/generate-guide.md +477 -0
  10. package/autodoc/language-plugin-guide.md +443 -0
  11. package/autodoc/modules-guide.md +352 -0
  12. package/autodoc/plugins-guide.md +720 -0
  13. package/autodoc/quickstart-guide.md +129 -0
  14. package/autodoc/skills-guide.md +468 -0
  15. package/autodoc/worker-guide.md +526 -0
  16. package/bin/knowhow.js +1 -1
  17. package/package.json +4 -32
  18. package/src/agents/tools/executeScript/index.ts +5 -0
  19. package/src/agents/tools/googleSearch.ts +2 -2
  20. package/src/agents/tools/index.ts +0 -3
  21. package/src/agents/tools/list.ts +0 -147
  22. package/src/agents/tools/loadWebpage.ts +3 -113
  23. package/src/ai.ts +33 -2
  24. package/src/auth/browserLogin.ts +10 -13
  25. package/src/cli.ts +63 -3
  26. package/src/clients/gemini.ts +96 -25
  27. package/src/clients/http.ts +7 -11
  28. package/src/clients/pricing/google.ts +122 -26
  29. package/src/config.ts +28 -4
  30. package/src/conversion.ts +24 -54
  31. package/src/index.ts +30 -3
  32. package/src/login.ts +5 -6
  33. package/src/plugins/language.ts +0 -4
  34. package/src/plugins/plugins.ts +0 -14
  35. package/src/plugins/url.ts +31 -12
  36. package/src/processors/TokenCompressor.ts +2 -2
  37. package/src/processors/ToolResponseCache.ts +3 -3
  38. package/src/processors/tools/grepToolResponse.ts +9 -4
  39. package/src/processors/tools/jqToolResponse.ts +11 -6
  40. package/src/processors/tools/listStoredToolResponses.ts +1 -1
  41. package/src/processors/tools/tailToolResponse.ts +9 -4
  42. package/src/services/GitHub.ts +2 -2
  43. package/src/services/KnowhowClient.ts +34 -34
  44. package/src/{plugins/downloader/downloader.ts → services/MediaProcessorService.ts} +109 -267
  45. package/src/services/S3.ts +16 -16
  46. package/src/services/index.ts +4 -4
  47. package/src/services/modules/index.ts +10 -2
  48. package/src/services/modules/types.ts +5 -2
  49. package/src/services/script-execution/ScriptExecutor.ts +29 -10
  50. package/src/services/script-execution/ScriptPolicy.ts +6 -2
  51. package/src/types.ts +1 -0
  52. package/src/utils/http.ts +127 -0
  53. package/src/workers/auth/PasskeySetup.ts +7 -11
  54. package/tests/clients/AIClient.test.ts +24 -21
  55. package/tests/manual/file-edits/figma.test.ts +3 -70
  56. package/tests/plugins/language/languagePlugin-content-triggers.test.ts +2 -0
  57. package/tests/plugins/language/languagePlugin.test.ts +2 -0
  58. package/tests/processors/ToolResponseCache.test.ts +2 -2
  59. package/tests/test.spec.ts +0 -14
  60. package/tests/unit/modules/moduleLoading.test.ts +7 -4
  61. package/tests/unit/plugins/pluginLoading.test.ts +6 -6
  62. package/ts_build/package.json +4 -32
  63. package/ts_build/src/agents/tools/ast/astAppendNode.d.ts +1 -1
  64. package/ts_build/src/agents/tools/ast/astAppendNode.js +2 -90
  65. package/ts_build/src/agents/tools/ast/astAppendNode.js.map +1 -1
  66. package/ts_build/src/agents/tools/ast/astDeleteNode.d.ts +1 -1
  67. package/ts_build/src/agents/tools/ast/astDeleteNode.js +2 -88
  68. package/ts_build/src/agents/tools/ast/astDeleteNode.js.map +1 -1
  69. package/ts_build/src/agents/tools/ast/astEditNode.d.ts +1 -1
  70. package/ts_build/src/agents/tools/ast/astEditNode.js +2 -90
  71. package/ts_build/src/agents/tools/ast/astEditNode.js.map +1 -1
  72. package/ts_build/src/agents/tools/ast/astGetPathForLine.d.ts +1 -1
  73. package/ts_build/src/agents/tools/ast/astGetPathForLine.js +2 -72
  74. package/ts_build/src/agents/tools/ast/astGetPathForLine.js.map +1 -1
  75. package/ts_build/src/agents/tools/ast/astListPaths.d.ts +1 -1
  76. package/ts_build/src/agents/tools/ast/astListPaths.js +2 -72
  77. package/ts_build/src/agents/tools/ast/astListPaths.js.map +1 -1
  78. package/ts_build/src/agents/tools/executeScript/index.d.ts +3 -2
  79. package/ts_build/src/agents/tools/executeScript/index.js +4 -1
  80. package/ts_build/src/agents/tools/executeScript/index.js.map +1 -1
  81. package/ts_build/src/agents/tools/googleSearch.js +2 -2
  82. package/ts_build/src/agents/tools/googleSearch.js.map +1 -1
  83. package/ts_build/src/agents/tools/index.d.ts +0 -3
  84. package/ts_build/src/agents/tools/index.js +0 -3
  85. package/ts_build/src/agents/tools/index.js.map +1 -1
  86. package/ts_build/src/agents/tools/list.js +0 -138
  87. package/ts_build/src/agents/tools/list.js.map +1 -1
  88. package/ts_build/src/agents/tools/loadWebpage.js +1 -89
  89. package/ts_build/src/agents/tools/loadWebpage.js.map +1 -1
  90. package/ts_build/src/agents/tools/textSearch.d.ts +1 -1
  91. package/ts_build/src/ai.js +18 -1
  92. package/ts_build/src/ai.js.map +1 -1
  93. package/ts_build/src/auth/browserLogin.js +7 -7
  94. package/ts_build/src/auth/browserLogin.js.map +1 -1
  95. package/ts_build/src/cli.d.ts +1 -1
  96. package/ts_build/src/cli.js +47 -1
  97. package/ts_build/src/cli.js.map +1 -1
  98. package/ts_build/src/clients/gemini.d.ts +1 -73
  99. package/ts_build/src/clients/gemini.js +57 -19
  100. package/ts_build/src/clients/gemini.js.map +1 -1
  101. package/ts_build/src/clients/http.js +5 -9
  102. package/ts_build/src/clients/http.js.map +1 -1
  103. package/ts_build/src/clients/pricing/google.d.ts +17 -73
  104. package/ts_build/src/clients/pricing/google.js +47 -10
  105. package/ts_build/src/clients/pricing/google.js.map +1 -1
  106. package/ts_build/src/config.js +17 -2
  107. package/ts_build/src/config.js.map +1 -1
  108. package/ts_build/src/conversion.d.ts +1 -4
  109. package/ts_build/src/conversion.js +12 -27
  110. package/ts_build/src/conversion.js.map +1 -1
  111. package/ts_build/src/index.d.ts +4 -0
  112. package/ts_build/src/index.js +19 -3
  113. package/ts_build/src/index.js.map +1 -1
  114. package/ts_build/src/login.js +5 -4
  115. package/ts_build/src/login.js.map +1 -1
  116. package/ts_build/src/plugins/downloader/downloader.js +3 -3
  117. package/ts_build/src/plugins/downloader/downloader.js.map +1 -1
  118. package/ts_build/src/plugins/language.js.map +1 -1
  119. package/ts_build/src/plugins/plugins.js +0 -14
  120. package/ts_build/src/plugins/plugins.js.map +1 -1
  121. package/ts_build/src/plugins/tree-sitter/editor.d.ts +3 -32
  122. package/ts_build/src/plugins/tree-sitter/editor.js +6 -208
  123. package/ts_build/src/plugins/tree-sitter/editor.js.map +1 -1
  124. package/ts_build/src/plugins/tree-sitter/parser.d.ts +19 -54
  125. package/ts_build/src/plugins/tree-sitter/parser.js +19 -293
  126. package/ts_build/src/plugins/tree-sitter/parser.js.map +1 -1
  127. package/ts_build/src/plugins/tree-sitter/simple-paths.d.ts +2 -15
  128. package/ts_build/src/plugins/tree-sitter/simple-paths.js +2 -324
  129. package/ts_build/src/plugins/tree-sitter/simple-paths.js.map +1 -1
  130. package/ts_build/src/plugins/url.js +27 -8
  131. package/ts_build/src/plugins/url.js.map +1 -1
  132. package/ts_build/src/processors/TokenCompressor.js +2 -2
  133. package/ts_build/src/processors/TokenCompressor.js.map +1 -1
  134. package/ts_build/src/processors/ToolResponseCache.js +3 -3
  135. package/ts_build/src/processors/ToolResponseCache.js.map +1 -1
  136. package/ts_build/src/processors/tools/grepToolResponse.d.ts +3 -1
  137. package/ts_build/src/processors/tools/grepToolResponse.js +8 -2
  138. package/ts_build/src/processors/tools/grepToolResponse.js.map +1 -1
  139. package/ts_build/src/processors/tools/jqToolResponse.d.ts +3 -1
  140. package/ts_build/src/processors/tools/jqToolResponse.js +10 -4
  141. package/ts_build/src/processors/tools/jqToolResponse.js.map +1 -1
  142. package/ts_build/src/processors/tools/listStoredToolResponses.js +1 -1
  143. package/ts_build/src/processors/tools/listStoredToolResponses.js.map +1 -1
  144. package/ts_build/src/processors/tools/tailToolResponse.d.ts +3 -1
  145. package/ts_build/src/processors/tools/tailToolResponse.js +8 -2
  146. package/ts_build/src/processors/tools/tailToolResponse.js.map +1 -1
  147. package/ts_build/src/services/GitHub.js +2 -2
  148. package/ts_build/src/services/GitHub.js.map +1 -1
  149. package/ts_build/src/services/KnowhowClient.d.ts +29 -29
  150. package/ts_build/src/services/KnowhowClient.js +33 -33
  151. package/ts_build/src/services/KnowhowClient.js.map +1 -1
  152. package/ts_build/src/services/MediaProcessorService.d.ts +22 -0
  153. package/ts_build/src/services/MediaProcessorService.js +215 -0
  154. package/ts_build/src/services/MediaProcessorService.js.map +1 -0
  155. package/ts_build/src/services/S3.js +12 -18
  156. package/ts_build/src/services/S3.js.map +1 -1
  157. package/ts_build/src/services/index.d.ts +3 -2
  158. package/ts_build/src/services/index.js +3 -3
  159. package/ts_build/src/services/index.js.map +1 -1
  160. package/ts_build/src/services/modules/index.js +10 -2
  161. package/ts_build/src/services/modules/index.js.map +1 -1
  162. package/ts_build/src/services/modules/types.d.ts +5 -2
  163. package/ts_build/src/services/script-execution/ScriptExecutor.js +22 -7
  164. package/ts_build/src/services/script-execution/ScriptExecutor.js.map +1 -1
  165. package/ts_build/src/services/script-execution/ScriptPolicy.d.ts +1 -1
  166. package/ts_build/src/services/script-execution/ScriptPolicy.js +4 -2
  167. package/ts_build/src/services/script-execution/ScriptPolicy.js.map +1 -1
  168. package/ts_build/src/types.d.ts +1 -0
  169. package/ts_build/src/types.js +1 -0
  170. package/ts_build/src/types.js.map +1 -1
  171. package/ts_build/src/utils/http.d.ts +27 -0
  172. package/ts_build/src/utils/http.js +98 -0
  173. package/ts_build/src/utils/http.js.map +1 -0
  174. package/ts_build/src/workers/auth/PasskeySetup.js +6 -7
  175. package/ts_build/src/workers/auth/PasskeySetup.js.map +1 -1
  176. package/ts_build/tests/clients/AIClient.test.js +11 -14
  177. package/ts_build/tests/clients/AIClient.test.js.map +1 -1
  178. package/ts_build/tests/manual/file-edits/figma.test.d.ts +0 -1
  179. package/ts_build/tests/manual/file-edits/figma.test.js +1 -46
  180. package/ts_build/tests/manual/file-edits/figma.test.js.map +1 -1
  181. package/ts_build/tests/plugins/language/languagePlugin-content-triggers.test.js +2 -0
  182. package/ts_build/tests/plugins/language/languagePlugin-content-triggers.test.js.map +1 -1
  183. package/ts_build/tests/plugins/language/languagePlugin.test.js +2 -0
  184. package/ts_build/tests/plugins/language/languagePlugin.test.js.map +1 -1
  185. package/ts_build/tests/processors/ToolResponseCache.test.js +2 -2
  186. package/ts_build/tests/processors/ToolResponseCache.test.js.map +1 -1
  187. package/ts_build/tests/test.spec.js +0 -14
  188. package/ts_build/tests/test.spec.js.map +1 -1
  189. package/ts_build/tests/tree-sitter/tree-sitter.test.d.ts +0 -1
  190. package/ts_build/tests/tree-sitter/tree-sitter.test.js +2 -183
  191. package/ts_build/tests/tree-sitter/tree-sitter.test.js.map +1 -1
  192. package/ts_build/tests/unit/modules/moduleLoading.test.js +6 -4
  193. package/ts_build/tests/unit/modules/moduleLoading.test.js.map +1 -1
  194. package/ts_build/tests/unit/plugins/pluginLoading.test.js +4 -4
  195. package/ts_build/tests/unit/plugins/pluginLoading.test.js.map +1 -1
  196. package/autodoc/chat.mdx +0 -20
  197. package/autodoc/cli.mdx +0 -11
  198. package/autodoc/plugins/asana.mdx +0 -47
  199. package/autodoc/plugins/downloader/downloader.mdx +0 -38
  200. package/autodoc/plugins/downloader/plugin.mdx +0 -37
  201. package/autodoc/plugins/downloader/types.mdx +0 -42
  202. package/autodoc/plugins/embedding.mdx +0 -41
  203. package/autodoc/plugins/figma.mdx +0 -45
  204. package/autodoc/plugins/github.mdx +0 -40
  205. package/autodoc/plugins/jira.mdx +0 -46
  206. package/autodoc/plugins/language.mdx +0 -37
  207. package/autodoc/plugins/linear.mdx +0 -35
  208. package/autodoc/plugins/notion.mdx +0 -38
  209. package/autodoc/plugins/plugins.mdx +0 -59
  210. package/autodoc/plugins/types.mdx +0 -51
  211. package/autodoc/plugins/vim.mdx +0 -39
  212. package/autodoc/tools/addInternalTools.mdx +0 -1
  213. package/autodoc/tools/agentCall.mdx +0 -1
  214. package/autodoc/tools/asana/definitions.mdx +0 -10
  215. package/autodoc/tools/asana/index.mdx +0 -12
  216. package/autodoc/tools/askHuman.mdx +0 -1
  217. package/autodoc/tools/callPlugin.mdx +0 -1
  218. package/autodoc/tools/embeddingSearch.mdx +0 -1
  219. package/autodoc/tools/execCommand.mdx +0 -1
  220. package/autodoc/tools/fileSearch.mdx +0 -1
  221. package/autodoc/tools/finalAnswer.mdx +0 -1
  222. package/autodoc/tools/github/definitions.mdx +0 -6
  223. package/autodoc/tools/github/index.mdx +0 -8
  224. package/autodoc/tools/index.mdx +0 -14
  225. package/autodoc/tools/lintFile.mdx +0 -7
  226. package/autodoc/tools/list.mdx +0 -16
  227. package/autodoc/tools/modifyFile.mdx +0 -7
  228. package/autodoc/tools/patch.mdx +0 -9
  229. package/autodoc/tools/readBlocks.mdx +0 -1
  230. package/autodoc/tools/readFile.mdx +0 -1
  231. package/autodoc/tools/scanFile.mdx +0 -1
  232. package/autodoc/tools/textSearch.mdx +0 -6
  233. package/autodoc/tools/types/fileblock.mdx +0 -1
  234. package/autodoc/tools/visionTool.mdx +0 -1
  235. package/autodoc/tools/writeFile.mdx +0 -1
  236. package/benchmarks/.dockerignore +0 -7
  237. package/benchmarks/README.md +0 -166
  238. package/benchmarks/docker/Dockerfile +0 -68
  239. package/benchmarks/example-config.yml +0 -27
  240. package/benchmarks/jest.config.js +0 -13
  241. package/benchmarks/package-lock.json +0 -4297
  242. package/benchmarks/package.json +0 -39
  243. package/benchmarks/results/27b0a06/2025-09-27/xai/xai-grok-code-fast-1.json +0 -2909
  244. package/benchmarks/results/4057aed/2025-08-14/anthropic/anthropic-claude-sonnet-4-20250514.json +0 -1671
  245. package/benchmarks/results/4542435/2025-08-05/lms/lms-openai-gpt-oss-20b.json +0 -2814
  246. package/benchmarks/results/4542435/2025-08-05/lms/lms-qwen-qwen3-30b-a3b-2507.json +0 -2014
  247. package/benchmarks/results/4fb9125/2025-08-07/anthropic/anthropic-claude-sonnet-4-20250514.json +0 -3121
  248. package/benchmarks/results/5766aee/2025-08-02/lms-qwen/qwen3-coder-30b.json +0 -98
  249. package/benchmarks/results/6d73808/2025-08-07/openai/openai-gpt-5.json +0 -3256
  250. package/benchmarks/results/77bf0a6/2025-08-02/lms-qwen/qwen3-30b-a3b-2507.json +0 -4298
  251. package/benchmarks/results/8c0d445/2025-08-03/anthropic/anthropic-claude-sonnet-4-20250514.json +0 -3031
  252. package/benchmarks/results/8c0d445/2025-08-03/openai/openai-gpt-4.1-2025-04-14.json +0 -2990
  253. package/benchmarks/results/ac6b2ab/2025-08-03/anthropic/anthropic-claude-sonnet-4-20250514.json +0 -3256
  254. package/benchmarks/results/ac6b2ab/2025-08-03/lms/lms-qwen-qwen3-coder-30b.json +0 -3007
  255. package/benchmarks/results/ac6b2ab/2025-08-03/openai/openai-gpt-4.1-2025-04-14.json +0 -3256
  256. package/benchmarks/results/ac6b2ab/2025-08-03/openai/openai-gpt-4.1-mini-2025-04-14.json +0 -3036
  257. package/benchmarks/results/ac6b2ab/2025-08-03/openai/openai-gpt-4.1-nano-2025-04-14.json +0 -3280
  258. package/benchmarks/results/adff675/2025-08-04/lms/lms-qwen-qwen3-30b-a3b-2507.json +0 -1920
  259. package/benchmarks/results/adff675/2025-08-04/lms/lms-qwen-qwen3-coder-30b.json +0 -3281
  260. package/benchmarks/results/b502ed9/2025-08-03/lms-qwen/qwen3-coder-30b.json +0 -2896
  261. package/benchmarks/results/d1a8129/2025-08-03/lms/lms-qwen-qwen3-coder-30b.json +0 -3011
  262. package/benchmarks/results/e60471c/2025-08-03/lms/qwen3-30b-a3b-2507.json +0 -3003
  263. package/benchmarks/scripts/build-and-run.sh +0 -47
  264. package/benchmarks/scripts/clone-exercism.sh +0 -92
  265. package/benchmarks/scripts/validate.sh +0 -48
  266. package/benchmarks/src/__tests__/runner.test.ts +0 -27
  267. package/benchmarks/src/cli.ts +0 -90
  268. package/benchmarks/src/evaluators/EvaluatorRegistry.ts +0 -64
  269. package/benchmarks/src/evaluators/JavaScriptEvaluator.ts +0 -183
  270. package/benchmarks/src/evaluators/index.ts +0 -3
  271. package/benchmarks/src/evaluators/types.ts +0 -22
  272. package/benchmarks/src/index.ts +0 -3
  273. package/benchmarks/src/providers.ts +0 -13
  274. package/benchmarks/src/runner.ts +0 -824
  275. package/benchmarks/src/types.ts +0 -63
  276. package/benchmarks/tsconfig.json +0 -19
  277. package/leaderboard/README.md +0 -148
  278. package/leaderboard/app/api/benchmark-data/route.ts +0 -131
  279. package/leaderboard/app/api/benchmark-detail/route.ts +0 -172
  280. package/leaderboard/app/details/[model]/[provider]/[language]/page.tsx +0 -501
  281. package/leaderboard/app/exercise/[model]/[provider]/[language]/[exercise]/page.tsx +0 -375
  282. package/leaderboard/app/globals.css +0 -27
  283. package/leaderboard/app/layout.tsx +0 -21
  284. package/leaderboard/app/page.tsx +0 -170
  285. package/leaderboard/components/LeaderboardTable.tsx +0 -168
  286. package/leaderboard/components/PerformanceChart.tsx +0 -109
  287. package/leaderboard/next-env.d.ts +0 -5
  288. package/leaderboard/next.config.js +0 -4
  289. package/leaderboard/package-lock.json +0 -6363
  290. package/leaderboard/package.json +0 -28
  291. package/leaderboard/postcss.config.js +0 -6
  292. package/leaderboard/tailwind.config.js +0 -17
  293. package/leaderboard/tsconfig.json +0 -28
  294. package/leaderboard/types/benchmark.ts +0 -67
  295. package/leaderboard/utils/dataProcessor.ts +0 -33
  296. package/src/agents/tools/asana/definitions.ts +0 -199
  297. package/src/agents/tools/asana/index.ts +0 -108
  298. package/src/agents/tools/ast/astAppendNode.ts +0 -90
  299. package/src/agents/tools/ast/astDeleteNode.ts +0 -88
  300. package/src/agents/tools/ast/astEditNode.ts +0 -95
  301. package/src/agents/tools/ast/astGetPathForLine.ts +0 -73
  302. package/src/agents/tools/ast/astListPaths.ts +0 -66
  303. package/src/agents/tools/ast/index.ts +0 -7
  304. package/src/agents/tools/github/definitions.ts +0 -89
  305. package/src/agents/tools/github/index.ts +0 -67
  306. package/src/chat-old.ts +0 -446
  307. package/src/plugins/asana.ts +0 -146
  308. package/src/plugins/downloader/plugin.ts +0 -103
  309. package/src/plugins/downloader/types.ts +0 -92
  310. package/src/plugins/figma.ts +0 -158
  311. package/src/plugins/github.ts +0 -219
  312. package/src/plugins/jira.ts +0 -115
  313. package/src/plugins/linear.ts +0 -230
  314. package/src/plugins/notion.ts +0 -179
  315. package/src/plugins/tree-sitter/editor.ts +0 -369
  316. package/src/plugins/tree-sitter/lang-packs/index.ts +0 -23
  317. package/src/plugins/tree-sitter/lang-packs/java.ts +0 -59
  318. package/src/plugins/tree-sitter/lang-packs/javascript.ts +0 -57
  319. package/src/plugins/tree-sitter/lang-packs/python.ts +0 -45
  320. package/src/plugins/tree-sitter/lang-packs/types.ts +0 -79
  321. package/src/plugins/tree-sitter/lang-packs/typescript.ts +0 -49
  322. package/src/plugins/tree-sitter/parser.ts +0 -470
  323. package/src/plugins/tree-sitter/simple-paths.ts +0 -467
  324. package/test-comprehensive.ts +0 -31
  325. package/tests/tree-sitter/editor.test.ts +0 -113
  326. package/tests/tree-sitter/invalid.test.ts +0 -299
  327. package/tests/tree-sitter/paths/common-edits.test.ts +0 -564
  328. package/tests/tree-sitter/paths/debug-exact-position.test.ts +0 -44
  329. package/tests/tree-sitter/paths/debug-line-indexing.test.ts +0 -49
  330. package/tests/tree-sitter/paths/debug-paths.test.ts +0 -90
  331. package/tests/tree-sitter/paths/paths.test.ts +0 -170
  332. package/tests/tree-sitter/paths/simple-paths.test.ts +0 -367
  333. package/tests/tree-sitter/sample-after.ts +0 -48
  334. package/tests/tree-sitter/sample-before.ts +0 -25
  335. package/tests/tree-sitter/test-files/completely-broken.ts +0 -7
  336. package/tests/tree-sitter/test-files/duplicate-braces.ts +0 -39
  337. package/tests/tree-sitter/test-files/invalid-nesting.ts +0 -39
  338. package/tests/tree-sitter/test-files/malformed-signature.ts +0 -39
  339. package/tests/tree-sitter/test-files/mismatched-parens.ts +0 -39
  340. package/tests/tree-sitter/test-files/missing-semicolon.ts +0 -39
  341. package/tests/tree-sitter/test-files/partially-broken.ts +0 -20
  342. package/tests/tree-sitter/test-files/specific-errors.ts +0 -14
  343. package/tests/tree-sitter/test-files/unclosed-string.ts +0 -39
  344. package/tests/tree-sitter/tree-sitter.test.ts +0 -251
@@ -1,82 +1,54 @@
1
1
  import * as fs from "fs";
2
2
  import * as path from "path";
3
- import ytdl from "youtube-dl-exec";
4
- import Logger from "progress-estimator";
5
- import { DownloadInfo, KeyframeInfo, TranscriptChunk } from "./types";
6
- import { visionTool } from "../../agents/tools/visionTool";
7
- import { execAsync, fileExists, readFile, mkdir } from "../../utils";
8
- import { Clients } from "../../clients";
9
- import { Models } from "../../types";
10
-
11
- const logger = Logger();
12
-
13
- export class DownloaderService {
14
- constructor(private clients: typeof Clients) {}
15
-
16
- async askGptVision(
17
- imageUrl: string,
18
- question: string,
19
- provider = "openai",
20
- model = Models.openai.GPT_4o
21
- ) {
22
- const response = await this.clients.createCompletion(provider, {
23
- model,
24
- max_tokens: 2500,
25
- messages: [
26
- {
27
- role: "user",
28
- content: [
29
- { type: "text", text: question },
30
- {
31
- type: "image_url",
32
- image_url: {
33
- url: imageUrl,
34
- },
35
- },
36
- ],
37
- },
38
- ],
39
- });
3
+ import { exec } from "child_process";
4
+ import { promisify } from "util";
5
+ import { fileExists, readFile, mkdir } from "../utils";
6
+ import { AIClient } from "../clients";
40
7
 
41
- return response;
42
- }
43
-
44
- async download(url: string, outputDir: string) {
45
- const info = await this.info(url);
46
- const exists = await fileExists(`${outputDir}/${info.id}.${info.ext}`);
8
+ const execPromise = promisify(exec);
47
9
 
48
- if (exists) {
49
- console.log("File already exists, skipping download");
50
- return info;
51
- }
10
+ async function execAsync(command: string): Promise<string> {
11
+ const { stdout, stderr } = await execPromise(command);
12
+ return stdout + stderr;
13
+ }
52
14
 
53
- const scrape = ytdl(url, { output: `${outputDir}/%(id)s.%(ext)s` });
54
- const result = await logger(scrape, `Obtaining ${url}`);
55
- return info;
56
- }
15
+ export interface TranscriptChunk {
16
+ chunkPath: string;
17
+ text: string;
18
+ usd_cost: number;
19
+ }
57
20
 
58
- async info(url: string) {
59
- const info = await ytdl(url, {
60
- dumpSingleJson: true,
61
- noWarnings: true,
62
- });
63
- console.log(info);
64
- return info;
65
- }
21
+ export interface KeyframeInfo {
22
+ path: string;
23
+ description: string;
24
+ timestamp: number;
25
+ usd_cost?: number;
26
+ }
66
27
 
28
+ /**
29
+ * MediaProcessorService handles audio/video processing using:
30
+ * - ffmpeg (system tool) for chunking audio/video
31
+ * - OpenAI Whisper API for transcription
32
+ *
33
+ * This is part of the core services because microphone recording and
34
+ * audio-to-text transcription are base CLI features. The DownloaderService
35
+ * (in @tyvm/knowhow-module-video-downloader) uses this service for the
36
+ * audio/video processing steps after downloading with ytdl.
37
+ */
38
+ export class MediaProcessorService {
39
+ constructor(private clients: any) {}
40
+
41
+ /**
42
+ * Split an audio/video file into fixed-length mp3 chunks using ffmpeg.
43
+ */
67
44
  public async chunk(
68
45
  filePath: string,
69
46
  outputDir: string,
70
47
  CHUNK_LENGTH_SECONDS = 30,
71
48
  reuseExistingChunks = true
72
- ) {
49
+ ): Promise<string[]> {
73
50
  const parsed = path.parse(filePath);
74
51
  const fileName = parsed.name;
75
- const fileExt = parsed.ext;
76
- console.log({ fileName, fileExt });
77
- console.log("Chunking file", filePath);
78
-
79
- // create a temp directory
80
52
  const outputDirPath = path.join(outputDir, `${fileName}/chunks`);
81
53
  await fs.promises.mkdir(outputDirPath, { recursive: true });
82
54
  const doneFilePath = path.join(outputDirPath, ".chunking_done");
@@ -90,10 +62,9 @@ export class DownloaderService {
90
62
  if (existingChunkNames.length > 0 && doneFileExists) {
91
63
  if (reuseExistingChunks) {
92
64
  console.log("Chunks already exist, skipping");
93
- const names = existingChunkNames.map((chunkName) =>
65
+ return existingChunkNames.map((chunkName) =>
94
66
  path.join(outputDirPath, chunkName)
95
67
  );
96
- return names;
97
68
  } else {
98
69
  for (const file of existingFolderFiles) {
99
70
  fs.rmSync(path.join(outputDirPath, file), { recursive: true });
@@ -113,6 +84,9 @@ export class DownloaderService {
113
84
  return chunkNames.map((chunkName) => path.join(outputDirPath, chunkName));
114
85
  }
115
86
 
87
+ /**
88
+ * Stream transcription of audio chunks using Whisper.
89
+ */
116
90
  public async *streamTranscription(
117
91
  files: string[],
118
92
  outputPath: string,
@@ -123,13 +97,11 @@ export class DownloaderService {
123
97
  console.log("Transcription already exists, using cached data");
124
98
  const contents = await readFile(outputPath);
125
99
  const data = JSON.parse(contents.toString()) as TranscriptChunk[];
126
- for (const item of data) {
127
- yield item;
128
- }
100
+ for (const item of data) yield item;
129
101
  return;
130
102
  }
131
103
 
132
- const allTranscripts = [];
104
+ const allTranscripts: TranscriptChunk[] = [];
133
105
  for (const file of files) {
134
106
  const chunkName = path.parse(file).name;
135
107
  const chunkTranscriptPath = path.join(
@@ -139,17 +111,12 @@ export class DownloaderService {
139
111
  const chunkExists = await fileExists(chunkTranscriptPath);
140
112
 
141
113
  if (chunkExists && reusePreviousTranscript) {
142
- console.log(
143
- chunkTranscriptPath,
144
- " transcription already exists, using cached data"
145
- );
146
114
  const contents = await readFile(chunkTranscriptPath);
147
- const cached = {
115
+ const cached: TranscriptChunk = {
148
116
  chunkPath: chunkTranscriptPath,
149
117
  text: contents.toString(),
150
118
  usd_cost: 0,
151
119
  };
152
-
153
120
  yield cached;
154
121
  allTranscripts.push(cached);
155
122
  continue;
@@ -163,7 +130,7 @@ export class DownloaderService {
163
130
  fileName: path.basename(file),
164
131
  model: "whisper-1",
165
132
  })
166
- .catch((e) => {
133
+ .catch((e: any) => {
167
134
  console.error("Error transcribing", file, e);
168
135
  return { text: "" };
169
136
  });
@@ -171,11 +138,10 @@ export class DownloaderService {
171
138
  await mkdir(path.dirname(chunkTranscriptPath), { recursive: true });
172
139
  await fs.promises.writeFile(chunkTranscriptPath, transcript.text);
173
140
 
174
- // save chunk transcript to file
175
- const data = {
141
+ const data: TranscriptChunk = {
176
142
  chunkPath: chunkTranscriptPath,
177
143
  text: transcript.text,
178
- usd_cost: 30 * 0.0001, // assume 30 seconds,
144
+ usd_cost: 30 * 0.0001,
179
145
  };
180
146
  yield data;
181
147
  allTranscripts.push(data);
@@ -184,6 +150,9 @@ export class DownloaderService {
184
150
  fs.writeFileSync(outputPath, JSON.stringify(allTranscripts, null, 2));
185
151
  }
186
152
 
153
+ /**
154
+ * Transcribe all audio chunks and return the text strings.
155
+ */
187
156
  public async transcribeChunks(
188
157
  files: string[],
189
158
  outputPath: string,
@@ -191,18 +160,16 @@ export class DownloaderService {
191
160
  ): Promise<string[]> {
192
161
  const exists = await fileExists(outputPath);
193
162
  if (exists && reusePreviousTranscript) {
194
- console.log("Transcription already exists, using cached data");
195
163
  const contents = await readFile(outputPath);
196
164
  return JSON.parse(contents.toString()) as string[];
197
165
  }
198
166
 
199
- const fullText = [];
200
- for await (const { chunkPath, text } of this.streamTranscription(
167
+ const fullText: string[] = [];
168
+ for await (const { text } of this.streamTranscription(
201
169
  files,
202
170
  outputPath,
203
171
  reusePreviousTranscript
204
172
  )) {
205
- console.log("Chunk transcribed:", chunkPath);
206
173
  fullText.push(text);
207
174
  }
208
175
 
@@ -210,42 +177,65 @@ export class DownloaderService {
210
177
  return fullText;
211
178
  }
212
179
 
180
+ /**
181
+ * Process an audio/video file: chunk it with ffmpeg, then transcribe each chunk.
182
+ * Returns an array of transcript strings (one per chunk).
183
+ */
184
+ public async processAudio(
185
+ filePath: string,
186
+ reusePreviousTranscript = true,
187
+ chunkTime = 30
188
+ ): Promise<string[]> {
189
+ const parsed = path.parse(filePath);
190
+ const outputPath = `${parsed.dir}/${parsed.name}/transcript.json`;
191
+
192
+ const exists = await fileExists(outputPath);
193
+ if (exists && reusePreviousTranscript) {
194
+ const fileContent = (await readFile(outputPath, "utf8")) as string;
195
+ return outputPath.endsWith("txt")
196
+ ? fileContent.split("\n")
197
+ : JSON.parse(fileContent);
198
+ }
199
+
200
+ const chunks = await this.chunk(
201
+ filePath,
202
+ parsed.dir,
203
+ chunkTime,
204
+ reusePreviousTranscript
205
+ );
206
+ return this.transcribeChunks(chunks, outputPath, reusePreviousTranscript);
207
+ }
208
+
209
+ /**
210
+ * Extract keyframes from a video file using ffmpeg, then describe each with vision AI.
211
+ */
213
212
  public async *streamKeyFrameExtraction(
214
213
  filePath: string,
215
214
  videoJsonPath: string,
216
- reusePreviousKeyframes: boolean = true,
217
- interval: number = 10
215
+ reusePreviousKeyframes = true,
216
+ interval = 10
218
217
  ): AsyncGenerator<KeyframeInfo> {
219
218
  if (reusePreviousKeyframes && fs.existsSync(videoJsonPath)) {
220
- console.log("Keyframes already exist, using cached data");
221
219
  const contents = await readFile(videoJsonPath);
222
220
  const data = JSON.parse(contents.toString()) as KeyframeInfo[];
223
- for (const keyframe of data) {
224
- yield { ...keyframe, usd_cost: 0 };
225
- }
221
+ for (const keyframe of data) yield { ...keyframe, usd_cost: 0 };
226
222
  return;
227
223
  }
228
224
 
229
- const parsed = path.parse(filePath);
230
225
  const outputDir = path.dirname(videoJsonPath);
231
- const fileName = parsed.name;
232
- const keyframesDir = path.join(outputDir, `/keyframes`);
226
+ const keyframesDir = path.join(outputDir, "keyframes");
233
227
  await fs.promises.mkdir(keyframesDir, { recursive: true });
234
228
 
235
229
  const command = `ffmpeg -i "${filePath}" -vf "fps=1/${interval},scale=640:-1" "${keyframesDir}/frame%04d.jpg"`;
236
230
  await execAsync(command);
237
- console.log("Extracting keyframe:", command);
238
231
 
239
232
  const keyframes = await fs.promises.readdir(keyframesDir);
233
+ const allKeyframes: KeyframeInfo[] = [];
240
234
 
241
- const allKeyframes = [];
242
235
  for (const keyframe of keyframes) {
243
236
  const keyframePath = path.join(keyframesDir, keyframe);
244
237
  const keyframeName = path.parse(keyframe).name;
245
- const keyframeDescriptionPath = path.join(
246
- keyframesDir,
247
- `${keyframeName}.json`
248
- );
238
+ const keyframeDescriptionPath = path.join(keyframesDir, `${keyframeName}.json`);
249
239
  const descriptionExists = await fileExists(keyframeDescriptionPath);
250
240
 
251
241
  if (descriptionExists && reusePreviousKeyframes) {
@@ -257,10 +247,11 @@ export class DownloaderService {
257
247
  }
258
248
 
259
249
  const description = await this.describeKeyframe(keyframePath);
260
- const keyframeJson = {
250
+ const frameNumber = parseInt(keyframe.match(/\d+/)?.[0] ?? "0", 10);
251
+ const keyframeJson: KeyframeInfo = {
261
252
  path: keyframePath,
262
253
  description: description.choices[0].message.content,
263
- timestamp: this.extractTimestamp(keyframe, interval),
254
+ timestamp: frameNumber * interval,
264
255
  usd_cost: description.usd_cost,
265
256
  };
266
257
  await fs.promises.writeFile(
@@ -271,17 +262,14 @@ export class DownloaderService {
271
262
  allKeyframes.push(keyframeJson);
272
263
  }
273
264
 
274
- await fs.promises.writeFile(
275
- videoJsonPath,
276
- JSON.stringify(allKeyframes, null, 2)
277
- );
265
+ await fs.promises.writeFile(videoJsonPath, JSON.stringify(allKeyframes, null, 2));
278
266
  }
279
267
 
280
268
  public async extractKeyframes(
281
269
  filePath: string,
282
270
  outputPath: string,
283
- reusePreviousKeyframes: boolean = true,
284
- interval: number = 10
271
+ reusePreviousKeyframes = true,
272
+ interval = 10
285
273
  ): Promise<KeyframeInfo[]> {
286
274
  const keyframes: KeyframeInfo[] = [];
287
275
  for await (const keyframe of this.streamKeyFrameExtraction(
@@ -292,7 +280,6 @@ export class DownloaderService {
292
280
  )) {
293
281
  keyframes.push(keyframe);
294
282
  }
295
-
296
283
  await fs.promises.writeFile(outputPath, JSON.stringify(keyframes, null, 2));
297
284
  return keyframes;
298
285
  }
@@ -300,165 +287,20 @@ export class DownloaderService {
300
287
  private async describeKeyframe(keyframePath: string) {
301
288
  const question =
302
289
  "Describe this image in detail, focusing on the main elements and actions visible.";
303
- const base64 = await fs.promises.readFile(keyframePath, {
304
- encoding: "base64",
305
- });
290
+ const base64 = await fs.promises.readFile(keyframePath, { encoding: "base64" });
306
291
  const image = `data:image/jpeg;base64,${base64}`;
307
- console.log("Describing keyframe:", keyframePath);
308
- const response = await this.askGptVision(image, question);
309
- return response;
310
- }
311
-
312
- private extractTimestamp(keyframeName: string, interval: number): number {
313
- const frameNumber = parseInt(keyframeName.match(/\d+/)[0], 10);
314
- return frameNumber * interval;
315
- }
316
-
317
- async processAudio(
318
- filePath: string,
319
- reusePreviousTranscript = true,
320
- chunkTime = 30
321
- ): Promise<string[]> {
322
- const parsed = path.parse(filePath);
323
- const outputPath = `${parsed.dir}/${parsed.name}/transcript.json`;
324
-
325
- // Skip chunking if the full output exists
326
- const exists = await fileExists(outputPath);
327
- if (exists && reusePreviousTranscript) {
328
- console.log(
329
- `Transcription ${outputPath} already exists, using cached data`
330
- );
331
- const fileContent = await readFile(outputPath, "utf8");
332
- return outputPath.endsWith("txt")
333
- ? fileContent.split("\n")
334
- : JSON.parse(fileContent);
335
- }
336
-
337
- const chunks = await this.chunk(
338
- filePath,
339
- parsed.dir,
340
- chunkTime,
341
- reusePreviousTranscript
342
- );
343
- const transcription = await this.transcribeChunks(
344
- chunks,
345
- outputPath,
346
- reusePreviousTranscript
347
- );
348
-
349
- return transcription;
350
- }
351
-
352
- async *streamProcessAudio(
353
- filePath: string,
354
- reusePreviousTranscript = true,
355
- chunkTime = 30
356
- ): AsyncGenerator<TranscriptChunk> {
357
- const parsed = path.parse(filePath);
358
- const outputPath = `${parsed.dir}/${parsed.name}/transcript.json`;
359
-
360
- // Skip chunking if the full output exists
361
- const exists = await fileExists(outputPath);
362
- if (exists && reusePreviousTranscript) {
363
- console.log(
364
- `Transcription ${outputPath} already exists, using cached data`
365
- );
366
- const fileContent = await readFile(outputPath, "utf8");
367
- const lines = outputPath.endsWith("txt")
368
- ? fileContent.split("\n")
369
- : JSON.parse(fileContent);
370
-
371
- for (const line of lines) {
372
- if (typeof line === "string") {
373
- yield { chunkPath: "", text: line, usd_cost: 0 };
374
- } else {
375
- yield line as TranscriptChunk;
376
- }
377
- }
378
- return;
379
- }
380
-
381
- const chunks = await this.chunk(
382
- filePath,
383
- parsed.dir,
384
- chunkTime,
385
- reusePreviousTranscript
386
- );
387
-
388
- for await (const chunk of this.streamTranscription(
389
- chunks,
390
- outputPath,
391
- reusePreviousTranscript
392
- )) {
393
- yield chunk;
394
- }
395
- }
396
-
397
- async processVideo(
398
- filePath: string,
399
- reusePreviousTranscript = true,
400
- chunkTime = 30
401
- ) {
402
- const parsed = path.parse(filePath);
403
- const outputPath = `${parsed.dir}/${parsed.name}/video.json`;
404
-
405
- console.log("Processing audio...");
406
- const transcriptions = await this.processAudio(
407
- filePath,
408
- reusePreviousTranscript,
409
- chunkTime
410
- );
411
-
412
- console.log("Extracting keyframes...");
413
- const videoAnalysis = await this.extractKeyframes(
414
- filePath,
415
- outputPath,
416
- reusePreviousTranscript,
417
- chunkTime
418
- );
419
-
420
- return videoAnalysis.map((frame, index) => {
421
- return {
422
- frame,
423
- transcription: transcriptions[index],
424
- };
425
- });
426
- }
427
-
428
- async *streamProcessVideo(
429
- filePath: string,
430
- reusePreviousTranscript = true,
431
- chunkTime = 30
432
- ) {
433
- const parsed = path.parse(filePath);
434
- const videoJson = `${parsed.dir}/${parsed.name}/video.json`;
435
-
436
- console.log("Processing audio...");
437
- const transcriptions = this.streamProcessAudio(
438
- filePath,
439
- reusePreviousTranscript,
440
- chunkTime
441
- );
442
-
443
- console.log("Extracting keyframes...");
444
- const videoAnalysis = this.streamKeyFrameExtraction(
445
- filePath,
446
- videoJson,
447
- reusePreviousTranscript,
448
- chunkTime
449
- );
450
-
451
- for await (const frame of videoAnalysis) {
452
- const transcription = (await transcriptions.next())
453
- ?.value as TranscriptChunk;
454
- yield {
455
- frame,
456
- transcription: transcription || {
457
- chunkPath: "",
458
- text: "[missing transcript]",
459
- usd_cost: 0,
292
+ return this.clients.createCompletion("openai", {
293
+ model: "gpt-4o",
294
+ max_tokens: 2500,
295
+ messages: [
296
+ {
297
+ role: "user",
298
+ content: [
299
+ { type: "text", text: question },
300
+ { type: "image_url", image_url: { url: image } },
301
+ ],
460
302
  },
461
- };
462
- }
303
+ ],
304
+ });
463
305
  }
464
306
  }
@@ -12,9 +12,8 @@ import * as fs from "fs";
12
12
  const fsPromises = fs.promises;
13
13
  import { createWriteStream } from "fs";
14
14
  import * as path from "path";
15
- import { pipeline } from "stream";
15
+ import { pipeline, Readable } from "stream";
16
16
  import * as util from "util";
17
- import axios from "axios";
18
17
 
19
18
  import { createReadStream } from "fs";
20
19
  const pipelineAsync = util.promisify(pipeline);
@@ -83,19 +82,19 @@ export class S3Service {
83
82
  const fileStream = createReadStream(filePath);
84
83
  const fileStats = await fsPromises.stat(filePath);
85
84
 
86
- const response = await axios.put(presignedUrl, fileStream, {
87
- headers: {
88
- "Content-Length": fileStats.size,
89
- },
90
- maxBodyLength: Infinity,
91
- maxContentLength: Infinity,
85
+ const response = await fetch(presignedUrl, {
86
+ method: "PUT",
87
+ headers: { "Content-Length": String(fileStats.size) },
88
+ body: fileStream as any,
89
+ // @ts-ignore - Node 18+ supports ReadableStream body with duplex
90
+ duplex: "half",
92
91
  });
93
92
 
94
- if (response.status === 200) {
95
- console.log("File uploaded successfully to pre-signed URL");
96
- } else {
93
+ if (!response.ok) {
97
94
  throw new Error(`Upload failed with status code: ${response.status}`);
98
95
  }
96
+
97
+ console.log("File uploaded successfully to pre-signed URL");
99
98
  } catch (error) {
100
99
  console.error("Error uploading file to pre-signed URL:", error);
101
100
  throw error;
@@ -107,12 +106,14 @@ export class S3Service {
107
106
  destinationPath: string
108
107
  ): Promise<void> {
109
108
  try {
110
- const response = await axios.get(presignedUrl, {
111
- responseType: "stream",
112
- });
109
+ const response = await fetch(presignedUrl);
110
+
111
+ if (!response.ok) {
112
+ throw new Error(`Download failed with status code: ${response.status}`);
113
+ }
113
114
 
114
115
  const fileStream = createWriteStream(destinationPath);
115
- await pipelineAsync(response.data, fileStream);
116
+ await pipelineAsync(Readable.from(response.body as any), fileStream);
116
117
 
117
118
  console.log(
118
119
  `File downloaded successfully from pre-signed URL to ${destinationPath}`
@@ -123,4 +124,3 @@ export class S3Service {
123
124
  }
124
125
  }
125
126
  }
126
-
@@ -1,4 +1,3 @@
1
- import { DownloaderService } from "../plugins/downloader/downloader";
2
1
  import { AIClient, Clients } from "../clients";
3
2
  import { AgentService } from "./AgentService";
4
3
  import { EventService } from "./EventService";
@@ -14,6 +13,7 @@ import { AgentSyncKnowhowWeb } from "./AgentSyncKnowhowWeb";
14
13
  import { AgentSyncFs } from "./AgentSyncFs";
15
14
  import { SessionManager } from "./SessionManager";
16
15
  import { TaskRegistry } from "./TaskRegistry";
16
+ import { MediaProcessorService } from "./MediaProcessorService";
17
17
 
18
18
  export * from "./AgentService";
19
19
  export * from "./EventService";
@@ -25,6 +25,7 @@ export * from "./LazyToolsService";
25
25
  export * as MCP from "./Mcp";
26
26
  export * from "./EmbeddingService";
27
27
  export * from "./DockerService";
28
+ export * from "./MediaProcessorService";
28
29
  export * from "./AgentSyncKnowhowWeb";
29
30
  export * from "./AgentSyncFs";
30
31
  export * from "./SessionManager";
@@ -45,7 +46,7 @@ let Singletons = {} as {
45
46
  knowhowApiClient: KnowhowSimpleClient;
46
47
  Plugins: PluginService;
47
48
  Clients: AIClient;
48
- Downloader: DownloaderService;
49
+ MediaProcessor: MediaProcessorService;
49
50
  };
50
51
 
51
52
  export const services = (): typeof Singletons => {
@@ -53,7 +54,6 @@ export const services = (): typeof Singletons => {
53
54
  const Tools = new ToolsService();
54
55
  const Events = new EventService();
55
56
  const Agents = new AgentService(Tools, Events);
56
- const Downloader = new DownloaderService(Clients);
57
57
  const Plugins = new PluginService({
58
58
  Agents,
59
59
  Events,
@@ -66,11 +66,11 @@ export const services = (): typeof Singletons => {
66
66
  AwsS3: new S3Service(),
67
67
  Clients,
68
68
  Docker: new DockerService(),
69
- Downloader,
70
69
  Events,
71
70
  Flags: new FlagsService(),
72
71
  GitHub: new GitHubService(),
73
72
  Mcp: new McpService(),
73
+ MediaProcessor: new MediaProcessorService(Clients),
74
74
  Plugins,
75
75
  Tools,
76
76
  knowhowApiClient: new KnowhowSimpleClient(),
@@ -10,12 +10,13 @@ export class ModulesService {
10
10
 
11
11
  // If no context provided, fall back to global singletons
12
12
  if (!context) {
13
- const { Clients, Plugins, Agents, Tools } = services();
13
+ const { Clients, Plugins, Agents, Tools, MediaProcessor } = services();
14
14
  context = {
15
15
  Agents,
16
16
  Plugins,
17
17
  Clients,
18
18
  Tools,
19
+ MediaProcessor,
19
20
  };
20
21
  }
21
22
 
@@ -46,7 +47,14 @@ export class ModulesService {
46
47
  }
47
48
 
48
49
  for (const plugin of importedModule.plugins) {
49
- pluginService.registerPlugin(plugin.name, plugin.plugin);
50
+ const pluginContext = {
51
+ Agents: agentService,
52
+ Clients: clients,
53
+ Tools: toolsService,
54
+ Plugins: pluginService,
55
+ ...(context.MediaProcessor ? { MediaProcessor: context.MediaProcessor } : {}),
56
+ };
57
+ pluginService.registerPlugin(plugin.name, new plugin.plugin(pluginContext as any));
50
58
  }
51
59
 
52
60
  for (const client of importedModule.clients) {