@arnilo/prism-tools 0.0.0-stage → 0.13.1

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 (331) hide show
  1. package/CHANGELOG.md +418 -0
  2. package/LICENSE +21 -0
  3. package/README.md +44 -2
  4. package/browser/README.md +98 -0
  5. package/browser/skills/browser-automation/SKILL.md +14 -0
  6. package/browser/src/actions/cdp.ts +131 -0
  7. package/browser/src/actions/dialog.ts +25 -0
  8. package/browser/src/actions/download_release.ts +22 -0
  9. package/browser/src/actions/index.ts +37 -0
  10. package/browser/src/actions/locator.ts +70 -0
  11. package/browser/src/actions/navigate.ts +23 -0
  12. package/browser/src/actions/screenshot.ts +27 -0
  13. package/browser/src/actions/scroll.ts +24 -0
  14. package/browser/src/actions/select_page.ts +21 -0
  15. package/browser/src/actions/types.ts +114 -0
  16. package/browser/src/actions/upload.ts +44 -0
  17. package/browser/src/actions/wait.ts +45 -0
  18. package/browser/src/browser.ts +197 -0
  19. package/browser/src/cdp.ts +178 -0
  20. package/browser/src/checkpoint.ts +189 -0
  21. package/browser/src/config.ts +72 -0
  22. package/browser/src/downloads.ts +253 -0
  23. package/browser/src/errors.ts +31 -0
  24. package/browser/src/evaluate.ts +101 -0
  25. package/browser/src/index.ts +129 -0
  26. package/browser/src/limits.ts +271 -0
  27. package/browser/src/manager.ts +730 -0
  28. package/browser/src/network.ts +263 -0
  29. package/browser/src/observe.ts +178 -0
  30. package/browser/src/policy.ts +133 -0
  31. package/browser/src/screenshot.ts +107 -0
  32. package/browser/src/shared-sandbox.ts +53 -0
  33. package/browser/src/snapshot.ts +99 -0
  34. package/browser/src/targets.ts +165 -0
  35. package/browser/src/tools.ts +480 -0
  36. package/browser/src/types.ts +316 -0
  37. package/browser/src/uploads.ts +174 -0
  38. package/coding/README.md +337 -0
  39. package/coding/src/agent/acp-operations.ts +146 -0
  40. package/coding/src/agent/artifacts.ts +38 -0
  41. package/coding/src/agent/ask-user-decision.ts +623 -0
  42. package/coding/src/agent/atomic-write.ts +23 -0
  43. package/coding/src/agent/bounded-file.ts +21 -0
  44. package/coding/src/agent/checks.ts +301 -0
  45. package/coding/src/agent/coding-checkpoint.ts +828 -0
  46. package/coding/src/agent/delete.ts +237 -0
  47. package/coding/src/agent/diagnostics-tool.ts +163 -0
  48. package/coding/src/agent/diagnostics.ts +262 -0
  49. package/coding/src/agent/edit-diff.ts +382 -0
  50. package/coding/src/agent/edit.ts +322 -0
  51. package/coding/src/agent/effects.ts +102 -0
  52. package/coding/src/agent/env.ts +28 -0
  53. package/coding/src/agent/execution-policy.ts +89 -0
  54. package/coding/src/agent/file-mutation-queue.ts +72 -0
  55. package/coding/src/agent/forge/github.ts +603 -0
  56. package/coding/src/agent/forge/index.ts +16 -0
  57. package/coding/src/agent/forge/types.ts +185 -0
  58. package/coding/src/agent/git-aware-repository.ts +305 -0
  59. package/coding/src/agent/git-exec.ts +326 -0
  60. package/coding/src/agent/git-status.ts +177 -0
  61. package/coding/src/agent/git-tools.ts +360 -0
  62. package/coding/src/agent/git.ts +1074 -0
  63. package/coding/src/agent/glob-match.ts +146 -0
  64. package/coding/src/agent/glob.ts +182 -0
  65. package/coding/src/agent/goal-verify.ts +385 -0
  66. package/coding/src/agent/grep-worker.ts +74 -0
  67. package/coding/src/agent/grep.ts +459 -0
  68. package/coding/src/agent/identity.ts +48 -0
  69. package/coding/src/agent/index.ts +676 -0
  70. package/coding/src/agent/language/client.ts +429 -0
  71. package/coding/src/agent/language/framing.ts +190 -0
  72. package/coding/src/agent/language/index.ts +23 -0
  73. package/coding/src/agent/language/intelligence.ts +682 -0
  74. package/coding/src/agent/language/types.ts +187 -0
  75. package/coding/src/agent/lifecycle-events.ts +121 -0
  76. package/coding/src/agent/lifecycle.ts +166 -0
  77. package/coding/src/agent/limits.ts +214 -0
  78. package/coding/src/agent/move.ts +178 -0
  79. package/coding/src/agent/mutation-path.ts +52 -0
  80. package/coding/src/agent/output-accumulator.ts +304 -0
  81. package/coding/src/agent/path-utils.ts +120 -0
  82. package/coding/src/agent/process/index.ts +58 -0
  83. package/coding/src/agent/process/recovery.ts +508 -0
  84. package/coding/src/agent/process/sessions-host.ts +222 -0
  85. package/coding/src/agent/process/sessions-monitor.ts +141 -0
  86. package/coding/src/agent/process/sessions-recovery.ts +241 -0
  87. package/coding/src/agent/process/sessions-spawn.ts +613 -0
  88. package/coding/src/agent/process/sessions-teardown.ts +281 -0
  89. package/coding/src/agent/process/sessions.ts +30 -0
  90. package/coding/src/agent/process/types.ts +358 -0
  91. package/coding/src/agent/read-path-set.ts +126 -0
  92. package/coding/src/agent/read.ts +713 -0
  93. package/coding/src/agent/repository/glob.ts +147 -0
  94. package/coding/src/agent/repository/indexed-search.ts +480 -0
  95. package/coding/src/agent/repository/list.ts +131 -0
  96. package/coding/src/agent/repository/operations.ts +22 -0
  97. package/coding/src/agent/repository/path.ts +98 -0
  98. package/coding/src/agent/repository/search.ts +277 -0
  99. package/coding/src/agent/repository/types.ts +243 -0
  100. package/coding/src/agent/repository/walk.ts +126 -0
  101. package/coding/src/agent/repository.ts +12 -0
  102. package/coding/src/agent/shell-background.ts +264 -0
  103. package/coding/src/agent/shell-output.ts +75 -0
  104. package/coding/src/agent/shell.ts +526 -0
  105. package/coding/src/agent/todo.ts +399 -0
  106. package/coding/src/agent/truncate.ts +251 -0
  107. package/coding/src/agent/write.ts +170 -0
  108. package/coding/src/ask-user-host.ts +48 -0
  109. package/coding/src/config.ts +278 -0
  110. package/coding/src/hard-deny.ts +97 -0
  111. package/coding/src/index.ts +318 -0
  112. package/coding/src/principal-guard.ts +84 -0
  113. package/coding/src/sandbox.ts +92 -0
  114. package/coding/src/security/approval.ts +144 -0
  115. package/coding/src/security/command-rules.ts +57 -0
  116. package/coding/src/security/docker-cli.ts +222 -0
  117. package/coding/src/security/docker-sandbox.ts +1205 -0
  118. package/coding/src/security/e2b-sandbox.ts +853 -0
  119. package/coding/src/security/egress/dns-pin.ts +88 -0
  120. package/coding/src/security/egress/index.ts +32 -0
  121. package/coding/src/security/egress/limits.ts +57 -0
  122. package/coding/src/security/egress/policy.ts +137 -0
  123. package/coding/src/security/egress/proxy.ts +599 -0
  124. package/coding/src/security/egress/types.ts +30 -0
  125. package/coding/src/security/index.ts +199 -0
  126. package/coding/src/security/native-sandbox.ts +639 -0
  127. package/coding/src/security/path-containment.ts +55 -0
  128. package/coding/src/security/sandbox-coding-operations.ts +392 -0
  129. package/coding/src/security/sandbox-fs-operations.ts +644 -0
  130. package/coding/src/security/sandbox-limits.ts +147 -0
  131. package/coding/src/security/sandbox-tar.ts +308 -0
  132. package/coding/src/security/sandbox.ts +179 -0
  133. package/coding/src/security/semaphore.ts +55 -0
  134. package/coding/src/targets.ts +110 -0
  135. package/coding/src/workspace/diff.ts +78 -0
  136. package/coding/src/workspace/errors.ts +29 -0
  137. package/coding/src/workspace/index.ts +5 -0
  138. package/coding/src/workspace/merge.ts +151 -0
  139. package/coding/src/workspace/routing.ts +76 -0
  140. package/coding/src/workspace/service.ts +584 -0
  141. package/computer-use/README.md +105 -0
  142. package/computer-use/skills/computer-use-linux/SKILL.md +17 -0
  143. package/computer-use/src/classify.ts +53 -0
  144. package/computer-use/src/command.ts +29 -0
  145. package/computer-use/src/config.ts +69 -0
  146. package/computer-use/src/create.ts +170 -0
  147. package/computer-use/src/devices.ts +247 -0
  148. package/computer-use/src/env.ts +28 -0
  149. package/computer-use/src/index.ts +157 -0
  150. package/computer-use/src/skill.ts +21 -0
  151. package/openapi/README.md +97 -0
  152. package/openapi/src/compile.ts +386 -0
  153. package/openapi/src/config.ts +73 -0
  154. package/openapi/src/errors.ts +16 -0
  155. package/openapi/src/execute.ts +241 -0
  156. package/openapi/src/index.ts +124 -0
  157. package/openapi/src/limits.ts +63 -0
  158. package/openapi/src/specs.ts +86 -0
  159. package/openapi/src/tools.ts +60 -0
  160. package/openapi/src/types.ts +61 -0
  161. package/package.json +171 -4
  162. package/research/README.md +72 -0
  163. package/research/skills/research-report/SKILL.md +12 -0
  164. package/research/skills/research-scoping/SKILL.md +10 -0
  165. package/research/skills/research-synthesis/SKILL.md +10 -0
  166. package/research/skills/source-triage/SKILL.md +10 -0
  167. package/research/src/check.ts +71 -0
  168. package/research/src/config.ts +41 -0
  169. package/research/src/index.ts +147 -0
  170. package/research/src/ledger.ts +157 -0
  171. package/research/src/urls.ts +21 -0
  172. package/web/README.md +287 -0
  173. package/web/skills/web-research/SKILL.md +17 -0
  174. package/web/src/brave.ts +66 -0
  175. package/web/src/cache.ts +151 -0
  176. package/web/src/config.ts +210 -0
  177. package/web/src/controls.ts +128 -0
  178. package/web/src/domains.ts +114 -0
  179. package/web/src/exa.ts +60 -0
  180. package/web/src/firecrawl.ts +160 -0
  181. package/web/src/index.ts +387 -0
  182. package/web/src/kagi.ts +73 -0
  183. package/web/src/limits.ts +55 -0
  184. package/web/src/native/convert.ts +84 -0
  185. package/web/src/native/fetch.ts +226 -0
  186. package/web/src/normalize.ts +131 -0
  187. package/web/src/obscura/cdp.ts +201 -0
  188. package/web/src/obscura/classify.ts +35 -0
  189. package/web/src/obscura/cli.ts +254 -0
  190. package/web/src/obscura/errors.ts +8 -0
  191. package/web/src/obscura/index.ts +59 -0
  192. package/web/src/obscura/limits.ts +92 -0
  193. package/web/src/obscura/mcp.ts +104 -0
  194. package/web/src/obscura/playwright.ts +133 -0
  195. package/web/src/obscura/process.ts +235 -0
  196. package/web/src/obscura/search-profile.ts +73 -0
  197. package/web/src/obscura/types.ts +48 -0
  198. package/web/src/obscura/web.ts +419 -0
  199. package/web/src/prompt-fetch.ts +88 -0
  200. package/web/src/render.ts +75 -0
  201. package/web/src/searxng.ts +78 -0
  202. package/web/src/slice.ts +139 -0
  203. package/web/src/tavily.ts +77 -0
  204. package/web/src/tools.ts +182 -0
  205. package/web/src/transport.ts +310 -0
  206. package/web/src/types.ts +199 -0
  207. package/work/README.md +77 -0
  208. package/work/THIRD_PARTY_NOTICES +24 -0
  209. package/work/docling/ocr.py +150 -0
  210. package/work/sandbox/Dockerfile +36 -0
  211. package/work/sandbox/soffice.sh +12 -0
  212. package/work/src/command.ts +29 -0
  213. package/work/src/config.ts +138 -0
  214. package/work/src/connectors/cli.ts +343 -0
  215. package/work/src/connectors/drafts.ts +647 -0
  216. package/work/src/connectors/errors.ts +8 -0
  217. package/work/src/connectors/file-bytes.ts +149 -0
  218. package/work/src/connectors/google-workspace-http.ts +399 -0
  219. package/work/src/connectors/google-workspace.ts +465 -0
  220. package/work/src/connectors/gws-tools.ts +378 -0
  221. package/work/src/connectors/http.ts +123 -0
  222. package/work/src/connectors/idempotency.ts +235 -0
  223. package/work/src/connectors/identity.ts +34 -0
  224. package/work/src/connectors/index.ts +93 -0
  225. package/work/src/connectors/limits.ts +50 -0
  226. package/work/src/connectors/m365-tools.ts +308 -0
  227. package/work/src/connectors/microsoft365-http.ts +428 -0
  228. package/work/src/connectors/microsoft365.ts +402 -0
  229. package/work/src/connectors/normalize.ts +120 -0
  230. package/work/src/connectors/tool-helpers.ts +398 -0
  231. package/work/src/connectors/tools.ts +42 -0
  232. package/work/src/connectors/types.ts +399 -0
  233. package/work/src/diagrams/canonicalize.ts +166 -0
  234. package/work/src/diagrams/caps.ts +52 -0
  235. package/work/src/diagrams/embed.ts +340 -0
  236. package/work/src/diagrams/errors.ts +76 -0
  237. package/work/src/diagrams/index.ts +81 -0
  238. package/work/src/diagrams/messages.ts +150 -0
  239. package/work/src/diagrams/origin.ts +52 -0
  240. package/work/src/diagrams/telemetry.ts +34 -0
  241. package/work/src/diagrams/xml.ts +267 -0
  242. package/work/src/document-extraction/index.ts +563 -0
  243. package/work/src/document-reader/ambient.d.ts +2 -0
  244. package/work/src/document-reader/errors.ts +7 -0
  245. package/work/src/document-reader/index.ts +181 -0
  246. package/work/src/document-reader/mistral-ocr.ts +318 -0
  247. package/work/src/document-reader/parsers.ts +171 -0
  248. package/work/src/documents/caps.ts +135 -0
  249. package/work/src/documents/diff.ts +269 -0
  250. package/work/src/documents/errors.ts +44 -0
  251. package/work/src/documents/fidelity.ts +98 -0
  252. package/work/src/documents/generate.ts +96 -0
  253. package/work/src/documents/hash.ts +8 -0
  254. package/work/src/documents/index.ts +14 -0
  255. package/work/src/documents/model-schema.ts +639 -0
  256. package/work/src/documents/parse.ts +252 -0
  257. package/work/src/documents/patch-history.ts +81 -0
  258. package/work/src/documents/patch.ts +476 -0
  259. package/work/src/documents/preview-html.ts +245 -0
  260. package/work/src/documents/preview.ts +227 -0
  261. package/work/src/documents/telemetry.ts +36 -0
  262. package/work/src/documents/translate/docx.ts +292 -0
  263. package/work/src/documents/translate/pptx.ts +304 -0
  264. package/work/src/documents/translate/xlsx.ts +137 -0
  265. package/work/src/documents/types.ts +188 -0
  266. package/work/src/index.ts +311 -0
  267. package/work/src/runtime/worker-pool-entry.ts +102 -0
  268. package/work/src/runtime/worker-pool.ts +267 -0
  269. package/work/src/sandbox/composition.ts +212 -0
  270. package/work/src/sandbox/image.ts +6 -0
  271. package/work/src/sandbox/index.ts +14 -0
  272. package/work/src/sheets/caps.ts +68 -0
  273. package/work/src/sheets/csv.ts +351 -0
  274. package/work/src/sheets/decimal.ts +388 -0
  275. package/work/src/sheets/errors.ts +52 -0
  276. package/work/src/sheets/index.ts +62 -0
  277. package/work/src/sheets/inference.ts +378 -0
  278. package/work/src/sheets/telemetry.ts +34 -0
  279. package/work/src/sheets/types.ts +162 -0
  280. package/work/src/sheets/xlsx.ts +388 -0
  281. package/work/src/skills/index.ts +8 -0
  282. package/work/src/skills/load.ts +43 -0
  283. package/work/src/tools/filesystem.ts +21 -0
  284. package/work/src/tools/index.ts +1 -0
  285. package/work/src/tools/local-filesystem.ts +33 -0
  286. package/work/src/tools/office.ts +311 -0
  287. package/work/vendor/hermes-agent/LICENSE +21 -0
  288. package/work/vendor/hermes-agent/skills/productivity/docx/LICENSE +21 -0
  289. package/work/vendor/hermes-agent/skills/productivity/docx/SKILL.md +196 -0
  290. package/work/vendor/hermes-agent/skills/productivity/docx/scripts/docx_comments.py +289 -0
  291. package/work/vendor/hermes-agent/skills/productivity/docx/scripts/docx_common.py +94 -0
  292. package/work/vendor/hermes-agent/skills/productivity/docx/scripts/docx_create.py +177 -0
  293. package/work/vendor/hermes-agent/skills/productivity/docx/scripts/docx_edit.py +250 -0
  294. package/work/vendor/hermes-agent/skills/productivity/docx/scripts/docx_read.py +149 -0
  295. package/work/vendor/hermes-agent/skills/productivity/docx/scripts/docx_revisions.py +147 -0
  296. package/work/vendor/hermes-agent/skills/productivity/docx/scripts/docx_template.py +70 -0
  297. package/work/vendor/hermes-agent/skills/productivity/docx/scripts/docx_validate.py +156 -0
  298. package/work/vendor/hermes-agent/skills/productivity/pdf/LICENSE +21 -0
  299. package/work/vendor/hermes-agent/skills/productivity/pdf/SKILL.md +125 -0
  300. package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/_raster.py +76 -0
  301. package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/extract_marker.py +88 -0
  302. package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/extract_pymupdf.py +101 -0
  303. package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_create.py +130 -0
  304. package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_fill_form.py +97 -0
  305. package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_form_layout.py +168 -0
  306. package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_make_form.py +145 -0
  307. package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_merge.py +50 -0
  308. package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_meta.py +115 -0
  309. package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_page_image.py +99 -0
  310. package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_read.py +153 -0
  311. package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_secure.py +71 -0
  312. package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_split.py +84 -0
  313. package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_stamp.py +143 -0
  314. package/work/vendor/hermes-agent/skills/productivity/pdf/scripts/pdf_watermark.py +51 -0
  315. package/work/vendor/hermes-agent/skills/productivity/powerpoint/LICENSE +21 -0
  316. package/work/vendor/hermes-agent/skills/productivity/powerpoint/SKILL.md +220 -0
  317. package/work/vendor/hermes-agent/skills/productivity/powerpoint/scripts/pptx_create.py +214 -0
  318. package/work/vendor/hermes-agent/skills/productivity/powerpoint/scripts/pptx_edit.py +436 -0
  319. package/work/vendor/hermes-agent/skills/productivity/powerpoint/scripts/pptx_from_template.py +88 -0
  320. package/work/vendor/hermes-agent/skills/productivity/powerpoint/scripts/pptx_read.py +131 -0
  321. package/work/vendor/hermes-agent/skills/productivity/powerpoint/scripts/pptx_render.py +93 -0
  322. package/work/vendor/hermes-agent/skills/productivity/xlsx/LICENSE +21 -0
  323. package/work/vendor/hermes-agent/skills/productivity/xlsx/SKILL.md +196 -0
  324. package/work/vendor/hermes-agent/skills/productivity/xlsx/scripts/csv_to_xlsx.py +104 -0
  325. package/work/vendor/hermes-agent/skills/productivity/xlsx/scripts/xlsx_create.py +259 -0
  326. package/work/vendor/hermes-agent/skills/productivity/xlsx/scripts/xlsx_edit.py +263 -0
  327. package/work/vendor/hermes-agent/skills/productivity/xlsx/scripts/xlsx_read.py +160 -0
  328. package/work/vendor/hermes-agent/skills/productivity/xlsx/scripts/xlsx_recalc.py +111 -0
  329. package/work/vendor/hermes-agent/skills/productivity/xlsx/scripts/xlsx_restructure.py +337 -0
  330. package/work/vendor/hermes-agent/skills/productivity/xlsx/scripts/xlsx_to_csv.py +69 -0
  331. package/work/vendor-lock.json +11 -0
