@google/gemini-cli-core 0.0.3-preview.4 → 0.0.3

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 (490) hide show
  1. package/LICENSE +2 -2
  2. package/README.md +12 -2
  3. package/dist/index.d.ts +6 -2
  4. package/dist/index.js +6 -2
  5. package/dist/index.js.map +1 -1
  6. package/dist/src/code_assist/codeAssist.d.ts +2 -0
  7. package/dist/src/code_assist/codeAssist.js +12 -0
  8. package/dist/src/code_assist/codeAssist.js.map +1 -1
  9. package/dist/src/code_assist/converter.d.ts +3 -1
  10. package/dist/src/code_assist/converter.js +2 -1
  11. package/dist/src/code_assist/converter.js.map +1 -1
  12. package/dist/src/code_assist/converter.test.js +10 -0
  13. package/dist/src/code_assist/converter.test.js.map +1 -1
  14. package/dist/src/code_assist/oauth-credential-storage.d.ts +25 -0
  15. package/dist/src/code_assist/oauth-credential-storage.js +109 -0
  16. package/dist/src/code_assist/oauth-credential-storage.js.map +1 -0
  17. package/dist/src/code_assist/oauth-credential-storage.test.js +136 -0
  18. package/dist/src/code_assist/oauth-credential-storage.test.js.map +1 -0
  19. package/dist/src/code_assist/oauth2.js +92 -29
  20. package/dist/src/code_assist/oauth2.js.map +1 -1
  21. package/dist/src/code_assist/oauth2.test.js +729 -339
  22. package/dist/src/code_assist/oauth2.test.js.map +1 -1
  23. package/dist/src/code_assist/server.d.ts +1 -1
  24. package/dist/src/code_assist/server.js +24 -1
  25. package/dist/src/code_assist/server.js.map +1 -1
  26. package/dist/src/code_assist/server.test.js +25 -0
  27. package/dist/src/code_assist/server.test.js.map +1 -1
  28. package/dist/src/code_assist/types.d.ts +17 -2
  29. package/dist/src/config/config.d.ts +72 -12
  30. package/dist/src/config/config.js +196 -64
  31. package/dist/src/config/config.js.map +1 -1
  32. package/dist/src/config/config.test.js +305 -178
  33. package/dist/src/config/config.test.js.map +1 -1
  34. package/dist/src/config/models.d.ts +16 -0
  35. package/dist/src/config/models.js +29 -0
  36. package/dist/src/config/models.js.map +1 -1
  37. package/dist/src/config/models.test.d.ts +6 -0
  38. package/dist/src/config/models.test.js +55 -0
  39. package/dist/src/config/models.test.js.map +1 -0
  40. package/dist/src/config/storage.d.ts +2 -0
  41. package/dist/src/config/storage.js +6 -1
  42. package/dist/src/config/storage.js.map +1 -1
  43. package/dist/src/config/storage.test.js +4 -0
  44. package/dist/src/config/storage.test.js.map +1 -1
  45. package/dist/src/confirmation-bus/index.d.ts +7 -0
  46. package/dist/src/confirmation-bus/index.js +8 -0
  47. package/dist/src/confirmation-bus/index.js.map +1 -0
  48. package/dist/src/confirmation-bus/message-bus.d.ts +17 -0
  49. package/dist/src/confirmation-bus/message-bus.js +81 -0
  50. package/dist/src/confirmation-bus/message-bus.js.map +1 -0
  51. package/dist/src/confirmation-bus/message-bus.test.d.ts +6 -0
  52. package/dist/src/confirmation-bus/message-bus.test.js +164 -0
  53. package/dist/src/confirmation-bus/message-bus.test.js.map +1 -0
  54. package/dist/src/confirmation-bus/types.d.ts +38 -0
  55. package/dist/src/confirmation-bus/types.js +15 -0
  56. package/dist/src/confirmation-bus/types.js.map +1 -0
  57. package/dist/src/core/baseLlmClient.d.ts +46 -0
  58. package/dist/src/core/baseLlmClient.js +112 -0
  59. package/dist/src/core/baseLlmClient.js.map +1 -0
  60. package/dist/src/core/baseLlmClient.test.d.ts +6 -0
  61. package/dist/src/core/baseLlmClient.test.js +253 -0
  62. package/dist/src/core/baseLlmClient.test.js.map +1 -0
  63. package/dist/src/core/client.d.ts +16 -21
  64. package/dist/src/core/client.js +145 -232
  65. package/dist/src/core/client.js.map +1 -1
  66. package/dist/src/core/client.test.js +393 -492
  67. package/dist/src/core/client.test.js.map +1 -1
  68. package/dist/src/core/contentGenerator.d.ts +2 -3
  69. package/dist/src/core/contentGenerator.js +0 -4
  70. package/dist/src/core/contentGenerator.js.map +1 -1
  71. package/dist/src/core/contentGenerator.test.js +1 -3
  72. package/dist/src/core/contentGenerator.test.js.map +1 -1
  73. package/dist/src/core/coreToolScheduler.d.ts +8 -3
  74. package/dist/src/core/coreToolScheduler.js +106 -5
  75. package/dist/src/core/coreToolScheduler.js.map +1 -1
  76. package/dist/src/core/coreToolScheduler.test.js +233 -5
  77. package/dist/src/core/coreToolScheduler.test.js.map +1 -1
  78. package/dist/src/core/geminiChat.d.ts +38 -32
  79. package/dist/src/core/geminiChat.js +209 -219
  80. package/dist/src/core/geminiChat.js.map +1 -1
  81. package/dist/src/core/geminiChat.test.js +674 -386
  82. package/dist/src/core/geminiChat.test.js.map +1 -1
  83. package/dist/src/core/loggingContentGenerator.js +13 -16
  84. package/dist/src/core/loggingContentGenerator.js.map +1 -1
  85. package/dist/src/core/nonInteractiveToolExecutor.test.js +59 -1
  86. package/dist/src/core/nonInteractiveToolExecutor.test.js.map +1 -1
  87. package/dist/src/core/prompts.d.ts +5 -0
  88. package/dist/src/core/prompts.js +63 -42
  89. package/dist/src/core/prompts.js.map +1 -1
  90. package/dist/src/core/prompts.test.js +130 -1
  91. package/dist/src/core/prompts.test.js.map +1 -1
  92. package/dist/src/core/subagent.js +7 -10
  93. package/dist/src/core/subagent.js.map +1 -1
  94. package/dist/src/core/subagent.test.js +32 -22
  95. package/dist/src/core/subagent.test.js.map +1 -1
  96. package/dist/src/core/turn.d.ts +21 -5
  97. package/dist/src/core/turn.js +45 -11
  98. package/dist/src/core/turn.js.map +1 -1
  99. package/dist/src/core/turn.test.js +340 -100
  100. package/dist/src/core/turn.test.js.map +1 -1
  101. package/dist/src/fallback/handler.d.ts +7 -0
  102. package/dist/src/fallback/handler.js +51 -0
  103. package/dist/src/fallback/handler.js.map +1 -0
  104. package/dist/src/fallback/handler.test.d.ts +6 -0
  105. package/dist/src/fallback/handler.test.js +130 -0
  106. package/dist/src/fallback/handler.test.js.map +1 -0
  107. package/dist/src/fallback/types.d.ts +14 -0
  108. package/dist/src/fallback/types.js +7 -0
  109. package/dist/src/fallback/types.js.map +1 -0
  110. package/dist/src/generated/git-commit.d.ts +2 -2
  111. package/dist/src/generated/git-commit.js +2 -2
  112. package/dist/src/generated/git-commit.js.map +1 -1
  113. package/dist/src/ide/constants.d.ts +3 -0
  114. package/dist/src/ide/constants.js +3 -0
  115. package/dist/src/ide/constants.js.map +1 -1
  116. package/dist/src/ide/detect-ide.d.ts +42 -14
  117. package/dist/src/ide/detect-ide.js +22 -68
  118. package/dist/src/ide/detect-ide.js.map +1 -1
  119. package/dist/src/ide/detect-ide.test.js +11 -51
  120. package/dist/src/ide/detect-ide.test.js.map +1 -1
  121. package/dist/src/ide/ide-client.d.ts +60 -18
  122. package/dist/src/ide/ide-client.js +275 -53
  123. package/dist/src/ide/ide-client.js.map +1 -1
  124. package/dist/src/ide/ide-client.test.js +239 -6
  125. package/dist/src/ide/ide-client.test.js.map +1 -1
  126. package/dist/src/ide/ide-installer.d.ts +2 -2
  127. package/dist/src/ide/ide-installer.js +15 -11
  128. package/dist/src/ide/ide-installer.js.map +1 -1
  129. package/dist/src/ide/ide-installer.test.js +30 -12
  130. package/dist/src/ide/ide-installer.test.js.map +1 -1
  131. package/dist/src/ide/ideContext.d.ts +35 -365
  132. package/dist/src/ide/ideContext.js +60 -106
  133. package/dist/src/ide/ideContext.js.map +1 -1
  134. package/dist/src/ide/ideContext.test.js +152 -24
  135. package/dist/src/ide/ideContext.test.js.map +1 -1
  136. package/dist/src/ide/process-utils.d.ts +0 -1
  137. package/dist/src/ide/process-utils.js +43 -25
  138. package/dist/src/ide/process-utils.js.map +1 -1
  139. package/dist/src/ide/process-utils.test.js +90 -4
  140. package/dist/src/ide/process-utils.test.js.map +1 -1
  141. package/dist/src/ide/types.d.ts +486 -0
  142. package/dist/src/ide/types.js +138 -0
  143. package/dist/src/ide/types.js.map +1 -0
  144. package/dist/src/index.d.ts +10 -2
  145. package/dist/src/index.js +11 -2
  146. package/dist/src/index.js.map +1 -1
  147. package/dist/src/mcp/oauth-provider.d.ts +15 -12
  148. package/dist/src/mcp/oauth-provider.js +63 -56
  149. package/dist/src/mcp/oauth-provider.js.map +1 -1
  150. package/dist/src/mcp/oauth-provider.test.js +74 -35
  151. package/dist/src/mcp/oauth-provider.test.js.map +1 -1
  152. package/dist/src/mcp/oauth-token-storage.d.ts +14 -10
  153. package/dist/src/mcp/oauth-token-storage.js +52 -20
  154. package/dist/src/mcp/oauth-token-storage.js.map +1 -1
  155. package/dist/src/mcp/oauth-token-storage.test.js +255 -162
  156. package/dist/src/mcp/oauth-token-storage.test.js.map +1 -1
  157. package/dist/src/mcp/token-storage/base-token-storage.d.ts +1 -1
  158. package/dist/src/mcp/token-storage/base-token-storage.js +1 -1
  159. package/dist/src/mcp/token-storage/base-token-storage.js.map +1 -1
  160. package/dist/src/mcp/token-storage/base-token-storage.test.js +1 -1
  161. package/dist/src/mcp/token-storage/base-token-storage.test.js.map +1 -1
  162. package/dist/src/mcp/token-storage/file-token-storage.d.ts +24 -0
  163. package/dist/src/mcp/token-storage/file-token-storage.js +144 -0
  164. package/dist/src/mcp/token-storage/file-token-storage.js.map +1 -0
  165. package/dist/src/mcp/token-storage/file-token-storage.test.d.ts +6 -0
  166. package/dist/src/mcp/token-storage/file-token-storage.test.js +235 -0
  167. package/dist/src/mcp/token-storage/file-token-storage.test.js.map +1 -0
  168. package/dist/src/mcp/token-storage/hybrid-token-storage.d.ts +23 -0
  169. package/dist/src/mcp/token-storage/hybrid-token-storage.js +78 -0
  170. package/dist/src/mcp/token-storage/hybrid-token-storage.js.map +1 -0
  171. package/dist/src/mcp/token-storage/hybrid-token-storage.test.d.ts +6 -0
  172. package/dist/src/mcp/token-storage/hybrid-token-storage.test.js +193 -0
  173. package/dist/src/mcp/token-storage/hybrid-token-storage.test.js.map +1 -0
  174. package/dist/src/mcp/token-storage/index.d.ts +11 -0
  175. package/dist/src/mcp/token-storage/index.js +12 -0
  176. package/dist/src/mcp/token-storage/index.js.map +1 -0
  177. package/dist/src/mcp/token-storage/keychain-token-storage.d.ts +31 -0
  178. package/dist/src/mcp/token-storage/keychain-token-storage.js +190 -0
  179. package/dist/src/mcp/token-storage/keychain-token-storage.js.map +1 -0
  180. package/dist/src/mcp/token-storage/keychain-token-storage.test.d.ts +6 -0
  181. package/dist/src/mcp/token-storage/keychain-token-storage.test.js +254 -0
  182. package/dist/src/mcp/token-storage/keychain-token-storage.test.js.map +1 -0
  183. package/dist/src/mcp/token-storage/types.d.ts +4 -0
  184. package/dist/src/mcp/token-storage/types.js +5 -1
  185. package/dist/src/mcp/token-storage/types.js.map +1 -1
  186. package/dist/src/output/json-formatter.d.ts +11 -0
  187. package/dist/src/output/json-formatter.js +30 -0
  188. package/dist/src/output/json-formatter.js.map +1 -0
  189. package/dist/src/output/json-formatter.test.d.ts +6 -0
  190. package/dist/src/output/json-formatter.test.js +266 -0
  191. package/dist/src/output/json-formatter.test.js.map +1 -0
  192. package/dist/src/output/types.d.ts +20 -0
  193. package/dist/src/output/types.js +11 -0
  194. package/dist/src/output/types.js.map +1 -0
  195. package/dist/src/policy/index.d.ts +7 -0
  196. package/dist/src/policy/index.js +8 -0
  197. package/dist/src/policy/index.js.map +1 -0
  198. package/dist/src/policy/policy-engine.d.ts +30 -0
  199. package/dist/src/policy/policy-engine.js +92 -0
  200. package/dist/src/policy/policy-engine.js.map +1 -0
  201. package/dist/src/policy/policy-engine.test.d.ts +6 -0
  202. package/dist/src/policy/policy-engine.test.js +515 -0
  203. package/dist/src/policy/policy-engine.test.js.map +1 -0
  204. package/dist/src/policy/stable-stringify.d.ts +58 -0
  205. package/dist/src/policy/stable-stringify.js +122 -0
  206. package/dist/src/policy/stable-stringify.js.map +1 -0
  207. package/dist/src/policy/types.d.ts +47 -0
  208. package/dist/src/policy/types.js +12 -0
  209. package/dist/src/policy/types.js.map +1 -0
  210. package/dist/src/routing/modelRouterService.d.ts +23 -0
  211. package/dist/src/routing/modelRouterService.js +70 -0
  212. package/dist/src/routing/modelRouterService.js.map +1 -0
  213. package/dist/src/routing/modelRouterService.test.d.ts +6 -0
  214. package/dist/src/routing/modelRouterService.test.js +98 -0
  215. package/dist/src/routing/modelRouterService.test.js.map +1 -0
  216. package/dist/src/routing/routingStrategy.d.ts +62 -0
  217. package/dist/src/routing/routingStrategy.js +7 -0
  218. package/dist/src/routing/routingStrategy.js.map +1 -0
  219. package/dist/src/routing/strategies/classifierStrategy.d.ts +12 -0
  220. package/dist/src/routing/strategies/classifierStrategy.js +173 -0
  221. package/dist/src/routing/strategies/classifierStrategy.js.map +1 -0
  222. package/dist/src/routing/strategies/classifierStrategy.test.d.ts +6 -0
  223. package/dist/src/routing/strategies/classifierStrategy.test.js +192 -0
  224. package/dist/src/routing/strategies/classifierStrategy.test.js.map +1 -0
  225. package/dist/src/routing/strategies/compositeStrategy.d.ts +26 -0
  226. package/dist/src/routing/strategies/compositeStrategy.js +67 -0
  227. package/dist/src/routing/strategies/compositeStrategy.js.map +1 -0
  228. package/dist/src/routing/strategies/compositeStrategy.test.d.ts +6 -0
  229. package/dist/src/routing/strategies/compositeStrategy.test.js +123 -0
  230. package/dist/src/routing/strategies/compositeStrategy.test.js.map +1 -0
  231. package/dist/src/routing/strategies/defaultStrategy.d.ts +12 -0
  232. package/dist/src/routing/strategies/defaultStrategy.js +20 -0
  233. package/dist/src/routing/strategies/defaultStrategy.js.map +1 -0
  234. package/dist/src/routing/strategies/defaultStrategy.test.d.ts +6 -0
  235. package/dist/src/routing/strategies/defaultStrategy.test.js +26 -0
  236. package/dist/src/routing/strategies/defaultStrategy.test.js.map +1 -0
  237. package/dist/src/routing/strategies/fallbackStrategy.d.ts +12 -0
  238. package/dist/src/routing/strategies/fallbackStrategy.js +25 -0
  239. package/dist/src/routing/strategies/fallbackStrategy.js.map +1 -0
  240. package/dist/src/routing/strategies/fallbackStrategy.test.d.ts +6 -0
  241. package/dist/src/routing/strategies/fallbackStrategy.test.js +55 -0
  242. package/dist/src/routing/strategies/fallbackStrategy.test.js.map +1 -0
  243. package/dist/src/routing/strategies/overrideStrategy.d.ts +15 -0
  244. package/dist/src/routing/strategies/overrideStrategy.js +28 -0
  245. package/dist/src/routing/strategies/overrideStrategy.js.map +1 -0
  246. package/dist/src/routing/strategies/overrideStrategy.test.d.ts +6 -0
  247. package/dist/src/routing/strategies/overrideStrategy.test.js +42 -0
  248. package/dist/src/routing/strategies/overrideStrategy.test.js.map +1 -0
  249. package/dist/src/services/chatRecordingService.d.ts +7 -13
  250. package/dist/src/services/chatRecordingService.js +28 -19
  251. package/dist/src/services/chatRecordingService.js.map +1 -1
  252. package/dist/src/services/chatRecordingService.test.js +62 -20
  253. package/dist/src/services/chatRecordingService.test.js.map +1 -1
  254. package/dist/src/services/fileDiscoveryService.d.ts +10 -0
  255. package/dist/src/services/fileDiscoveryService.js +31 -17
  256. package/dist/src/services/fileDiscoveryService.js.map +1 -1
  257. package/dist/src/services/gitService.js +9 -12
  258. package/dist/src/services/gitService.js.map +1 -1
  259. package/dist/src/services/gitService.test.js +10 -20
  260. package/dist/src/services/gitService.test.js.map +1 -1
  261. package/dist/src/services/loopDetectionService.d.ts +5 -0
  262. package/dist/src/services/loopDetectionService.js +36 -20
  263. package/dist/src/services/loopDetectionService.js.map +1 -1
  264. package/dist/src/services/loopDetectionService.test.js +41 -12
  265. package/dist/src/services/loopDetectionService.test.js.map +1 -1
  266. package/dist/src/services/shellExecutionService.d.ts +34 -2
  267. package/dist/src/services/shellExecutionService.js +192 -43
  268. package/dist/src/services/shellExecutionService.js.map +1 -1
  269. package/dist/src/services/shellExecutionService.test.js +184 -55
  270. package/dist/src/services/shellExecutionService.test.js.map +1 -1
  271. package/dist/src/telemetry/activity-detector.d.ts +41 -0
  272. package/dist/src/telemetry/activity-detector.js +61 -0
  273. package/dist/src/telemetry/activity-detector.js.map +1 -0
  274. package/dist/src/telemetry/activity-detector.test.d.ts +6 -0
  275. package/dist/src/telemetry/activity-detector.test.js +136 -0
  276. package/dist/src/telemetry/activity-detector.test.js.map +1 -0
  277. package/dist/src/telemetry/activity-types.d.ts +19 -0
  278. package/dist/src/telemetry/activity-types.js +21 -0
  279. package/dist/src/telemetry/activity-types.js.map +1 -0
  280. package/dist/src/telemetry/clearcut-logger/clearcut-logger.d.ts +16 -2
  281. package/dist/src/telemetry/clearcut-logger/clearcut-logger.js +143 -24
  282. package/dist/src/telemetry/clearcut-logger/clearcut-logger.js.map +1 -1
  283. package/dist/src/telemetry/clearcut-logger/clearcut-logger.test.js +101 -1
  284. package/dist/src/telemetry/clearcut-logger/clearcut-logger.test.js.map +1 -1
  285. package/dist/src/telemetry/clearcut-logger/event-metadata-key.d.ts +19 -2
  286. package/dist/src/telemetry/clearcut-logger/event-metadata-key.js +48 -2
  287. package/dist/src/telemetry/clearcut-logger/event-metadata-key.js.map +1 -1
  288. package/dist/src/telemetry/constants.d.ts +8 -0
  289. package/dist/src/telemetry/constants.js +8 -0
  290. package/dist/src/telemetry/constants.js.map +1 -1
  291. package/dist/src/telemetry/gcp-exporters.d.ts +34 -0
  292. package/dist/src/telemetry/gcp-exporters.js +117 -0
  293. package/dist/src/telemetry/gcp-exporters.js.map +1 -0
  294. package/dist/src/telemetry/gcp-exporters.test.d.ts +6 -0
  295. package/dist/src/telemetry/gcp-exporters.test.js +318 -0
  296. package/dist/src/telemetry/gcp-exporters.test.js.map +1 -0
  297. package/dist/src/telemetry/high-water-mark-tracker.d.ts +43 -0
  298. package/dist/src/telemetry/high-water-mark-tracker.js +88 -0
  299. package/dist/src/telemetry/high-water-mark-tracker.js.map +1 -0
  300. package/dist/src/telemetry/high-water-mark-tracker.test.d.ts +6 -0
  301. package/dist/src/telemetry/high-water-mark-tracker.test.js +152 -0
  302. package/dist/src/telemetry/high-water-mark-tracker.test.js.map +1 -0
  303. package/dist/src/telemetry/index.d.ts +7 -2
  304. package/dist/src/telemetry/index.js +7 -2
  305. package/dist/src/telemetry/index.js.map +1 -1
  306. package/dist/src/telemetry/loggers.d.ts +8 -1
  307. package/dist/src/telemetry/loggers.js +140 -8
  308. package/dist/src/telemetry/loggers.js.map +1 -1
  309. package/dist/src/telemetry/loggers.test.js +268 -39
  310. package/dist/src/telemetry/loggers.test.js.map +1 -1
  311. package/dist/src/telemetry/metrics.d.ts +4 -3
  312. package/dist/src/telemetry/metrics.js +33 -10
  313. package/dist/src/telemetry/metrics.js.map +1 -1
  314. package/dist/src/telemetry/metrics.test.js +47 -25
  315. package/dist/src/telemetry/metrics.test.js.map +1 -1
  316. package/dist/src/telemetry/rate-limiter.d.ts +48 -0
  317. package/dist/src/telemetry/rate-limiter.js +100 -0
  318. package/dist/src/telemetry/rate-limiter.js.map +1 -0
  319. package/dist/src/telemetry/rate-limiter.test.d.ts +6 -0
  320. package/dist/src/telemetry/rate-limiter.test.js +207 -0
  321. package/dist/src/telemetry/rate-limiter.test.js.map +1 -0
  322. package/dist/src/telemetry/sdk.js +16 -1
  323. package/dist/src/telemetry/sdk.js.map +1 -1
  324. package/dist/src/telemetry/sdk.test.js +95 -0
  325. package/dist/src/telemetry/sdk.test.js.map +1 -1
  326. package/dist/src/telemetry/types.d.ts +70 -6
  327. package/dist/src/telemetry/types.js +112 -8
  328. package/dist/src/telemetry/types.js.map +1 -1
  329. package/dist/src/telemetry/uiTelemetry.d.ts +1 -1
  330. package/dist/src/telemetry/uiTelemetry.js +6 -7
  331. package/dist/src/telemetry/uiTelemetry.js.map +1 -1
  332. package/dist/src/telemetry/uiTelemetry.test.js +15 -15
  333. package/dist/src/telemetry/uiTelemetry.test.js.map +1 -1
  334. package/dist/src/test-utils/index.d.ts +6 -0
  335. package/dist/src/test-utils/index.js +7 -0
  336. package/dist/src/test-utils/index.js.map +1 -0
  337. package/dist/src/test-utils/mock-tool.d.ts +41 -0
  338. package/dist/src/test-utils/mock-tool.js +51 -0
  339. package/dist/src/test-utils/mock-tool.js.map +1 -0
  340. package/dist/src/tools/diffOptions.js +21 -13
  341. package/dist/src/tools/diffOptions.js.map +1 -1
  342. package/dist/src/tools/diffOptions.test.js +58 -22
  343. package/dist/src/tools/diffOptions.test.js.map +1 -1
  344. package/dist/src/tools/edit.d.ts +2 -2
  345. package/dist/src/tools/edit.js +35 -44
  346. package/dist/src/tools/edit.js.map +1 -1
  347. package/dist/src/tools/edit.test.js +124 -13
  348. package/dist/src/tools/edit.test.js.map +1 -1
  349. package/dist/src/tools/glob.d.ts +5 -1
  350. package/dist/src/tools/glob.js +24 -17
  351. package/dist/src/tools/glob.js.map +1 -1
  352. package/dist/src/tools/glob.test.js +51 -0
  353. package/dist/src/tools/glob.test.js.map +1 -1
  354. package/dist/src/tools/ls.js +19 -32
  355. package/dist/src/tools/ls.js.map +1 -1
  356. package/dist/src/tools/ls.test.js +140 -280
  357. package/dist/src/tools/ls.test.js.map +1 -1
  358. package/dist/src/tools/mcp-client-manager.d.ts +5 -3
  359. package/dist/src/tools/mcp-client-manager.js +13 -4
  360. package/dist/src/tools/mcp-client-manager.js.map +1 -1
  361. package/dist/src/tools/mcp-client-manager.test.js +20 -1
  362. package/dist/src/tools/mcp-client-manager.test.js.map +1 -1
  363. package/dist/src/tools/mcp-client.d.ts +5 -5
  364. package/dist/src/tools/mcp-client.js +40 -35
  365. package/dist/src/tools/mcp-client.js.map +1 -1
  366. package/dist/src/tools/mcp-client.test.js +3 -3
  367. package/dist/src/tools/mcp-client.test.js.map +1 -1
  368. package/dist/src/tools/mcp-tool.d.ts +3 -2
  369. package/dist/src/tools/mcp-tool.js +9 -9
  370. package/dist/src/tools/mcp-tool.js.map +1 -1
  371. package/dist/src/tools/mcp-tool.test.js +28 -7
  372. package/dist/src/tools/mcp-tool.test.js.map +1 -1
  373. package/dist/src/tools/memoryTool.js +5 -33
  374. package/dist/src/tools/memoryTool.js.map +1 -1
  375. package/dist/src/tools/read-file.js +8 -3
  376. package/dist/src/tools/read-file.js.map +1 -1
  377. package/dist/src/tools/read-file.test.js +29 -0
  378. package/dist/src/tools/read-file.test.js.map +1 -1
  379. package/dist/src/tools/read-many-files.d.ts +1 -1
  380. package/dist/src/tools/read-many-files.js +18 -50
  381. package/dist/src/tools/read-many-files.js.map +1 -1
  382. package/dist/src/tools/read-many-files.test.js +4 -4
  383. package/dist/src/tools/read-many-files.test.js.map +1 -1
  384. package/dist/src/tools/ripGrep.d.ts +8 -0
  385. package/dist/src/tools/ripGrep.js +26 -1
  386. package/dist/src/tools/ripGrep.js.map +1 -1
  387. package/dist/src/tools/ripGrep.test.js +107 -5
  388. package/dist/src/tools/ripGrep.test.js.map +1 -1
  389. package/dist/src/tools/shell.d.ts +12 -2
  390. package/dist/src/tools/shell.js +20 -24
  391. package/dist/src/tools/shell.js.map +1 -1
  392. package/dist/src/tools/shell.test.js +35 -70
  393. package/dist/src/tools/shell.test.js.map +1 -1
  394. package/dist/src/tools/smart-edit.d.ts +72 -0
  395. package/dist/src/tools/smart-edit.js +594 -0
  396. package/dist/src/tools/smart-edit.js.map +1 -0
  397. package/dist/src/tools/smart-edit.test.d.ts +6 -0
  398. package/dist/src/tools/smart-edit.test.js +419 -0
  399. package/dist/src/tools/smart-edit.test.js.map +1 -0
  400. package/dist/src/tools/tool-registry.d.ts +2 -1
  401. package/dist/src/tools/tool-registry.js +6 -5
  402. package/dist/src/tools/tool-registry.js.map +1 -1
  403. package/dist/src/tools/tools.d.ts +14 -7
  404. package/dist/src/tools/tools.js +9 -2
  405. package/dist/src/tools/tools.js.map +1 -1
  406. package/dist/src/tools/web-fetch.js +4 -3
  407. package/dist/src/tools/web-fetch.js.map +1 -1
  408. package/dist/src/tools/web-search.d.ts +1 -1
  409. package/dist/src/tools/web-search.js +3 -1
  410. package/dist/src/tools/web-search.js.map +1 -1
  411. package/dist/src/tools/write-file.js +14 -19
  412. package/dist/src/tools/write-file.js.map +1 -1
  413. package/dist/src/tools/write-file.test.js +99 -19
  414. package/dist/src/tools/write-file.test.js.map +1 -1
  415. package/dist/src/utils/bfsFileSearch.js +11 -5
  416. package/dist/src/utils/bfsFileSearch.js.map +1 -1
  417. package/dist/src/utils/editCorrector.d.ts +7 -6
  418. package/dist/src/utils/editCorrector.js +61 -18
  419. package/dist/src/utils/editCorrector.js.map +1 -1
  420. package/dist/src/utils/editCorrector.test.js +30 -79
  421. package/dist/src/utils/editCorrector.test.js.map +1 -1
  422. package/dist/src/utils/editor.js +31 -44
  423. package/dist/src/utils/editor.js.map +1 -1
  424. package/dist/src/utils/editor.test.js +61 -75
  425. package/dist/src/utils/editor.test.js.map +1 -1
  426. package/dist/src/utils/errorParsing.js +2 -2
  427. package/dist/src/utils/errorParsing.js.map +1 -1
  428. package/dist/src/utils/errorParsing.test.js +7 -7
  429. package/dist/src/utils/errorParsing.test.js.map +1 -1
  430. package/dist/src/utils/errors.d.ts +6 -0
  431. package/dist/src/utils/errors.js +10 -0
  432. package/dist/src/utils/errors.js.map +1 -1
  433. package/dist/src/utils/fileUtils.d.ts +20 -3
  434. package/dist/src/utils/fileUtils.js +154 -32
  435. package/dist/src/utils/fileUtils.js.map +1 -1
  436. package/dist/src/utils/fileUtils.test.js +347 -29
  437. package/dist/src/utils/fileUtils.test.js.map +1 -1
  438. package/dist/src/utils/flashFallback.test.d.ts +6 -0
  439. package/dist/src/utils/{flashFallback.integration.test.js → flashFallback.test.js} +31 -27
  440. package/dist/src/utils/flashFallback.test.js.map +1 -0
  441. package/dist/src/utils/geminiIgnoreParser.d.ts +18 -0
  442. package/dist/src/utils/geminiIgnoreParser.js +61 -0
  443. package/dist/src/utils/geminiIgnoreParser.js.map +1 -0
  444. package/dist/src/utils/geminiIgnoreParser.test.d.ts +6 -0
  445. package/dist/src/utils/geminiIgnoreParser.test.js +50 -0
  446. package/dist/src/utils/geminiIgnoreParser.test.js.map +1 -0
  447. package/dist/src/utils/gitIgnoreParser.d.ts +3 -7
  448. package/dist/src/utils/gitIgnoreParser.js +125 -34
  449. package/dist/src/utils/gitIgnoreParser.js.map +1 -1
  450. package/dist/src/utils/gitIgnoreParser.test.js +66 -35
  451. package/dist/src/utils/gitIgnoreParser.test.js.map +1 -1
  452. package/dist/src/utils/llm-edit-fixer.d.ts +26 -0
  453. package/dist/src/utils/llm-edit-fixer.js +121 -0
  454. package/dist/src/utils/llm-edit-fixer.js.map +1 -0
  455. package/dist/src/utils/llm-edit-fixer.test.d.ts +6 -0
  456. package/dist/src/utils/llm-edit-fixer.test.js +105 -0
  457. package/dist/src/utils/llm-edit-fixer.test.js.map +1 -0
  458. package/dist/src/utils/memoryDiscovery.d.ts +5 -4
  459. package/dist/src/utils/memoryDiscovery.js +10 -9
  460. package/dist/src/utils/memoryDiscovery.js.map +1 -1
  461. package/dist/src/utils/memoryDiscovery.test.js +50 -25
  462. package/dist/src/utils/memoryDiscovery.test.js.map +1 -1
  463. package/dist/src/utils/nextSpeakerChecker.d.ts +2 -2
  464. package/dist/src/utils/nextSpeakerChecker.js +8 -2
  465. package/dist/src/utils/nextSpeakerChecker.js.map +1 -1
  466. package/dist/src/utils/nextSpeakerChecker.test.js +75 -64
  467. package/dist/src/utils/nextSpeakerChecker.test.js.map +1 -1
  468. package/dist/src/utils/promptIdContext.d.ts +7 -0
  469. package/dist/src/utils/promptIdContext.js +8 -0
  470. package/dist/src/utils/promptIdContext.js.map +1 -0
  471. package/dist/src/utils/shell-utils.d.ts +5 -0
  472. package/dist/src/utils/shell-utils.js +23 -0
  473. package/dist/src/utils/shell-utils.js.map +1 -1
  474. package/dist/src/utils/terminalSerializer.d.ts +28 -0
  475. package/dist/src/utils/terminalSerializer.js +432 -0
  476. package/dist/src/utils/terminalSerializer.js.map +1 -0
  477. package/dist/src/utils/terminalSerializer.test.d.ts +6 -0
  478. package/dist/src/utils/terminalSerializer.test.js +176 -0
  479. package/dist/src/utils/terminalSerializer.test.js.map +1 -0
  480. package/dist/src/utils/textUtils.d.ts +5 -0
  481. package/dist/src/utils/textUtils.js +14 -0
  482. package/dist/src/utils/textUtils.js.map +1 -1
  483. package/dist/src/utils/textUtils.test.d.ts +6 -0
  484. package/dist/src/utils/textUtils.test.js +59 -0
  485. package/dist/src/utils/textUtils.test.js.map +1 -0
  486. package/dist/tsconfig.tsbuildinfo +1 -1
  487. package/package.json +9 -3
  488. package/dist/google-gemini-cli-core-0.3.0-preview.3.tgz +0 -0
  489. package/dist/src/utils/flashFallback.integration.test.js.map +0 -1
  490. /package/dist/src/{utils/flashFallback.integration.test.d.ts → code_assist/oauth-credential-storage.test.d.ts} +0 -0
