@astralyn/pi 1.0.5 → 1.1.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 (285) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/dist/bundle/chunks/{anthropic-messages-YIOJIMVL.js → anthropic-messages-3K3DWVY6.js} +1 -1
  3. package/dist/bundle/chunks/anthropic.js +1 -1
  4. package/dist/bundle/chunks/{azure-openai-responses-LDW4FGAQ.js → azure-openai-responses-CH74HGP4.js} +1 -1
  5. package/dist/bundle/chunks/bedrock-converse-stream.js +2 -2
  6. package/dist/bundle/chunks/{chunk-UCXU7KLO.js → chunk-3MMB5I7J.js} +1 -1
  7. package/dist/bundle/chunks/{chunk-GIG5IHES.js → chunk-5KJH3GGH.js} +1 -1
  8. package/dist/bundle/chunks/chunk-6VYRLG5F.js +2 -0
  9. package/dist/bundle/chunks/{chunk-HDHRZ7GT.js → chunk-C2ARIVS3.js} +1 -1
  10. package/dist/bundle/chunks/{chunk-IE3Z7ESB.js → chunk-D3XW5X3Z.js} +371 -246
  11. package/dist/bundle/chunks/{chunk-IFELM7MW.js → chunk-EJEOEBSJ.js} +1 -1
  12. package/dist/bundle/chunks/chunk-H35KWBT5.js +11 -0
  13. package/dist/bundle/chunks/chunk-KAC2YPTW.js +2 -0
  14. package/dist/bundle/chunks/chunk-MAC4N3BW.js +2 -0
  15. package/dist/bundle/chunks/chunk-QLU3LESZ.js +2 -0
  16. package/dist/bundle/chunks/{chunk-A6ALEEY5.js → chunk-TEAAYBTS.js} +1 -1
  17. package/dist/bundle/chunks/cloudflare-workers-ai-system-one-XO45ZT3V.js +2 -0
  18. package/dist/bundle/chunks/codemode-worker.js +2 -2
  19. package/dist/bundle/chunks/github-copilot.js +1 -1
  20. package/dist/bundle/chunks/{google-generative-ai-S5A5IBU4.js → google-generative-ai-VVO64ZRQ.js} +1 -1
  21. package/dist/bundle/chunks/{google-vertex-UBRQFDB4.js → google-vertex-LKS5NUHA.js} +1 -1
  22. package/dist/bundle/chunks/image-resize-worker.js +1 -1
  23. package/dist/bundle/chunks/{llama-cpp-classify-R7DQLTQN.js → llama-cpp-classify-H3IZDR5O.js} +2 -2
  24. package/dist/bundle/chunks/{mistral-conversations-A2N5FSOV.js → mistral-conversations-NJUOG4ET.js} +2 -2
  25. package/dist/bundle/chunks/openai-chatgpt.js +1 -1
  26. package/dist/bundle/chunks/{openai-codex-responses-Q6JHCWLE.js → openai-codex-responses-TZWBTTYM.js} +2 -2
  27. package/dist/bundle/chunks/openai-codex.js +1 -1
  28. package/dist/bundle/chunks/{openai-completions-H4VMO7AU.js → openai-completions-7HTTU6FH.js} +1 -1
  29. package/dist/bundle/chunks/openai-decisions-776H4VDZ.js +5 -0
  30. package/dist/bundle/chunks/{openai-responses-G4PGNM2H.js → openai-responses-7A6BAQ5X.js} +1 -1
  31. package/dist/bundle/chunks/{openrouter-images-YP3YVB54.js → openrouter-images-TDLJV5N4.js} +1 -1
  32. package/dist/bundle/chunks/{pi-messages-HZZ7GEEW.js → pi-messages-ZXNERWJG.js} +1 -1
  33. package/dist/bundle/chunks/typesafe-system-one-2DQLG4R2.js +2 -0
  34. package/dist/bundle/chunks/{virtual-modules-46MSJGKO.js → virtual-modules-2NHGQRS6.js} +1 -1
  35. package/dist/bundle/cli.js +1 -1
  36. package/dist/bundle/index.js +1 -1
  37. package/dist/bundle/rpc-entry.js +1 -1
  38. package/dist/cli/args.d.ts +1 -1
  39. package/dist/cli/args.d.ts.map +1 -1
  40. package/dist/cli/args.js +13 -1
  41. package/dist/cli/args.js.map +1 -1
  42. package/dist/core/agent-session.d.ts +12 -6
  43. package/dist/core/agent-session.d.ts.map +1 -1
  44. package/dist/core/agent-session.js +146 -174
  45. package/dist/core/agent-session.js.map +1 -1
  46. package/dist/core/bash-executor.d.ts.map +1 -1
  47. package/dist/core/bash-executor.js +40 -45
  48. package/dist/core/bash-executor.js.map +1 -1
  49. package/dist/core/context-snapshot.d.ts +4 -1
  50. package/dist/core/context-snapshot.d.ts.map +1 -1
  51. package/dist/core/context-snapshot.js +45 -0
  52. package/dist/core/context-snapshot.js.map +1 -1
  53. package/dist/core/export-html/tool-renderer.d.ts.map +1 -1
  54. package/dist/core/export-html/tool-renderer.js +2 -0
  55. package/dist/core/export-html/tool-renderer.js.map +1 -1
  56. package/dist/core/extensions/types.d.ts +11 -0
  57. package/dist/core/extensions/types.d.ts.map +1 -1
  58. package/dist/core/extensions/types.js.map +1 -1
  59. package/dist/core/model-runtime.d.ts.map +1 -1
  60. package/dist/core/model-runtime.js +2 -1
  61. package/dist/core/model-runtime.js.map +1 -1
  62. package/dist/core/nested-tool-calls.d.ts +1 -0
  63. package/dist/core/nested-tool-calls.d.ts.map +1 -1
  64. package/dist/core/nested-tool-calls.js +1 -0
  65. package/dist/core/nested-tool-calls.js.map +1 -1
  66. package/dist/core/sdk.d.ts +4 -0
  67. package/dist/core/sdk.d.ts.map +1 -1
  68. package/dist/core/sdk.js +17 -5
  69. package/dist/core/sdk.js.map +1 -1
  70. package/dist/core/settings-manager.d.ts +13 -0
  71. package/dist/core/settings-manager.d.ts.map +1 -1
  72. package/dist/core/settings-manager.js +37 -13
  73. package/dist/core/settings-manager.js.map +1 -1
  74. package/dist/core/tasks/session.d.ts +9 -0
  75. package/dist/core/tasks/session.d.ts.map +1 -1
  76. package/dist/core/tasks/session.js +21 -0
  77. package/dist/core/tasks/session.js.map +1 -1
  78. package/dist/core/tools/renderers/bash.d.ts.map +1 -1
  79. package/dist/core/tools/renderers/bash.js +8 -3
  80. package/dist/core/tools/renderers/bash.js.map +1 -1
  81. package/dist/core/tools/renderers/edit.js +1 -1
  82. package/dist/core/tools/renderers/edit.js.map +1 -1
  83. package/dist/extensions/codemode/execute.d.ts.map +1 -1
  84. package/dist/extensions/codemode/execute.js +63 -7
  85. package/dist/extensions/codemode/execute.js.map +1 -1
  86. package/dist/extensions/codemode/tool.js +2 -2
  87. package/dist/extensions/codemode/tool.js.map +1 -1
  88. package/dist/extensions/index.js +2 -2
  89. package/dist/extensions/index.js.map +1 -1
  90. package/dist/extensions/llama/provider.d.ts.map +1 -1
  91. package/dist/extensions/llama/provider.js +34 -11
  92. package/dist/extensions/llama/provider.js.map +1 -1
  93. package/dist/extensions/mcp/cli.js +17 -23
  94. package/dist/extensions/mcp/cli.js.map +1 -1
  95. package/dist/extensions/mcp/index.d.ts.map +1 -1
  96. package/dist/extensions/mcp/index.js +165 -64
  97. package/dist/extensions/mcp/index.js.map +1 -1
  98. package/dist/extensions/mcp/oauth.d.ts +5 -3
  99. package/dist/extensions/mcp/oauth.d.ts.map +1 -1
  100. package/dist/extensions/mcp/oauth.js +36 -10
  101. package/dist/extensions/mcp/oauth.js.map +1 -1
  102. package/dist/extensions/mcp/ui.d.ts +3 -3
  103. package/dist/extensions/mcp/ui.d.ts.map +1 -1
  104. package/dist/extensions/mcp/ui.js +10 -2
  105. package/dist/extensions/mcp/ui.js.map +1 -1
  106. package/dist/extensions/search/code-search.d.ts +29 -0
  107. package/dist/extensions/search/code-search.d.ts.map +1 -0
  108. package/dist/extensions/search/code-search.js +120 -0
  109. package/dist/extensions/search/code-search.js.map +1 -0
  110. package/dist/extensions/search/command.d.ts +5 -0
  111. package/dist/extensions/search/command.d.ts.map +1 -0
  112. package/dist/extensions/search/command.js +69 -0
  113. package/dist/extensions/search/command.js.map +1 -0
  114. package/dist/extensions/search/credential.d.ts +25 -0
  115. package/dist/extensions/search/credential.d.ts.map +1 -0
  116. package/dist/extensions/search/credential.js +117 -0
  117. package/dist/extensions/search/credential.js.map +1 -0
  118. package/dist/extensions/search/devin.d.ts +34 -0
  119. package/dist/extensions/search/devin.d.ts.map +1 -0
  120. package/dist/extensions/search/devin.js +210 -0
  121. package/dist/extensions/search/devin.js.map +1 -0
  122. package/dist/extensions/search/index.d.ts +5 -0
  123. package/dist/extensions/search/index.d.ts.map +1 -0
  124. package/dist/extensions/search/index.js +110 -0
  125. package/dist/extensions/search/index.js.map +1 -0
  126. package/dist/extensions/search/panel.d.ts +53 -0
  127. package/dist/extensions/search/panel.d.ts.map +1 -0
  128. package/dist/extensions/search/panel.js +157 -0
  129. package/dist/extensions/search/panel.js.map +1 -0
  130. package/dist/extensions/search/prompt.d.ts +14 -0
  131. package/dist/extensions/search/prompt.d.ts.map +1 -0
  132. package/dist/extensions/search/prompt.js +221 -0
  133. package/dist/extensions/search/prompt.js.map +1 -0
  134. package/dist/extensions/search/render.d.ts +28 -0
  135. package/dist/extensions/search/render.d.ts.map +1 -0
  136. package/dist/extensions/search/render.js +86 -0
  137. package/dist/extensions/search/render.js.map +1 -0
  138. package/dist/extensions/search/workspace.d.ts +38 -0
  139. package/dist/extensions/search/workspace.d.ts.map +1 -0
  140. package/dist/extensions/search/workspace.js +245 -0
  141. package/dist/extensions/search/workspace.js.map +1 -0
  142. package/dist/modes/interactive/components/bash-execution.d.ts +5 -1
  143. package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
  144. package/dist/modes/interactive/components/bash-execution.js +14 -14
  145. package/dist/modes/interactive/components/bash-execution.js.map +1 -1
  146. package/dist/modes/interactive/components/branch-summary-message.d.ts +2 -1
  147. package/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -1
  148. package/dist/modes/interactive/components/branch-summary-message.js +5 -2
  149. package/dist/modes/interactive/components/branch-summary-message.js.map +1 -1
  150. package/dist/modes/interactive/components/compaction-summary-message.d.ts +2 -1
  151. package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -1
  152. package/dist/modes/interactive/components/compaction-summary-message.js +5 -2
  153. package/dist/modes/interactive/components/compaction-summary-message.js.map +1 -1
  154. package/dist/modes/interactive/components/custom-entry.d.ts +3 -1
  155. package/dist/modes/interactive/components/custom-entry.d.ts.map +1 -1
  156. package/dist/modes/interactive/components/custom-entry.js +7 -2
  157. package/dist/modes/interactive/components/custom-entry.js.map +1 -1
  158. package/dist/modes/interactive/components/custom-message.d.ts.map +1 -1
  159. package/dist/modes/interactive/components/custom-message.js +1 -0
  160. package/dist/modes/interactive/components/custom-message.js.map +1 -1
  161. package/dist/modes/interactive/components/settings-selector.js +1 -1
  162. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  163. package/dist/modes/interactive/components/skill-invocation-message.d.ts +2 -1
  164. package/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -1
  165. package/dist/modes/interactive/components/skill-invocation-message.js +5 -2
  166. package/dist/modes/interactive/components/skill-invocation-message.js.map +1 -1
  167. package/dist/modes/interactive/interactive-mode.d.ts +2 -0
  168. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  169. package/dist/modes/interactive/interactive-mode.js +52 -59
  170. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  171. package/dist/modes/interactive/program-status-reporter.d.ts +34 -0
  172. package/dist/modes/interactive/program-status-reporter.d.ts.map +1 -0
  173. package/dist/modes/interactive/program-status-reporter.js +107 -0
  174. package/dist/modes/interactive/program-status-reporter.js.map +1 -0
  175. package/dist/modes/interactive/tasks/host-hooks.d.ts +14 -0
  176. package/dist/modes/interactive/tasks/host-hooks.d.ts.map +1 -0
  177. package/dist/modes/interactive/tasks/host-hooks.js +31 -0
  178. package/dist/modes/interactive/tasks/host-hooks.js.map +1 -0
  179. package/dist/modes/interactive/tool-view/tool-execution.d.ts +5 -0
  180. package/dist/modes/interactive/tool-view/tool-execution.d.ts.map +1 -1
  181. package/dist/modes/interactive/tool-view/tool-execution.js +7 -0
  182. package/dist/modes/interactive/tool-view/tool-execution.js.map +1 -1
  183. package/dist/package-manager-cli.d.ts.map +1 -1
  184. package/dist/package-manager-cli.js +24 -0
  185. package/dist/package-manager-cli.js.map +1 -1
  186. package/dist/utils/ansi.d.ts +8 -0
  187. package/dist/utils/ansi.d.ts.map +1 -1
  188. package/dist/utils/ansi.js +30 -11
  189. package/dist/utils/ansi.js.map +1 -1
  190. package/dist/utils/clipboard.d.ts.map +1 -1
  191. package/dist/utils/clipboard.js +12 -11
  192. package/dist/utils/clipboard.js.map +1 -1
  193. package/dist/utils/image-resize-core.d.ts +12 -0
  194. package/dist/utils/image-resize-core.d.ts.map +1 -1
  195. package/dist/utils/image-resize-core.js +5 -0
  196. package/dist/utils/image-resize-core.js.map +1 -1
  197. package/dist/utils/image-resize-worker.js +3 -2
  198. package/dist/utils/image-resize-worker.js.map +1 -1
  199. package/dist/utils/image-resize.d.ts.map +1 -1
  200. package/dist/utils/image-resize.js +9 -10
  201. package/dist/utils/image-resize.js.map +1 -1
  202. package/docs/bundled/README.md +1 -1
  203. package/docs/bundled/extensions/provider.md +1 -1
  204. package/docs/bundled/extensions/question.md +1 -1
  205. package/docs/bundled/extensions/search.md +50 -0
  206. package/docs/bundled/extensions/statusline.md +15 -41
  207. package/docs/codemode.md +23 -3
  208. package/docs/docs.json +0 -4
  209. package/docs/environment-variables.md +1 -0
  210. package/docs/extensions.md +5 -1
  211. package/docs/json.md +2 -2
  212. package/docs/llama-cpp.md +8 -1
  213. package/docs/models.md +4 -1
  214. package/docs/providers.md +17 -0
  215. package/docs/rpc.md +6 -3
  216. package/docs/session-format.md +1 -0
  217. package/docs/settings.md +3 -3
  218. package/docs/terminal-setup.md +14 -0
  219. package/docs/usage.md +5 -3
  220. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  221. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  222. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  223. package/examples/extensions/gondolin/package-lock.json +2 -2
  224. package/examples/extensions/gondolin/package.json +1 -1
  225. package/examples/extensions/sandbox/package-lock.json +2 -2
  226. package/examples/extensions/sandbox/package.json +1 -1
  227. package/examples/extensions/with-deps/package-lock.json +2 -2
  228. package/examples/extensions/with-deps/package.json +1 -1
  229. package/npm-shrinkwrap.json +54 -54
  230. package/package.json +13 -11
  231. package/dist/bundle/chunks/chunk-57XI7YJH.js +0 -2
  232. package/dist/bundle/chunks/chunk-JI4WGISU.js +0 -2
  233. package/dist/bundle/chunks/chunk-TCQ4HHQD.js +0 -2
  234. package/dist/bundle/chunks/chunk-TXJO4AJ7.js +0 -11
  235. package/dist/bundle/chunks/cloudflare-workers-ai-system-one-OWU5SM6J.js +0 -2
  236. package/dist/bundle/chunks/typesafe-system-one-KYKKNU3E.js +0 -2
  237. package/dist/extensions/web-search/auth.d.ts +0 -12
  238. package/dist/extensions/web-search/auth.d.ts.map +0 -1
  239. package/dist/extensions/web-search/auth.js +0 -90
  240. package/dist/extensions/web-search/auth.js.map +0 -1
  241. package/dist/extensions/web-search/constants.d.ts +0 -13
  242. package/dist/extensions/web-search/constants.d.ts.map +0 -1
  243. package/dist/extensions/web-search/constants.js +0 -26
  244. package/dist/extensions/web-search/constants.js.map +0 -1
  245. package/dist/extensions/web-search/execute.d.ts +0 -9
  246. package/dist/extensions/web-search/execute.d.ts.map +0 -1
  247. package/dist/extensions/web-search/execute.js +0 -96
  248. package/dist/extensions/web-search/execute.js.map +0 -1
  249. package/dist/extensions/web-search/format.d.ts +0 -21
  250. package/dist/extensions/web-search/format.d.ts.map +0 -1
  251. package/dist/extensions/web-search/format.js +0 -117
  252. package/dist/extensions/web-search/format.js.map +0 -1
  253. package/dist/extensions/web-search/index.d.ts +0 -6
  254. package/dist/extensions/web-search/index.d.ts.map +0 -1
  255. package/dist/extensions/web-search/index.js +0 -50
  256. package/dist/extensions/web-search/index.js.map +0 -1
  257. package/dist/extensions/web-search/providers/deepseek.d.ts +0 -13
  258. package/dist/extensions/web-search/providers/deepseek.d.ts.map +0 -1
  259. package/dist/extensions/web-search/providers/deepseek.js +0 -110
  260. package/dist/extensions/web-search/providers/deepseek.js.map +0 -1
  261. package/dist/extensions/web-search/providers/http.d.ts +0 -4
  262. package/dist/extensions/web-search/providers/http.d.ts.map +0 -1
  263. package/dist/extensions/web-search/providers/http.js +0 -42
  264. package/dist/extensions/web-search/providers/http.js.map +0 -1
  265. package/dist/extensions/web-search/providers/minimax.d.ts +0 -12
  266. package/dist/extensions/web-search/providers/minimax.d.ts.map +0 -1
  267. package/dist/extensions/web-search/providers/minimax.js +0 -61
  268. package/dist/extensions/web-search/providers/minimax.js.map +0 -1
  269. package/dist/extensions/web-search/render.d.ts +0 -15
  270. package/dist/extensions/web-search/render.d.ts.map +0 -1
  271. package/dist/extensions/web-search/render.js +0 -142
  272. package/dist/extensions/web-search/render.js.map +0 -1
  273. package/dist/extensions/web-search/results.d.ts +0 -35
  274. package/dist/extensions/web-search/results.d.ts.map +0 -1
  275. package/dist/extensions/web-search/results.js +0 -167
  276. package/dist/extensions/web-search/results.js.map +0 -1
  277. package/dist/extensions/web-search/schema.d.ts +0 -10
  278. package/dist/extensions/web-search/schema.d.ts.map +0 -1
  279. package/dist/extensions/web-search/schema.js +0 -36
  280. package/dist/extensions/web-search/schema.js.map +0 -1
  281. package/dist/extensions/web-search/types.d.ts +0 -51
  282. package/dist/extensions/web-search/types.d.ts.map +0 -1
  283. package/dist/extensions/web-search/types.js +0 -5
  284. package/dist/extensions/web-search/types.js.map +0 -1
  285. package/docs/bundled/extensions/web-search.md +0 -46