@@ -0,0 +1,131 @@
1
+ /** Repository list family (0.2.5 plan 025 Task 1 split).
2
+ * Moved verbatim from repository.ts; public surface unchanged behind the barrel. */
3
+
4
+ import { lstat } from "node:fs/promises";
5
+ import {
6
+ HARD_MAX_REPO_DEPTH,
7
+ HARD_MAX_REPO_ENTRIES,
8
+ HARD_MAX_REPO_RESULTS,
9
+ validateCodingLimit,
10
+ validateCodingLimitAllowZero,
11
+ } from "../limits.js";
12
+ import { resolveRepoPath } from "./path.js";
13
+ import type { RepoEntryKind, RepoListEntry, RepositoryListRequest, RepositoryListResult, ResolvedRepositoryLimits } from "./types.js";
14
+ import { RepositoryError } from "./types.js";
15
+ import type { RepositoryWalk } from "./walk.js";
16
+
17
+ export async function listLocal(
18
+ request: RepositoryListRequest,
19
+ defaults: ResolvedRepositoryLimits,
20
+ walk: RepositoryWalk,
21
+ ): Promise<RepositoryListResult> {
22
+ const resolved = await resolveRepoPath(request.root, request.path);
23
+ const maxResults = validateCodingLimit("maxResults", request.maxResults ?? defaults.maxResults, HARD_MAX_REPO_RESULTS);
24
+ const offset = validateCodingLimitAllowZero("offset", request.offset ?? 0, HARD_MAX_REPO_ENTRIES);
25
+ const maxDepth = validateCodingLimit("maxDepth", request.maxDepth ?? defaults.maxDepth, HARD_MAX_REPO_DEPTH);
26
+ const exclude = new Set(request.exclude ?? defaults.exclude);
27
+ const deadlineAt = request.deadlineMs !== undefined ? Date.now() + request.deadlineMs : Date.now() + defaults.maxTimeMs;
28
+
29
+ const collected: RepoListEntry[] = [];
30
+ let scannedEntries = 0;
31
+ let scannedFiles = 0;
32
+ let seen = 0;
33
+ let truncated = false;
34
+ let truncatedBy: RepositoryListResult["truncatedBy"] = null;
35
+
36
+ // Single-file start: return that entry when it falls within the page window.
37
+ try {
38
+ const startStat = await lstat(resolved.absolute);
39
+ if (!startStat.isDirectory()) {
40
+ let kind: RepoEntryKind = "other";
41
+ if (startStat.isSymbolicLink()) kind = "symlink";
42
+ else if (startStat.isFile()) kind = "file";
43
+ const entry: RepoListEntry =
44
+ kind === "file" ? { path: resolved.relative, kind, size: startStat.size } : { path: resolved.relative, kind };
45
+ scannedEntries = 1;
46
+ scannedFiles = kind === "file" ? 1 : 0;
47
+ if (offset === 0 && maxResults > 0) collected.push(entry);
48
+ else if (offset === 0 && maxResults === 0) {
49
+ truncated = true;
50
+ truncatedBy = "results";
51
+ }
52
+ return {
53
+ entries: collected,
54
+ truncated,
55
+ truncatedBy,
56
+ scannedEntries,
57
+ scannedFiles,
58
+ offset,
59
+ nextOffset: undefined,
60
+ };
61
+ }
62
+ } catch (error) {
63
+ const message = error instanceof Error ? error.message : String(error);
64
+ throw new RepositoryError(`cannot open path: ${message}`);
65
+ }
66
+
67
+ try {
68
+ for await (const event of walk(resolved.rootReal, resolved.absolute, {
69
+ maxDepth,
70
+ maxEntries: defaults.maxEntries,
71
+ maxFiles: defaults.maxFiles,
72
+ exclude,
73
+ includeHidden: request.includeHidden === true,
74
+ signal: request.signal,
75
+ deadlineAt,
76
+ })) {
77
+ if (event.type === "limit") {
78
+ truncated = true;
79
+ truncatedBy = event.truncatedBy;
80
+ break;
81
+ }
82
+ scannedEntries++;
83
+ if (event.entry.kind === "file") scannedFiles++;
84
+ if (seen < offset) {
85
+ seen++;
86
+ continue;
87
+ }
88
+ if (collected.length >= maxResults) {
89
+ truncated = true;
90
+ truncatedBy = "results";
91
+ break;
92
+ }
93
+ collected.push(event.entry);
94
+ seen++;
95
+ }
96
+ } catch (error) {
97
+ if (error instanceof RepositoryError && error.message === "Operation aborted") {
98
+ return {
99
+ entries: collected,
100
+ truncated: true,
101
+ truncatedBy: "abort",
102
+ scannedEntries,
103
+ scannedFiles,
104
+ offset,
105
+ nextOffset: collected.length > 0 || offset > 0 ? offset + collected.length : undefined,
106
+ };
107
+ }
108
+ if (error instanceof RepositoryError && error.message === "Repository operation exceeded time limit") {
109
+ return {
110
+ entries: collected,
111
+ truncated: true,
112
+ truncatedBy: "time",
113
+ scannedEntries,
114
+ scannedFiles,
115
+ offset,
116
+ nextOffset: offset + collected.length,
117
+ };
118
+ }
119
+ throw error;
120
+ }
121
+
122
+ return {
123
+ entries: collected,
124
+ truncated,
125
+ truncatedBy,
126
+ scannedEntries,
127
+ scannedFiles,
128
+ offset,
129
+ nextOffset: truncated ? offset + collected.length : undefined,
130
+ };
131
+ }
@@ -0,0 +1,22 @@
1
+ /** Repository operations family (0.2.5 plan 025 Task 1 split).
2
+ * Moved verbatim from repository.ts; public surface unchanged behind the barrel. */
3
+
4
+ import { globLocal } from "./glob.js";
5
+ import { listLocal } from "./list.js";
6
+ import { searchLocal } from "./search.js";
7
+ import type { RepositoryLimitOptions, RepositoryOperations } from "./types.js";
8
+ import { resolveRepositoryLimits } from "./types.js";
9
+ import type { RepositoryWalk } from "./walk.js";
10
+ import { walkRepository } from "./walk.js";
11
+
12
+ export function createLocalRepositoryOperations(
13
+ limits?: RepositoryLimitOptions,
14
+ walk: RepositoryWalk = walkRepository,
15
+ ): RepositoryOperations {
16
+ const resolved = resolveRepositoryLimits(limits);
17
+ return {
18
+ list: (request) => listLocal(request, resolved, walk),
19
+ search: (request) => searchLocal(request, resolved, walk),
20
+ glob: (request) => globLocal(request, resolved, walk),
21
+ };
22
+ }
@@ -0,0 +1,98 @@
1
+ /** Repository path family (0.2.5 plan 025 Task 1 split).
2
+ * Moved verbatim from repository.ts; public surface unchanged behind the barrel. */
3
+
4
+ import type { Dirent } from "node:fs";
5
+ import { realpath } from "node:fs/promises";
6
+ import { isAbsolute, relative, resolve, sep } from "node:path";
7
+ import { DEFAULT_BINARY_SNIFF_BYTES } from "../limits.js";
8
+ import { resolveToCwd } from "../path-utils.js";
9
+ import type { RepoEntryKind } from "./types.js";
10
+ import { RepositoryError } from "./types.js";
11
+
12
+ export function toRepoRelative(root: string, absolutePath: string): string {
13
+ const rel = relative(root, absolutePath);
14
+ if (rel === "") return ".";
15
+ return rel.split(sep).join("/");
16
+ }
17
+
18
+ export function isPathInsideRoot(root: string, target: string): boolean {
19
+ const from = resolve(root);
20
+ const to = resolve(target);
21
+ if (to === from) return true;
22
+ const rel = relative(from, to);
23
+ return rel !== "" && !rel.startsWith("..") && !isAbsolute(rel);
24
+ }
25
+
26
+ /**
27
+ * Resolve a list/search start path under the workspace root.
28
+ * Symlink escapes fail closed after realpath when the path exists.
29
+ */
30
+ export async function resolveRepoPath(
31
+ root: string,
32
+ inputPath: string | undefined,
33
+ ): Promise<{
34
+ absolute: string;
35
+ relative: string;
36
+ rootReal: string;
37
+ }> {
38
+ const rootResolved = resolve(root);
39
+ let rootReal: string;
40
+ try {
41
+ rootReal = await realpath(rootResolved);
42
+ } catch {
43
+ throw new RepositoryError(`workspace root is missing or unreadable: ${rootResolved}`);
44
+ }
45
+
46
+ if (!inputPath || inputPath === "." || inputPath === "./") {
47
+ return { absolute: rootReal, relative: ".", rootReal };
48
+ }
49
+
50
+ const candidate = resolveToCwd(inputPath, rootReal);
51
+ if (!isPathInsideRoot(rootReal, candidate)) {
52
+ throw new RepositoryError(`path escapes workspace root: ${inputPath}`);
53
+ }
54
+
55
+ try {
56
+ const real = await realpath(candidate);
57
+ if (!isPathInsideRoot(rootReal, real)) {
58
+ throw new RepositoryError(`path resolves outside workspace root: ${inputPath}`);
59
+ }
60
+ return { absolute: real, relative: toRepoRelative(rootReal, real), rootReal };
61
+ } catch (error) {
62
+ if (error instanceof RepositoryError) throw error;
63
+ // ENOENT: allow listing a missing path to fail later with a clear error.
64
+ return { absolute: candidate, relative: toRepoRelative(rootReal, candidate), rootReal };
65
+ }
66
+ }
67
+
68
+ export function shouldSkipName(name: string, includeHidden: boolean, exclude: ReadonlySet<string>): boolean {
69
+ if (name === "." || name === "..") return true;
70
+ if (exclude.has(name)) return true;
71
+ if (!includeHidden && name.startsWith(".")) return true;
72
+ return false;
73
+ }
74
+
75
+ export function kindFromDirent(dirent: Dirent): RepoEntryKind {
76
+ if (dirent.isSymbolicLink()) return "symlink";
77
+ if (dirent.isDirectory()) return "directory";
78
+ if (dirent.isFile()) return "file";
79
+ return "other";
80
+ }
81
+
82
+ export function assertNotAborted(signal: AbortSignal | undefined): void {
83
+ if (signal?.aborted) throw new RepositoryError("Operation aborted");
84
+ }
85
+
86
+ export function assertDeadline(deadlineAt: number | undefined): void {
87
+ if (deadlineAt !== undefined && Date.now() >= deadlineAt) {
88
+ throw new RepositoryError("Repository operation exceeded time limit");
89
+ }
90
+ }
91
+
92
+ export function isBinaryBuffer(buffer: Buffer): boolean {
93
+ const length = Math.min(buffer.length, DEFAULT_BINARY_SNIFF_BYTES);
94
+ for (let i = 0; i < length; i++) {
95
+ if (buffer[i] === 0) return true;
96
+ }
97
+ return false;
98
+ }
@@ -0,0 +1,277 @@
1
+ /** Repository search family (0.2.5 plan 025 Task 1 split).
2
+ * Moved verbatim from repository.ts; public surface unchanged behind the barrel. */
3
+
4
+ import { lstat, open } from "node:fs/promises";
5
+ import { HARD_MAX_SEARCH_CONTEXT_LINES, HARD_MAX_SEARCH_MATCHES, validateCodingLimit, validateCodingLimitAllowZero } from "../limits.js";
6
+ import { compileGlob } from "../glob-match.js";
7
+ import { assertDeadline, assertNotAborted, isBinaryBuffer, resolveRepoPath } from "./path.js";
8
+ import type {
9
+ RepositoryFileMatcher,
10
+ RepositoryLineMatch,
11
+ RepositorySearchMatch,
12
+ RepositorySearchRequest,
13
+ RepositorySearchResult,
14
+ ResolvedRepositoryLimits,
15
+ } from "./types.js";
16
+ import { RepositoryError } from "./types.js";
17
+ import type { RepositoryWalk } from "./walk.js";
18
+
19
+ export function compileSearchPattern(
20
+ query: string,
21
+ caseSensitive: boolean,
22
+ maxPatternBytes: number,
23
+ ): { testLine: (line: string) => { column: number } | null; patternBytes: number } {
24
+ const patternBytes = Buffer.byteLength(query, "utf8");
25
+ if (patternBytes < 1) throw new RepositoryError("query must be non-empty");
26
+ if (patternBytes > maxPatternBytes) {
27
+ throw new RepositoryError(`query exceeds ${maxPatternBytes} byte pattern limit`);
28
+ }
29
+
30
+ if (caseSensitive) {
31
+ return {
32
+ patternBytes,
33
+ testLine: (line) => {
34
+ const column = line.indexOf(query);
35
+ return column >= 0 ? { column: column + 1 } : null;
36
+ },
37
+ };
38
+ }
39
+ const needle = query.toLowerCase();
40
+ return {
41
+ patternBytes,
42
+ testLine: (line) => {
43
+ const column = line.toLowerCase().indexOf(needle);
44
+ return column >= 0 ? { column: column + 1 } : null;
45
+ },
46
+ };
47
+ }
48
+
49
+ /**
50
+ * One file's text → its line matches with context. Pure, no I/O: the `grep` worker imports this
51
+ * too, so literal and regex modes format from the same matches.
52
+ */
53
+ export function matchLines(
54
+ text: string,
55
+ testLine: (line: string) => { column: number } | null,
56
+ context: number,
57
+ maxLineBytes: number,
58
+ ): RepositoryLineMatch[] {
59
+ const lines = text.split("\n");
60
+ if (lines.length > 0 && lines[lines.length - 1] === "") lines.pop();
61
+ const out: RepositoryLineMatch[] = [];
62
+ for (let i = 0; i < lines.length; i++) {
63
+ const line = lines[i]!;
64
+ if (maxLineBytes > 0 && Buffer.byteLength(line, "utf8") > maxLineBytes) continue;
65
+ const hit = testLine(line);
66
+ if (!hit) continue;
67
+ out.push({
68
+ line: i + 1,
69
+ column: hit.column,
70
+ text: line,
71
+ before: lines.slice(Math.max(0, i - context), i),
72
+ after: lines.slice(i + 1, i + 1 + context),
73
+ });
74
+ }
75
+ return out;
76
+ }
77
+
78
+ /** The literal matcher: `matchLines` over a compiled substring test. */
79
+ export function literalMatcher(
80
+ query: string,
81
+ caseSensitive: boolean,
82
+ maxPatternBytes: number,
83
+ maxLineBytes: number,
84
+ ): RepositoryFileMatcher {
85
+ const { testLine } = compileSearchPattern(query, caseSensitive, maxPatternBytes);
86
+ return (text, context) => matchLines(text, testLine, context, maxLineBytes);
87
+ }
88
+
89
+ type FileOutcome = "ok" | "binary" | "oversize" | "scan" | "matches";
90
+
91
+ /** Read one bounded file and collect its matches. Whole-file read: the file is already size-capped. */
92
+ async function searchFile(
93
+ absolutePath: string,
94
+ relativePath: string,
95
+ matcher: RepositoryFileMatcher,
96
+ options: {
97
+ maxFileBytes: number;
98
+ maxScanBytesRemaining: () => number;
99
+ chargeScan: (n: number) => void;
100
+ context: number;
101
+ maxMatchesRemaining: () => number;
102
+ pushMatch: (match: RepositorySearchMatch) => void;
103
+ signal?: AbortSignal;
104
+ deadlineAt?: number;
105
+ binarySniffBytes: number;
106
+ },
107
+ ): Promise<FileOutcome> {
108
+ assertNotAborted(options.signal);
109
+ assertDeadline(options.deadlineAt);
110
+
111
+ const handle = await open(absolutePath, "r");
112
+ try {
113
+ const st = await handle.stat();
114
+ if (st.size > options.maxFileBytes) return "oversize";
115
+ const remaining = options.maxScanBytesRemaining();
116
+ if (remaining <= 0) return "scan";
117
+ if (st.size > remaining) {
118
+ options.chargeScan(remaining);
119
+ return "scan";
120
+ }
121
+ const buf = await handle.readFile();
122
+ options.chargeScan(buf.length);
123
+ if (isBinaryBuffer(buf.subarray(0, options.binarySniffBytes))) return "binary";
124
+
125
+ for (const match of await matcher(buf.toString("utf8"), options.context)) {
126
+ if (options.maxMatchesRemaining() <= 0) return "matches";
127
+ options.pushMatch({ path: relativePath, ...match });
128
+ }
129
+ return "ok";
130
+ } finally {
131
+ await handle.close();
132
+ }
133
+ }
134
+
135
+ export async function searchLocal(
136
+ request: RepositorySearchRequest,
137
+ defaults: ResolvedRepositoryLimits,
138
+ walk: RepositoryWalk,
139
+ ): Promise<RepositorySearchResult> {
140
+ const mode = request.mode ?? "literal";
141
+ if (mode !== "literal") {
142
+ throw new RepositoryError(`unsupported search mode: ${String(mode)} (literal only)`);
143
+ }
144
+ const caseSensitive = request.caseSensitive === true;
145
+ const matcher = request.matcher ?? literalMatcher(request.query, caseSensitive, defaults.maxPatternBytes, defaults.maxLineBytes);
146
+
147
+ const resolved = await resolveRepoPath(request.root, request.path);
148
+ const maxMatches = validateCodingLimit("maxMatches", request.maxMatches ?? defaults.maxMatches, HARD_MAX_SEARCH_MATCHES);
149
+ const context = validateCodingLimitAllowZero("context", request.context ?? defaults.maxContextLines, HARD_MAX_SEARCH_CONTEXT_LINES);
150
+ const exclude = new Set(request.exclude ?? defaults.exclude);
151
+ const globMatch = request.glob !== undefined ? compileGlob(request.glob) : undefined;
152
+ const deadlineAt = request.deadlineMs !== undefined ? Date.now() + request.deadlineMs : Date.now() + defaults.maxTimeMs;
153
+
154
+ const matches: RepositorySearchMatch[] = [];
155
+ let scannedBytes = 0;
156
+ let scannedFiles = 0;
157
+ let scannedEntries = 0;
158
+ let filesSkippedBinary = 0;
159
+ let filesSkippedOversize = 0;
160
+ let truncated = false;
161
+ let truncatedBy: RepositorySearchResult["truncatedBy"] = null;
162
+
163
+ const runFile = async (absolutePath: string, relativePath: string): Promise<void> => {
164
+ if (truncated) return;
165
+ const status = await searchFile(absolutePath, relativePath, matcher, {
166
+ maxFileBytes: defaults.maxFileBytes,
167
+ maxScanBytesRemaining: () => defaults.maxScanBytes - scannedBytes,
168
+ chargeScan: (n) => {
169
+ scannedBytes += n;
170
+ },
171
+ context,
172
+ maxMatchesRemaining: () => maxMatches - matches.length,
173
+ pushMatch: (match) => {
174
+ if (matches.length < maxMatches) matches.push(match);
175
+ },
176
+ signal: request.signal,
177
+ deadlineAt,
178
+ binarySniffBytes: defaults.binarySniffBytes,
179
+ });
180
+ if (status === "binary") filesSkippedBinary++;
181
+ else if (status === "oversize") filesSkippedOversize++;
182
+ else if (status === "scan") {
183
+ truncated = true;
184
+ truncatedBy = "scan";
185
+ } else if (status === "matches") {
186
+ truncated = true;
187
+ truncatedBy = "matches";
188
+ }
189
+ };
190
+
191
+ try {
192
+ const startStat = await lstat(resolved.absolute);
193
+ if (startStat.isFile()) {
194
+ scannedEntries = 1;
195
+ if (globMatch === undefined || globMatch(resolved.relative)) {
196
+ scannedFiles = 1;
197
+ await runFile(resolved.absolute, resolved.relative);
198
+ }
199
+ } else if (startStat.isDirectory()) {
200
+ for await (const event of walk(resolved.rootReal, resolved.absolute, {
201
+ maxDepth: defaults.maxDepth,
202
+ maxEntries: defaults.maxEntries,
203
+ maxFiles: defaults.maxFiles,
204
+ exclude,
205
+ includeHidden: request.includeHidden === true,
206
+ signal: request.signal,
207
+ deadlineAt,
208
+ })) {
209
+ if (truncated) break;
210
+ if (event.type === "limit") {
211
+ truncated = true;
212
+ truncatedBy = event.truncatedBy;
213
+ break;
214
+ }
215
+ scannedEntries++;
216
+ if (event.entry.kind !== "file") continue;
217
+ if (globMatch !== undefined && !globMatch(event.entry.path)) continue;
218
+ scannedFiles++;
219
+ try {
220
+ await runFile(event.absolutePath, event.entry.path);
221
+ } catch (error) {
222
+ if (error instanceof RepositoryError) {
223
+ if (error.message === "Operation aborted") {
224
+ truncated = true;
225
+ truncatedBy = "abort";
226
+ break;
227
+ }
228
+ if (error.message === "Repository operation exceeded time limit") {
229
+ truncated = true;
230
+ truncatedBy = "time";
231
+ break;
232
+ }
233
+ }
234
+ // Unreadable files are skipped; walk continues.
235
+ }
236
+ }
237
+ } else if (startStat.isSymbolicLink()) {
238
+ // Symlink starts are not followed for search content.
239
+ scannedEntries = 1;
240
+ }
241
+ } catch (error) {
242
+ if (error instanceof RepositoryError && error.message === "Operation aborted") {
243
+ truncated = true;
244
+ truncatedBy = "abort";
245
+ } else if (error instanceof RepositoryError && error.message === "Repository operation exceeded time limit") {
246
+ truncated = true;
247
+ truncatedBy = "time";
248
+ } else if (error instanceof RepositoryError) {
249
+ throw error;
250
+ } else {
251
+ const message = error instanceof Error ? error.message : String(error);
252
+ throw new RepositoryError(`cannot search path: ${message}`);
253
+ }
254
+ }
255
+
256
+ if (!truncated && matches.length >= maxMatches) {
257
+ truncated = true;
258
+ truncatedBy = "matches";
259
+ }
260
+
261
+ matches.sort((a, b) => {
262
+ if (a.path !== b.path) return a.path < b.path ? -1 : 1;
263
+ if (a.line !== b.line) return a.line - b.line;
264
+ return a.column - b.column;
265
+ });
266
+
267
+ return {
268
+ matches: matches.slice(0, maxMatches),
269
+ truncated,
270
+ truncatedBy,
271
+ scannedBytes,
272
+ scannedFiles,
273
+ scannedEntries,
274
+ filesSkippedBinary,
275
+ filesSkippedOversize,
276
+ };
277
+ }