micro-models-agent 0.6.4 → 0.7.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 (472) hide show
  1. package/README.md +286 -436
  2. package/dist/main.js +11744 -0
  3. package/dist/skills/builtin/git.md +36 -0
  4. package/dist/skills/builtin/typescript.md +35 -0
  5. package/package.json +41 -47
  6. package/dist/agent/context.d.ts +0 -73
  7. package/dist/agent/context.d.ts.map +0 -1
  8. package/dist/agent/context.js +0 -262
  9. package/dist/agent/context.js.map +0 -1
  10. package/dist/agent/error-escalation.d.ts +0 -19
  11. package/dist/agent/error-escalation.d.ts.map +0 -1
  12. package/dist/agent/error-escalation.js +0 -168
  13. package/dist/agent/error-escalation.js.map +0 -1
  14. package/dist/agent/loop.d.ts +0 -46
  15. package/dist/agent/loop.d.ts.map +0 -1
  16. package/dist/agent/loop.js +0 -1043
  17. package/dist/agent/loop.js.map +0 -1
  18. package/dist/agent/memory.d.ts +0 -40
  19. package/dist/agent/memory.d.ts.map +0 -1
  20. package/dist/agent/memory.js +0 -231
  21. package/dist/agent/memory.js.map +0 -1
  22. package/dist/agent/modes/applier.d.ts +0 -43
  23. package/dist/agent/modes/applier.d.ts.map +0 -1
  24. package/dist/agent/modes/applier.js +0 -30
  25. package/dist/agent/modes/applier.js.map +0 -1
  26. package/dist/agent/modes/registry.d.ts +0 -35
  27. package/dist/agent/modes/registry.d.ts.map +0 -1
  28. package/dist/agent/modes/registry.js +0 -169
  29. package/dist/agent/modes/registry.js.map +0 -1
  30. package/dist/agent/modes/types.d.ts +0 -61
  31. package/dist/agent/modes/types.d.ts.map +0 -1
  32. package/dist/agent/modes/types.js +0 -13
  33. package/dist/agent/modes/types.js.map +0 -1
  34. package/dist/agent/react-loop.d.ts +0 -65
  35. package/dist/agent/react-loop.d.ts.map +0 -1
  36. package/dist/agent/react-loop.js +0 -101
  37. package/dist/agent/react-loop.js.map +0 -1
  38. package/dist/agent/system-prompt.d.ts +0 -26
  39. package/dist/agent/system-prompt.d.ts.map +0 -1
  40. package/dist/agent/system-prompt.js +0 -478
  41. package/dist/agent/system-prompt.js.map +0 -1
  42. package/dist/agent/tool-executor.d.ts +0 -36
  43. package/dist/agent/tool-executor.d.ts.map +0 -1
  44. package/dist/agent/tool-executor.js +0 -346
  45. package/dist/agent/tool-executor.js.map +0 -1
  46. package/dist/agent/user-profile.d.ts +0 -26
  47. package/dist/agent/user-profile.d.ts.map +0 -1
  48. package/dist/agent/user-profile.js +0 -169
  49. package/dist/agent/user-profile.js.map +0 -1
  50. package/dist/agent/write-enforcer.d.ts +0 -7
  51. package/dist/agent/write-enforcer.d.ts.map +0 -1
  52. package/dist/agent/write-enforcer.js +0 -8
  53. package/dist/agent/write-enforcer.js.map +0 -1
  54. package/dist/cli/config.d.ts +0 -13
  55. package/dist/cli/config.d.ts.map +0 -1
  56. package/dist/cli/config.js +0 -82
  57. package/dist/cli/config.js.map +0 -1
  58. package/dist/cli/model.d.ts +0 -13
  59. package/dist/cli/model.d.ts.map +0 -1
  60. package/dist/cli/model.js +0 -170
  61. package/dist/cli/model.js.map +0 -1
  62. package/dist/cli/provider.d.ts +0 -12
  63. package/dist/cli/provider.d.ts.map +0 -1
  64. package/dist/cli/provider.js +0 -90
  65. package/dist/cli/provider.js.map +0 -1
  66. package/dist/cli/update.d.ts +0 -12
  67. package/dist/cli/update.d.ts.map +0 -1
  68. package/dist/cli/update.js +0 -69
  69. package/dist/cli/update.js.map +0 -1
  70. package/dist/cli.d.ts +0 -3
  71. package/dist/cli.d.ts.map +0 -1
  72. package/dist/cli.js +0 -310
  73. package/dist/cli.js.map +0 -1
  74. package/dist/config/config.d.ts +0 -59
  75. package/dist/config/config.d.ts.map +0 -1
  76. package/dist/config/config.js +0 -232
  77. package/dist/config/config.js.map +0 -1
  78. package/dist/config/types.d.ts +0 -122
  79. package/dist/config/types.d.ts.map +0 -1
  80. package/dist/config/types.js +0 -13
  81. package/dist/config/types.js.map +0 -1
  82. package/dist/file-loader.d.ts +0 -20
  83. package/dist/file-loader.d.ts.map +0 -1
  84. package/dist/file-loader.js +0 -121
  85. package/dist/file-loader.js.map +0 -1
  86. package/dist/index.d.ts +0 -9
  87. package/dist/index.d.ts.map +0 -1
  88. package/dist/index.js +0 -11
  89. package/dist/index.js.map +0 -1
  90. package/dist/indexer/cache.d.ts +0 -34
  91. package/dist/indexer/cache.d.ts.map +0 -1
  92. package/dist/indexer/cache.js +0 -221
  93. package/dist/indexer/cache.js.map +0 -1
  94. package/dist/indexer/code-index.d.ts +0 -27
  95. package/dist/indexer/code-index.d.ts.map +0 -1
  96. package/dist/indexer/code-index.js +0 -219
  97. package/dist/indexer/code-index.js.map +0 -1
  98. package/dist/indexer/project-map.d.ts +0 -23
  99. package/dist/indexer/project-map.d.ts.map +0 -1
  100. package/dist/indexer/project-map.js +0 -264
  101. package/dist/indexer/project-map.js.map +0 -1
  102. package/dist/llm/lmstudio-native.d.ts +0 -10
  103. package/dist/llm/lmstudio-native.d.ts.map +0 -1
  104. package/dist/llm/lmstudio-native.js +0 -179
  105. package/dist/llm/lmstudio-native.js.map +0 -1
  106. package/dist/llm/openai-compat.d.ts +0 -11
  107. package/dist/llm/openai-compat.d.ts.map +0 -1
  108. package/dist/llm/openai-compat.js +0 -161
  109. package/dist/llm/openai-compat.js.map +0 -1
  110. package/dist/llm/provider.d.ts +0 -46
  111. package/dist/llm/provider.d.ts.map +0 -1
  112. package/dist/llm/provider.js +0 -13
  113. package/dist/llm/provider.js.map +0 -1
  114. package/dist/llm/token-counter.d.ts +0 -15
  115. package/dist/llm/token-counter.d.ts.map +0 -1
  116. package/dist/llm/token-counter.js +0 -51
  117. package/dist/llm/token-counter.js.map +0 -1
  118. package/dist/logger.d.ts +0 -24
  119. package/dist/logger.d.ts.map +0 -1
  120. package/dist/logger.js +0 -156
  121. package/dist/logger.js.map +0 -1
  122. package/dist/plugins/adapters/hooks-adapter.d.ts +0 -12
  123. package/dist/plugins/adapters/hooks-adapter.d.ts.map +0 -1
  124. package/dist/plugins/adapters/hooks-adapter.js +0 -23
  125. package/dist/plugins/adapters/hooks-adapter.js.map +0 -1
  126. package/dist/plugins/builtin/lint-on-write/index.d.ts +0 -12
  127. package/dist/plugins/builtin/lint-on-write/index.d.ts.map +0 -1
  128. package/dist/plugins/builtin/lint-on-write/index.js +0 -98
  129. package/dist/plugins/builtin/lint-on-write/index.js.map +0 -1
  130. package/dist/plugins/builtin/lint-on-write/index.ts +0 -97
  131. package/dist/plugins/builtin/lint-on-write/manifest.json +0 -13
  132. package/dist/plugins/builtin/notify/index.d.ts +0 -11
  133. package/dist/plugins/builtin/notify/index.d.ts.map +0 -1
  134. package/dist/plugins/builtin/notify/index.js +0 -102
  135. package/dist/plugins/builtin/notify/index.js.map +0 -1
  136. package/dist/plugins/builtin/notify/index.ts +0 -105
  137. package/dist/plugins/builtin/notify/manifest.json +0 -16
  138. package/dist/plugins/loader.d.ts +0 -18
  139. package/dist/plugins/loader.d.ts.map +0 -1
  140. package/dist/plugins/loader.js +0 -182
  141. package/dist/plugins/loader.js.map +0 -1
  142. package/dist/plugins/manager.d.ts +0 -55
  143. package/dist/plugins/manager.d.ts.map +0 -1
  144. package/dist/plugins/manager.js +0 -190
  145. package/dist/plugins/manager.js.map +0 -1
  146. package/dist/plugins/types.d.ts +0 -76
  147. package/dist/plugins/types.d.ts.map +0 -1
  148. package/dist/plugins/types.js +0 -8
  149. package/dist/plugins/types.js.map +0 -1
  150. package/dist/repl/commands.d.ts +0 -63
  151. package/dist/repl/commands.d.ts.map +0 -1
  152. package/dist/repl/commands.js +0 -1070
  153. package/dist/repl/commands.js.map +0 -1
  154. package/dist/repl/history.d.ts +0 -32
  155. package/dist/repl/history.d.ts.map +0 -1
  156. package/dist/repl/history.js +0 -40
  157. package/dist/repl/history.js.map +0 -1
  158. package/dist/repl/index.d.ts +0 -14
  159. package/dist/repl/index.d.ts.map +0 -1
  160. package/dist/repl/index.js +0 -235
  161. package/dist/repl/index.js.map +0 -1
  162. package/dist/session/store.d.ts +0 -46
  163. package/dist/session/store.d.ts.map +0 -1
  164. package/dist/session/store.js +0 -154
  165. package/dist/session/store.js.map +0 -1
  166. package/dist/setup-wizard.d.ts +0 -3
  167. package/dist/setup-wizard.d.ts.map +0 -1
  168. package/dist/setup-wizard.js +0 -410
  169. package/dist/setup-wizard.js.map +0 -1
  170. package/dist/skills/builtin/agent-test/SKILL.md +0 -68
  171. package/dist/skills/builtin/cli-expert/SKILL.md +0 -848
  172. package/dist/skills/builtin/code-review/SKILL.md +0 -424
  173. package/dist/skills/builtin/git-expert/SKILL.md +0 -522
  174. package/dist/skills/builtin/grill-me/SKILL.md +0 -46
  175. package/dist/skills/builtin/jest-testing-expert/SKILL.md +0 -957
  176. package/dist/skills/builtin/nodejs-expert/SKILL.md +0 -192
  177. package/dist/skills/builtin/systematic-debugging/SKILL.md +0 -296
  178. package/dist/skills/builtin/test-driven-development/SKILL.md +0 -371
  179. package/dist/skills/builtin/typescript-expert/SKILL.md +0 -429
  180. package/dist/skills/builtin/web-scraping/SKILL.md +0 -422
  181. package/dist/skills/loader.d.ts +0 -49
  182. package/dist/skills/loader.d.ts.map +0 -1
  183. package/dist/skills/loader.js +0 -401
  184. package/dist/skills/loader.js.map +0 -1
  185. package/dist/styling/engine.d.ts +0 -39
  186. package/dist/styling/engine.d.ts.map +0 -1
  187. package/dist/styling/engine.js +0 -122
  188. package/dist/styling/engine.js.map +0 -1
  189. package/dist/styling/index.d.ts +0 -15
  190. package/dist/styling/index.d.ts.map +0 -1
  191. package/dist/styling/index.js +0 -40
  192. package/dist/styling/index.js.map +0 -1
  193. package/dist/styling/loader.d.ts +0 -9
  194. package/dist/styling/loader.d.ts.map +0 -1
  195. package/dist/styling/loader.js +0 -169
  196. package/dist/styling/loader.js.map +0 -1
  197. package/dist/styling/types.d.ts +0 -64
  198. package/dist/styling/types.d.ts.map +0 -1
  199. package/dist/styling/types.js +0 -12
  200. package/dist/styling/types.js.map +0 -1
  201. package/dist/subagent/chat.d.ts +0 -19
  202. package/dist/subagent/chat.d.ts.map +0 -1
  203. package/dist/subagent/chat.js +0 -279
  204. package/dist/subagent/chat.js.map +0 -1
  205. package/dist/subagent/context.d.ts +0 -9
  206. package/dist/subagent/context.d.ts.map +0 -1
  207. package/dist/subagent/context.js +0 -75
  208. package/dist/subagent/context.js.map +0 -1
  209. package/dist/subagent/definition.d.ts +0 -7
  210. package/dist/subagent/definition.d.ts.map +0 -1
  211. package/dist/subagent/definition.js +0 -26
  212. package/dist/subagent/definition.js.map +0 -1
  213. package/dist/subagent/errors.d.ts +0 -24
  214. package/dist/subagent/errors.d.ts.map +0 -1
  215. package/dist/subagent/errors.js +0 -85
  216. package/dist/subagent/errors.js.map +0 -1
  217. package/dist/subagent/validation.d.ts +0 -17
  218. package/dist/subagent/validation.d.ts.map +0 -1
  219. package/dist/subagent/validation.js +0 -83
  220. package/dist/subagent/validation.js.map +0 -1
  221. package/dist/tools/adapters/bash-to-fs.d.ts +0 -20
  222. package/dist/tools/adapters/bash-to-fs.d.ts.map +0 -1
  223. package/dist/tools/adapters/bash-to-fs.js +0 -74
  224. package/dist/tools/adapters/bash-to-fs.js.map +0 -1
  225. package/dist/tools/adapters/content-sanitizer.d.ts +0 -13
  226. package/dist/tools/adapters/content-sanitizer.d.ts.map +0 -1
  227. package/dist/tools/adapters/content-sanitizer.js +0 -73
  228. package/dist/tools/adapters/content-sanitizer.js.map +0 -1
  229. package/dist/tools/adapters/grounding-fixer.d.ts +0 -15
  230. package/dist/tools/adapters/grounding-fixer.d.ts.map +0 -1
  231. package/dist/tools/adapters/grounding-fixer.js +0 -57
  232. package/dist/tools/adapters/grounding-fixer.js.map +0 -1
  233. package/dist/tools/adapters/index.d.ts +0 -6
  234. package/dist/tools/adapters/index.d.ts.map +0 -1
  235. package/dist/tools/adapters/index.js +0 -5
  236. package/dist/tools/adapters/index.js.map +0 -1
  237. package/dist/tools/adapters/pipeline.d.ts +0 -34
  238. package/dist/tools/adapters/pipeline.d.ts.map +0 -1
  239. package/dist/tools/adapters/pipeline.js +0 -58
  240. package/dist/tools/adapters/pipeline.js.map +0 -1
  241. package/dist/tools/adapters/types.d.ts +0 -46
  242. package/dist/tools/adapters/types.d.ts.map +0 -1
  243. package/dist/tools/adapters/types.js +0 -2
  244. package/dist/tools/adapters/types.js.map +0 -1
  245. package/dist/tools/approve.d.ts +0 -27
  246. package/dist/tools/approve.d.ts.map +0 -1
  247. package/dist/tools/approve.js +0 -45
  248. package/dist/tools/approve.js.map +0 -1
  249. package/dist/tools/bash.d.ts +0 -25
  250. package/dist/tools/bash.d.ts.map +0 -1
  251. package/dist/tools/bash.js +0 -292
  252. package/dist/tools/bash.js.map +0 -1
  253. package/dist/tools/fs/copy-file.d.ts +0 -13
  254. package/dist/tools/fs/copy-file.d.ts.map +0 -1
  255. package/dist/tools/fs/copy-file.js +0 -54
  256. package/dist/tools/fs/copy-file.js.map +0 -1
  257. package/dist/tools/fs/create-dir.d.ts +0 -13
  258. package/dist/tools/fs/create-dir.d.ts.map +0 -1
  259. package/dist/tools/fs/create-dir.js +0 -44
  260. package/dist/tools/fs/create-dir.js.map +0 -1
  261. package/dist/tools/fs/delete-file.d.ts +0 -13
  262. package/dist/tools/fs/delete-file.d.ts.map +0 -1
  263. package/dist/tools/fs/delete-file.js +0 -52
  264. package/dist/tools/fs/delete-file.js.map +0 -1
  265. package/dist/tools/fs/edit-file.d.ts +0 -15
  266. package/dist/tools/fs/edit-file.d.ts.map +0 -1
  267. package/dist/tools/fs/edit-file.js +0 -95
  268. package/dist/tools/fs/edit-file.js.map +0 -1
  269. package/dist/tools/fs/file-info.d.ts +0 -13
  270. package/dist/tools/fs/file-info.d.ts.map +0 -1
  271. package/dist/tools/fs/file-info.js +0 -50
  272. package/dist/tools/fs/file-info.js.map +0 -1
  273. package/dist/tools/fs/glob-search.d.ts +0 -13
  274. package/dist/tools/fs/glob-search.d.ts.map +0 -1
  275. package/dist/tools/fs/glob-search.js +0 -112
  276. package/dist/tools/fs/glob-search.js.map +0 -1
  277. package/dist/tools/fs/grep-search.d.ts +0 -14
  278. package/dist/tools/fs/grep-search.d.ts.map +0 -1
  279. package/dist/tools/fs/grep-search.js +0 -135
  280. package/dist/tools/fs/grep-search.js.map +0 -1
  281. package/dist/tools/fs/list-dir.d.ts +0 -13
  282. package/dist/tools/fs/list-dir.d.ts.map +0 -1
  283. package/dist/tools/fs/list-dir.js +0 -74
  284. package/dist/tools/fs/list-dir.js.map +0 -1
  285. package/dist/tools/fs/move-file.d.ts +0 -13
  286. package/dist/tools/fs/move-file.d.ts.map +0 -1
  287. package/dist/tools/fs/move-file.js +0 -54
  288. package/dist/tools/fs/move-file.js.map +0 -1
  289. package/dist/tools/fs/path-utils.d.ts +0 -51
  290. package/dist/tools/fs/path-utils.d.ts.map +0 -1
  291. package/dist/tools/fs/path-utils.js +0 -103
  292. package/dist/tools/fs/path-utils.js.map +0 -1
  293. package/dist/tools/fs/read-file.d.ts +0 -13
  294. package/dist/tools/fs/read-file.d.ts.map +0 -1
  295. package/dist/tools/fs/read-file.js +0 -64
  296. package/dist/tools/fs/read-file.js.map +0 -1
  297. package/dist/tools/fs/tree.d.ts +0 -13
  298. package/dist/tools/fs/tree.d.ts.map +0 -1
  299. package/dist/tools/fs/tree.js +0 -89
  300. package/dist/tools/fs/tree.js.map +0 -1
  301. package/dist/tools/fs/types.d.ts +0 -14
  302. package/dist/tools/fs/types.d.ts.map +0 -1
  303. package/dist/tools/fs/types.js +0 -11
  304. package/dist/tools/fs/types.js.map +0 -1
  305. package/dist/tools/fs/write-file.d.ts +0 -14
  306. package/dist/tools/fs/write-file.d.ts.map +0 -1
  307. package/dist/tools/fs/write-file.js +0 -83
  308. package/dist/tools/fs/write-file.js.map +0 -1
  309. package/dist/tools/hooks.d.ts +0 -44
  310. package/dist/tools/hooks.d.ts.map +0 -1
  311. package/dist/tools/hooks.js +0 -217
  312. package/dist/tools/hooks.js.map +0 -1
  313. package/dist/tools/id-gen.d.ts +0 -4
  314. package/dist/tools/id-gen.d.ts.map +0 -1
  315. package/dist/tools/id-gen.js +0 -12
  316. package/dist/tools/id-gen.js.map +0 -1
  317. package/dist/tools/mcp.d.ts +0 -36
  318. package/dist/tools/mcp.d.ts.map +0 -1
  319. package/dist/tools/mcp.js +0 -93
  320. package/dist/tools/mcp.js.map +0 -1
  321. package/dist/tools/native/approve.d.ts +0 -11
  322. package/dist/tools/native/approve.d.ts.map +0 -1
  323. package/dist/tools/native/approve.js +0 -39
  324. package/dist/tools/native/approve.js.map +0 -1
  325. package/dist/tools/native/plan.d.ts +0 -10
  326. package/dist/tools/native/plan.d.ts.map +0 -1
  327. package/dist/tools/native/plan.js +0 -115
  328. package/dist/tools/native/plan.js.map +0 -1
  329. package/dist/tools/native/project-map.d.ts +0 -15
  330. package/dist/tools/native/project-map.d.ts.map +0 -1
  331. package/dist/tools/native/project-map.js +0 -203
  332. package/dist/tools/native/project-map.js.map +0 -1
  333. package/dist/tools/native/question.d.ts +0 -11
  334. package/dist/tools/native/question.d.ts.map +0 -1
  335. package/dist/tools/native/question.js +0 -41
  336. package/dist/tools/native/question.js.map +0 -1
  337. package/dist/tools/native/scaffold.d.ts +0 -5
  338. package/dist/tools/native/scaffold.d.ts.map +0 -1
  339. package/dist/tools/native/scaffold.js +0 -246
  340. package/dist/tools/native/scaffold.js.map +0 -1
  341. package/dist/tools/native/theme.d.ts +0 -10
  342. package/dist/tools/native/theme.d.ts.map +0 -1
  343. package/dist/tools/native/theme.js +0 -101
  344. package/dist/tools/native/theme.js.map +0 -1
  345. package/dist/tools/native/todo.d.ts +0 -10
  346. package/dist/tools/native/todo.d.ts.map +0 -1
  347. package/dist/tools/native/todo.js +0 -58
  348. package/dist/tools/native/todo.js.map +0 -1
  349. package/dist/tools/native/user-profile.d.ts +0 -10
  350. package/dist/tools/native/user-profile.d.ts.map +0 -1
  351. package/dist/tools/native/user-profile.js +0 -58
  352. package/dist/tools/native/user-profile.js.map +0 -1
  353. package/dist/tools/parse-tool-call.d.ts +0 -11
  354. package/dist/tools/parse-tool-call.d.ts.map +0 -1
  355. package/dist/tools/parse-tool-call.js +0 -87
  356. package/dist/tools/parse-tool-call.js.map +0 -1
  357. package/dist/tools/plan.d.ts +0 -40
  358. package/dist/tools/plan.d.ts.map +0 -1
  359. package/dist/tools/plan.js +0 -143
  360. package/dist/tools/plan.js.map +0 -1
  361. package/dist/tools/question.d.ts +0 -33
  362. package/dist/tools/question.d.ts.map +0 -1
  363. package/dist/tools/question.js +0 -62
  364. package/dist/tools/question.js.map +0 -1
  365. package/dist/tools/registry.d.ts +0 -37
  366. package/dist/tools/registry.d.ts.map +0 -1
  367. package/dist/tools/registry.js +0 -97
  368. package/dist/tools/registry.js.map +0 -1
  369. package/dist/tools/sanitizer.d.ts +0 -30
  370. package/dist/tools/sanitizer.d.ts.map +0 -1
  371. package/dist/tools/sanitizer.js +0 -77
  372. package/dist/tools/sanitizer.js.map +0 -1
  373. package/dist/tools/subagent.d.ts +0 -39
  374. package/dist/tools/subagent.d.ts.map +0 -1
  375. package/dist/tools/subagent.js +0 -225
  376. package/dist/tools/subagent.js.map +0 -1
  377. package/dist/tools/syntax-check.d.ts +0 -32
  378. package/dist/tools/syntax-check.d.ts.map +0 -1
  379. package/dist/tools/syntax-check.js +0 -272
  380. package/dist/tools/syntax-check.js.map +0 -1
  381. package/dist/tools/todo.d.ts +0 -28
  382. package/dist/tools/todo.d.ts.map +0 -1
  383. package/dist/tools/todo.js +0 -125
  384. package/dist/tools/todo.js.map +0 -1
  385. package/dist/tools/types.d.ts +0 -51
  386. package/dist/tools/types.d.ts.map +0 -1
  387. package/dist/tools/types.js +0 -2
  388. package/dist/tools/types.js.map +0 -1
  389. package/dist/tools/web-browse.d.ts +0 -5
  390. package/dist/tools/web-browse.d.ts.map +0 -1
  391. package/dist/tools/web-browse.js +0 -757
  392. package/dist/tools/web-browse.js.map +0 -1
  393. package/dist/tools/web-fetch.d.ts +0 -19
  394. package/dist/tools/web-fetch.d.ts.map +0 -1
  395. package/dist/tools/web-fetch.js +0 -90
  396. package/dist/tools/web-fetch.js.map +0 -1
  397. package/dist/tools/web-search.d.ts +0 -19
  398. package/dist/tools/web-search.d.ts.map +0 -1
  399. package/dist/tools/web-search.js +0 -97
  400. package/dist/tools/web-search.js.map +0 -1
  401. package/dist/ui/components/status-bar.d.ts +0 -11
  402. package/dist/ui/components/status-bar.d.ts.map +0 -1
  403. package/dist/ui/components/status-bar.js +0 -20
  404. package/dist/ui/components/status-bar.js.map +0 -1
  405. package/dist/ui/index.d.ts +0 -8
  406. package/dist/ui/index.d.ts.map +0 -1
  407. package/dist/ui/index.js +0 -9
  408. package/dist/ui/index.js.map +0 -1
  409. package/dist/ui/ink/components/App.d.ts +0 -98
  410. package/dist/ui/ink/components/App.d.ts.map +0 -1
  411. package/dist/ui/ink/components/App.js +0 -212
  412. package/dist/ui/ink/components/App.js.map +0 -1
  413. package/dist/ui/ink/components/Conversation.d.ts +0 -20
  414. package/dist/ui/ink/components/Conversation.d.ts.map +0 -1
  415. package/dist/ui/ink/components/Conversation.js +0 -15
  416. package/dist/ui/ink/components/Conversation.js.map +0 -1
  417. package/dist/ui/ink/components/MessageBlock.d.ts +0 -27
  418. package/dist/ui/ink/components/MessageBlock.d.ts.map +0 -1
  419. package/dist/ui/ink/components/MessageBlock.js +0 -41
  420. package/dist/ui/ink/components/MessageBlock.js.map +0 -1
  421. package/dist/ui/ink/components/PromptInput.d.ts +0 -25
  422. package/dist/ui/ink/components/PromptInput.d.ts.map +0 -1
  423. package/dist/ui/ink/components/PromptInput.js +0 -167
  424. package/dist/ui/ink/components/PromptInput.js.map +0 -1
  425. package/dist/ui/ink/components/SelectInput.d.ts +0 -9
  426. package/dist/ui/ink/components/SelectInput.d.ts.map +0 -1
  427. package/dist/ui/ink/components/SelectInput.js +0 -30
  428. package/dist/ui/ink/components/SelectInput.js.map +0 -1
  429. package/dist/ui/ink/components/StatusBar.d.ts +0 -14
  430. package/dist/ui/ink/components/StatusBar.d.ts.map +0 -1
  431. package/dist/ui/ink/components/StatusBar.js +0 -20
  432. package/dist/ui/ink/components/StatusBar.js.map +0 -1
  433. package/dist/ui/ink/components/StreamBlock.d.ts +0 -8
  434. package/dist/ui/ink/components/StreamBlock.d.ts.map +0 -1
  435. package/dist/ui/ink/components/StreamBlock.js +0 -15
  436. package/dist/ui/ink/components/StreamBlock.js.map +0 -1
  437. package/dist/ui/ink/components/Tags.d.ts +0 -26
  438. package/dist/ui/ink/components/Tags.d.ts.map +0 -1
  439. package/dist/ui/ink/components/Tags.js +0 -37
  440. package/dist/ui/ink/components/Tags.js.map +0 -1
  441. package/dist/ui/ink/components/ToolCallBlock.d.ts +0 -31
  442. package/dist/ui/ink/components/ToolCallBlock.d.ts.map +0 -1
  443. package/dist/ui/ink/components/ToolCallBlock.js +0 -17
  444. package/dist/ui/ink/components/ToolCallBlock.js.map +0 -1
  445. package/dist/ui/ink/renderer.d.ts +0 -69
  446. package/dist/ui/ink/renderer.d.ts.map +0 -1
  447. package/dist/ui/ink/renderer.js +0 -142
  448. package/dist/ui/ink/renderer.js.map +0 -1
  449. package/dist/ui/ink/theme.d.ts +0 -28
  450. package/dist/ui/ink/theme.d.ts.map +0 -1
  451. package/dist/ui/ink/theme.js +0 -45
  452. package/dist/ui/ink/theme.js.map +0 -1
  453. package/dist/ui/md-formatter.d.ts +0 -16
  454. package/dist/ui/md-formatter.d.ts.map +0 -1
  455. package/dist/ui/md-formatter.js +0 -448
  456. package/dist/ui/md-formatter.js.map +0 -1
  457. package/dist/ui/readline-renderer.d.ts +0 -45
  458. package/dist/ui/readline-renderer.d.ts.map +0 -1
  459. package/dist/ui/readline-renderer.js +0 -181
  460. package/dist/ui/readline-renderer.js.map +0 -1
  461. package/dist/ui/renderer.d.ts +0 -66
  462. package/dist/ui/renderer.d.ts.map +0 -1
  463. package/dist/ui/renderer.js +0 -16
  464. package/dist/ui/renderer.js.map +0 -1
  465. package/dist/ui/subagent-renderer.d.ts +0 -58
  466. package/dist/ui/subagent-renderer.d.ts.map +0 -1
  467. package/dist/ui/subagent-renderer.js +0 -100
  468. package/dist/ui/subagent-renderer.js.map +0 -1
  469. package/dist/updater.d.ts +0 -15
  470. package/dist/updater.d.ts.map +0 -1
  471. package/dist/updater.js +0 -166
  472. package/dist/updater.js.map +0 -1
