@asterxsk/kiln 0.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 (263) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +170 -0
  3. package/agent/AGENTS.md +67 -0
  4. package/agent/README.md +5 -0
  5. package/agent/extensions/AGENTS.md +68 -0
  6. package/agent/extensions/ask-user/index.ts +418 -0
  7. package/agent/extensions/ask-user/install.ps1 +23 -0
  8. package/agent/extensions/ask-user/install.sh +21 -0
  9. package/agent/extensions/ask-user/package-lock.json +769 -0
  10. package/agent/extensions/ask-user/package.json +19 -0
  11. package/agent/extensions/ask-user/prompt.ts +45 -0
  12. package/agent/extensions/ask-user/tsconfig.json +7 -0
  13. package/agent/extensions/background-terminals/docs/implementation-guide.md +942 -0
  14. package/agent/extensions/background-terminals/index.ts +627 -0
  15. package/agent/extensions/background-terminals/install.ps1 +23 -0
  16. package/agent/extensions/background-terminals/install.sh +21 -0
  17. package/agent/extensions/background-terminals/manager.test.ts +735 -0
  18. package/agent/extensions/background-terminals/output.test.ts +109 -0
  19. package/agent/extensions/background-terminals/package-lock.json +769 -0
  20. package/agent/extensions/background-terminals/package.json +17 -0
  21. package/agent/extensions/background-terminals/prompt.test.ts +125 -0
  22. package/agent/extensions/background-terminals/ps.test.ts +82 -0
  23. package/agent/extensions/background-terminals/result-delivery.test.ts +44 -0
  24. package/agent/extensions/background-terminals/src/domain.ts +87 -0
  25. package/agent/extensions/background-terminals/src/manager.ts +907 -0
  26. package/agent/extensions/background-terminals/src/output.ts +84 -0
  27. package/agent/extensions/background-terminals/src/prompt.ts +142 -0
  28. package/agent/extensions/background-terminals/src/result-delivery.ts +27 -0
  29. package/agent/extensions/background-terminals/src/runtime.ts +36 -0
  30. package/agent/extensions/background-terminals/src/ui/output-view.ts +79 -0
  31. package/agent/extensions/background-terminals/src/ui/ps.ts +621 -0
  32. package/agent/extensions/background-terminals/tsconfig.json +7 -0
  33. package/agent/extensions/destructive/README.md +31 -0
  34. package/agent/extensions/destructive/index.ts +88 -0
  35. package/agent/extensions/destructive/install.ps1 +23 -0
  36. package/agent/extensions/destructive/install.sh +21 -0
  37. package/agent/extensions/file-search/index.spec.ts +443 -0
  38. package/agent/extensions/file-search/index.ts +459 -0
  39. package/agent/extensions/file-search/install.ps1 +23 -0
  40. package/agent/extensions/file-search/install.sh +21 -0
  41. package/agent/extensions/file-search/package-lock.json +2253 -0
  42. package/agent/extensions/file-search/package.json +23 -0
  43. package/agent/extensions/file-search/src/args.ts +122 -0
  44. package/agent/extensions/file-search/src/binaries.ts +422 -0
  45. package/agent/extensions/file-search/src/output.ts +126 -0
  46. package/agent/extensions/file-search/src/process.ts +146 -0
  47. package/agent/extensions/file-search/src/prompt.ts +52 -0
  48. package/agent/extensions/file-search/tsconfig.json +7 -0
  49. package/agent/extensions/goal/README.md +50 -0
  50. package/agent/extensions/goal/index.ts +155 -0
  51. package/agent/extensions/goal/install.ps1 +23 -0
  52. package/agent/extensions/goal/install.sh +21 -0
  53. package/agent/extensions/modelconf/PLAN.md +915 -0
  54. package/agent/extensions/modelconf/README.md +66 -0
  55. package/agent/extensions/modelconf/index.ts +296 -0
  56. package/agent/extensions/modelconf/install.ps1 +23 -0
  57. package/agent/extensions/modelconf/install.sh +21 -0
  58. package/agent/extensions/modelconf/package-lock.json +1809 -0
  59. package/agent/extensions/modelconf/package.json +13 -0
  60. package/agent/extensions/modelconf/src/fuzzy.ts +26 -0
  61. package/agent/extensions/modelconf/src/glob.ts +13 -0
  62. package/agent/extensions/modelconf/src/persistence.test.ts +46 -0
  63. package/agent/extensions/modelconf/src/persistence.ts +164 -0
  64. package/agent/extensions/modelconf/src/ui/ModelConfView.test.ts +75 -0
  65. package/agent/extensions/modelconf/src/ui/ModelConfView.ts +1101 -0
  66. package/agent/extensions/modelconf/tsconfig.json +15 -0
  67. package/agent/extensions/pi-web-access/CHANGELOG.md +690 -0
  68. package/agent/extensions/pi-web-access/LICENSE +21 -0
  69. package/agent/extensions/pi-web-access/README.md +470 -0
  70. package/agent/extensions/pi-web-access/SECURITY.md +5 -0
  71. package/agent/extensions/pi-web-access/activity.ts +101 -0
  72. package/agent/extensions/pi-web-access/auth-fetch.ts +148 -0
  73. package/agent/extensions/pi-web-access/banner.png +0 -0
  74. package/agent/extensions/pi-web-access/brightdata-unlocker.ts +272 -0
  75. package/agent/extensions/pi-web-access/chrome-cookies.ts +669 -0
  76. package/agent/extensions/pi-web-access/content-find.ts +139 -0
  77. package/agent/extensions/pi-web-access/credential-source.ts +191 -0
  78. package/agent/extensions/pi-web-access/data-uri-sanitize.ts +406 -0
  79. package/agent/extensions/pi-web-access/datalab-pdf-extract.ts +568 -0
  80. package/agent/extensions/pi-web-access/declared-web-links.ts +173 -0
  81. package/agent/extensions/pi-web-access/evidence/CONTRACT-EVIDENCE.md +496 -0
  82. package/agent/extensions/pi-web-access/evidence/contract-probe.mjs +140 -0
  83. package/agent/extensions/pi-web-access/exa.ts +526 -0
  84. package/agent/extensions/pi-web-access/extract.ts +1196 -0
  85. package/agent/extensions/pi-web-access/feature-config.ts +29 -0
  86. package/agent/extensions/pi-web-access/fetch-params.ts +111 -0
  87. package/agent/extensions/pi-web-access/gemini-adc.ts +298 -0
  88. package/agent/extensions/pi-web-access/gemini-api.ts +353 -0
  89. package/agent/extensions/pi-web-access/gemini-pdf-extract.ts +108 -0
  90. package/agent/extensions/pi-web-access/gemini-search.ts +21 -0
  91. package/agent/extensions/pi-web-access/gemini-url-context.ts +128 -0
  92. package/agent/extensions/pi-web-access/gemini-web-config.ts +101 -0
  93. package/agent/extensions/pi-web-access/gemini-web.ts +487 -0
  94. package/agent/extensions/pi-web-access/github-api.ts +197 -0
  95. package/agent/extensions/pi-web-access/github-extract.ts +746 -0
  96. package/agent/extensions/pi-web-access/github-issue-pr.ts +700 -0
  97. package/agent/extensions/pi-web-access/index.ts +1737 -0
  98. package/agent/extensions/pi-web-access/package-lock.json +5808 -0
  99. package/agent/extensions/pi-web-access/package.json +64 -0
  100. package/agent/extensions/pi-web-access/page-query.ts +96 -0
  101. package/agent/extensions/pi-web-access/pdf-extract.ts +409 -0
  102. package/agent/extensions/pi-web-access/pi-web-fetch-demo.mp4 +0 -0
  103. package/agent/extensions/pi-web-access/promise-try.d.ts +7 -0
  104. package/agent/extensions/pi-web-access/query-rewrite.ts +51 -0
  105. package/agent/extensions/pi-web-access/render-search-error.ts +170 -0
  106. package/agent/extensions/pi-web-access/rsc-extract.ts +338 -0
  107. package/agent/extensions/pi-web-access/search-types.ts +20 -0
  108. package/agent/extensions/pi-web-access/source-check.ts +282 -0
  109. package/agent/extensions/pi-web-access/ssrf-protection.ts +526 -0
  110. package/agent/extensions/pi-web-access/storage.ts +521 -0
  111. package/agent/extensions/pi-web-access/summary-model-scope.ts +125 -0
  112. package/agent/extensions/pi-web-access/test/auth-fetch.test.mjs +208 -0
  113. package/agent/extensions/pi-web-access/test/brightdata-unlocker.test.mjs +840 -0
  114. package/agent/extensions/pi-web-access/test/chrome-cookie-extraction.test.mjs +441 -0
  115. package/agent/extensions/pi-web-access/test/config-path.test.mjs +283 -0
  116. package/agent/extensions/pi-web-access/test/content-find.test.mjs +25 -0
  117. package/agent/extensions/pi-web-access/test/credential-source.test.mjs +118 -0
  118. package/agent/extensions/pi-web-access/test/data-uri-sanitize.test.mjs +210 -0
  119. package/agent/extensions/pi-web-access/test/datalab-pdf-extract.test.mjs +552 -0
  120. package/agent/extensions/pi-web-access/test/declared-web-links.test.mjs +212 -0
  121. package/agent/extensions/pi-web-access/test/fetch-answer-storage.test.mjs +40 -0
  122. package/agent/extensions/pi-web-access/test/fetch-cache-storage.test.mjs +334 -0
  123. package/agent/extensions/pi-web-access/test/fetch-content-domain-policy.test.mjs +95 -0
  124. package/agent/extensions/pi-web-access/test/fetch-modes.test.mjs +53 -0
  125. package/agent/extensions/pi-web-access/test/fetch-not-found-guidance.test.mjs +92 -0
  126. package/agent/extensions/pi-web-access/test/fetch-params.test.mjs +86 -0
  127. package/agent/extensions/pi-web-access/test/fetch-render-call.test.mjs +34 -0
  128. package/agent/extensions/pi-web-access/test/fetch-routing.test.mjs +173 -0
  129. package/agent/extensions/pi-web-access/test/gemini-adc-auth.test.mjs +257 -0
  130. package/agent/extensions/pi-web-access/test/gemini-api-transport.test.mjs +170 -0
  131. package/agent/extensions/pi-web-access/test/gemini-pdf-extract.test.mjs +133 -0
  132. package/agent/extensions/pi-web-access/test/gemini-web-cookie-opt-in.test.mjs +178 -0
  133. package/agent/extensions/pi-web-access/test/gemini-web-header-overflow.test.mjs +148 -0
  134. package/agent/extensions/pi-web-access/test/get-search-content.test.mjs +223 -0
  135. package/agent/extensions/pi-web-access/test/github-extract.test.mjs +378 -0
  136. package/agent/extensions/pi-web-access/test/github-issue-pr.test.mjs +565 -0
  137. package/agent/extensions/pi-web-access/test/inline-content-config.test.mjs +99 -0
  138. package/agent/extensions/pi-web-access/test/lazy-extract-load.test.mjs +118 -0
  139. package/agent/extensions/pi-web-access/test/local-video-oversize.test.mjs +52 -0
  140. package/agent/extensions/pi-web-access/test/package-typebox-dependency.test.mjs +50 -0
  141. package/agent/extensions/pi-web-access/test/page-query.test.mjs +51 -0
  142. package/agent/extensions/pi-web-access/test/pdf-config.test.mjs +140 -0
  143. package/agent/extensions/pi-web-access/test/pdf-extract.test.mjs +500 -0
  144. package/agent/extensions/pi-web-access/test/proxy-transport.test.mjs +286 -0
  145. package/agent/extensions/pi-web-access/test/query-rewrite.test.mjs +52 -0
  146. package/agent/extensions/pi-web-access/test/rsc-fallback.test.mjs +102 -0
  147. package/agent/extensions/pi-web-access/test/search-error-render.test.mjs +152 -0
  148. package/agent/extensions/pi-web-access/test/search-providers.test.mjs +274 -0
  149. package/agent/extensions/pi-web-access/test/source-check.test.mjs +179 -0
  150. package/agent/extensions/pi-web-access/test/ssrf-allow-ranges-config.test.mjs +205 -0
  151. package/agent/extensions/pi-web-access/test/ssrf-protection.test.mjs +456 -0
  152. package/agent/extensions/pi-web-access/test/summary-model-scope.test.mjs +106 -0
  153. package/agent/extensions/pi-web-access/test/tool-registration-config.test.mjs +182 -0
  154. package/agent/extensions/pi-web-access/test/web-search-answer-render.test.mjs +66 -0
  155. package/agent/extensions/pi-web-access/test/youtube-extract-errors.test.mjs +64 -0
  156. package/agent/extensions/pi-web-access/tsconfig.json +11 -0
  157. package/agent/extensions/pi-web-access/utils.ts +451 -0
  158. package/agent/extensions/pi-web-access/video-extract.ts +392 -0
  159. package/agent/extensions/pi-web-access/youtube-extract.ts +328 -0
  160. package/agent/extensions/shared/activity-status.ts +31 -0
  161. package/agent/extensions/shared/child-session.test.ts +270 -0
  162. package/agent/extensions/shared/child-session.ts +148 -0
  163. package/agent/extensions/shared/context-utilization.test.ts +48 -0
  164. package/agent/extensions/shared/context-utilization.ts +47 -0
  165. package/agent/extensions/shared/dashboard-state.ts +99 -0
  166. package/agent/extensions/shared/install.ps1 +23 -0
  167. package/agent/extensions/shared/install.sh +21 -0
  168. package/agent/extensions/shared/tool-call-timeout.test.ts +117 -0
  169. package/agent/extensions/shared/tool-call-timeout.ts +104 -0
  170. package/agent/extensions/skillsconf/README.md +74 -0
  171. package/agent/extensions/skillsconf/index.ts +92 -0
  172. package/agent/extensions/skillsconf/install.ps1 +23 -0
  173. package/agent/extensions/skillsconf/install.sh +21 -0
  174. package/agent/extensions/skillsconf/package-lock.json +1809 -0
  175. package/agent/extensions/skillsconf/package.json +18 -0
  176. package/agent/extensions/skillsconf/src/delete-skill.test.ts +64 -0
  177. package/agent/extensions/skillsconf/src/delete-skill.ts +45 -0
  178. package/agent/extensions/skillsconf/src/filter.test.ts +80 -0
  179. package/agent/extensions/skillsconf/src/filter.ts +74 -0
  180. package/agent/extensions/skillsconf/src/fuzzy.ts +26 -0
  181. package/agent/extensions/skillsconf/src/persistence.test.ts +72 -0
  182. package/agent/extensions/skillsconf/src/persistence.ts +169 -0
  183. package/agent/extensions/skillsconf/src/ui/SkillConfView.test.ts +337 -0
  184. package/agent/extensions/skillsconf/src/ui/SkillConfView.ts +758 -0
  185. package/agent/extensions/skillsconf/src/ui/text-input.ts +91 -0
  186. package/agent/extensions/skillsconf/src/ui/tui-helpers.ts +144 -0
  187. package/agent/extensions/skillsconf/tsconfig.json +15 -0
  188. package/agent/extensions/status line/index.ts +282 -0
  189. package/agent/extensions/status line/install.ps1 +23 -0
  190. package/agent/extensions/status line/install.sh +21 -0
  191. package/agent/extensions/subagents/by-the-way.test.ts +29 -0
  192. package/agent/extensions/subagents/claude.test.ts +119 -0
  193. package/agent/extensions/subagents/codex.test.ts +102 -0
  194. package/agent/extensions/subagents/context-usage.test.ts +107 -0
  195. package/agent/extensions/subagents/docs/design-plan.md +568 -0
  196. package/agent/extensions/subagents/docs/effect-v4-extension-guide.md +354 -0
  197. package/agent/extensions/subagents/docs/effect-v4-notes.md +571 -0
  198. package/agent/extensions/subagents/index.ts +779 -0
  199. package/agent/extensions/subagents/install.ps1 +23 -0
  200. package/agent/extensions/subagents/install.sh +21 -0
  201. package/agent/extensions/subagents/manager.test.ts +276 -0
  202. package/agent/extensions/subagents/package-lock.json +2244 -0
  203. package/agent/extensions/subagents/package.json +19 -0
  204. package/agent/extensions/subagents/result-delivery.test.ts +27 -0
  205. package/agent/extensions/subagents/src/backend.ts +73 -0
  206. package/agent/extensions/subagents/src/backends/claude.ts +701 -0
  207. package/agent/extensions/subagents/src/backends/codex.ts +1060 -0
  208. package/agent/extensions/subagents/src/backends/pi.ts +575 -0
  209. package/agent/extensions/subagents/src/backends/stub.ts +300 -0
  210. package/agent/extensions/subagents/src/by-the-way.ts +21 -0
  211. package/agent/extensions/subagents/src/domain.ts +253 -0
  212. package/agent/extensions/subagents/src/format.ts +74 -0
  213. package/agent/extensions/subagents/src/manager.ts +736 -0
  214. package/agent/extensions/subagents/src/prompt.ts +92 -0
  215. package/agent/extensions/subagents/src/result-delivery.ts +20 -0
  216. package/agent/extensions/subagents/src/runtime.ts +53 -0
  217. package/agent/extensions/subagents/src/ui/takeover.ts +583 -0
  218. package/agent/extensions/subagents/src/ui/transcript.ts +201 -0
  219. package/agent/extensions/subagents/takeover.test.ts +29 -0
  220. package/agent/extensions/subagents/tsconfig.json +7 -0
  221. package/agent/extensions/taste/index.ts +443 -0
  222. package/agent/extensions/taste/install.ps1 +23 -0
  223. package/agent/extensions/taste/install.sh +21 -0
  224. package/agent/extensions/todo/AGENTS.md +38 -0
  225. package/agent/extensions/todo/LICENSE +21 -0
  226. package/agent/extensions/todo/config.ts +55 -0
  227. package/agent/extensions/todo/index.ts +151 -0
  228. package/agent/extensions/todo/install.ps1 +23 -0
  229. package/agent/extensions/todo/install.sh +21 -0
  230. package/agent/extensions/todo/locales/de.json +17 -0
  231. package/agent/extensions/todo/locales/en.json +15 -0
  232. package/agent/extensions/todo/locales/es.json +17 -0
  233. package/agent/extensions/todo/locales/fr.json +17 -0
  234. package/agent/extensions/todo/locales/pt-BR.json +17 -0
  235. package/agent/extensions/todo/locales/pt.json +17 -0
  236. package/agent/extensions/todo/locales/ru.json +17 -0
  237. package/agent/extensions/todo/locales/uk.json +17 -0
  238. package/agent/extensions/todo/locales/zh.json +17 -0
  239. package/agent/extensions/todo/package-lock.json +3358 -0
  240. package/agent/extensions/todo/package.json +67 -0
  241. package/agent/extensions/todo/state/i18n-bridge.ts +64 -0
  242. package/agent/extensions/todo/state/invariants.ts +20 -0
  243. package/agent/extensions/todo/state/replay.ts +38 -0
  244. package/agent/extensions/todo/state/selectors.ts +107 -0
  245. package/agent/extensions/todo/state/state-reducer.ts +326 -0
  246. package/agent/extensions/todo/state/state.ts +18 -0
  247. package/agent/extensions/todo/state/store.ts +82 -0
  248. package/agent/extensions/todo/state/task-graph.ts +57 -0
  249. package/agent/extensions/todo/todo-overlay.ts +200 -0
  250. package/agent/extensions/todo/todo.ts +155 -0
  251. package/agent/extensions/todo/tool/response-envelope.ts +109 -0
  252. package/agent/extensions/todo/tool/types.ts +206 -0
  253. package/agent/extensions/todo/verify-ref-system.js +0 -0
  254. package/agent/extensions/todo/view/format.ts +177 -0
  255. package/agent/extensions/trim-context/README.md +54 -0
  256. package/agent/extensions/trim-context/index.ts +487 -0
  257. package/agent/extensions/trim-context/install.ps1 +23 -0
  258. package/agent/extensions/trim-context/install.sh +21 -0
  259. package/agent/install.ps1 +527 -0
  260. package/agent/install.sh +511 -0
  261. package/agent/keybindings.json +7 -0
  262. package/bin/kiln.js +359 -0
  263. package/package.json +21 -0