@@ -0,0 +1,110 @@
1
+ /**
2
+ * Devin Search: `code_search` and `web_search`, both authenticated by one Devin sign-in.
3
+ *
4
+ * Both tools stay registered so `/tools` and old transcripts know them; a session without a
5
+ * credential starts with them inactive, and signing in or out through `/search` toggles them.
6
+ */
7
+ import { resolve } from "node:path";
8
+ import { Type } from "typebox";
9
+ import { truncateHead } from "../../core/tools/truncate.js";
10
+ import { codeSearch, formatLocations } from "./code-search.js";
11
+ import { registerSearchCommand, syncTools } from "./command.js";
12
+ import { getCredential } from "./credential.js";
13
+ import { DevinAuthError, webSearch } from "./devin.js";
14
+ import { renderCodeSearchCall, renderCodeSearchResult, renderWebSearchCall, renderWebSearchResult, } from "./render.js";
15
+ import { isInside } from "./workspace.js";
16
+ const WEB_EXCERPT_MAX_CHARS = 1500;
17
+ const CodeSearchParams = Type.Object({
18
+ query: Type.String({
19
+ description: "Natural-language description of the behavior, flow, error, or concept to locate, in concise English. Keep identifiers, API names, and error text verbatim. Not for a bare symbol, filename, or literal.",
20
+ }),
21
+ path: Type.Optional(Type.String({
22
+ description: "Subdirectory to search, relative to or inside the working directory. Defaults to the working directory.",
23
+ })),
24
+ });
25
+ const WebSearchParams = Type.Object({
26
+ query: Type.String({
27
+ description: "Web search query. Be specific: product names, versions, exact error text. English matches best.",
28
+ }),
29
+ max_results: Type.Optional(Type.Integer({ minimum: 1, maximum: 10, description: "Results to return (default 5)." })),
30
+ });
31
+ const SIGN_IN_HINT = "Ask the user to run /search to sign in to Devin.";
32
+ /** Run a Devin call with the current credential; credential failures tell the model what fixes them. */
33
+ async function withDevin(run) {
34
+ const credential = getCredential();
35
+ if (!credential)
36
+ throw new Error(`Devin Search is not signed in. ${SIGN_IN_HINT}`);
37
+ try {
38
+ return await run(credential.apiKey);
39
+ }
40
+ catch (error) {
41
+ throw error instanceof DevinAuthError ? new Error(`${error.message} ${SIGN_IN_HINT}`) : error;
42
+ }
43
+ }
44
+ export function formatWebResults(query, results) {
45
+ if (!results.length)
46
+ return `No web results for "${query}".`;
47
+ const blocks = results.map((r, i) => {
48
+ const lines = [`${i + 1}. ${r.title ?? r.url}`, ` ${r.url}`];
49
+ if (r.summary) {
50
+ const excerpt = r.summary.replace(/\n\s*\n+/g, "\n").trim();
51
+ lines.push(excerpt.length > WEB_EXCERPT_MAX_CHARS ? `${excerpt.slice(0, WEB_EXCERPT_MAX_CHARS)}…` : excerpt);
52
+ }
53
+ return lines.join("\n");
54
+ });
55
+ return truncateHead(`Web results for "${query}":\n\n${blocks.join("\n\n")}`).content;
56
+ }
57
+ export default function search(pi) {
58
+ pi.registerTool({
59
+ name: "code_search",
60
+ label: "Code Search",
61
+ description: "Find where behavior lives in the local repository by describing it, powered by Devin's SWE-grep agent. " +
62
+ "Use it when the relevant files are unknown: exploration, tracing a flow or bug, planning a change in " +
63
+ "unfamiliar code. Returns candidate files with line ranges — a reading list to verify with read or grep. " +
64
+ "For known paths, exact symbols, or literal strings, use find, grep, or read directly.",
65
+ promptSnippet: "Locate unknown local code by describing behavior; verify results with read",
66
+ promptGuidelines: [
67
+ "Use `code_search` first when you do not know which files implement a behavior; use grep/find for exact names and literals.",
68
+ "Treat `code_search` results as a reading list: read the returned ranges before editing.",
69
+ ],
70
+ parameters: CodeSearchParams,
71
+ renderCall: renderCodeSearchCall,
72
+ renderResult: renderCodeSearchResult,
73
+ async execute(_toolCallId, params, signal, onUpdate, ctx) {
74
+ const root = resolve(ctx.cwd, params.path ?? ".");
75
+ if (!isInside(ctx.cwd, root))
76
+ throw new Error(`path must be inside the working directory (${ctx.cwd}).`);
77
+ const locations = await withDevin((apiKey) => codeSearch({
78
+ apiKey,
79
+ query: params.query.trim(),
80
+ root,
81
+ cwd: ctx.cwd,
82
+ signal,
83
+ onProgress: (text) => onUpdate?.({ content: [{ type: "text", text }], details: { locations: [] } }),
84
+ }));
85
+ return { content: [{ type: "text", text: formatLocations(locations) }], details: { locations } };
86
+ },
87
+ });
88
+ pi.registerTool({
89
+ name: "web_search",
90
+ label: "Web Search",
91
+ description: "Search the live web through Devin. Use it for facts outside the repository: library and API docs, error " +
92
+ "messages, release notes, current versions. Returns titles, URLs, and query-relevant page excerpts.",
93
+ promptSnippet: "Live web search: external docs, errors, releases, current facts",
94
+ promptGuidelines: ["Use `web_search` instead of guessing for version-, release-, or date-sensitive facts."],
95
+ parameters: WebSearchParams,
96
+ renderCall: renderWebSearchCall,
97
+ renderResult: renderWebSearchResult,
98
+ async execute(_toolCallId, params, signal) {
99
+ const query = params.query.trim();
100
+ const results = await withDevin((apiKey) => webSearch(apiKey, query, params.max_results ?? 5, signal));
101
+ return {
102
+ content: [{ type: "text", text: formatWebResults(query, results) }],
103
+ details: { results: results.map(({ url, title }) => ({ url, title })) },
104
+ };
105
+ },
106
+ });
107
+ registerSearchCommand(pi);
108
+ pi.on("session_start", async () => syncTools(pi));
109
+ }
110
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/extensions/search/index.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Devin Search: `code_search` and `web_search`, both authenticated by one Devin sign-in.\n *\n * Both tools stay registered so `/tools` and old transcripts know them; a session without a\n * credential starts with them inactive, and signing in or out through `/search` toggles them.\n */\nimport { resolve } from \"node:path\";\nimport { type Static, Type } from \"typebox\";\nimport type { ExtensionAPI } from \"../../core/extensions/types.ts\";\nimport { truncateHead } from \"../../core/tools/truncate.ts\";\nimport { codeSearch, formatLocations } from \"./code-search.ts\";\nimport { registerSearchCommand, syncTools } from \"./command.ts\";\nimport { getCredential } from \"./credential.ts\";\nimport { DevinAuthError, type WebResult, webSearch } from \"./devin.ts\";\nimport {\n\ttype CodeSearchDetails,\n\trenderCodeSearchCall,\n\trenderCodeSearchResult,\n\trenderWebSearchCall,\n\trenderWebSearchResult,\n\ttype WebSearchDetails,\n} from \"./render.ts\";\nimport { isInside } from \"./workspace.ts\";\n\nconst WEB_EXCERPT_MAX_CHARS = 1500;\n\nconst CodeSearchParams = Type.Object({\n\tquery: Type.String({\n\t\tdescription:\n\t\t\t\"Natural-language description of the behavior, flow, error, or concept to locate, in concise English. Keep identifiers, API names, and error text verbatim. Not for a bare symbol, filename, or literal.\",\n\t}),\n\tpath: Type.Optional(\n\t\tType.String({\n\t\t\tdescription:\n\t\t\t\t\"Subdirectory to search, relative to or inside the working directory. Defaults to the working directory.\",\n\t\t}),\n\t),\n});\n\nconst WebSearchParams = Type.Object({\n\tquery: Type.String({\n\t\tdescription: \"Web search query. Be specific: product names, versions, exact error text. English matches best.\",\n\t}),\n\tmax_results: Type.Optional(Type.Integer({ minimum: 1, maximum: 10, description: \"Results to return (default 5).\" })),\n});\n\nconst SIGN_IN_HINT = \"Ask the user to run /search to sign in to Devin.\";\n\n/** Run a Devin call with the current credential; credential failures tell the model what fixes them. */\nasync function withDevin<T>(run: (apiKey: string) => Promise<T>): Promise<T> {\n\tconst credential = getCredential();\n\tif (!credential) throw new Error(`Devin Search is not signed in. ${SIGN_IN_HINT}`);\n\ttry {\n\t\treturn await run(credential.apiKey);\n\t} catch (error) {\n\t\tthrow error instanceof DevinAuthError ? new Error(`${error.message} ${SIGN_IN_HINT}`) : error;\n\t}\n}\n\nexport function formatWebResults(query: string, results: WebResult[]): string {\n\tif (!results.length) return `No web results for \"${query}\".`;\n\tconst blocks = results.map((r, i) => {\n\t\tconst lines = [`${i + 1}. ${r.title ?? r.url}`, ` ${r.url}`];\n\t\tif (r.summary) {\n\t\t\tconst excerpt = r.summary.replace(/\\n\\s*\\n+/g, \"\\n\").trim();\n\t\t\tlines.push(excerpt.length > WEB_EXCERPT_MAX_CHARS ? `${excerpt.slice(0, WEB_EXCERPT_MAX_CHARS)}…` : excerpt);\n\t\t}\n\t\treturn lines.join(\"\\n\");\n\t});\n\treturn truncateHead(`Web results for \"${query}\":\\n\\n${blocks.join(\"\\n\\n\")}`).content;\n}\n\nexport default function search(pi: ExtensionAPI): void {\n\tpi.registerTool<typeof CodeSearchParams, CodeSearchDetails>({\n\t\tname: \"code_search\",\n\t\tlabel: \"Code Search\",\n\t\tdescription:\n\t\t\t\"Find where behavior lives in the local repository by describing it, powered by Devin's SWE-grep agent. \" +\n\t\t\t\"Use it when the relevant files are unknown: exploration, tracing a flow or bug, planning a change in \" +\n\t\t\t\"unfamiliar code. Returns candidate files with line ranges — a reading list to verify with read or grep. \" +\n\t\t\t\"For known paths, exact symbols, or literal strings, use find, grep, or read directly.\",\n\t\tpromptSnippet: \"Locate unknown local code by describing behavior; verify results with read\",\n\t\tpromptGuidelines: [\n\t\t\t\"Use `code_search` first when you do not know which files implement a behavior; use grep/find for exact names and literals.\",\n\t\t\t\"Treat `code_search` results as a reading list: read the returned ranges before editing.\",\n\t\t],\n\t\tparameters: CodeSearchParams,\n\t\trenderCall: renderCodeSearchCall,\n\t\trenderResult: renderCodeSearchResult,\n\n\t\tasync execute(_toolCallId, params: Static<typeof CodeSearchParams>, signal, onUpdate, ctx) {\n\t\t\tconst root = resolve(ctx.cwd, params.path ?? \".\");\n\t\t\tif (!isInside(ctx.cwd, root)) throw new Error(`path must be inside the working directory (${ctx.cwd}).`);\n\t\t\tconst locations = await withDevin((apiKey) =>\n\t\t\t\tcodeSearch({\n\t\t\t\t\tapiKey,\n\t\t\t\t\tquery: params.query.trim(),\n\t\t\t\t\troot,\n\t\t\t\t\tcwd: ctx.cwd,\n\t\t\t\t\tsignal,\n\t\t\t\t\tonProgress: (text) => onUpdate?.({ content: [{ type: \"text\", text }], details: { locations: [] } }),\n\t\t\t\t}),\n\t\t\t);\n\t\t\treturn { content: [{ type: \"text\", text: formatLocations(locations) }], details: { locations } };\n\t\t},\n\t});\n\n\tpi.registerTool<typeof WebSearchParams, WebSearchDetails>({\n\t\tname: \"web_search\",\n\t\tlabel: \"Web Search\",\n\t\tdescription:\n\t\t\t\"Search the live web through Devin. Use it for facts outside the repository: library and API docs, error \" +\n\t\t\t\"messages, release notes, current versions. Returns titles, URLs, and query-relevant page excerpts.\",\n\t\tpromptSnippet: \"Live web search: external docs, errors, releases, current facts\",\n\t\tpromptGuidelines: [\"Use `web_search` instead of guessing for version-, release-, or date-sensitive facts.\"],\n\t\tparameters: WebSearchParams,\n\t\trenderCall: renderWebSearchCall,\n\t\trenderResult: renderWebSearchResult,\n\n\t\tasync execute(_toolCallId, params: Static<typeof WebSearchParams>, signal) {\n\t\t\tconst query = params.query.trim();\n\t\t\tconst results = await withDevin((apiKey) => webSearch(apiKey, query, params.max_results ?? 5, signal));\n\t\t\treturn {\n\t\t\t\tcontent: [{ type: \"text\", text: formatWebResults(query, results) }],\n\t\t\t\tdetails: { results: results.map(({ url, title }) => ({ url, title })) },\n\t\t\t};\n\t\t},\n\t});\n\n\tregisterSearchCommand(pi);\n\tpi.on(\"session_start\", async () => syncTools(pi));\n}\n"]}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * The `/search` panel: one framed view whose screens replace each other in place — the account
3
+ * menu, browser sign-in, token paste, sign-out confirmation, and a working state between them.
4
+ */
5
+ import { type Component, type Focusable, type TUI } from "@earendil-works/pi-tui";
6
+ import type { KeybindingsManager } from "../../core/keybindings.ts";
7
+ import { type Theme } from "../../modes/interactive/theme/theme.ts";
8
+ export interface AccountState {
9
+ /** Where the credential comes from, or undefined when signed out. */
10
+ source?: "saved" | "env";
11
+ /** Where a saved credential lives, or the environment variable that supplies one. */
12
+ location: string;
13
+ }
14
+ export interface PanelActions {
15
+ state(): AccountState;
16
+ /** A sign-in URL and a function that turns the pasted code or token into a verified, saved sign-in. */
17
+ startSignIn(): {
18
+ url: string;
19
+ complete(input: string): Promise<void>;
20
+ };
21
+ /** Verify and save a pasted token. */
22
+ saveToken(input: string): Promise<void>;
23
+ signOut(): void;
24
+ }
25
+ export declare class SearchPanel implements Component, Focusable {
26
+ private content;
27
+ private inputHandler;
28
+ private input;
29
+ private notice;
30
+ private _focused;
31
+ private readonly tui;
32
+ private readonly theme;
33
+ private readonly keybindings;
34
+ private readonly actions;
35
+ private readonly close;
36
+ constructor(tui: TUI, theme: Theme, keybindings: KeybindingsManager, actions: PanelActions, close: () => void);
37
+ get focused(): boolean;
38
+ set focused(value: boolean);
39
+ private show;
40
+ private muted;
41
+ private showMenu;
42
+ /** A screen with one text field; Enter submits a non-empty value, Esc returns to the menu. */
43
+ private showInputScreen;
44
+ private showBrowserSignIn;
45
+ private showPaste;
46
+ private showSignOut;
47
+ /** Show a working state while `task` runs, then return to the menu with its outcome. */
48
+ private run;
49
+ handleInput(data: string): void;
50
+ render(width: number): string[];
51
+ invalidate(): void;
52
+ }
53
+ //# sourceMappingURL=panel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"panel.d.ts","sourceRoot":"","sources":["../../../src/extensions/search/panel.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EACN,KAAK,SAAS,EAEd,KAAK,SAAS,EAMd,KAAK,GAAG,EAGR,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAIpE,OAAO,EAAsB,KAAK,KAAK,EAAE,MAAM,wCAAwC,CAAC;AAExF,MAAM,WAAW,YAAY;IAC5B,qEAAqE;IACrE,MAAM,CAAC,EAAE,OAAO,GAAG,KAAK,CAAC;IACzB,qFAAqF;IACrF,QAAQ,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,YAAY;IAC5B,KAAK,IAAI,YAAY,CAAC;IACtB,uGAAuG;IACvG,WAAW,IAAI;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;KAAE,CAAC;IACvE,sCAAsC;IACtC,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxC,OAAO,IAAI,IAAI,CAAC;CAChB;AAID,qBAAa,WAAY,YAAW,SAAS,EAAE,SAAS;IACvD,OAAO,CAAC,OAAO,CAAmB;IAClC,OAAO,CAAC,YAAY,CAAuC;IAC3D,OAAO,CAAC,KAAK,CAAoB;IACjC,OAAO,CAAC,MAAM,CAAqB;IACnC,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAM;IAC1B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAQ;IAC9B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAqB;IACjD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAe;IACvC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAa;gBAEvB,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,kBAAkB,EAAE,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,IAAI;IAS7G,IAAI,OAAO,IAAI,OAAO,CAErB;IAED,IAAI,OAAO,CAAC,KAAK,EAAE,OAAO,EAGzB;IAED,OAAO,CAAC,IAAI;IAuBZ,OAAO,CAAC,KAAK;IAIb,OAAO,CAAC,QAAQ;IAsChB,8FAA8F;IAC9F,OAAO,CAAC,eAAe;IA0BvB,OAAO,CAAC,iBAAiB;IAgBzB,OAAO,CAAC,SAAS;IAIjB,OAAO,CAAC,WAAW;IAuBnB,wFAAwF;YAC1E,GAAG;IAejB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAK/B,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE;IAM/B,UAAU,IAAI,IAAI;CAGlB"}
@@ -0,0 +1,157 @@
1
+ /**
2
+ * The `/search` panel: one framed view whose screens replace each other in place — the account
3
+ * menu, browser sign-in, token paste, sign-out confirmation, and a working state between them.
4
+ */
5
+ import { Container, Input, SelectList, Spacer, Text, truncateToWidth, visibleWidth, } from "@earendil-works/pi-tui";
6
+ import { AuthUrlComponent } from "../../modes/interactive/components/auth-url.js";
7
+ import { DynamicBorder } from "../../modes/interactive/components/dynamic-border.js";
8
+ import { keyHint } from "../../modes/interactive/components/keybinding-hints.js";
9
+ import { getSelectListTheme } from "../../modes/interactive/theme/theme.js";
10
+ export class SearchPanel {
11
+ constructor(tui, theme, keybindings, actions, close) {
12
+ this.content = new Container();
13
+ this._focused = false;
14
+ this.tui = tui;
15
+ this.theme = theme;
16
+ this.keybindings = keybindings;
17
+ this.actions = actions;
18
+ this.close = close;
19
+ this.showMenu();
20
+ }
21
+ get focused() {
22
+ return this._focused;
23
+ }
24
+ set focused(value) {
25
+ this._focused = value;
26
+ if (this.input)
27
+ this.input.focused = value;
28
+ }
29
+ show(title, body, hints, handler, input) {
30
+ const accent = (text) => this.theme.fg("accent", text);
31
+ const frame = new Container();
32
+ frame.addChild(new DynamicBorder(accent));
33
+ frame.addChild(new Text(accent(this.theme.bold(title)), 1, 0));
34
+ for (const child of body)
35
+ frame.addChild(child);
36
+ frame.addChild(new Spacer(1));
37
+ frame.addChild(new Text(hints.join(this.theme.fg("dim", " • ")), 1, 0));
38
+ frame.addChild(new DynamicBorder(accent));
39
+ if (this.input)
40
+ this.input.focused = false;
41
+ this.content = frame;
42
+ this.inputHandler = handler;
43
+ this.input = input;
44
+ if (input)
45
+ input.focused = this._focused;
46
+ this.tui.requestRender();
47
+ }
48
+ muted(text) {
49
+ return new Text(this.theme.fg("muted", text), 1, 0);
50
+ }
51
+ showMenu() {
52
+ const { source, location } = this.actions.state();
53
+ const status = source
54
+ ? `${this.theme.fg("success", "●")} Signed in${source === "env" ? " from the environment" : ""} ${this.theme.fg("dim", `· ${location}`)}`
55
+ : `${this.theme.fg("dim", "○")} Not signed in ${this.theme.fg("dim", "· code_search and web_search are off")}`;
56
+ const body = [new Text(status, 1, 0)];
57
+ if (this.notice) {
58
+ body.push(new Text(this.theme.fg(this.notice.kind, this.notice.text), 1, 0));
59
+ this.notice = undefined;
60
+ }
61
+ body.push(new Spacer(1));
62
+ const items = [
63
+ {
64
+ value: "browser",
65
+ label: source ? "Switch account" : "Sign in with browser",
66
+ description: "Authorize on devin.ai, then paste the code",
67
+ },
68
+ { value: "paste", label: "Paste a token", description: "Use an existing Devin session token" },
69
+ ];
70
+ if (source === "saved")
71
+ items.push({ value: "signout", label: "Sign out", description: "Forget the saved token" });
72
+ const list = new SelectList(items, items.length, getSelectListTheme());
73
+ list.onSelect = (item) => {
74
+ if (item.value === "browser")
75
+ this.showBrowserSignIn();
76
+ else if (item.value === "paste")
77
+ this.showPaste();
78
+ else
79
+ this.showSignOut();
80
+ };
81
+ list.onCancel = () => this.close();
82
+ body.push(list);
83
+ this.show("Devin Search", body, [keyHint("tui.select.confirm", "select"), keyHint("tui.select.cancel", "close")], (data) => list.handleInput(data));
84
+ }
85
+ /** A screen with one text field; Enter submits a non-empty value, Esc returns to the menu. */
86
+ showInputScreen(title, intro, label, submit, extraKey) {
87
+ const input = new Input();
88
+ this.show(title, [...intro, new Spacer(1), this.muted(label), input], [keyHint("tui.select.confirm", "submit"), keyHint("tui.select.cancel", "back")], (data) => {
89
+ if (this.keybindings.matches(data, "tui.select.confirm")) {
90
+ const value = input.getValue().trim();
91
+ if (value)
92
+ void this.run(() => submit(value));
93
+ }
94
+ else if (this.keybindings.matches(data, "tui.select.cancel")) {
95
+ this.showMenu();
96
+ }
97
+ else if (!extraKey?.(data)) {
98
+ input.handleInput(data);
99
+ }
100
+ }, input);
101
+ }
102
+ showBrowserSignIn() {
103
+ const attempt = this.actions.startSignIn();
104
+ const link = new AuthUrlComponent(this.tui, attempt.url);
105
+ this.showInputScreen("Sign in to Devin", [new Spacer(1), this.muted("Approve access in your browser. If it did not open, visit:"), link], "Paste the code Devin shows:", (value) => attempt.complete(value), (data) => {
106
+ if (!this.keybindings.matches(data, "app.message.copy"))
107
+ return false;
108
+ void link.copy();
109
+ return true;
110
+ });
111
+ }
112
+ showPaste() {
113
+ this.showInputScreen("Paste a token", [], "Devin session token:", (value) => this.actions.saveToken(value));
114
+ }
115
+ showSignOut() {
116
+ const { location } = this.actions.state();
117
+ const items = [
118
+ { value: "signout", label: "Sign out", description: `Remove the token from ${location}` },
119
+ { value: "back", label: "Cancel" },
120
+ ];
121
+ const list = new SelectList(items, items.length, getSelectListTheme());
122
+ list.onSelect = (item) => {
123
+ if (item.value === "signout") {
124
+ this.actions.signOut();
125
+ this.notice = { kind: "success", text: "Signed out." };
126
+ }
127
+ this.showMenu();
128
+ };
129
+ list.onCancel = () => this.showMenu();
130
+ this.show("Sign out of Devin?", [new Spacer(1), list], [keyHint("tui.select.confirm", "select"), keyHint("tui.select.cancel", "back")], (data) => list.handleInput(data));
131
+ }
132
+ /** Show a working state while `task` runs, then return to the menu with its outcome. */
133
+ async run(task) {
134
+ this.show("Devin Search", [new Spacer(1), this.muted("Verifying with Devin…")], [this.theme.fg("dim", "please wait")]);
135
+ try {
136
+ await task();
137
+ this.notice = { kind: "success", text: "Signed in — code_search and web_search are on." };
138
+ }
139
+ catch (error) {
140
+ this.notice = { kind: "error", text: error instanceof Error ? error.message : String(error) };
141
+ }
142
+ this.showMenu();
143
+ }
144
+ handleInput(data) {
145
+ this.inputHandler?.(data);
146
+ this.tui.requestRender();
147
+ }
148
+ render(width) {
149
+ return this.content
150
+ .render(width)
151
+ .map((line) => (visibleWidth(line) > width ? truncateToWidth(line, width, "") : line));
152
+ }
153
+ invalidate() {
154
+ this.content.invalidate();
155
+ }
156
+ }
157
+ //# sourceMappingURL=panel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"panel.js","sourceRoot":"","sources":["../../../src/extensions/search/panel.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The `/search` panel: one framed view whose screens replace each other in place — the account\n * menu, browser sign-in, token paste, sign-out confirmation, and a working state between them.\n */\nimport {\n\ttype Component,\n\tContainer,\n\ttype Focusable,\n\tInput,\n\ttype SelectItem,\n\tSelectList,\n\tSpacer,\n\tText,\n\ttype TUI,\n\ttruncateToWidth,\n\tvisibleWidth,\n} from \"@earendil-works/pi-tui\";\nimport type { KeybindingsManager } from \"../../core/keybindings.ts\";\nimport { AuthUrlComponent } from \"../../modes/interactive/components/auth-url.ts\";\nimport { DynamicBorder } from \"../../modes/interactive/components/dynamic-border.ts\";\nimport { keyHint } from \"../../modes/interactive/components/keybinding-hints.ts\";\nimport { getSelectListTheme, type Theme } from \"../../modes/interactive/theme/theme.ts\";\n\nexport interface AccountState {\n\t/** Where the credential comes from, or undefined when signed out. */\n\tsource?: \"saved\" | \"env\";\n\t/** Where a saved credential lives, or the environment variable that supplies one. */\n\tlocation: string;\n}\n\nexport interface PanelActions {\n\tstate(): AccountState;\n\t/** A sign-in URL and a function that turns the pasted code or token into a verified, saved sign-in. */\n\tstartSignIn(): { url: string; complete(input: string): Promise<void> };\n\t/** Verify and save a pasted token. */\n\tsaveToken(input: string): Promise<void>;\n\tsignOut(): void;\n}\n\ntype Notice = { kind: \"success\" | \"error\"; text: string };\n\nexport class SearchPanel implements Component, Focusable {\n\tprivate content = new Container();\n\tprivate inputHandler: ((data: string) => void) | undefined;\n\tprivate input: Input | undefined;\n\tprivate notice: Notice | undefined;\n\tprivate _focused = false;\n\tprivate readonly tui: TUI;\n\tprivate readonly theme: Theme;\n\tprivate readonly keybindings: KeybindingsManager;\n\tprivate readonly actions: PanelActions;\n\tprivate readonly close: () => void;\n\n\tconstructor(tui: TUI, theme: Theme, keybindings: KeybindingsManager, actions: PanelActions, close: () => void) {\n\t\tthis.tui = tui;\n\t\tthis.theme = theme;\n\t\tthis.keybindings = keybindings;\n\t\tthis.actions = actions;\n\t\tthis.close = close;\n\t\tthis.showMenu();\n\t}\n\n\tget focused(): boolean {\n\t\treturn this._focused;\n\t}\n\n\tset focused(value: boolean) {\n\t\tthis._focused = value;\n\t\tif (this.input) this.input.focused = value;\n\t}\n\n\tprivate show(\n\t\ttitle: string,\n\t\tbody: Component[],\n\t\thints: string[],\n\t\thandler?: (data: string) => void,\n\t\tinput?: Input,\n\t): void {\n\t\tconst accent = (text: string) => this.theme.fg(\"accent\", text);\n\t\tconst frame = new Container();\n\t\tframe.addChild(new DynamicBorder(accent));\n\t\tframe.addChild(new Text(accent(this.theme.bold(title)), 1, 0));\n\t\tfor (const child of body) frame.addChild(child);\n\t\tframe.addChild(new Spacer(1));\n\t\tframe.addChild(new Text(hints.join(this.theme.fg(\"dim\", \" • \")), 1, 0));\n\t\tframe.addChild(new DynamicBorder(accent));\n\t\tif (this.input) this.input.focused = false;\n\t\tthis.content = frame;\n\t\tthis.inputHandler = handler;\n\t\tthis.input = input;\n\t\tif (input) input.focused = this._focused;\n\t\tthis.tui.requestRender();\n\t}\n\n\tprivate muted(text: string): Text {\n\t\treturn new Text(this.theme.fg(\"muted\", text), 1, 0);\n\t}\n\n\tprivate showMenu(): void {\n\t\tconst { source, location } = this.actions.state();\n\t\tconst status = source\n\t\t\t? `${this.theme.fg(\"success\", \"●\")} Signed in${source === \"env\" ? \" from the environment\" : \"\"} ${this.theme.fg(\"dim\", `· ${location}`)}`\n\t\t\t: `${this.theme.fg(\"dim\", \"○\")} Not signed in ${this.theme.fg(\"dim\", \"· code_search and web_search are off\")}`;\n\t\tconst body: Component[] = [new Text(status, 1, 0)];\n\t\tif (this.notice) {\n\t\t\tbody.push(new Text(this.theme.fg(this.notice.kind, this.notice.text), 1, 0));\n\t\t\tthis.notice = undefined;\n\t\t}\n\t\tbody.push(new Spacer(1));\n\n\t\tconst items: SelectItem[] = [\n\t\t\t{\n\t\t\t\tvalue: \"browser\",\n\t\t\t\tlabel: source ? \"Switch account\" : \"Sign in with browser\",\n\t\t\t\tdescription: \"Authorize on devin.ai, then paste the code\",\n\t\t\t},\n\t\t\t{ value: \"paste\", label: \"Paste a token\", description: \"Use an existing Devin session token\" },\n\t\t];\n\t\tif (source === \"saved\")\n\t\t\titems.push({ value: \"signout\", label: \"Sign out\", description: \"Forget the saved token\" });\n\t\tconst list = new SelectList(items, items.length, getSelectListTheme());\n\t\tlist.onSelect = (item) => {\n\t\t\tif (item.value === \"browser\") this.showBrowserSignIn();\n\t\t\telse if (item.value === \"paste\") this.showPaste();\n\t\t\telse this.showSignOut();\n\t\t};\n\t\tlist.onCancel = () => this.close();\n\t\tbody.push(list);\n\t\tthis.show(\n\t\t\t\"Devin Search\",\n\t\t\tbody,\n\t\t\t[keyHint(\"tui.select.confirm\", \"select\"), keyHint(\"tui.select.cancel\", \"close\")],\n\t\t\t(data) => list.handleInput(data),\n\t\t);\n\t}\n\n\t/** A screen with one text field; Enter submits a non-empty value, Esc returns to the menu. */\n\tprivate showInputScreen(\n\t\ttitle: string,\n\t\tintro: Component[],\n\t\tlabel: string,\n\t\tsubmit: (value: string) => Promise<void>,\n\t\textraKey?: (data: string) => boolean,\n\t): void {\n\t\tconst input = new Input();\n\t\tthis.show(\n\t\t\ttitle,\n\t\t\t[...intro, new Spacer(1), this.muted(label), input],\n\t\t\t[keyHint(\"tui.select.confirm\", \"submit\"), keyHint(\"tui.select.cancel\", \"back\")],\n\t\t\t(data) => {\n\t\t\t\tif (this.keybindings.matches(data, \"tui.select.confirm\")) {\n\t\t\t\t\tconst value = input.getValue().trim();\n\t\t\t\t\tif (value) void this.run(() => submit(value));\n\t\t\t\t} else if (this.keybindings.matches(data, \"tui.select.cancel\")) {\n\t\t\t\t\tthis.showMenu();\n\t\t\t\t} else if (!extraKey?.(data)) {\n\t\t\t\t\tinput.handleInput(data);\n\t\t\t\t}\n\t\t\t},\n\t\t\tinput,\n\t\t);\n\t}\n\n\tprivate showBrowserSignIn(): void {\n\t\tconst attempt = this.actions.startSignIn();\n\t\tconst link = new AuthUrlComponent(this.tui, attempt.url);\n\t\tthis.showInputScreen(\n\t\t\t\"Sign in to Devin\",\n\t\t\t[new Spacer(1), this.muted(\"Approve access in your browser. If it did not open, visit:\"), link],\n\t\t\t\"Paste the code Devin shows:\",\n\t\t\t(value) => attempt.complete(value),\n\t\t\t(data) => {\n\t\t\t\tif (!this.keybindings.matches(data, \"app.message.copy\")) return false;\n\t\t\t\tvoid link.copy();\n\t\t\t\treturn true;\n\t\t\t},\n\t\t);\n\t}\n\n\tprivate showPaste(): void {\n\t\tthis.showInputScreen(\"Paste a token\", [], \"Devin session token:\", (value) => this.actions.saveToken(value));\n\t}\n\n\tprivate showSignOut(): void {\n\t\tconst { location } = this.actions.state();\n\t\tconst items: SelectItem[] = [\n\t\t\t{ value: \"signout\", label: \"Sign out\", description: `Remove the token from ${location}` },\n\t\t\t{ value: \"back\", label: \"Cancel\" },\n\t\t];\n\t\tconst list = new SelectList(items, items.length, getSelectListTheme());\n\t\tlist.onSelect = (item) => {\n\t\t\tif (item.value === \"signout\") {\n\t\t\t\tthis.actions.signOut();\n\t\t\t\tthis.notice = { kind: \"success\", text: \"Signed out.\" };\n\t\t\t}\n\t\t\tthis.showMenu();\n\t\t};\n\t\tlist.onCancel = () => this.showMenu();\n\t\tthis.show(\n\t\t\t\"Sign out of Devin?\",\n\t\t\t[new Spacer(1), list],\n\t\t\t[keyHint(\"tui.select.confirm\", \"select\"), keyHint(\"tui.select.cancel\", \"back\")],\n\t\t\t(data) => list.handleInput(data),\n\t\t);\n\t}\n\n\t/** Show a working state while `task` runs, then return to the menu with its outcome. */\n\tprivate async run(task: () => Promise<void>): Promise<void> {\n\t\tthis.show(\n\t\t\t\"Devin Search\",\n\t\t\t[new Spacer(1), this.muted(\"Verifying with Devin…\")],\n\t\t\t[this.theme.fg(\"dim\", \"please wait\")],\n\t\t);\n\t\ttry {\n\t\t\tawait task();\n\t\t\tthis.notice = { kind: \"success\", text: \"Signed in — code_search and web_search are on.\" };\n\t\t} catch (error) {\n\t\t\tthis.notice = { kind: \"error\", text: error instanceof Error ? error.message : String(error) };\n\t\t}\n\t\tthis.showMenu();\n\t}\n\n\thandleInput(data: string): void {\n\t\tthis.inputHandler?.(data);\n\t\tthis.tui.requestRender();\n\t}\n\n\trender(width: number): string[] {\n\t\treturn this.content\n\t\t\t.render(width)\n\t\t\t.map((line) => (visibleWidth(line) > width ? truncateToWidth(line, width, \"\") : line));\n\t}\n\n\tinvalidate(): void {\n\t\tthis.content.invalidate();\n\t}\n}\n"]}
@@ -0,0 +1,14 @@
1
+ /** Planning rounds before the final answer is forced, commands per round, and files per answer. */
2
+ export declare const MAX_TURNS = 3;
3
+ export declare const MAX_COMMANDS = 8;
4
+ export declare const MAX_RESULTS = 10;
5
+ /**
6
+ * The SWE-grep planner prompt, kept close to the Windsurf original the backend model was tuned on.
7
+ * The budgets it states are the ones code-search.ts enforces.
8
+ */
9
+ export declare const SYSTEM_PROMPT = "You are an expert software engineer, responsible for providing context to another engineer to solve a code issue in the current codebase. The user will present you with a description of the issue, and it is your job to provide a series of file paths with associated line ranges that contain ALL the information relevant to understand and correctly address the issue.\n\n# IMPORTANT:\n- A relevant file does not mean only the files that must be modified to solve the task. It means any file that contains information relevant to planning and implementing the fix, such as the definitions of classes and functions that are relevant to the pieces of code that will have to be modified.\n- You should include enough context around the relevant lines to allow the engineer to understand the task correctly. You must include ENTIRE semantic blocks (functions, classes, definitions, etc). For example:\nIf addressing the issue requires modifying a method within a class, then you should include the entire class definition, not just the lines around the method we want to modify.\n- NEVER truncate these blocks unless they are very large (hundreds of lines or more, in which case providing only a relevant portion of the block is acceptable).\n- Your job is to essentially alleviate the job of the other engineer by giving them a clean starting context from which to start working. More precisely, you should minimize the number of files the engineer has to read to understand and solve the task correctly (while not providing irrelevant code snippets).\n\n# ENVIRONMENT\n- Working directory: /codebase. Make sure to run commands in this directory, not `.\n- Tool access: use the restricted_exec tool ONLY\n- Allowed sub-commands (schema-enforced):\n - rg: Search for patterns in files using ripgrep\n - Required: pattern (string), path (string)\n - Optional: include (array of globs), exclude (array of globs)\n - readfile: Read contents of a file with optional line range\n - Required: file (string)\n - Optional: start_line (int), end_line (int) \u2014 1-indexed, inclusive\n - tree: Display directory structure as a tree\n - Required: path (string)\n - Optional: levels (int)\n\n# THINKING RULES\n- Think step-by-step. Plan, reason, and reflect before each tool call.\n- Use tool calls liberally and purposefully to ground every conclusion in real code, not assumptions.\n- If a command fails, rethink and try something different; do not complain to the user.\n\n# FAST-SEARCH DEFAULTS (optimize rg/tree on large repos)\n- Start NARROW, then widen only if needed. Prefer searching likely code roots first (e.g., `src/`, `lib/`, `app/`, `packages/`, `services/`) instead of `/codebase`.\n- Prefer fixed-string search for literals: escape patterns or keep regex simple. Use smart case; avoid case-insensitive unless necessary.\n- Prefer file-type filters and globs (in include) over full-repo scans.\n- Default EXCLUDES for speed (apply via the exclude array): node_modules, .git, dist, build, coverage, .venv, venv, target, out, .cache, __pycache__, vendor, deps, third_party, logs, data, *.min.*\n- Skip huge files where possible; when opening files, prefer reading only relevant ranges with readfile.\n- Limit directory traversal with tree levels to quickly orient before deeper inspection.\n\n# SOME EXAMPLES OF WORKFLOWS\n- MAP \u2013 Use `tree` with small levels; `rg` on likely roots to grasp structure and hotspots.\n- ANCHOR \u2013 `rg` for problem keywords and anchor symbols; restrict by language globs via include.\n- TRACE \u2013 Follow imports with targeted `rg` in narrowed roots; open files with `readfile` scoped to entire semantic blocks.\n- VERIFY \u2013 Confirm each candidate path exists by reading or additional searches; drop false positives (tests, vendored, generated) unless they must change.\n\n# TOOL USE GUIDELINES\n- You must use a SINGLE restricted_exec call in your answer, that lets you execute at most 8 commands in a single turn. Each command must be an object with a `type` field of `rg`, `readfile`, or `tree` and the appropriate fields for that type.\n- Example restricted_exec usage:\n[TOOL_CALLS]restricted_exec[ARGS]{\n \"command1\": {\n \"type\": \"rg\",\n \"pattern\": \"Controller\",\n \"path\": \"/codebase/slime\",\n \"include\": [\"**/*.py\"],\n \"exclude\": [\"**/node_modules/**\", \"**/.git/**\", \"**/dist/**\", \"**/build/**\", \"**/.venv/**\", \"**/__pycache__/**\"]\n },\n \"command2\": {\n \"type\": \"readfile\",\n \"file\": \"/codebase/slime/train.py\",\n \"start_line\": 1,\n \"end_line\": 200\n },\n \"command3\": {\n \"type\": \"tree\",\n \"path\": \"/codebase/slime/\",\n \"levels\": 2\n }\n}\n- You have at most 3 turns to interact with the environment by calling tools, so issuing multiple commands at once is necessary and encouraged to speed up your research.\n- Each command result may be truncated to 40 lines; prefer multiple targeted reads/searches to build complete context.\n- DO NOT EVER USE MORE THAN 8 commands in a single turn, or you will be penalized.\n\n# ANSWER FORMAT (strict format, including tags)\n- You will output an XML structure with a root element \"ANSWER\" containing \"file\" elements. Each \"file\" element will have a \"path\" attribute and contain \"range\" elements.\n- You will output this as your final response.\n- The line ranges must be inclusive.\n\nOutput example inside the \"answer\" tool argument:\n<ANSWER>\n <file path=\"/codebase/info_theory/formulas/entropy.py\">\n <range>10-60</range>\n <range>150-210</range>\n </file>\n <file path=\"/codebase/info_theory/data_structures/bits.py\">\n <range>1-40</range>\n <range>110-170</range>\n </file>\n</ANSWER>\n\nRemember: Prefer narrow, fixed-string, and type-filtered searches with aggressive excludes and size/depth limits. Widen scope only as needed. Use the restricted tools available to you, and output your answer in exactly the specified format.\n\n# NO RESULTS POLICY\nIf after thorough searching you are confident that NO relevant files exist for the given query (e.g., the function/class/concept does not exist in the codebase), you MUST return an empty ANSWER:\n<ANSWER></ANSWER>\nDo NOT return irrelevant files (such as entry points or config files) just to provide some output. An empty answer is always better than a misleading one.\n\n# RESULT COUNT\nAim to return at most 10 files in your answer. Focus on the most relevant files first. If fewer files are relevant, return fewer.\n";
10
+ export declare const FORCE_ANSWER = "You have no turns left. Now you MUST provide your final ANSWER, even if it's not complete.";
11
+ export declare const RETRY_TOOL_CALL = "Your last reply did not contain a valid tool call. Reply with exactly one restricted_exec or answer call.";
12
+ /** The planner's two tools, in the OpenAI function format the backend expects. */
13
+ export declare const TOOL_DEFINITIONS: string;
14
+ //# sourceMappingURL=prompt.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt.d.ts","sourceRoot":"","sources":["../../../src/extensions/search/prompt.ts"],"names":[],"mappings":"AAAA,mGAAmG;AACnG,eAAO,MAAM,SAAS,IAAI,CAAC;AAC3B,eAAO,MAAM,YAAY,IAAI,CAAC;AAC9B,eAAO,MAAM,WAAW,KAAK,CAAC;AAE9B;;;GAGG;AACH,eAAO,MAAM,aAAa,k4MAiJzB,CAAC;AAEF,eAAO,MAAM,YAAY,+FACoE,CAAC;AAE9F,eAAO,MAAM,eAAe,8GACgF,CAAC;AAwC7G,kFAAkF;AAClF,eAAO,MAAM,gBAAgB,QA2B3B,CAAC"}
@@ -0,0 +1,221 @@
1
+ /** Planning rounds before the final answer is forced, commands per round, and files per answer. */
2
+ export const MAX_TURNS = 3;
3
+ export const MAX_COMMANDS = 8;
4
+ export const MAX_RESULTS = 10;
5
+ /**
6
+ * The SWE-grep planner prompt, kept close to the Windsurf original the backend model was tuned on.
7
+ * The budgets it states are the ones code-search.ts enforces.
8
+ */
9
+ export const SYSTEM_PROMPT = `You are an expert software engineer, responsible for providing context \
10
+ to another engineer to solve a code issue in the current codebase. \
11
+ The user will present you with a description of the issue, and it is \
12
+ your job to provide a series of file paths with associated line ranges \
13
+ that contain ALL the information relevant to understand and correctly \
14
+ address the issue.
15
+
16
+ # IMPORTANT:
17
+ - A relevant file does not mean only the files that must be modified to \
18
+ solve the task. It means any file that contains information relevant to \
19
+ planning and implementing the fix, such as the definitions of classes \
20
+ and functions that are relevant to the pieces of code that will have to \
21
+ be modified.
22
+ - You should include enough context around the relevant lines to allow \
23
+ the engineer to understand the task correctly. You must include ENTIRE \
24
+ semantic blocks (functions, classes, definitions, etc). For example:
25
+ If addressing the issue requires modifying a method within a class, then \
26
+ you should include the entire class definition, not just the lines around \
27
+ the method we want to modify.
28
+ - NEVER truncate these blocks unless they are very large (hundreds of \
29
+ lines or more, in which case providing only a relevant portion of the \
30
+ block is acceptable).
31
+ - Your job is to essentially alleviate the job of the other engineer by \
32
+ giving them a clean starting context from which to start working. More \
33
+ precisely, you should minimize the number of files the engineer has to \
34
+ read to understand and solve the task correctly (while not providing \
35
+ irrelevant code snippets).
36
+
37
+ # ENVIRONMENT
38
+ - Working directory: /codebase. Make sure to run commands in this \
39
+ directory, not \`.
40
+ - Tool access: use the restricted_exec tool ONLY
41
+ - Allowed sub-commands (schema-enforced):
42
+ - rg: Search for patterns in files using ripgrep
43
+ - Required: pattern (string), path (string)
44
+ - Optional: include (array of globs), exclude (array of globs)
45
+ - readfile: Read contents of a file with optional line range
46
+ - Required: file (string)
47
+ - Optional: start_line (int), end_line (int) — 1-indexed, inclusive
48
+ - tree: Display directory structure as a tree
49
+ - Required: path (string)
50
+ - Optional: levels (int)
51
+
52
+ # THINKING RULES
53
+ - Think step-by-step. Plan, reason, and reflect before each tool call.
54
+ - Use tool calls liberally and purposefully to ground every conclusion \
55
+ in real code, not assumptions.
56
+ - If a command fails, rethink and try something different; do not \
57
+ complain to the user.
58
+
59
+ # FAST-SEARCH DEFAULTS (optimize rg/tree on large repos)
60
+ - Start NARROW, then widen only if needed. Prefer searching likely code \
61
+ roots first (e.g., \`src/\`, \`lib/\`, \`app/\`, \`packages/\`, \`services/\`) \
62
+ instead of \`/codebase\`.
63
+ - Prefer fixed-string search for literals: escape patterns or keep regex \
64
+ simple. Use smart case; avoid case-insensitive unless necessary.
65
+ - Prefer file-type filters and globs (in include) over full-repo scans.
66
+ - Default EXCLUDES for speed (apply via the exclude array): \
67
+ node_modules, .git, dist, build, coverage, .venv, venv, target, out, \
68
+ .cache, __pycache__, vendor, deps, third_party, logs, data, *.min.*
69
+ - Skip huge files where possible; when opening files, prefer reading \
70
+ only relevant ranges with readfile.
71
+ - Limit directory traversal with tree levels to quickly orient before \
72
+ deeper inspection.
73
+
74
+ # SOME EXAMPLES OF WORKFLOWS
75
+ - MAP – Use \`tree\` with small levels; \`rg\` on likely roots to grasp \
76
+ structure and hotspots.
77
+ - ANCHOR – \`rg\` for problem keywords and anchor symbols; restrict by \
78
+ language globs via include.
79
+ - TRACE – Follow imports with targeted \`rg\` in narrowed roots; open \
80
+ files with \`readfile\` scoped to entire semantic blocks.
81
+ - VERIFY – Confirm each candidate path exists by reading or additional \
82
+ searches; drop false positives (tests, vendored, generated) unless they \
83
+ must change.
84
+
85
+ # TOOL USE GUIDELINES
86
+ - You must use a SINGLE restricted_exec call in your answer, that lets \
87
+ you execute at most ${MAX_COMMANDS} commands in a single turn. Each command must be \
88
+ an object with a \`type\` field of \`rg\`, \`readfile\`, or \`tree\` and the appropriate fields for that type.
89
+ - Example restricted_exec usage:
90
+ [TOOL_CALLS]restricted_exec[ARGS]{
91
+ "command1": {
92
+ "type": "rg",
93
+ "pattern": "Controller",
94
+ "path": "/codebase/slime",
95
+ "include": ["**/*.py"],
96
+ "exclude": ["**/node_modules/**", "**/.git/**", "**/dist/**", \
97
+ "**/build/**", "**/.venv/**", "**/__pycache__/**"]
98
+ },
99
+ "command2": {
100
+ "type": "readfile",
101
+ "file": "/codebase/slime/train.py",
102
+ "start_line": 1,
103
+ "end_line": 200
104
+ },
105
+ "command3": {
106
+ "type": "tree",
107
+ "path": "/codebase/slime/",
108
+ "levels": 2
109
+ }
110
+ }
111
+ - You have at most ${MAX_TURNS} turns to interact with the environment by calling \
112
+ tools, so issuing multiple commands at once is necessary and encouraged \
113
+ to speed up your research.
114
+ - Each command result may be truncated to 40 lines; prefer multiple \
115
+ targeted reads/searches to build complete context.
116
+ - DO NOT EVER USE MORE THAN ${MAX_COMMANDS} commands in a single turn, or you will \
117
+ be penalized.
118
+
119
+ # ANSWER FORMAT (strict format, including tags)
120
+ - You will output an XML structure with a root element "ANSWER" \
121
+ containing "file" elements. Each "file" element will have a "path" \
122
+ attribute and contain "range" elements.
123
+ - You will output this as your final response.
124
+ - The line ranges must be inclusive.
125
+
126
+ Output example inside the "answer" tool argument:
127
+ <ANSWER>
128
+ <file path="/codebase/info_theory/formulas/entropy.py">
129
+ <range>10-60</range>
130
+ <range>150-210</range>
131
+ </file>
132
+ <file path="/codebase/info_theory/data_structures/bits.py">
133
+ <range>1-40</range>
134
+ <range>110-170</range>
135
+ </file>
136
+ </ANSWER>
137
+
138
+ Remember: Prefer narrow, fixed-string, and type-filtered searches with \
139
+ aggressive excludes and size/depth limits. Widen scope only as needed. \
140
+ Use the restricted tools available to you, and output your answer in \
141
+ exactly the specified format.
142
+
143
+ # NO RESULTS POLICY
144
+ If after thorough searching you are confident that NO relevant files exist \
145
+ for the given query (e.g., the function/class/concept does not exist in the \
146
+ codebase), you MUST return an empty ANSWER:
147
+ <ANSWER></ANSWER>
148
+ Do NOT return irrelevant files (such as entry points or config files) just \
149
+ to provide some output. An empty answer is always better than a misleading one.
150
+
151
+ # RESULT COUNT
152
+ Aim to return at most ${MAX_RESULTS} files in your answer. Focus on the most \
153
+ relevant files first. If fewer files are relevant, return fewer.
154
+ `;
155
+ export const FORCE_ANSWER = "You have no turns left. Now you MUST provide your final ANSWER, even if it's not complete.";
156
+ export const RETRY_TOOL_CALL = "Your last reply did not contain a valid tool call. Reply with exactly one restricted_exec or answer call.";
157
+ const COMMAND_SCHEMA = {
158
+ type: "object",
159
+ description: "One command. Must be one of: rg, readfile, or tree.",
160
+ oneOf: [
161
+ {
162
+ properties: {
163
+ type: { type: "string", const: "rg", description: "Search for patterns in files using ripgrep." },
164
+ pattern: { type: "string", description: "The regex pattern to search for." },
165
+ path: { type: "string", description: "The path to search in." },
166
+ include: { type: "array", items: { type: "string" }, description: "File patterns to include." },
167
+ exclude: { type: "array", items: { type: "string" }, description: "File patterns to exclude." },
168
+ },
169
+ required: ["type", "pattern", "path"],
170
+ },
171
+ {
172
+ properties: {
173
+ type: {
174
+ type: "string",
175
+ const: "readfile",
176
+ description: "Read contents of a file with optional line range.",
177
+ },
178
+ file: { type: "string", description: "Path to the file to read." },
179
+ start_line: { type: "integer", description: "Starting line number (1-indexed)." },
180
+ end_line: { type: "integer", description: "Ending line number (1-indexed)." },
181
+ },
182
+ required: ["type", "file"],
183
+ },
184
+ {
185
+ properties: {
186
+ type: { type: "string", const: "tree", description: "Display directory structure as a tree." },
187
+ path: { type: "string", description: "Path to the directory." },
188
+ levels: { type: "integer", description: "Number of directory levels." },
189
+ },
190
+ required: ["type", "path"],
191
+ },
192
+ ],
193
+ };
194
+ /** The planner's two tools, in the OpenAI function format the backend expects. */
195
+ export const TOOL_DEFINITIONS = JSON.stringify([
196
+ {
197
+ type: "function",
198
+ function: {
199
+ name: "restricted_exec",
200
+ description: "Execute restricted commands (rg, readfile, tree) in parallel.",
201
+ parameters: {
202
+ type: "object",
203
+ properties: Object.fromEntries(Array.from({ length: MAX_COMMANDS }, (_, i) => [`command${i + 1}`, COMMAND_SCHEMA])),
204
+ required: ["command1"],
205
+ },
206
+ },
207
+ },
208
+ {
209
+ type: "function",
210
+ function: {
211
+ name: "answer",
212
+ description: "Final answer with relevant files and line ranges.",
213
+ parameters: {
214
+ type: "object",
215
+ properties: { answer: { type: "string", description: "The final answer in XML format." } },
216
+ required: ["answer"],
217
+ },
218
+ },
219
+ },
220
+ ]);
221
+ //# sourceMappingURL=prompt.js.map