@@ -1,1043 +0,0 @@
1
- import { readFileSync } from "node:fs";
2
- import { join, isAbsolute, relative } from "node:path";
3
- import { parseToolCalls } from "../tools/parse-tool-call.js";
4
- import { AdapterPipeline, } from "../tools/adapters/index.js";
5
- import { parseFileRefs } from "../file-loader.js";
6
- import { ContextManager } from "./context.js";
7
- import { ModeRegistry } from "./modes/registry.js";
8
- import { applyMode } from "./modes/applier.js";
9
- import { loadSkill, loadSkillsFromRefs, enforceSkillBudget, preloadSkillsByKeywords, isSkillRequest, listInstalledSkills, } from "../skills/loader.js";
10
- import { runPreToolUseHooks, runPostToolUseHooks } from "../tools/hooks.js";
11
- import { isMCPCommand, parseMCPCommand, callMCPTool } from "../tools/mcp.js";
12
- import { showPlan, getPlanPendingCount, clearPlan, getNextPendingStep, updatePlanStep, extractDirFromStepTitle, } from "../tools/plan.js";
13
- import { generateToolCallId } from "../tools/id-gen.js";
14
- import { checkFileSyntax } from "../tools/syntax-check.js";
15
- import { loadConfig, getLLMConfig, getGroundingConfig, } from "../config/config.js";
16
- import { buildSystemPrompt } from "./system-prompt.js";
17
- import { logInfo, logWarn, logError, logDebug, logToolCall, logToolOutput, setStatusWriter, writeStatus, } from "../logger.js";
18
- import { runIsolatedLoop } from "../tools/subagent.js";
19
- import { buildSubagentContext } from "../subagent/context.js";
20
- import { doChat } from "../subagent/chat.js";
21
- import { SubagentRenderer } from "../ui/subagent-renderer.js";
22
- import { ToolExecutor } from "./tool-executor.js";
23
- import { ErrorEscalator } from "./error-escalation.js";
24
- import { WriteEnforcer } from "./write-enforcer.js";
25
- import { sendNotification } from "../tools/hooks.js";
26
- import { planHandler } from "../tools/native/plan.js";
27
- import { todoHandler } from "../tools/native/todo.js";
28
- import { questionHandler } from "../tools/native/question.js";
29
- import { approveHandler } from "../tools/native/approve.js";
30
- import { userProfileHandler } from "../tools/native/user-profile.js";
31
- import { themeHandler } from "../tools/native/theme.js";
32
- import { scaffoldHandler } from "../tools/native/scaffold.js";
33
- import { projectMapHandler } from "../tools/native/project-map.js";
34
- import { bashHandler } from "../tools/bash.js";
35
- import { readFileHandler } from "../tools/fs/read-file.js";
36
- import { writeFileHandler } from "../tools/fs/write-file.js";
37
- import { editFileHandler } from "../tools/fs/edit-file.js";
38
- import { listDirHandler } from "../tools/fs/list-dir.js";
39
- import { createDirHandler } from "../tools/fs/create-dir.js";
40
- import { deleteFileHandler } from "../tools/fs/delete-file.js";
41
- import { copyFileHandler } from "../tools/fs/copy-file.js";
42
- import { moveFileHandler } from "../tools/fs/move-file.js";
43
- import { fileInfoHandler } from "../tools/fs/file-info.js";
44
- import { globSearchHandler } from "../tools/fs/glob-search.js";
45
- import { grepSearchHandler } from "../tools/fs/grep-search.js";
46
- import { treeHandler } from "../tools/fs/tree.js";
47
- import { webSearchHandler } from "../tools/web-search.js";
48
- import { webFetchHandler } from "../tools/web-fetch.js";
49
- import { webBrowseHandler } from "../tools/web-browse.js";
50
- import { truncateOutput } from "./tool-executor.js";
51
- /**
52
- * Основной цикл ReAct-агента.
53
- *
54
- * Архитектура цикла:
55
- * 1. Сборка system prompt (кэшируется на время сессии)
56
- * 2. Отправка запроса к LLM с историей сообщений
57
- * 3. Парсинг tool calls из ответа модели (формат <tool_call>)
58
- * 4. Валидация и исполнение каждого tool call
59
- * 5. Добавление результата в историю
60
- * 6. Повтор до завершения (нет tool calls) или лимита ходов
61
- *
62
- * Особенности:
63
- * - Fallback-парсинг при невалидном JSON от маленьких моделей
64
- * - Автоматическое компактирование контекста при приближении к лимиту
65
- * - Retry с увеличением temperature при пустых ответах
66
- * - Автодобавление скилов при обнаружении релевантных паттернов
67
- *
68
- * @module agent/loop
69
- */
70
- /**
71
- * Fallback-извлечение bash-команды из ответа модели.
72
- * Используется когда основной парсинг tool calls не сработал
73
- * (маленькие модели часто генерируют невалидный JSON).
74
- * Применяет три regex-паттерна для поиска команды bash.
75
- */
76
- function extractToolCallFallback(response) {
77
- const patterns = [
78
- /"name"\s*:\s*"bash"\s*,\s*"arguments"\s*:\s*\{\s*"cmd"\s*:\s*"((?:[^"\\]|\\.)*)"/,
79
- /"cmd"\s*:\s*"((?:[^"\\]|\\.)*)"/,
80
- /name['"]\s*:\s*['"]bash['"]\s*,.*?cmd['"]\s*:\s*['"]((?:[^'\\]|\\.)*)['"]/,
81
- ];
82
- for (const re of patterns) {
83
- const m = response.match(re);
84
- if (m) {
85
- let cmd = m[1];
86
- cmd = cmd
87
- .replace(/\\n/g, "\n")
88
- .replace(/\\t/g, "\t")
89
- .replace(/\\"/g, '"')
90
- .replace(/\\\\/g, "\\");
91
- return { name: "bash", cmd };
92
- }
93
- }
94
- return null;
95
- }
96
- /**
97
- * Запускает основной цикл ReAct-агента.
98
- *
99
- * Цикл работает до тех пор, пока:
100
- * - Агент не перестанет вызывать инструменты (финальный текстовый ответ)
101
- * - Не будет достигнут лимит ходов (maxSteps из конфига)
102
- * - Пользователь не прервёт выполнение (abort signal)
103
- *
104
- * Стратегия обработки ошибок:
105
- * - Пустые ответы: retry до 3 раз с подсказкой "Continue."
106
- * - Невалидные tool calls: отправка обратной связи модели
107
- * - Пустые ответы подряд >3: остановка цикла
108
- *
109
- * @param provider - провайдер LLM (OpenAI-совместимый)
110
- * @param registry - реестр доступных инструментов
111
- * @param prompt - начальный промпт пользователя
112
- * @param contextManager - менеджер контекста (опциональный, создаётся новый если не передан)
113
- * @param previousMessages - предыдущие сообщения для восстановления сессии
114
- * @param renderer - рендерер вывода (Ink или readline)
115
- * @param providerName - имя провайдера для отображения
116
- * @param options - дополнительные опции (quiet, showThinking, mode)
117
- */
118
- export async function runAgentLoop(provider, registry, prompt, contextManager, previousMessages, renderer, providerName, options) {
119
- logInfo("LOOP", `runAgentLoop started: "${prompt.slice(0, 50)}"`);
120
- renderer?.resetAbort?.();
121
- // Парсинг ссылок на файлы в промпте (формат @path/to/file)
122
- const { prompt: enrichedPrompt, files } = await parseFileRefs(prompt);
123
- logDebug("LOOP", `parseFileRefs: ${files.length} files, prompt len: ${enrichedPrompt.length}`);
124
- const quiet = options?.quiet ?? false;
125
- // ─── ModeRegistry: конфигурация режима ──
126
- const modeRegistry = new ModeRegistry();
127
- const modeConfig = modeRegistry.getOrFallback(options?.mode || "base");
128
- const applied = applyMode(modeConfig, {
129
- quiet,
130
- showThinking: options?.showThinking,
131
- });
132
- // ─── Behaviors из ModeConfig ──────────────────────────
133
- const behaviors = modeConfig.behaviors;
134
- const continuationNudgeMax = typeof behaviors.continuationNudgeLimit === "number"
135
- ? behaviors.continuationNudgeLimit
136
- : behaviors.continuationNudge !== false
137
- ? 2
138
- : 0;
139
- const readConsolidation = behaviors.readConsolidation !== false;
140
- // Автозагрузка скилов из промпта (если пользователь указал @skill-name)
141
- const loadedSkills = loadSkillsFromRefs(prompt, process.cwd());
142
- const loadedSkillNames = new Set(loadedSkills.map((s) => s.name));
143
- if (loadedSkills.length > 0) {
144
- logInfo("LOOP", `loaded ${loadedSkills.length} skills: ${loadedSkills.map((s) => s.name).join(", ")}`);
145
- renderer?.writeInfo(` Loaded ${loadedSkills.length} skill(s): ${loadedSkills.map((s) => s.name).join(", ")}`);
146
- }
147
- // Принудительная загрузка скилов из ModeConfig
148
- for (const skillName of applied.forcedSkills) {
149
- if (!loadedSkillNames.has(skillName)) {
150
- const skill = loadSkill(skillName, process.cwd());
151
- if (skill) {
152
- loadedSkills.push(skill);
153
- loadedSkillNames.add(skillName);
154
- logInfo("LOOP", `auto-loaded ${skillName} skill for ${modeConfig.id} mode`);
155
- renderer?.writeInfo(` Auto-loaded @${skillName} skill for ${modeConfig.id} mode`);
156
- }
157
- else {
158
- logWarn("LOOP", `${skillName} skill not found for ${modeConfig.id} mode`);
159
- renderer?.writeWarning(` Warning: @${skillName} skill not found (required for ${modeConfig.id} mode)`);
160
- }
161
- }
162
- }
163
- // Ограничение количества скилов из ModeConfig (не deprecated config)
164
- const config = loadConfig();
165
- const maxSkills = modeConfig.constraints.maxSkills;
166
- if (maxSkills > 0 && loadedSkills.length > maxSkills) {
167
- const budget = applied.skillTokenBudget;
168
- const { allowed, dropped } = enforceSkillBudget(loadedSkills, budget);
169
- if (dropped.length > 0) {
170
- renderer?.writeWarning(` Skill limit (${maxSkills} max in ${modeConfig.id} mode): dropped ${dropped.join(", ")}`);
171
- logInfo("LOOP", `skill limit: dropped ${dropped.join(", ")} (budget: ${budget} tokens)`);
172
- loadedSkills.length = 0;
173
- loadedSkills.push(...allowed);
174
- for (const s of dropped)
175
- loadedSkillNames.delete(s);
176
- }
177
- }
178
- // Pre-load скиллов по ключевым словам (без language-specific хардкода)
179
- if (loadedSkills.length === 0 && options?.mode !== "fast") {
180
- const keywordSkills = preloadSkillsByKeywords(prompt, process.cwd());
181
- for (const skill of keywordSkills) {
182
- if (!loadedSkillNames.has(skill.name)) {
183
- loadedSkills.push(skill);
184
- loadedSkillNames.add(skill.name);
185
- logInfo("LOOP", `keyword preloaded skill: ${skill.name}`);
186
- renderer?.writeInfo(` Auto-loaded @${skill.name} (keyword match)`);
187
- }
188
- }
189
- // Если пользователь явно просит "используй скилл", но ничего не нашлось — подсказываем
190
- if (keywordSkills.length === 0 && isSkillRequest(prompt)) {
191
- const allSkills = listInstalledSkills(process.cwd());
192
- renderer?.writeWarning(` You asked for a skill but none matched. Use @skill-name in your prompt to load one. Installed: ${allSkills
193
- .slice(0, 10)
194
- .map((s) => "@" + s)
195
- .join(", ")}... (${allSkills.length} total)`);
196
- }
197
- }
198
- if (files.length > 0) {
199
- const totalTokens = files.reduce((sum, f) => sum + f.tokens, 0);
200
- const totalLines = files.reduce((sum, f) => sum + f.content.split("\n").length, 0);
201
- logInfo("LOOP", `loaded ${files.length} files (${totalLines} lines, ~${totalTokens} tokens)`);
202
- renderer?.writeInfo(` Loaded ${files.length} files (${totalLines} lines, ~${totalTokens} tokens)`);
203
- }
204
- setStatusWriter(renderer ? (msg) => renderer.writeDim(` ${msg}`) : null);
205
- writeStatus("Initializing...");
206
- // Сборка system prompt (включает инструкции, информацию о проекте, скилы, память)
207
- const sysStart = performance.now();
208
- const systemPrompt = buildSystemPrompt(loadedSkills.length > 0 ? loadedSkills : undefined, options?.mode, options?.flags);
209
- const sysElapsed = ((performance.now() - sysStart) / 1000).toFixed(1);
210
- writeStatus(`Done (${sysElapsed}s)`);
211
- setStatusWriter(null);
212
- // Начальное состояние истории: system prompt + предыдущие сообщения + текущий промпт
213
- const messages = [
214
- { role: "system", content: systemPrompt },
215
- ...(previousMessages || []),
216
- { role: "user", content: enrichedPrompt },
217
- ];
218
- const ctx = contextManager ?? new ContextManager(provider.contextWindow);
219
- const maxSteps = config.agent.maxSteps ?? 100;
220
- let consecutiveEmpty = 0; // счётчик пустых ответов подряд
221
- let pendingSyntaxError = "";
222
- let aborted = false;
223
- let continuationNudges = 0; // счётчик подсказок "продолжай работать"
224
- let consecutiveMissingGrounding = 0; // счётчик пропущенных grounding-маркеров подряд
225
- let groundingRetryCount = 0; // сколько раз был CRITICAL retry подряд
226
- let consecutiveInvalidCalls = 0; // счётчик невалидных tool call подряд
227
- /** Счётчик бесполезных действий (list_dir, glob_search, todo list без результата) */
228
- let uselessActionCount = 0;
229
- /** Сколько шагов подряд модель не вызывает инструменты */
230
- let consecutiveNoToolSteps = 0;
231
- /** История последних чтений файлов для.detect повторных чтений */
232
- const recentReadFiles = [];
233
- /** История последних grep-запросов для detect повторных поисков */
234
- const recentGrepPatterns = [];
235
- /** История последних вопросов для detect повторных вопросов */
236
- const recentQuestions = [];
237
- /** История последних bash-команд для detect циклических вызовов */
238
- const recentBashCommands = [];
239
- // ─── ToolExecutor: единый диспетчер инструментов ──
240
- const toolExecutor = new ToolExecutor();
241
- toolExecutor.registerAll([
242
- planHandler,
243
- todoHandler,
244
- questionHandler,
245
- approveHandler,
246
- userProfileHandler,
247
- themeHandler,
248
- scaffoldHandler,
249
- projectMapHandler,
250
- bashHandler,
251
- readFileHandler,
252
- writeFileHandler,
253
- editFileHandler,
254
- listDirHandler,
255
- createDirHandler,
256
- deleteFileHandler,
257
- copyFileHandler,
258
- moveFileHandler,
259
- fileInfoHandler,
260
- globSearchHandler,
261
- grepSearchHandler,
262
- treeHandler,
263
- webSearchHandler,
264
- webFetchHandler,
265
- webBrowseHandler,
266
- ]);
267
- const errorEscalator = new ErrorEscalator();
268
- const writeEnforcer = new WriteEnforcer();
269
- const adapterPipeline = new AdapterPipeline();
270
- /** Отслеживание созданных/изменённых файлов за всю сессию */
271
- const createdFiles = [];
272
- // Reset abort signal for this run
273
- if (renderer?.abortSignal === undefined) {
274
- // Renderer doesn't support abort — fine
275
- }
276
- renderer?.updateStatus({
277
- provider: providerName,
278
- model: provider.model,
279
- contextWindow: provider.contextWindow,
280
- step: 0,
281
- maxSteps,
282
- });
283
- // ─── Основной цикл ReAct ────────────────────────────────
284
- for (let step = 0; step < maxSteps; step++) {
285
- if (renderer?.abortSignal?.aborted) {
286
- aborted = true;
287
- break;
288
- }
289
- let skillsAutoLoaded = false;
290
- logDebug("LOOP", `step ${step + 1}/${maxSteps} starting, ${messages.length} messages`);
291
- // Инкрементальный подсчёт — считаем только новые сообщения (быстрее в ~100x на больших историях)
292
- if (step === 0) {
293
- await ctx.updateTokens(messages);
294
- }
295
- else {
296
- await ctx.updateTokensIncremental(messages);
297
- }
298
- const skillNames = loadedSkills.map((s) => s.name).join(", ");
299
- const usageText = ctx.formatUsage() + (skillNames ? ` │ skills: ${skillNames}` : "");
300
- renderer?.updateContextUsage(usageText);
301
- renderer?.updateStatus({
302
- provider: providerName,
303
- model: provider.model,
304
- tokens: ctx.totalTokens,
305
- contextWindow: provider.contextWindow,
306
- step: step + 1,
307
- maxSteps,
308
- });
309
- // ─── Compaction контекста ─────────────────────────────
310
- // Если использование контекста превышает порог (70%), компактируем историю
311
- if (ctx.needsCompact && messages.length > 6) {
312
- const before = messages.length;
313
- messages.splice(0, messages.length, ...ctx.compact(messages));
314
- if (!quiet) {
315
- renderer?.writeBracketTag("compact", `${before} → ${messages.length} messages`);
316
- }
317
- await ctx.updateTokens(messages);
318
- }
319
- // ─── Emergency compact при переполнении контекста ─────
320
- if (ctx.isOverflow) {
321
- const before = messages.length;
322
- messages.splice(0, messages.length, ...ctx.emergencyCompact(messages));
323
- if (!quiet) {
324
- renderer?.writeBracketTag("compact", `EMERGENCY: ${before} → ${messages.length}`);
325
- }
326
- await ctx.updateTokens(messages);
327
- }
328
- // ─── Запрос к LLM ────────────────────────────────────
329
- const llmCfg = getLLMConfig(config);
330
- logInfo("LOOP", `calling doChat, messages: ${messages.length}, options.showThinking: ${options?.showThinking}, retries: ${llmCfg.retries}`);
331
- const { response } = await doChat(provider, messages, renderer, renderer?.abortSignal, llmCfg.retries, options?.showThinking);
332
- // Проверка прерывания во время генерации
333
- if (renderer?.abortSignal?.aborted) {
334
- aborted = true;
335
- break;
336
- }
337
- // ─── Автозагрузка скилов по @упоминаниям ─────────────
338
- // Если ответ содержит @skill-name, загружаем скил и пересобираем system prompt
339
- if (options?.mode !== "fast" && !skillsAutoLoaded) {
340
- const suggestedSkillRefs = response.matchAll(/@(\w[\w-]*)/g);
341
- const newSkillNames = [];
342
- for (const match of suggestedSkillRefs) {
343
- const name = match[1];
344
- if ([
345
- "email",
346
- "api",
347
- "id",
348
- "json",
349
- "url",
350
- "http",
351
- "https",
352
- "localhost",
353
- ].includes(name))
354
- continue;
355
- if (!loadedSkillNames.has(name)) {
356
- const skill = loadSkill(name, process.cwd());
357
- if (skill) {
358
- newSkillNames.push(name);
359
- loadedSkills.push(skill);
360
- loadedSkillNames.add(name);
361
- }
362
- }
363
- }
364
- if (newSkillNames.length > 0) {
365
- skillsAutoLoaded = true;
366
- // Применяем бюджет скилов после автозагрузки (Баг 1 fix)
367
- const autoBudget = applied.skillTokenBudget;
368
- if (autoBudget > 0 && loadedSkills.length > 0) {
369
- const { allowed, dropped } = enforceSkillBudget(loadedSkills, autoBudget);
370
- if (dropped.length > 0) {
371
- renderer?.writeWarning(` Auto-load budget: dropped ${dropped.join(", ")} (budget: ${autoBudget} tokens)`);
372
- logInfo("LOOP", `auto-load budget: dropped ${dropped.join(", ")} (budget: ${autoBudget} tokens)`);
373
- loadedSkills.length = 0;
374
- loadedSkills.push(...allowed);
375
- for (const s of dropped)
376
- loadedSkillNames.delete(s);
377
- }
378
- }
379
- renderer?.writeInfo(` Auto-loaded ${loadedSkills.filter((s) => newSkillNames.includes(s.name)).length} skill(s): ${newSkillNames.join(", ")}`);
380
- messages[0] = {
381
- role: "system",
382
- content: buildSystemPrompt(loadedSkills, options?.mode, options?.flags),
383
- };
384
- // Не удаляем ответ ассистента — skills загружаются для следующих шагов цикла.
385
- // Модель увидит обновлённый system prompt на следующей итерации и сможет
386
- // использовать загруженные скилы без retry (экономит ~один LLM-запрос).
387
- }
388
- } // end fast-mode skill block
389
- // ─── Обработка пустых ответов ────────────────────────
390
- // Маленькие модели иногда возвращают пустой ответ — повторяем с подсказкой
391
- if (!response.trim()) {
392
- logWarn("LOOP", `empty response #${consecutiveEmpty + 1}`);
393
- consecutiveEmpty++;
394
- // После компактинга — более информативное сообщение о пустом ответе
395
- if (ctx.compactCount > 0 && consecutiveEmpty <= 3) {
396
- let lastUserMsg;
397
- for (let i = messages.length - 1; i >= 0; i--) {
398
- if (messages[i].role === "user") {
399
- lastUserMsg = messages[i];
400
- break;
401
- }
402
- }
403
- const contextHint = lastUserMsg
404
- ? `[System: You returned an empty response. The conversation was compressed. Continue working on: "${(lastUserMsg.content || "").slice(0, 150)}..."]`
405
- : "[System: You returned an empty response. Continue with the task.]";
406
- messages.push({ role: "user", content: contextHint });
407
- if (!quiet) {
408
- renderer?.writeDim(` [empty response, retrying with context hint]`);
409
- }
410
- continue;
411
- }
412
- const lastMsg = messages[messages.length - 1];
413
- if ((lastMsg?.role === "user" || lastMsg?.role === "system") &&
414
- consecutiveEmpty <= 2) {
415
- // После system-сообщений (например "Skills loaded") — более конкретная подсказка
416
- const isAfterSystem = lastMsg?.role === "system";
417
- const hint = isAfterSystem
418
- ? "[System: You returned an empty response. The skills are loaded. Now create a plan and start working on the task. Do NOT wait for further instructions.]"
419
- : "Continue.";
420
- messages.push({ role: "user", content: hint });
421
- if (!quiet) {
422
- renderer?.writeDim(` [empty response, retrying${isAfterSystem ? " with skills hint" : ""}]`);
423
- }
424
- continue;
425
- }
426
- renderer?.writeError(consecutiveEmpty > 3
427
- ? `[model returned empty response ${consecutiveEmpty} times in a row — stopping]`
428
- : "[model returned empty response]");
429
- return { messages };
430
- }
431
- consecutiveEmpty = 0;
432
- messages.push({ role: "assistant", content: response });
433
- const { validCalls, invalidCalls } = parseToolCalls(response);
434
- // ─── Adapter pipeline: чиним кривые вызовы маленьких моделей ──
435
- const adapterLogs = [];
436
- const adaptedCalls = [];
437
- const adapterCtx = {
438
- cwd: process.cwd(),
439
- config,
440
- responseText: response,
441
- existingFiles: createdFiles,
442
- };
443
- for (const call of validCalls) {
444
- const result = await adapterPipeline.process(call, adapterCtx);
445
- if ("blocked" in result) {
446
- invalidCalls.push(result.blocked);
447
- if (!quiet)
448
- renderer?.writeError(` [adapter blocked: ${result.blocked}]`);
449
- }
450
- else {
451
- adaptedCalls.push(result.call);
452
- adapterLogs.push(...result.logs);
453
- }
454
- }
455
- // Заменяем validCalls на адаптированные
456
- validCalls.length = 0;
457
- validCalls.push(...adaptedCalls);
458
- // Показываем логи адаптеров
459
- if (adapterLogs.length > 0 && !quiet) {
460
- for (const log of adapterLogs) {
461
- renderer?.writeDim(` ${log}`);
462
- }
463
- }
464
- if (validCalls.length === 0) {
465
- if (invalidCalls.length > 0) {
466
- // If the response has substantial text outside tool_call tags,
467
- // it's a description with examples — not a failed tool call attempt.
468
- const textOutside = response
469
- .replace(/<tool_call>[\s\S]*?<\/tool_call>/g, "")
470
- .trim();
471
- if (textOutside.length < 100) {
472
- consecutiveInvalidCalls++;
473
- const feedback = `[Tool call error(s)]\n${invalidCalls.join("\n")}\n\nFix the tool call format and try again. Use ONLY valid tool names: bash, read_file, write_file, edit_file, list_dir, create_dir, delete_file, copy_file, move_file, file_info, glob_search, grep_search, tree, web_search, web_fetch, web_browse, plan, todo, question, approve, subagent, scaffold.`;
474
- if (!quiet) {
475
- renderer?.writeError(` [invalid tool call: ${invalidCalls[0]}]`);
476
- }
477
- messages.push({
478
- role: "tool",
479
- content: feedback,
480
- tool_call_id: `invalid-${Date.now()}`,
481
- });
482
- continue;
483
- }
484
- // Text response with example tool calls — just log and proceed
485
- logWarn("LOOP", `ignored ${invalidCalls.length} invalid tool call(s) in descriptive text`);
486
- }
487
- // Nudge model to continue working after a tool result — but only if
488
- // the agent didn't already write a meaningful text response.
489
- if (continuationNudgeMax > 0 &&
490
- continuationNudges < continuationNudgeMax) {
491
- const lastThree = messages.slice(-3);
492
- const hasRecentToolResult = lastThree.some((m) => m.role === "tool");
493
- const alreadyResponded = response.replace(/<tool_call>[\s\S]*?<\/tool_call>/g, "").trim()
494
- .length > 20;
495
- if (hasRecentToolResult && !alreadyResponded) {
496
- continuationNudges++;
497
- if (!quiet) {
498
- renderer?.writeDim(` [nudge ${continuationNudges}/${continuationNudgeMax}: continue working]`);
499
- }
500
- messages.push({
501
- role: "user",
502
- content: "[Tool result received. Write a response to the user. Do NOT call additional tools unless there is actual work to do.]",
503
- });
504
- continue;
505
- }
506
- }
507
- // ─── Определение завершения: только по поведению, не по тексту ──────
508
- // Смотрим: были ли хоть какие-то вызовы инструментов за сессию.
509
- // Сообщения с role:tool — это результаты выполненных инструментов
510
- // (кроме invalid — они с tool_call_id вида "invalid-...").
511
- const hasDoneAnyWork = messages.some((m) => m.role === "tool" &&
512
- m.tool_call_id &&
513
- !m.tool_call_id.startsWith("invalid-"));
514
- const planStatus = showPlan();
515
- const hasActivePlan = !planStatus.startsWith("No active plan");
516
- const pendingSteps = getPlanPendingCount();
517
- // === 1. Незавершённый план ===
518
- if (hasActivePlan && pendingSteps > 0) {
519
- const planText = planStatus;
520
- const pendingLines = planText
521
- .split("\n")
522
- .filter((l) => l.includes("○") || l.includes("►"));
523
- const nextStep = pendingLines[0] || "";
524
- const nextStepMatch = nextStep.match(/\d+\.\s*(.+)/);
525
- const nextStepTitle = nextStepMatch ? nextStepMatch[1].trim() : "";
526
- if (!quiet) {
527
- renderer?.writeWarning(` [plan has ${pendingSteps} incomplete steps — pushing agent to continue]`);
528
- }
529
- const nudge = nextStepTitle
530
- ? `Plan is NOT complete! ${pendingSteps} step(s) remaining.\nNext step: ${nextStepTitle}\nUse subagent to execute this step, then mark done: plan({action:"done", step:N})`
531
- : `Plan is NOT complete! ${pendingSteps} step(s) remaining:\n${pendingLines.join("\n")}\n\nContinue with the next step using subagent.`;
532
- messages.push({ role: "user", content: nudge });
533
- continuationNudges = 0;
534
- continue;
535
- }
536
- // === 2. План завершён — аудит файлов ===
537
- // Проверка по createdFiles: если план выполнен, но ни одного файла
538
- // не создано — скорее всего модель галлюцинирует выполнение шагов.
539
- if (hasActivePlan && pendingSteps === 0) {
540
- if (createdFiles.length === 0) {
541
- if (!quiet) {
542
- renderer?.writeWarning(` [plan complete but no files created]`);
543
- }
544
- messages.push({
545
- role: "user",
546
- content: `Plan claims all steps done, but no files were created via write_file or subagent. Either execute the steps properly or explain why files aren't needed.`,
547
- });
548
- continuationNudges = 0;
549
- continue;
550
- }
551
- // Файлы созданы — выходим
552
- return { messages };
553
- }
554
- // === 3. Нет активного плана ===
555
- // Stuck detection (без изменений)
556
- const isUselessAction = validCalls.some((c) => c.parsed.name === "list_dir" ||
557
- c.parsed.name === "tree" ||
558
- (c.parsed.name === "todo" && c.parsed.arguments?.action === "list") ||
559
- (c.parsed.name === "question" && !c.parsed.arguments?.question));
560
- let isRepeatedRead = false;
561
- let isRepeatedSearch = false;
562
- let isRepeatedQuestion = false;
563
- let isRepeatedBash = false;
564
- for (const c of validCalls) {
565
- if (c.parsed.name === "read_file") {
566
- const filePath = String(c.parsed.arguments?.path || "");
567
- if (recentReadFiles.includes(filePath))
568
- isRepeatedRead = true;
569
- recentReadFiles.push(filePath);
570
- if (recentReadFiles.length > 5)
571
- recentReadFiles.shift();
572
- }
573
- if (c.parsed.name === "grep_search") {
574
- const pattern = String(c.parsed.arguments?.pattern || "");
575
- if (recentGrepPatterns.includes(pattern))
576
- isRepeatedSearch = true;
577
- recentGrepPatterns.push(pattern);
578
- if (recentGrepPatterns.length > 5)
579
- recentGrepPatterns.shift();
580
- }
581
- if (c.parsed.name === "question") {
582
- const questionText = String(c.parsed.arguments?.question || "");
583
- if (recentQuestions.includes(questionText))
584
- isRepeatedQuestion = true;
585
- recentQuestions.push(questionText);
586
- if (recentQuestions.length > 3)
587
- recentQuestions.shift();
588
- }
589
- if (c.parsed.name === "bash") {
590
- const cmdText = String(c.parsed.arguments?.cmd || "");
591
- const normalized = cmdText.trim().replace(/\s+/g, " ");
592
- if (recentBashCommands.includes(normalized))
593
- isRepeatedBash = true;
594
- recentBashCommands.push(normalized);
595
- if (recentBashCommands.length > 5)
596
- recentBashCommands.shift();
597
- }
598
- }
599
- const isStuckAction = isUselessAction ||
600
- isRepeatedRead ||
601
- isRepeatedSearch ||
602
- isRepeatedQuestion ||
603
- isRepeatedBash;
604
- if (isStuckAction) {
605
- uselessActionCount++;
606
- if (uselessActionCount >= 3) {
607
- if (!quiet) {
608
- renderer?.writeWarning(` [agent stuck: ${uselessActionCount} useless actions]`);
609
- }
610
- clearPlan();
611
- messages.push({
612
- role: "user",
613
- content: `[System: You are stuck in a loop. STOP calling the same tool. The user already answered your question — use their answer and proceed with the task. Do NOT ask the same question again.]`,
614
- });
615
- uselessActionCount = 0;
616
- continuationNudges = 0;
617
- continue;
618
- }
619
- }
620
- else {
621
- consecutiveNoToolSteps = 0;
622
- if (validCalls.some((c) => c.parsed.name === "subagent" ||
623
- c.parsed.name === "write_file" ||
624
- c.parsed.name === "bash")) {
625
- uselessActionCount = 0;
626
- recentReadFiles.length = 0;
627
- recentGrepPatterns.length = 0;
628
- recentQuestions.length = 0;
629
- recentBashCommands.length = 0;
630
- }
631
- }
632
- // === 4. Выход: решение по поведению, а не по тексту ===
633
- // 4a. Модель ничего не делала — поведенческий контроль
634
- if (!hasDoneAnyWork) {
635
- if (step > 0)
636
- consecutiveNoToolSteps++;
637
- if (consecutiveNoToolSteps >= 2) {
638
- if (!quiet) {
639
- renderer?.writeWarning(` [idle: ${consecutiveNoToolSteps} steps without any tool calls]`);
640
- }
641
- const idleSteps = consecutiveNoToolSteps;
642
- consecutiveNoToolSteps = 0;
643
- messages.push({
644
- role: "user",
645
- content: `[System: ты уже ${idleSteps} шага подряд не вызываешь инструменты. Если задача требует создания/изменения файлов — вызови write_file или edit_file. Если задача уже выполнена — напиши результат пользователю.]`,
646
- });
647
- continuationNudges = 0;
648
- continue;
649
- }
650
- // Первый ответ (step === 0) без инструментов — обычный диалог
651
- return { messages };
652
- }
653
- // 4b. Модель что-то делала — проверяем созданные файлы перед выходом
654
- if (createdFiles.length > 0) {
655
- messages.push({
656
- role: "user",
657
- content: `Files created: ${createdFiles.join(", ")}. Before finishing — verify all files are complete and connected to each other. If any file is missing or not imported, continue working.`,
658
- });
659
- continuationNudges = 0;
660
- continue;
661
- }
662
- // 4c. Модель работала, но не создавала файлы (читала, искала и т.д.)
663
- return { messages };
664
- }
665
- // ─── Grounding check (ослаблен, агент больше не блокирует) ──
666
- // Раньше grounding strict mode гонял модель по retry-циклу.
667
- // Теперь grounding-fixer адаптер сам добавляет маркеры —
668
- // grounding check только предупреждает, никогда не блокирует.
669
- if (validCalls.length > 0) {
670
- const groundingCfg = getGroundingConfig(config);
671
- if (groundingCfg.enabled) {
672
- const groundMatches = Array.from(response.matchAll(/@grounding\[файл:\s*([^\]]+)\]/g));
673
- const groundPaths = groundMatches.map((m) => m[1].trim());
674
- const pathCalls = validCalls.filter((c) => c.parsed.arguments?.path);
675
- if (pathCalls.length > groundPaths.length) {
676
- consecutiveMissingGrounding++;
677
- if (!quiet)
678
- renderer?.writeDim(` [grounding: ${consecutiveMissingGrounding} call(s) without marker — adapter handled]`);
679
- }
680
- else {
681
- consecutiveMissingGrounding = 0;
682
- groundingRetryCount = 0;
683
- }
684
- }
685
- }
686
- // ─── Enforce: лимиты на запись файлов ────────────────
687
- // Отслеживание повторных чтений одного файла для консолидации
688
- let lastReadFile = null;
689
- writeEnforcer.enforceSingleWrite(validCalls, messages, quiet, renderer);
690
- // ─── Исполнение tool calls ──────────────────────────
691
- for (const call of validCalls) {
692
- const { parsed, cmd } = call;
693
- const isBash = parsed.name === "bash";
694
- const preview = isBash
695
- ? cmd.length > 200
696
- ? cmd.slice(0, 200) + "..."
697
- : cmd
698
- : JSON.stringify(parsed.arguments);
699
- const toolId = generateToolCallId();
700
- logToolCall(parsed.name, preview);
701
- if (!quiet) {
702
- renderer?.writeToolHeader(parsed.name);
703
- renderer?.writeToolPreview(parsed.name, preview);
704
- }
705
- const hookCmd = isBash
706
- ? cmd
707
- : `${parsed.name} ${JSON.stringify(parsed.arguments)}`;
708
- const preHook = runPreToolUseHooks(process.cwd(), parsed.name, hookCmd);
709
- if (!preHook.continue) {
710
- if (!quiet) {
711
- renderer?.writeBracketTag("hook blocked", preHook.message || "");
712
- }
713
- messages.push({
714
- role: "tool",
715
- content: `Hook blocked: ${preHook.message || ""}`,
716
- tool_call_id: toolId,
717
- });
718
- continue;
719
- }
720
- if (pendingSyntaxError && !isBash) {
721
- const isWriteOp = parsed.name === "write_file" || parsed.name === "edit_file";
722
- const newPath = String(parsed.arguments.path ?? "");
723
- // Allow editing config files (tsconfig.json, package.json) since they
724
- // can fix syntax errors (e.g. missing jsx flag in tsconfig)
725
- const isConfig = newPath === "tsconfig.json" || newPath === "package.json";
726
- if (isWriteOp && newPath !== pendingSyntaxError && !isConfig) {
727
- const blocked = `BLOCKED: You have a syntax error in ${pendingSyntaxError}. Fix it first before writing new files. If the error is about jsx, module resolution, or missing types — edit tsconfig.json (it is exempt from this block).`;
728
- if (!quiet) {
729
- renderer?.writeError(` [blocked: fix syntax error in ${pendingSyntaxError} first]`);
730
- }
731
- messages.push({
732
- role: "tool",
733
- content: blocked,
734
- tool_call_id: toolId,
735
- });
736
- continue;
737
- }
738
- }
739
- let toolOutput = "";
740
- let toolExitCode = 0;
741
- const toolStart = performance.now();
742
- // ─── Субагент: изолированный контекст для задачи ──
743
- if (parsed.name === "subagent") {
744
- const task = String(parsed.arguments.task || "");
745
- if (!task) {
746
- toolOutput = "Error: task is required";
747
- toolExitCode = 1;
748
- }
749
- else {
750
- // Авто-пометка следующего pending шага как in_progress
751
- const nextPending = getNextPendingStep();
752
- if (nextPending) {
753
- updatePlanStep(nextPending.id, "in_progress");
754
- }
755
- // Phase 2.3: Авто-подстановка полного пути в задачу subagent
756
- let finalTask = task;
757
- if (nextPending) {
758
- const expectedDir = extractDirFromStepTitle(nextPending.title);
759
- if (expectedDir && !task.includes(expectedDir + "/")) {
760
- finalTask = `${task} (directory: ${expectedDir}/)`;
761
- logInfo("AGENT", `injected dir path into subagent task: "${expectedDir}"`);
762
- }
763
- }
764
- const autoContext = buildSubagentContext(process.cwd());
765
- logInfo("AGENT", `spawning subagent | task: "${finalTask.slice(0, 150)}"`);
766
- // Показываем спиннер на основном рендерере пока субагент работает
767
- if (!quiet) {
768
- const taskPreview = finalTask.length > 60
769
- ? finalTask.slice(0, 57) + "..."
770
- : finalTask;
771
- renderer?.writeSpinner(` [subagent] ${taskPreview}`);
772
- }
773
- const subRenderer = new SubagentRenderer();
774
- try {
775
- const childResult = await runIsolatedLoop(provider, registry, finalTask, autoContext, process.cwd(), quiet, subRenderer, toolExecutor, modeConfig.subagentMode, llmCfg.toolModel);
776
- toolOutput = childResult.summary;
777
- logInfo("AGENT", `subagent returned | files: ${childResult.createdFiles.length} | turns: ${childResult.turns} | errors: ${childResult.errors.length}`);
778
- if (childResult.createdFiles.length > 0) {
779
- for (const f of childResult.createdFiles) {
780
- if (!createdFiles.includes(f))
781
- createdFiles.push(f);
782
- }
783
- }
784
- // Phase 2.1: Валидация путей созданных файлов
785
- if (nextPending && childResult.createdFiles.length > 0) {
786
- const expectedDir = extractDirFromStepTitle(nextPending.title);
787
- if (expectedDir) {
788
- const wrongDirFiles = childResult.createdFiles.filter((f) => !f.startsWith(expectedDir + "/") &&
789
- !f.startsWith(expectedDir + "\\"));
790
- if (wrongDirFiles.length > 0) {
791
- const correction = `⚠ Files created in wrong directory: ${wrongDirFiles.join(", ")}. ` +
792
- `They should be in ${expectedDir}/. Use write_file with full path: ${expectedDir}/${wrongDirFiles[0]}`;
793
- toolOutput += "\n\n" + correction;
794
- if (!quiet) {
795
- renderer?.writeWarning(` [subagent created files in wrong directory: ${wrongDirFiles.join(", ")}]`);
796
- }
797
- }
798
- }
799
- }
800
- const buf = subRenderer.getBuffer();
801
- if (!quiet && renderer?.writeToolBuffer) {
802
- renderer.writeToolBuffer({
803
- lines: buf.lines,
804
- toolCalls: buf.toolCalls,
805
- });
806
- }
807
- }
808
- catch (e) {
809
- toolOutput = `Subagent error: ${e.message}`;
810
- toolExitCode = 1;
811
- logError("AGENT", `subagent error: ${e.message}`);
812
- }
813
- }
814
- const subTime = ((performance.now() - toolStart) / 1000).toFixed(1);
815
- if (!quiet) {
816
- renderer?.clearSpinner();
817
- renderer?.writeToolResult(subTime, toolExitCode, toolOutput.slice(0, 300));
818
- }
819
- // ─── MCP: внешние tool-серверы ────────────────────────
820
- }
821
- else if (isBash && isMCPCommand(cmd)) {
822
- const mcpCmd = parseMCPCommand(cmd);
823
- if (mcpCmd) {
824
- if (!quiet) {
825
- renderer?.writeBracketTag("mcp", `${mcpCmd.server}/${mcpCmd.tool}`);
826
- }
827
- toolOutput = callMCPTool(process.cwd(), mcpCmd.server, mcpCmd.tool, mcpCmd.args);
828
- }
829
- else {
830
- toolOutput =
831
- "Invalid MCP command. Format: mcp <server> <tool> [args_json]";
832
- toolExitCode = 1;
833
- }
834
- // ─── Все инструменты через ToolExecutor ──────────────
835
- }
836
- else {
837
- // Определяем инструмент и аргументы для executor'а
838
- let executorName = parsed.name;
839
- let executorArgs = parsed.arguments;
840
- // Bash-команды
841
- if (isBash) {
842
- executorName = "bash";
843
- executorArgs = { cmd, timeout: parsed.arguments?.timeout ?? 120 };
844
- }
845
- const hookCmd = isBash
846
- ? cmd
847
- : `${parsed.name} ${JSON.stringify(parsed.arguments)}`;
848
- const executorCall = {
849
- id: toolId,
850
- name: executorName,
851
- arguments: executorArgs,
852
- };
853
- const toolCtx = { cwd: process.cwd(), renderer, quiet };
854
- const { result, elapsed } = await toolExecutor.execute(executorCall, toolCtx, hookCmd);
855
- toolOutput = result.stdout || result.stderr || "(no output)";
856
- toolExitCode = result.exitCode;
857
- // Обработка question/approve: приостанавливаем цикл
858
- if (result.questionPending) {
859
- messages.push({
860
- role: "tool",
861
- content: toolOutput,
862
- tool_call_id: toolId,
863
- });
864
- return {
865
- messages,
866
- questionPending: { question: toolOutput, choices: undefined },
867
- };
868
- }
869
- }
870
- const toolTime = ((performance.now() - toolStart) / 1000).toFixed(1);
871
- logToolOutput(parsed.name, toolOutput, toolExitCode);
872
- logToolCall(parsed.name, preview, toolExitCode !== 0
873
- ? `exit:${toolExitCode} ${toolOutput.slice(0, 100)}`
874
- : `ok (${toolTime}s)`);
875
- if (toolExitCode !== 0) {
876
- const errText = toolOutput;
877
- if (!quiet) {
878
- for (const line of errText.split("\n").slice(0, 5)) {
879
- renderer?.writeError(` ${line}`);
880
- }
881
- }
882
- }
883
- runPostToolUseHooks(process.cwd(), parsed.name, hookCmd, toolOutput, toolExitCode);
884
- // Track sequential read_file calls for consolidation
885
- if (readConsolidation &&
886
- !isBash &&
887
- parsed.name === "read_file" &&
888
- toolExitCode === 0) {
889
- const filePath = String(parsed.arguments.path ?? "");
890
- const offset = Math.max(1, Number(parsed.arguments.offset ?? 1));
891
- const limit = Math.max(1, Number(parsed.arguments.limit ?? 500));
892
- if (lastReadFile &&
893
- lastReadFile.path === filePath &&
894
- offset === lastReadFile.offset + lastReadFile.limit) {
895
- // Sequential read — merge with previous result
896
- lastReadFile.content += "\n" + toolOutput;
897
- lastReadFile.offset = offset;
898
- lastReadFile.limit = limit;
899
- // Replace the last tool message content with merged content
900
- for (let i = messages.length - 1; i >= 0; i--) {
901
- if (messages[i].role === "tool" &&
902
- messages[i].tool_call_id === lastReadFile.toolId) {
903
- messages[i].content = lastReadFile.content;
904
- break;
905
- }
906
- }
907
- // Remove the trailing "Continue." nudge + the new tool result that would be pushed
908
- while (messages.length > 0 &&
909
- messages[messages.length - 1].role === "user" &&
910
- messages[messages.length - 1].content === "Continue.") {
911
- messages.pop();
912
- }
913
- if (!quiet) {
914
- renderer?.writeBracketTag("consolidate", `merged read_file chunks for ${filePath}`);
915
- }
916
- continue; // skip pushing this result separately
917
- }
918
- else {
919
- // New file or non-sequential — start fresh
920
- lastReadFile = {
921
- path: filePath,
922
- offset,
923
- limit,
924
- content: toolOutput,
925
- toolId,
926
- };
927
- }
928
- }
929
- else if (!isBash && parsed.name !== "read_file") {
930
- lastReadFile = null;
931
- }
932
- // ─── Проверка синтаксиса после записи файлов ────────
933
- let hasSyntaxError = false;
934
- if (toolExitCode === 0) {
935
- const isWriteOp = isBash
936
- ? cmd.match(/Set-Content|Add-Content/)
937
- : parsed.name === "write_file" || parsed.name === "edit_file";
938
- if (isWriteOp) {
939
- const rawPath = isBash
940
- ? cmd
941
- .match(/-Path\s+['"]?([^'"]+\.(?:ts|tsx|js|jsx|json))['"]?/i)?.[1]
942
- ?.replace(/\\/g, "/")
943
- : String(parsed.arguments.path ?? "");
944
- if (rawPath && /\.(ts|tsx|js|jsx|json)$/.test(rawPath)) {
945
- const filePath = isAbsolute(rawPath)
946
- ? relative(process.cwd(), rawPath).replace(/\\/g, "/")
947
- : rawPath;
948
- const syntaxResult = checkFileSyntax(filePath, process.cwd());
949
- if (syntaxResult !== "OK") {
950
- const errorLines = syntaxResult
951
- .split("\n")
952
- .filter((l) => l.trim());
953
- const shown = errorLines.slice(0, 3);
954
- const extra = errorLines.length > 3
955
- ? ` (+${errorLines.length - 3} more)`
956
- : "";
957
- if (!quiet) {
958
- renderer?.writeBracketTag("syntax", `${shown.join("; ")}${extra}`);
959
- }
960
- const fullPath = join(process.cwd(), filePath);
961
- let contextLines = "";
962
- try {
963
- const fileContent = readFileSync(fullPath, "utf-8").split("\n");
964
- for (const errLine of shown) {
965
- const lineMatch = errLine.match(/\((\d+),\d+\)/);
966
- if (lineMatch) {
967
- const lineNum = parseInt(lineMatch[1]);
968
- if (lineNum > 0 && lineNum <= fileContent.length) {
969
- const start = Math.max(0, lineNum - 2);
970
- const end = Math.min(fileContent.length, lineNum + 1);
971
- contextLines += `\n--- Line ${lineNum} ---\n`;
972
- for (let i = start; i < end; i++) {
973
- const marker = i === lineNum - 1 ? " >> " : " ";
974
- contextLines += `${marker}${i + 1}: ${fileContent[i]}\n`;
975
- }
976
- }
977
- }
978
- }
979
- }
980
- catch {
981
- /* ignore read errors */
982
- }
983
- toolOutput = `SYNTAX ERROR in ${filePath}:\n${syntaxResult}${contextLines}`;
984
- // Detect config-related TS errors (missing jsx, module issues, etc.)
985
- const isConfigError = syntaxResult.includes("TS17004") ||
986
- syntaxResult.includes("TS2307") ||
987
- syntaxResult.includes("Cannot find module") ||
988
- syntaxResult.includes("jsx") ||
989
- syntaxResult.includes("moduleResolution");
990
- if (isConfigError) {
991
- toolOutput += `\n\nThis looks like a tsconfig.json configuration issue, NOT a code syntax error.\nCheck tsconfig.json — you may need to add "jsx": "react-jsx" or fix module settings.\nDo NOT modify the source code to work around config errors. Edit tsconfig.json instead.`;
992
- }
993
- else {
994
- toolOutput += `\n\nDo NOT rewrite the entire file. Use edit_file to fix the specific broken line:\n{"name":"edit_file","arguments":{"path":"${filePath}","old_string":"broken code here","new_string":"fixed code here"}}`;
995
- }
996
- hasSyntaxError = true;
997
- pendingSyntaxError = filePath;
998
- }
999
- else {
1000
- if (pendingSyntaxError === filePath) {
1001
- pendingSyntaxError = "";
1002
- }
1003
- }
1004
- // Track created/modified files for completion reporting
1005
- if (!hasSyntaxError && !createdFiles.includes(filePath)) {
1006
- createdFiles.push(filePath);
1007
- }
1008
- }
1009
- }
1010
- }
1011
- // Любой выполненный tool call сбрасывает счётчик бездействия
1012
- consecutiveNoToolSteps = 0;
1013
- messages.push({
1014
- role: "tool",
1015
- content: truncateOutput(parsed.name, toolOutput, toolExitCode),
1016
- tool_call_id: toolId,
1017
- });
1018
- // ─── Error escalation + specialist auto-delegation ──────────
1019
- if (toolExitCode !== 0 && toolOutput) {
1020
- const action = await errorEscalator.handleToolError(parsed.name, toolOutput, parsed.arguments, toolId, messages, createdFiles, enrichedPrompt, renderer, quiet, provider, registry, toolExecutor, modeConfig, llmCfg);
1021
- if (action === "continue")
1022
- continue;
1023
- }
1024
- else {
1025
- errorEscalator.resetForTool(parsed.name);
1026
- }
1027
- // НЕ добавляем "Continue." после каждого tool-вызова — это нарушает
1028
- // структуру диалога. Bounded nudge (строка 418) уже покрывает
1029
- // случай когда модель застряла без tool-вызова.
1030
- if (hasSyntaxError) {
1031
- break;
1032
- }
1033
- }
1034
- }
1035
- if (aborted) {
1036
- renderer?.writeWarning("Agent stopped (double ESC)");
1037
- }
1038
- logInfo("LOOP", `runAgentLoop done: ${messages.length} total messages, aborted=${aborted}`);
1039
- // Agent:done notification
1040
- sendNotification("MMA", "Agent finished the task");
1041
- return { messages };
1042
- }
1043
- //# sourceMappingURL=loop.js.map