@@ -0,0 +1,459 @@
1
+ /**
2
+ * file-search — first-class `fd` and `rg` tools for pi.
3
+ *
4
+ * On session start the extension resolves a usable binary for each tool:
5
+ * a normally installed system binary is preferred (silently), then an
6
+ * existing fallback in this repo's `bin/` directory (silently), and only
7
+ * when neither exists is an official release downloaded into `bin/` — the
8
+ * single case that shows a UI notification. Tools await that initialization
9
+ * before executing, and report a clear error if it failed.
10
+ */
11
+
12
+ // Deep import: the package root eagerly loads NodeRedis -> ioredis, whose CJS
13
+ // interop (`exports = module.exports = ...`) blows Bun's stack when imported
14
+ // from pi's embedded runtime.
15
+ import * as NodeServices from "@effect/platform-node/NodeServices";
16
+ import type {
17
+ AgentToolResult,
18
+ ExtensionAPI,
19
+ ExtensionContext,
20
+ } from "@earendil-works/pi-coding-agent";
21
+ import { Text } from "@earendil-works/pi-tui";
22
+ import { Cause, Data, Effect, Exit } from "effect";
23
+ import { Type } from "typebox";
24
+ import { StringEnum } from "@earendil-works/pi-ai";
25
+ import {
26
+ buildFdArgs,
27
+ buildRgArgs,
28
+ FD_MAX_DEPTH_LIMIT,
29
+ FD_MAX_LIMIT,
30
+ RG_MAX_CONTEXT,
31
+ RG_MAX_COUNT_LIMIT,
32
+ } from "./src/args.ts";
33
+ import {
34
+ currentTarget,
35
+ liveBinaryEnv,
36
+ repositoryBinDir,
37
+ resolveBinary,
38
+ TOOL_SPECS,
39
+ type BinaryEnv,
40
+ type BinarySource,
41
+ type PlatformTarget,
42
+ type ResolvedBinary,
43
+ } from "./src/binaries.ts";
44
+ import { formatCapturedOutput, type CapturedOutput } from "./src/output.ts";
45
+ import {
46
+ FD_PARAMETER_DESCRIPTIONS,
47
+ FD_PROMPT_GUIDELINES,
48
+ FD_PROMPT_SNIPPET,
49
+ FD_TOOL_DESCRIPTION,
50
+ RG_PARAMETER_DESCRIPTIONS,
51
+ RG_PROMPT_GUIDELINES,
52
+ RG_PROMPT_SNIPPET,
53
+ RG_TOOL_DESCRIPTION,
54
+ } from "./src/prompt.ts";
55
+ import { discardCapturedOutput, executeSearchProcess } from "./src/process.ts";
56
+
57
+ export function makeBinaryInitializers(
58
+ binDir: string,
59
+ target: PlatformTarget,
60
+ env: BinaryEnv,
61
+ ) {
62
+ return {
63
+ fd: Effect.runSync(
64
+ Effect.cached(resolveBinary(TOOL_SPECS.fd, binDir, target, env)),
65
+ ),
66
+ rg: Effect.runSync(
67
+ Effect.cached(resolveBinary(TOOL_SPECS.rg, binDir, target, env)),
68
+ ),
69
+ };
70
+ }
71
+
72
+ /** Human-readable install notice, shown only for fresh downloads. */
73
+ export function installNotifications(binaries: readonly ResolvedBinary[]) {
74
+ return binaries
75
+ .filter((binary) => binary.source === "installed")
76
+ .map(
77
+ (binary) =>
78
+ `file-search: no system ${binary.tool} found — downloaded ${binary.tool} ${binary.version ?? ""}`.trimEnd() +
79
+ ` to ${repositoryBinDir()}`,
80
+ );
81
+ }
82
+
83
+ class SearchError extends Data.TaggedError("SearchError")<{
84
+ readonly message: string;
85
+ }> {}
86
+
87
+ interface SearchOutcome {
88
+ readonly output: CapturedOutput;
89
+ readonly noMatches: boolean;
90
+ readonly binarySource: BinarySource;
91
+ }
92
+
93
+ export interface FdToolDetails {
94
+ readonly binarySource: BinarySource;
95
+ readonly matchCount: number;
96
+ readonly truncated: boolean;
97
+ readonly fullOutputPath?: string;
98
+ }
99
+
100
+ export interface RgToolDetails {
101
+ readonly binarySource: BinarySource;
102
+ readonly outputLines: number;
103
+ readonly truncated: boolean;
104
+ readonly fullOutputPath?: string;
105
+ }
106
+
107
+ const EXEC_TIMEOUT_MS = 60_000;
108
+
109
+ function causeMessage<E>(cause: Cause.Cause<E>) {
110
+ const [first] = Cause.prettyErrors(cause);
111
+ return first?.message ?? Cause.pretty(cause);
112
+ }
113
+
114
+ function unwrapToolExit<A, E>(exit: Exit.Exit<A, E>, tool: "fd" | "rg") {
115
+ if (Exit.isSuccess(exit)) return exit.value;
116
+ if (Cause.hasInterruptsOnly(exit.cause)) {
117
+ throw new Error(`${tool} search was cancelled.`);
118
+ }
119
+ throw new Error(causeMessage(exit.cause));
120
+ }
121
+
122
+ export default function fileSearchTools(pi: ExtensionAPI) {
123
+ let notified = false;
124
+
125
+ const binDir = repositoryBinDir();
126
+ const target = currentTarget();
127
+ const initializers = makeBinaryInitializers(binDir, target, liveBinaryEnv);
128
+
129
+ pi.on("session_start", async (_event, ctx) => {
130
+ const exit = await Effect.runPromiseExit(
131
+ Effect.gen(function* () {
132
+ const initialized = yield* Effect.all(
133
+ {
134
+ fd: Effect.exit(initializers.fd),
135
+ rg: Effect.exit(initializers.rg),
136
+ },
137
+ { concurrency: "unbounded" },
138
+ );
139
+ if (!ctx.hasUI || notified) return;
140
+
141
+ notified = true;
142
+ for (const tool of ["fd", "rg"] as const) {
143
+ const toolExit = initialized[tool];
144
+ if (Exit.isSuccess(toolExit)) {
145
+ for (const message of installNotifications([toolExit.value])) {
146
+ ctx.ui.notify(message, "info");
147
+ }
148
+ } else {
149
+ ctx.ui.notify(
150
+ `file-search ${tool} setup failed: ${causeMessage(toolExit.cause)}`,
151
+ "error",
152
+ );
153
+ }
154
+ }
155
+ }),
156
+ );
157
+
158
+ if (Exit.isFailure(exit) && ctx.hasUI && !notified) {
159
+ notified = true;
160
+ ctx.ui.notify(
161
+ `file-search setup failed: ${causeMessage(exit.cause)}`,
162
+ "error",
163
+ );
164
+ }
165
+ });
166
+
167
+ /** Await init, stream the binary output to disk, and classify its exit. */
168
+ function runSearch(tool: "fd" | "rg", args: string[], ctx: ExtensionContext) {
169
+ return Effect.gen(function* () {
170
+ const binary = yield* initializers[tool];
171
+ const result = yield* executeSearchProcess({
172
+ command: binary.command,
173
+ args,
174
+ cwd: ctx.cwd,
175
+ tempPrefix: `pi-${tool}-`,
176
+ });
177
+
178
+ // ripgrep exits 1 for "no matches"; fd exits 0 even with no results.
179
+ if (tool === "rg" && result.code === 1 && result.output.lineCount === 0) {
180
+ return {
181
+ output: result.output,
182
+ noMatches: true,
183
+ binarySource: binary.source,
184
+ } satisfies SearchOutcome;
185
+ }
186
+ if (result.code !== 0) {
187
+ yield* discardCapturedOutput(result.output);
188
+ const detail = result.stderr.trim() || `exit code ${result.code}`;
189
+ return yield* new SearchError({ message: `${tool} failed: ${detail}` });
190
+ }
191
+ return {
192
+ output: result.output,
193
+ noMatches: result.output.lineCount === 0,
194
+ binarySource: binary.source,
195
+ } satisfies SearchOutcome;
196
+ }).pipe(
197
+ Effect.timeout(EXEC_TIMEOUT_MS),
198
+ Effect.mapError((error) => {
199
+ if (error instanceof SearchError) return error;
200
+ return new SearchError({
201
+ message:
202
+ error._tag === "TimeoutError"
203
+ ? `${tool} timed out.`
204
+ : error instanceof Error
205
+ ? error.message
206
+ : String(error),
207
+ });
208
+ }),
209
+ Effect.provide(NodeServices.layer),
210
+ );
211
+ }
212
+
213
+ pi.registerTool<ReturnType<typeof fdParameters>, FdToolDetails>({
214
+ name: "fd",
215
+ label: "Find Files",
216
+ description: FD_TOOL_DESCRIPTION,
217
+ promptSnippet: FD_PROMPT_SNIPPET,
218
+ promptGuidelines: FD_PROMPT_GUIDELINES,
219
+ parameters: fdParameters(),
220
+
221
+ async execute(_toolCallId, params, signal, _onUpdate, ctx) {
222
+ const exit = await Effect.runPromiseExit(
223
+ Effect.gen(function* () {
224
+ const outcome = yield* runSearch("fd", buildFdArgs(params), ctx);
225
+ if (outcome.noMatches) {
226
+ return {
227
+ content: [{ type: "text", text: "No files found" }],
228
+ details: {
229
+ binarySource: outcome.binarySource,
230
+ matchCount: 0,
231
+ truncated: false,
232
+ },
233
+ } satisfies AgentToolResult<FdToolDetails>;
234
+ }
235
+
236
+ const formatted = formatCapturedOutput(outcome.output);
237
+ return {
238
+ content: [{ type: "text", text: formatted.text }],
239
+ details: {
240
+ binarySource: outcome.binarySource,
241
+ matchCount: formatted.lineCount,
242
+ truncated: formatted.truncated,
243
+ fullOutputPath: formatted.fullOutputPath,
244
+ },
245
+ } satisfies AgentToolResult<FdToolDetails>;
246
+ }),
247
+ signal ? { signal } : undefined,
248
+ );
249
+ return unwrapToolExit(exit, "fd");
250
+ },
251
+
252
+ renderCall(args, theme) {
253
+ let text = theme.fg("toolTitle", theme.bold("fd "));
254
+ text += theme.fg("accent", args.pattern ? `"${args.pattern}"` : "(all)");
255
+ if (args.path) text += theme.fg("muted", ` in ${args.path}`);
256
+ const flags = [
257
+ args.type && `type=${args.type}`,
258
+ args.extension && `ext=${args.extension}`,
259
+ args.glob && "glob",
260
+ args.hidden && "hidden",
261
+ args.max_depth !== undefined && `depth≤${args.max_depth}`,
262
+ ].filter((flag): flag is string => typeof flag === "string");
263
+ if (flags.length > 0) text += " " + theme.fg("dim", flags.join(" "));
264
+ return new Text(text, 1, 0);
265
+ },
266
+
267
+ renderResult(result, { expanded, isPartial }, theme) {
268
+ if (isPartial) return new Text(theme.fg("warning", "Searching..."), 1, 0);
269
+ const details = result.details;
270
+ if (!details || !details.matchCount) {
271
+ return new Text(theme.fg("dim", "No files found"), 1, 0);
272
+ }
273
+ let text = theme.fg(
274
+ "success",
275
+ `${details.matchCount} ${details.matchCount === 1 ? "entry" : "entries"}`,
276
+ );
277
+ if (details.truncated) text += theme.fg("warning", " (truncated)");
278
+ if (expanded)
279
+ text += expandedPreview(result, details.fullOutputPath, theme);
280
+ return new Text(text, 1, 0);
281
+ },
282
+ });
283
+
284
+ pi.registerTool<ReturnType<typeof rgParameters>, RgToolDetails>({
285
+ name: "rg",
286
+ label: "Search Content",
287
+ description: RG_TOOL_DESCRIPTION,
288
+ promptSnippet: RG_PROMPT_SNIPPET,
289
+ promptGuidelines: RG_PROMPT_GUIDELINES,
290
+ parameters: rgParameters(),
291
+
292
+ async execute(_toolCallId, params, signal, _onUpdate, ctx) {
293
+ const exit = await Effect.runPromiseExit(
294
+ Effect.gen(function* () {
295
+ const outcome = yield* runSearch("rg", buildRgArgs(params), ctx);
296
+ if (outcome.noMatches) {
297
+ return {
298
+ content: [{ type: "text", text: "No matches found" }],
299
+ details: {
300
+ binarySource: outcome.binarySource,
301
+ outputLines: 0,
302
+ truncated: false,
303
+ },
304
+ } satisfies AgentToolResult<RgToolDetails>;
305
+ }
306
+
307
+ const formatted = formatCapturedOutput(outcome.output);
308
+ return {
309
+ content: [{ type: "text", text: formatted.text }],
310
+ details: {
311
+ binarySource: outcome.binarySource,
312
+ outputLines: formatted.lineCount,
313
+ truncated: formatted.truncated,
314
+ fullOutputPath: formatted.fullOutputPath,
315
+ },
316
+ } satisfies AgentToolResult<RgToolDetails>;
317
+ }),
318
+ signal ? { signal } : undefined,
319
+ );
320
+ return unwrapToolExit(exit, "rg");
321
+ },
322
+
323
+ renderCall(args, theme) {
324
+ let text = theme.fg("toolTitle", theme.bold("rg "));
325
+ text += theme.fg("accent", `"${args.pattern}"`);
326
+ if (args.path) text += theme.fg("muted", ` in ${args.path}`);
327
+ const flags = [
328
+ args.glob && `glob=${args.glob}`,
329
+ args.file_type && `type=${args.file_type}`,
330
+ args.fixed_strings && "literal",
331
+ args.hidden && "hidden",
332
+ args.context !== undefined && `ctx=${args.context}`,
333
+ ].filter((flag): flag is string => typeof flag === "string");
334
+ if (flags.length > 0) text += " " + theme.fg("dim", flags.join(" "));
335
+ return new Text(text, 1, 0);
336
+ },
337
+
338
+ renderResult(result, { expanded, isPartial }, theme) {
339
+ if (isPartial) return new Text(theme.fg("warning", "Searching..."), 1, 0);
340
+ const details = result.details;
341
+ if (!details || !details.outputLines) {
342
+ return new Text(theme.fg("dim", "No matches found"), 1, 0);
343
+ }
344
+ let text = theme.fg(
345
+ "success",
346
+ `${details.outputLines} output ${details.outputLines === 1 ? "line" : "lines"}`,
347
+ );
348
+ if (details.truncated) text += theme.fg("warning", " (truncated)");
349
+ if (expanded)
350
+ text += expandedPreview(result, details.fullOutputPath, theme);
351
+ return new Text(text, 1, 0);
352
+ },
353
+ });
354
+ }
355
+
356
+ const PREVIEW_LINES = 20;
357
+
358
+ interface ThemeLike {
359
+ fg(color: string, text: string): string;
360
+ }
361
+
362
+ function expandedPreview(
363
+ result: { content: { type: string; text?: string }[] },
364
+ fullOutputPath: string | undefined,
365
+ theme: ThemeLike,
366
+ ) {
367
+ let text = "";
368
+ const content = result.content[0];
369
+ if (content?.type === "text" && content.text) {
370
+ const lines = content.text.split("\n");
371
+ for (const line of lines.slice(0, PREVIEW_LINES)) {
372
+ text += `\n${theme.fg("dim", line)}`;
373
+ }
374
+ if (lines.length > PREVIEW_LINES) {
375
+ text += `\n${theme.fg("muted", `... ${lines.length - PREVIEW_LINES} more lines`)}`;
376
+ }
377
+ }
378
+ if (fullOutputPath) {
379
+ text += `\n${theme.fg("dim", `Full output: ${fullOutputPath}`)}`;
380
+ }
381
+ return text;
382
+ }
383
+
384
+ function fdParameters() {
385
+ return Type.Object({
386
+ pattern: Type.Optional(
387
+ Type.String({ description: FD_PARAMETER_DESCRIPTIONS.pattern }),
388
+ ),
389
+ path: Type.Optional(
390
+ Type.String({ description: FD_PARAMETER_DESCRIPTIONS.path }),
391
+ ),
392
+ type: Type.Optional(
393
+ StringEnum(["file", "directory", "symlink"] as const, {
394
+ description: FD_PARAMETER_DESCRIPTIONS.type,
395
+ }),
396
+ ),
397
+ extension: Type.Optional(
398
+ Type.String({ description: FD_PARAMETER_DESCRIPTIONS.extension }),
399
+ ),
400
+ glob: Type.Optional(
401
+ Type.Boolean({ description: FD_PARAMETER_DESCRIPTIONS.glob }),
402
+ ),
403
+ hidden: Type.Optional(
404
+ Type.Boolean({ description: FD_PARAMETER_DESCRIPTIONS.hidden }),
405
+ ),
406
+ max_depth: Type.Optional(
407
+ Type.Integer({
408
+ description: FD_PARAMETER_DESCRIPTIONS.max_depth,
409
+ minimum: 1,
410
+ maximum: FD_MAX_DEPTH_LIMIT,
411
+ }),
412
+ ),
413
+ limit: Type.Optional(
414
+ Type.Integer({
415
+ description: FD_PARAMETER_DESCRIPTIONS.limit,
416
+ minimum: 1,
417
+ maximum: FD_MAX_LIMIT,
418
+ }),
419
+ ),
420
+ });
421
+ }
422
+
423
+ function rgParameters() {
424
+ return Type.Object({
425
+ pattern: Type.String({ description: RG_PARAMETER_DESCRIPTIONS.pattern }),
426
+ path: Type.Optional(
427
+ Type.String({ description: RG_PARAMETER_DESCRIPTIONS.path }),
428
+ ),
429
+ glob: Type.Optional(
430
+ Type.String({ description: RG_PARAMETER_DESCRIPTIONS.glob }),
431
+ ),
432
+ file_type: Type.Optional(
433
+ Type.String({ description: RG_PARAMETER_DESCRIPTIONS.file_type }),
434
+ ),
435
+ case_sensitive: Type.Optional(
436
+ Type.Boolean({ description: RG_PARAMETER_DESCRIPTIONS.case_sensitive }),
437
+ ),
438
+ fixed_strings: Type.Optional(
439
+ Type.Boolean({ description: RG_PARAMETER_DESCRIPTIONS.fixed_strings }),
440
+ ),
441
+ hidden: Type.Optional(
442
+ Type.Boolean({ description: RG_PARAMETER_DESCRIPTIONS.hidden }),
443
+ ),
444
+ context: Type.Optional(
445
+ Type.Integer({
446
+ description: RG_PARAMETER_DESCRIPTIONS.context,
447
+ minimum: 0,
448
+ maximum: RG_MAX_CONTEXT,
449
+ }),
450
+ ),
451
+ limit: Type.Optional(
452
+ Type.Integer({
453
+ description: RG_PARAMETER_DESCRIPTIONS.limit,
454
+ minimum: 1,
455
+ maximum: RG_MAX_COUNT_LIMIT,
456
+ }),
457
+ ),
458
+ });
459
+ }
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env pwsh
2
+ # Per-extension installer — Windows / PowerShell
3
+ # Installs npm dependencies for this extension. No-op if no package.json.
4
+ $ErrorActionPreference = "Stop"
5
+ $extDir = $PSScriptRoot
6
+ if (-not $extDir) { $extDir = Split-Path -Parent $MyInvocation.MyCommand.Path }
7
+ if (-not (Test-Path "$extDir/package.json")) {
8
+ Write-Host " - no package.json — skipping" -ForegroundColor DarkGray
9
+ exit 0
10
+ }
11
+ if (-not (Get-Command npm -ErrorAction SilentlyContinue)) {
12
+ Write-Host " x npm not found — install Node.js 22+" -ForegroundColor Red
13
+ exit 1
14
+ }
15
+ if (Test-Path "$extDir/package-lock.json") {
16
+ Write-Host " - npm ci" -ForegroundColor DarkGray
17
+ npm --prefix $extDir ci --no-audit --no-fund
18
+ } else {
19
+ Write-Host " - npm install" -ForegroundColor DarkGray
20
+ npm --prefix $extDir install --no-audit --no-fund
21
+ }
22
+ if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
23
+ Write-Host " + done" -ForegroundColor Green
@@ -0,0 +1,21 @@
1
+ #!/bin/sh
2
+ # Per-extension installer — macOS / Linux
3
+ # Installs npm dependencies for this extension. No-op if no package.json.
4
+ set -eu
5
+ EXT_DIR="$(cd "$(dirname "$0")" && pwd)"
6
+ if [ ! -f "$EXT_DIR/package.json" ]; then
7
+ echo " · no package.json — skipping"
8
+ exit 0
9
+ fi
10
+ if ! command -v npm >/dev/null 2>&1; then
11
+ echo " ✖ npm not found — install Node.js 22+" >&2
12
+ exit 1
13
+ fi
14
+ if [ -f "$EXT_DIR/package-lock.json" ]; then
15
+ echo " · npm ci"
16
+ npm --prefix "$EXT_DIR" ci --no-audit --no-fund
17
+ else
18
+ echo " · npm install"
19
+ npm --prefix "$EXT_DIR" install --no-audit --no-fund
20
+ fi
21
+ echo " ✓ done"