@@ -0,0 +1,72 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import { BaseDeclarativeTool, type ToolInvocation, type ToolResult } from './tools.js';
7
+ import { ToolErrorType } from './tool-error.js';
8
+ import { type Config } from '../config/config.js';
9
+ import { type ModifiableDeclarativeTool, type ModifyContext } from './modifiable-tool.js';
10
+ interface ReplacementContext {
11
+ params: EditToolParams;
12
+ currentContent: string;
13
+ abortSignal: AbortSignal;
14
+ }
15
+ interface ReplacementResult {
16
+ newContent: string;
17
+ occurrences: number;
18
+ finalOldString: string;
19
+ finalNewString: string;
20
+ }
21
+ export declare function calculateReplacement(context: ReplacementContext): Promise<ReplacementResult>;
22
+ export declare function getErrorReplaceResult(params: EditToolParams, occurrences: number, expectedReplacements: number, finalOldString: string, finalNewString: string): {
23
+ display: string;
24
+ raw: string;
25
+ type: ToolErrorType;
26
+ } | undefined;
27
+ /**
28
+ * Parameters for the Edit tool
29
+ */
30
+ export interface EditToolParams {
31
+ /**
32
+ * The absolute path to the file to modify
33
+ */
34
+ file_path: string;
35
+ /**
36
+ * The text to replace
37
+ */
38
+ old_string: string;
39
+ /**
40
+ * The text to replace it with
41
+ */
42
+ new_string: string;
43
+ /**
44
+ * The instruction for what needs to be done.
45
+ */
46
+ instruction: string;
47
+ /**
48
+ * Whether the edit was modified manually by the user.
49
+ */
50
+ modified_by_user?: boolean;
51
+ /**
52
+ * Initially proposed string.
53
+ */
54
+ ai_proposed_string?: string;
55
+ }
56
+ /**
57
+ * Implementation of the Edit tool logic
58
+ */
59
+ export declare class SmartEditTool extends BaseDeclarativeTool<EditToolParams, ToolResult> implements ModifiableDeclarativeTool<EditToolParams> {
60
+ private readonly config;
61
+ static readonly Name = "replace";
62
+ constructor(config: Config);
63
+ /**
64
+ * Validates the parameters for the Edit tool
65
+ * @param params Parameters to validate
66
+ * @returns Error message string or null if valid
67
+ */
68
+ protected validateToolParamValues(params: EditToolParams): string | null;
69
+ protected createInvocation(params: EditToolParams): ToolInvocation<EditToolParams, ToolResult>;
70
+ getModifyContext(_: AbortSignal): ModifyContext<EditToolParams>;
71
+ }
72
+ export {};
@@ -0,0 +1,594 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import * as fs from 'node:fs';
7
+ import * as path from 'node:path';
8
+ import * as Diff from 'diff';
9
+ import { BaseDeclarativeTool, Kind, ToolConfirmationOutcome, } from './tools.js';
10
+ import { ToolErrorType } from './tool-error.js';
11
+ import { makeRelative, shortenPath } from '../utils/paths.js';
12
+ import { isNodeError } from '../utils/errors.js';
13
+ import { ApprovalMode } from '../config/config.js';
14
+ import { DEFAULT_DIFF_OPTIONS, getDiffStat } from './diffOptions.js';
15
+ import { ReadFileTool } from './read-file.js';
16
+ import {} from './modifiable-tool.js';
17
+ import { IdeClient } from '../ide/ide-client.js';
18
+ import { FixLLMEditWithInstruction } from '../utils/llm-edit-fixer.js';
19
+ import { applyReplacement } from './edit.js';
20
+ import { safeLiteralReplace } from '../utils/textUtils.js';
21
+ function restoreTrailingNewline(originalContent, modifiedContent) {
22
+ const hadTrailingNewline = originalContent.endsWith('\n');
23
+ if (hadTrailingNewline && !modifiedContent.endsWith('\n')) {
24
+ return modifiedContent + '\n';
25
+ }
26
+ else if (!hadTrailingNewline && modifiedContent.endsWith('\n')) {
27
+ return modifiedContent.replace(/\n$/, '');
28
+ }
29
+ return modifiedContent;
30
+ }
31
+ async function calculateExactReplacement(context) {
32
+ const { currentContent, params } = context;
33
+ const { old_string, new_string } = params;
34
+ const normalizedCode = currentContent;
35
+ const normalizedSearch = old_string.replace(/\r\n/g, '\n');
36
+ const normalizedReplace = new_string.replace(/\r\n/g, '\n');
37
+ const exactOccurrences = normalizedCode.split(normalizedSearch).length - 1;
38
+ if (exactOccurrences > 0) {
39
+ let modifiedCode = safeLiteralReplace(normalizedCode, normalizedSearch, normalizedReplace);
40
+ modifiedCode = restoreTrailingNewline(currentContent, modifiedCode);
41
+ return {
42
+ newContent: modifiedCode,
43
+ occurrences: exactOccurrences,
44
+ finalOldString: normalizedSearch,
45
+ finalNewString: normalizedReplace,
46
+ };
47
+ }
48
+ return null;
49
+ }
50
+ async function calculateFlexibleReplacement(context) {
51
+ const { currentContent, params } = context;
52
+ const { old_string, new_string } = params;
53
+ const normalizedCode = currentContent;
54
+ const normalizedSearch = old_string.replace(/\r\n/g, '\n');
55
+ const normalizedReplace = new_string.replace(/\r\n/g, '\n');
56
+ const sourceLines = normalizedCode.match(/.*(?:\n|$)/g)?.slice(0, -1) ?? [];
57
+ const searchLinesStripped = normalizedSearch
58
+ .split('\n')
59
+ .map((line) => line.trim());
60
+ const replaceLines = normalizedReplace.split('\n');
61
+ let flexibleOccurrences = 0;
62
+ let i = 0;
63
+ while (i <= sourceLines.length - searchLinesStripped.length) {
64
+ const window = sourceLines.slice(i, i + searchLinesStripped.length);
65
+ const windowStripped = window.map((line) => line.trim());
66
+ const isMatch = windowStripped.every((line, index) => line === searchLinesStripped[index]);
67
+ if (isMatch) {
68
+ flexibleOccurrences++;
69
+ const firstLineInMatch = window[0];
70
+ const indentationMatch = firstLineInMatch.match(/^(\s*)/);
71
+ const indentation = indentationMatch ? indentationMatch[1] : '';
72
+ const newBlockWithIndent = replaceLines.map((line) => `${indentation}${line}`);
73
+ sourceLines.splice(i, searchLinesStripped.length, newBlockWithIndent.join('\n'));
74
+ i += replaceLines.length;
75
+ }
76
+ else {
77
+ i++;
78
+ }
79
+ }
80
+ if (flexibleOccurrences > 0) {
81
+ let modifiedCode = sourceLines.join('');
82
+ modifiedCode = restoreTrailingNewline(currentContent, modifiedCode);
83
+ return {
84
+ newContent: modifiedCode,
85
+ occurrences: flexibleOccurrences,
86
+ finalOldString: normalizedSearch,
87
+ finalNewString: normalizedReplace,
88
+ };
89
+ }
90
+ return null;
91
+ }
92
+ /**
93
+ * Detects the line ending style of a string.
94
+ * @param content The string content to analyze.
95
+ * @returns '\r\n' for Windows-style, '\n' for Unix-style.
96
+ */
97
+ function detectLineEnding(content) {
98
+ // If a Carriage Return is found, assume Windows-style endings.
99
+ // This is a simple but effective heuristic.
100
+ return content.includes('\r\n') ? '\r\n' : '\n';
101
+ }
102
+ export async function calculateReplacement(context) {
103
+ const { currentContent, params } = context;
104
+ const { old_string, new_string } = params;
105
+ const normalizedSearch = old_string.replace(/\r\n/g, '\n');
106
+ const normalizedReplace = new_string.replace(/\r\n/g, '\n');
107
+ if (normalizedSearch === '') {
108
+ return {
109
+ newContent: currentContent,
110
+ occurrences: 0,
111
+ finalOldString: normalizedSearch,
112
+ finalNewString: normalizedReplace,
113
+ };
114
+ }
115
+ const exactResult = await calculateExactReplacement(context);
116
+ if (exactResult) {
117
+ return exactResult;
118
+ }
119
+ const flexibleResult = await calculateFlexibleReplacement(context);
120
+ if (flexibleResult) {
121
+ return flexibleResult;
122
+ }
123
+ return {
124
+ newContent: currentContent,
125
+ occurrences: 0,
126
+ finalOldString: normalizedSearch,
127
+ finalNewString: normalizedReplace,
128
+ };
129
+ }
130
+ export function getErrorReplaceResult(params, occurrences, expectedReplacements, finalOldString, finalNewString) {
131
+ let error = undefined;
132
+ if (occurrences === 0) {
133
+ error = {
134
+ display: `Failed to edit, could not find the string to replace.`,
135
+ raw: `Failed to edit, 0 occurrences found for old_string (${finalOldString}). Original old_string was (${params.old_string}) in ${params.file_path}. No edits made. The exact text in old_string was not found. Ensure you're not escaping content incorrectly and check whitespace, indentation, and context. Use ${ReadFileTool.Name} tool to verify.`,
136
+ type: ToolErrorType.EDIT_NO_OCCURRENCE_FOUND,
137
+ };
138
+ }
139
+ else if (occurrences !== expectedReplacements) {
140
+ const occurrenceTerm = expectedReplacements === 1 ? 'occurrence' : 'occurrences';
141
+ error = {
142
+ display: `Failed to edit, expected ${expectedReplacements} ${occurrenceTerm} but found ${occurrences}.`,
143
+ raw: `Failed to edit, Expected ${expectedReplacements} ${occurrenceTerm} but found ${occurrences} for old_string in file: ${params.file_path}`,
144
+ type: ToolErrorType.EDIT_EXPECTED_OCCURRENCE_MISMATCH,
145
+ };
146
+ }
147
+ else if (finalOldString === finalNewString) {
148
+ error = {
149
+ display: `No changes to apply. The old_string and new_string are identical.`,
150
+ raw: `No changes to apply. The old_string and new_string are identical in file: ${params.file_path}`,
151
+ type: ToolErrorType.EDIT_NO_CHANGE,
152
+ };
153
+ }
154
+ return error;
155
+ }
156
+ class EditToolInvocation {
157
+ config;
158
+ params;
159
+ constructor(config, params) {
160
+ this.config = config;
161
+ this.params = params;
162
+ }
163
+ toolLocations() {
164
+ return [{ path: this.params.file_path }];
165
+ }
166
+ async attemptSelfCorrection(params, currentContent, initialError, abortSignal, originalLineEnding) {
167
+ const fixedEdit = await FixLLMEditWithInstruction(params.instruction, params.old_string, params.new_string, initialError.raw, currentContent, this.config.getBaseLlmClient(), abortSignal);
168
+ if (fixedEdit.noChangesRequired) {
169
+ return {
170
+ currentContent,
171
+ newContent: currentContent,
172
+ occurrences: 0,
173
+ isNewFile: false,
174
+ error: {
175
+ display: `No changes required. The file already meets the specified conditions.`,
176
+ raw: `A secondary check determined that no changes were necessary to fulfill the instruction. Explanation: ${fixedEdit.explanation}. Original error with the parameters given: ${initialError.raw}`,
177
+ type: ToolErrorType.EDIT_NO_CHANGE,
178
+ },
179
+ originalLineEnding,
180
+ };
181
+ }
182
+ const secondAttemptResult = await calculateReplacement({
183
+ params: {
184
+ ...params,
185
+ old_string: fixedEdit.search,
186
+ new_string: fixedEdit.replace,
187
+ },
188
+ currentContent,
189
+ abortSignal,
190
+ });
191
+ const secondError = getErrorReplaceResult(params, secondAttemptResult.occurrences, 1, // expectedReplacements is always 1 for smart_edit
192
+ secondAttemptResult.finalOldString, secondAttemptResult.finalNewString);
193
+ if (secondError) {
194
+ // The fix failed, return the original error
195
+ return {
196
+ currentContent,
197
+ newContent: currentContent,
198
+ occurrences: 0,
199
+ isNewFile: false,
200
+ error: initialError,
201
+ originalLineEnding,
202
+ };
203
+ }
204
+ return {
205
+ currentContent,
206
+ newContent: secondAttemptResult.newContent,
207
+ occurrences: secondAttemptResult.occurrences,
208
+ isNewFile: false,
209
+ error: undefined,
210
+ originalLineEnding,
211
+ };
212
+ }
213
+ /**
214
+ * Calculates the potential outcome of an edit operation.
215
+ * @param params Parameters for the edit operation
216
+ * @returns An object describing the potential edit outcome
217
+ * @throws File system errors if reading the file fails unexpectedly (e.g., permissions)
218
+ */
219
+ async calculateEdit(params, abortSignal) {
220
+ const expectedReplacements = 1;
221
+ let currentContent = null;
222
+ let fileExists = false;
223
+ let originalLineEnding = '\n'; // Default for new files
224
+ try {
225
+ currentContent = await this.config
226
+ .getFileSystemService()
227
+ .readTextFile(params.file_path);
228
+ originalLineEnding = detectLineEnding(currentContent);
229
+ currentContent = currentContent.replace(/\r\n/g, '\n');
230
+ fileExists = true;
231
+ }
232
+ catch (err) {
233
+ if (!isNodeError(err) || err.code !== 'ENOENT') {
234
+ throw err;
235
+ }
236
+ fileExists = false;
237
+ }
238
+ const isNewFile = params.old_string === '' && !fileExists;
239
+ if (isNewFile) {
240
+ return {
241
+ currentContent,
242
+ newContent: params.new_string,
243
+ occurrences: 1,
244
+ isNewFile: true,
245
+ error: undefined,
246
+ originalLineEnding,
247
+ };
248
+ }
249
+ // after this point, it's not a new file/edit
250
+ if (!fileExists) {
251
+ return {
252
+ currentContent,
253
+ newContent: '',
254
+ occurrences: 0,
255
+ isNewFile: false,
256
+ error: {
257
+ display: `File not found. Cannot apply edit. Use an empty old_string to create a new file.`,
258
+ raw: `File not found: ${params.file_path}`,
259
+ type: ToolErrorType.FILE_NOT_FOUND,
260
+ },
261
+ originalLineEnding,
262
+ };
263
+ }
264
+ if (currentContent === null) {
265
+ return {
266
+ currentContent,
267
+ newContent: '',
268
+ occurrences: 0,
269
+ isNewFile: false,
270
+ error: {
271
+ display: `Failed to read content of file.`,
272
+ raw: `Failed to read content of existing file: ${params.file_path}`,
273
+ type: ToolErrorType.READ_CONTENT_FAILURE,
274
+ },
275
+ originalLineEnding,
276
+ };
277
+ }
278
+ if (params.old_string === '') {
279
+ return {
280
+ currentContent,
281
+ newContent: currentContent,
282
+ occurrences: 0,
283
+ isNewFile: false,
284
+ error: {
285
+ display: `Failed to edit. Attempted to create a file that already exists.`,
286
+ raw: `File already exists, cannot create: ${params.file_path}`,
287
+ type: ToolErrorType.ATTEMPT_TO_CREATE_EXISTING_FILE,
288
+ },
289
+ originalLineEnding,
290
+ };
291
+ }
292
+ const replacementResult = await calculateReplacement({
293
+ params,
294
+ currentContent,
295
+ abortSignal,
296
+ });
297
+ const initialError = getErrorReplaceResult(params, replacementResult.occurrences, expectedReplacements, replacementResult.finalOldString, replacementResult.finalNewString);
298
+ if (!initialError) {
299
+ return {
300
+ currentContent,
301
+ newContent: replacementResult.newContent,
302
+ occurrences: replacementResult.occurrences,
303
+ isNewFile: false,
304
+ error: undefined,
305
+ originalLineEnding,
306
+ };
307
+ }
308
+ // If there was an error, try to self-correct.
309
+ return this.attemptSelfCorrection(params, currentContent, initialError, abortSignal, originalLineEnding);
310
+ }
311
+ /**
312
+ * Handles the confirmation prompt for the Edit tool in the CLI.
313
+ * It needs to calculate the diff to show the user.
314
+ */
315
+ async shouldConfirmExecute(abortSignal) {
316
+ if (this.config.getApprovalMode() === ApprovalMode.AUTO_EDIT) {
317
+ return false;
318
+ }
319
+ let editData;
320
+ try {
321
+ editData = await this.calculateEdit(this.params, abortSignal);
322
+ }
323
+ catch (error) {
324
+ const errorMsg = error instanceof Error ? error.message : String(error);
325
+ console.log(`Error preparing edit: ${errorMsg}`);
326
+ return false;
327
+ }
328
+ if (editData.error) {
329
+ console.log(`Error: ${editData.error.display}`);
330
+ return false;
331
+ }
332
+ const fileName = path.basename(this.params.file_path);
333
+ const fileDiff = Diff.createPatch(fileName, editData.currentContent ?? '', editData.newContent, 'Current', 'Proposed', DEFAULT_DIFF_OPTIONS);
334
+ const ideClient = await IdeClient.getInstance();
335
+ const ideConfirmation = this.config.getIdeMode() && ideClient.isDiffingEnabled()
336
+ ? ideClient.openDiff(this.params.file_path, editData.newContent)
337
+ : undefined;
338
+ const confirmationDetails = {
339
+ type: 'edit',
340
+ title: `Confirm Edit: ${shortenPath(makeRelative(this.params.file_path, this.config.getTargetDir()))}`,
341
+ fileName,
342
+ filePath: this.params.file_path,
343
+ fileDiff,
344
+ originalContent: editData.currentContent,
345
+ newContent: editData.newContent,
346
+ onConfirm: async (outcome) => {
347
+ if (outcome === ToolConfirmationOutcome.ProceedAlways) {
348
+ this.config.setApprovalMode(ApprovalMode.AUTO_EDIT);
349
+ }
350
+ if (ideConfirmation) {
351
+ const result = await ideConfirmation;
352
+ if (result.status === 'accepted' && result.content) {
353
+ // TODO(chrstn): See https://github.com/google-gemini/gemini-cli/pull/5618#discussion_r2255413084
354
+ // for info on a possible race condition where the file is modified on disk while being edited.
355
+ this.params.old_string = editData.currentContent ?? '';
356
+ this.params.new_string = result.content;
357
+ }
358
+ }
359
+ },
360
+ ideConfirmation,
361
+ };
362
+ return confirmationDetails;
363
+ }
364
+ getDescription() {
365
+ const relativePath = makeRelative(this.params.file_path, this.config.getTargetDir());
366
+ if (this.params.old_string === '') {
367
+ return `Create ${shortenPath(relativePath)}`;
368
+ }
369
+ const oldStringSnippet = this.params.old_string.split('\n')[0].substring(0, 30) +
370
+ (this.params.old_string.length > 30 ? '...' : '');
371
+ const newStringSnippet = this.params.new_string.split('\n')[0].substring(0, 30) +
372
+ (this.params.new_string.length > 30 ? '...' : '');
373
+ if (this.params.old_string === this.params.new_string) {
374
+ return `No file changes to ${shortenPath(relativePath)}`;
375
+ }
376
+ return `${shortenPath(relativePath)}: ${oldStringSnippet} => ${newStringSnippet}`;
377
+ }
378
+ /**
379
+ * Executes the edit operation with the given parameters.
380
+ * @param params Parameters for the edit operation
381
+ * @returns Result of the edit operation
382
+ */
383
+ async execute(signal) {
384
+ let editData;
385
+ try {
386
+ editData = await this.calculateEdit(this.params, signal);
387
+ }
388
+ catch (error) {
389
+ const errorMsg = error instanceof Error ? error.message : String(error);
390
+ return {
391
+ llmContent: `Error preparing edit: ${errorMsg}`,
392
+ returnDisplay: `Error preparing edit: ${errorMsg}`,
393
+ error: {
394
+ message: errorMsg,
395
+ type: ToolErrorType.EDIT_PREPARATION_FAILURE,
396
+ },
397
+ };
398
+ }
399
+ if (editData.error) {
400
+ return {
401
+ llmContent: editData.error.raw,
402
+ returnDisplay: `Error: ${editData.error.display}`,
403
+ error: {
404
+ message: editData.error.raw,
405
+ type: editData.error.type,
406
+ },
407
+ };
408
+ }
409
+ try {
410
+ this.ensureParentDirectoriesExist(this.params.file_path);
411
+ let finalContent = editData.newContent;
412
+ // Restore original line endings if they were CRLF
413
+ if (!editData.isNewFile && editData.originalLineEnding === '\r\n') {
414
+ finalContent = finalContent.replace(/\n/g, '\r\n');
415
+ }
416
+ await this.config
417
+ .getFileSystemService()
418
+ .writeTextFile(this.params.file_path, finalContent);
419
+ let displayResult;
420
+ if (editData.isNewFile) {
421
+ displayResult = `Created ${shortenPath(makeRelative(this.params.file_path, this.config.getTargetDir()))}`;
422
+ }
423
+ else {
424
+ // Generate diff for display, even though core logic doesn't technically need it
425
+ // The CLI wrapper will use this part of the ToolResult
426
+ const fileName = path.basename(this.params.file_path);
427
+ const fileDiff = Diff.createPatch(fileName, editData.currentContent ?? '', // Should not be null here if not isNewFile
428
+ editData.newContent, 'Current', 'Proposed', DEFAULT_DIFF_OPTIONS);
429
+ const originallyProposedContent = this.params.ai_proposed_string || this.params.new_string;
430
+ const diffStat = getDiffStat(fileName, editData.currentContent ?? '', originallyProposedContent, this.params.new_string);
431
+ displayResult = {
432
+ fileDiff,
433
+ fileName,
434
+ originalContent: editData.currentContent,
435
+ newContent: editData.newContent,
436
+ diffStat,
437
+ };
438
+ }
439
+ const llmSuccessMessageParts = [
440
+ editData.isNewFile
441
+ ? `Created new file: ${this.params.file_path} with provided content.`
442
+ : `Successfully modified file: ${this.params.file_path} (${editData.occurrences} replacements).`,
443
+ ];
444
+ if (this.params.modified_by_user) {
445
+ llmSuccessMessageParts.push(`User modified the \`new_string\` content to be: ${this.params.new_string}.`);
446
+ }
447
+ return {
448
+ llmContent: llmSuccessMessageParts.join(' '),
449
+ returnDisplay: displayResult,
450
+ };
451
+ }
452
+ catch (error) {
453
+ const errorMsg = error instanceof Error ? error.message : String(error);
454
+ return {
455
+ llmContent: `Error executing edit: ${errorMsg}`,
456
+ returnDisplay: `Error writing file: ${errorMsg}`,
457
+ error: {
458
+ message: errorMsg,
459
+ type: ToolErrorType.FILE_WRITE_FAILURE,
460
+ },
461
+ };
462
+ }
463
+ }
464
+ /**
465
+ * Creates parent directories if they don't exist
466
+ */
467
+ ensureParentDirectoriesExist(filePath) {
468
+ const dirName = path.dirname(filePath);
469
+ if (!fs.existsSync(dirName)) {
470
+ fs.mkdirSync(dirName, { recursive: true });
471
+ }
472
+ }
473
+ }
474
+ /**
475
+ * Implementation of the Edit tool logic
476
+ */
477
+ export class SmartEditTool extends BaseDeclarativeTool {
478
+ config;
479
+ static Name = 'replace';
480
+ constructor(config) {
481
+ super(SmartEditTool.Name, 'Edit', `Replaces text within a file. Replaces a single occurrence. This tool requires providing significant context around the change to ensure precise targeting. Always use the ${ReadFileTool.Name} tool to examine the file's current content before attempting a text replacement.
482
+
483
+ The user has the ability to modify the \`new_string\` content. If modified, this will be stated in the response.
484
+
485
+ Expectation for required parameters:
486
+ 1. \`file_path\` MUST be an absolute path; otherwise an error will be thrown.
487
+ 2. \`old_string\` MUST be the exact literal text to replace (including all whitespace, indentation, newlines, and surrounding code etc.).
488
+ 3. \`new_string\` MUST be the exact literal text to replace \`old_string\` with (also including all whitespace, indentation, newlines, and surrounding code etc.). Ensure the resulting code is correct and idiomatic and that \`old_string\` and \`new_string\` are different.
489
+ 4. \`instruction\` is the detailed instruction of what needs to be changed. It is important to Make it specific and detailed so developers or large language models can understand what needs to be changed and perform the changes on their own if necessary.
490
+ 5. NEVER escape \`old_string\` or \`new_string\`, that would break the exact literal text requirement.
491
+ **Important:** If ANY of the above are not satisfied, the tool will fail. CRITICAL for \`old_string\`: Must uniquely identify the single instance to change. Include at least 3 lines of context BEFORE and AFTER the target text, matching whitespace and indentation precisely. If this string matches multiple locations, or does not match exactly, the tool will fail.
492
+ 6. Prefer to break down complex and long changes into multiple smaller atomic calls to this tool. Always check the content of the file after changes or not finding a string to match.
493
+ **Multiple replacements:** If there are multiple and ambiguous occurences of the \`old_string\` in the file, the tool will also fail.`, Kind.Edit, {
494
+ properties: {
495
+ file_path: {
496
+ description: "The absolute path to the file to modify. Must start with '/'.",
497
+ type: 'string',
498
+ },
499
+ instruction: {
500
+ description: `A clear, semantic instruction for the code change, acting as a high-quality prompt for an expert LLM assistant. It must be self-contained and explain the goal of the change.
501
+
502
+ A good instruction should concisely answer:
503
+ 1. WHY is the change needed? (e.g., "To fix a bug where users can be null...")
504
+ 2. WHERE should the change happen? (e.g., "...in the 'renderUserProfile' function...")
505
+ 3. WHAT is the high-level change? (e.g., "...add a null check for the 'user' object...")
506
+ 4. WHAT is the desired outcome? (e.g., "...so that it displays a loading spinner instead of crashing.")
507
+
508
+ **GOOD Example:** "In the 'calculateTotal' function, correct the sales tax calculation by updating the 'taxRate' constant from 0.05 to 0.075 to reflect the new regional tax laws."
509
+
510
+ **BAD Examples:**
511
+ - "Change the text." (Too vague)
512
+ - "Fix the bug." (Doesn't explain the bug or the fix)
513
+ - "Replace the line with this new line." (Brittle, just repeats the other parameters)
514
+ `,
515
+ type: 'string',
516
+ },
517
+ old_string: {
518
+ description: 'The exact literal text to replace, preferably unescaped. Include at least 3 lines of context BEFORE and AFTER the target text, matching whitespace and indentation precisely. If this string is not the exact literal text (i.e. you escaped it) or does not match exactly, the tool will fail.',
519
+ type: 'string',
520
+ },
521
+ new_string: {
522
+ description: 'The exact literal text to replace `old_string` with, preferably unescaped. Provide the EXACT text. Ensure the resulting code is correct and idiomatic.',
523
+ type: 'string',
524
+ },
525
+ },
526
+ required: ['file_path', 'instruction', 'old_string', 'new_string'],
527
+ type: 'object',
528
+ });
529
+ this.config = config;
530
+ }
531
+ /**
532
+ * Validates the parameters for the Edit tool
533
+ * @param params Parameters to validate
534
+ * @returns Error message string or null if valid
535
+ */
536
+ validateToolParamValues(params) {
537
+ if (!params.file_path) {
538
+ return "The 'file_path' parameter must be non-empty.";
539
+ }
540
+ if (!path.isAbsolute(params.file_path)) {
541
+ return `File path must be absolute: ${params.file_path}`;
542
+ }
543
+ const workspaceContext = this.config.getWorkspaceContext();
544
+ if (!workspaceContext.isPathWithinWorkspace(params.file_path)) {
545
+ const directories = workspaceContext.getDirectories();
546
+ return `File path must be within one of the workspace directories: ${directories.join(', ')}`;
547
+ }
548
+ return null;
549
+ }
550
+ createInvocation(params) {
551
+ return new EditToolInvocation(this.config, params);
552
+ }
553
+ getModifyContext(_) {
554
+ return {
555
+ getFilePath: (params) => params.file_path,
556
+ getCurrentContent: async (params) => {
557
+ try {
558
+ return this.config
559
+ .getFileSystemService()
560
+ .readTextFile(params.file_path);
561
+ }
562
+ catch (err) {
563
+ if (!isNodeError(err) || err.code !== 'ENOENT')
564
+ throw err;
565
+ return '';
566
+ }
567
+ },
568
+ getProposedContent: async (params) => {
569
+ try {
570
+ const currentContent = await this.config
571
+ .getFileSystemService()
572
+ .readTextFile(params.file_path);
573
+ return applyReplacement(currentContent, params.old_string, params.new_string, params.old_string === '' && currentContent === '');
574
+ }
575
+ catch (err) {
576
+ if (!isNodeError(err) || err.code !== 'ENOENT')
577
+ throw err;
578
+ return '';
579
+ }
580
+ },
581
+ createUpdatedParams: (oldContent, modifiedProposedContent, originalParams) => {
582
+ const content = originalParams.new_string;
583
+ return {
584
+ ...originalParams,
585
+ ai_proposed_string: content,
586
+ old_string: oldContent,
587
+ new_string: modifiedProposedContent,
588
+ modified_by_user: true,
589
+ };
590
+ },
591
+ };
592
+ }
593
+ }
594
+ //# sourceMappingURL=smart-edit.js.map