berry-agent 0.1.0-alpha.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 (401) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +96 -0
  3. package/dist/.api-emit.stamp +1 -0
  4. package/dist/.build-meta.json +6 -0
  5. package/dist/agent/events.js +1 -0
  6. package/dist/agent/index.js +2 -0
  7. package/dist/agent/loop.js +139 -0
  8. package/dist/agent/queue.js +119 -0
  9. package/dist/agent/stream.js +62 -0
  10. package/dist/agent/tools-batch.js +221 -0
  11. package/dist/agent/types.js +8 -0
  12. package/dist/api/berry-agent-llm.d.ts +18 -0
  13. package/dist/api/berry-agent.d.ts +9 -0
  14. package/dist/api/surface.json +1632 -0
  15. package/dist/api/tsconfig.paths.json +12 -0
  16. package/dist/api/typebox-compile.d.ts +2 -0
  17. package/dist/api/typebox-value.d.ts +2 -0
  18. package/dist/api/typebox.d.ts +2 -0
  19. package/dist/browser/cdp.js +256 -0
  20. package/dist/browser/codes.js +31 -0
  21. package/dist/browser/discover.js +107 -0
  22. package/dist/browser/engine.js +169 -0
  23. package/dist/browser/index.js +17 -0
  24. package/dist/browser/install.js +342 -0
  25. package/dist/browser/page.js +321 -0
  26. package/dist/browser/service.js +158 -0
  27. package/dist/browser/tools.js +198 -0
  28. package/dist/browser/types.js +81 -0
  29. package/dist/channels/ask-queue.js +82 -0
  30. package/dist/channels/commands.js +129 -0
  31. package/dist/channels/engine/cell.js +143 -0
  32. package/dist/channels/engine/diff.js +143 -0
  33. package/dist/channels/engine/engine.js +416 -0
  34. package/dist/channels/engine/index.js +8 -0
  35. package/dist/channels/engine/input-keys.js +86 -0
  36. package/dist/channels/engine/input.js +596 -0
  37. package/dist/channels/engine/memory-io.js +71 -0
  38. package/dist/channels/engine/process-io.js +38 -0
  39. package/dist/channels/engine/types.js +17 -0
  40. package/dist/channels/engine/width.js +122 -0
  41. package/dist/channels/index.js +15 -0
  42. package/dist/channels/registry.js +56 -0
  43. package/dist/channels/sdk/admit.js +29 -0
  44. package/dist/channels/sdk/backend.js +102 -0
  45. package/dist/channels/sdk/cursor.js +56 -0
  46. package/dist/channels/sdk/index.js +20 -0
  47. package/dist/channels/sdk/jsonl.js +208 -0
  48. package/dist/channels/sdk/protocol.js +24 -0
  49. package/dist/channels/sdk/schema.js +91 -0
  50. package/dist/channels/sdk/wire-core.js +446 -0
  51. package/dist/channels/service.js +169 -0
  52. package/dist/channels/tui/autocomplete/autocomplete.js +42 -0
  53. package/dist/channels/tui/autocomplete/file-mentions.js +106 -0
  54. package/dist/channels/tui/autocomplete/popup.js +148 -0
  55. package/dist/channels/tui/autocomplete/provider.js +7 -0
  56. package/dist/channels/tui/autocomplete/token.js +68 -0
  57. package/dist/channels/tui/backend/ansi-rows.js +211 -0
  58. package/dist/channels/tui/backend/main-screen.js +207 -0
  59. package/dist/channels/tui/backend/osc.js +91 -0
  60. package/dist/channels/tui/backend/transcript.js +230 -0
  61. package/dist/channels/tui/backend/tui-backend.js +915 -0
  62. package/dist/channels/tui/editor/editor-model.js +543 -0
  63. package/dist/channels/tui/editor/editor-view.js +140 -0
  64. package/dist/channels/tui/editor/editor.js +275 -0
  65. package/dist/channels/tui/editor/undo-stack.js +35 -0
  66. package/dist/channels/tui/editor/visual-lines.js +126 -0
  67. package/dist/channels/tui/editor/word-nav.js +106 -0
  68. package/dist/channels/tui/history/history-viewer.js +403 -0
  69. package/dist/channels/tui/index.js +3 -0
  70. package/dist/channels/tui/layout.js +167 -0
  71. package/dist/channels/tui/markdown/blocks.js +111 -0
  72. package/dist/channels/tui/markdown/inline.js +75 -0
  73. package/dist/channels/tui/markdown/markdown.js +182 -0
  74. package/dist/channels/tui/memory/memory-viewer.js +540 -0
  75. package/dist/channels/tui/overlay/alt-screen.js +105 -0
  76. package/dist/channels/tui/overlay/overlay.js +57 -0
  77. package/dist/channels/tui/overlay/select-confirm.js +156 -0
  78. package/dist/channels/tui/panels/todo-panel.js +54 -0
  79. package/dist/channels/tui/panels/tool-progress-panel.js +93 -0
  80. package/dist/channels/tui/scroll/scroll-view.js +254 -0
  81. package/dist/channels/tui/status/status-line.js +76 -0
  82. package/dist/channels/tui/text.js +38 -0
  83. package/dist/channels/tui/theme.js +41 -0
  84. package/dist/channels/types.js +11 -0
  85. package/dist/channels/ui-core.js +209 -0
  86. package/dist/checkpoint/capture.js +45 -0
  87. package/dist/checkpoint/codes.js +33 -0
  88. package/dist/checkpoint/command.js +82 -0
  89. package/dist/checkpoint/gate.js +48 -0
  90. package/dist/checkpoint/index.js +20 -0
  91. package/dist/checkpoint/restore.js +172 -0
  92. package/dist/checkpoint/store.js +247 -0
  93. package/dist/checkpoint/types.js +4 -0
  94. package/dist/checkpoint/walk.js +146 -0
  95. package/dist/compaction/ccr-tools.js +112 -0
  96. package/dist/compaction/ccr.js +84 -0
  97. package/dist/compaction/codes.js +21 -0
  98. package/dist/compaction/index.js +18 -0
  99. package/dist/compaction/policy.js +200 -0
  100. package/dist/compaction/service.js +422 -0
  101. package/dist/compaction/slots.js +121 -0
  102. package/dist/compaction/types.js +10 -0
  103. package/dist/context/codes.js +40 -0
  104. package/dist/context/events.js +160 -0
  105. package/dist/context/index.js +13 -0
  106. package/dist/context/logger.js +136 -0
  107. package/dist/context/scope.js +182 -0
  108. package/dist/context/workspace.js +104 -0
  109. package/dist/contracts/agent-events.d.ts +73 -0
  110. package/dist/contracts/agent-events.js +16 -0
  111. package/dist/contracts/api.d.ts +249 -0
  112. package/dist/contracts/api.js +299 -0
  113. package/dist/contracts/approval.d.ts +37 -0
  114. package/dist/contracts/approval.js +9 -0
  115. package/dist/contracts/env-ref.d.ts +41 -0
  116. package/dist/contracts/env-ref.js +32 -0
  117. package/dist/contracts/errors.d.ts +56 -0
  118. package/dist/contracts/errors.js +266 -0
  119. package/dist/contracts/events.d.ts +70 -0
  120. package/dist/contracts/events.js +329 -0
  121. package/dist/contracts/index.d.ts +32 -0
  122. package/dist/contracts/index.js +26 -0
  123. package/dist/contracts/llm.d.ts +275 -0
  124. package/dist/contracts/llm.js +12 -0
  125. package/dist/contracts/messages.d.ts +63 -0
  126. package/dist/contracts/messages.js +65 -0
  127. package/dist/contracts/redact.d.ts +46 -0
  128. package/dist/contracts/redact.js +223 -0
  129. package/dist/contracts/tools.d.ts +197 -0
  130. package/dist/contracts/tools.js +33 -0
  131. package/dist/contracts/types.d.ts +302 -0
  132. package/dist/contracts/types.js +50 -0
  133. package/dist/contracts/ui.d.ts +122 -0
  134. package/dist/contracts/ui.js +15 -0
  135. package/dist/conversation/agent-service.js +61 -0
  136. package/dist/conversation/approval-wiring.js +82 -0
  137. package/dist/conversation/backoff.js +38 -0
  138. package/dist/conversation/codes.js +47 -0
  139. package/dist/conversation/control-tools.js +102 -0
  140. package/dist/conversation/control.js +210 -0
  141. package/dist/conversation/driver.js +922 -0
  142. package/dist/conversation/index.js +12 -0
  143. package/dist/conversation/model-visible.js +169 -0
  144. package/dist/conversation/open-tools.js +126 -0
  145. package/dist/conversation/reseed.js +132 -0
  146. package/dist/conversation/sessions.js +215 -0
  147. package/dist/conversation/todo.js +203 -0
  148. package/dist/conversation/types.js +35 -0
  149. package/dist/conversation/wiring.js +204 -0
  150. package/dist/credentials/codes.js +59 -0
  151. package/dist/credentials/commands.js +186 -0
  152. package/dist/credentials/env-ref.js +34 -0
  153. package/dist/credentials/index.js +28 -0
  154. package/dist/credentials/migration.js +18 -0
  155. package/dist/credentials/oauth.js +266 -0
  156. package/dist/credentials/refresh.js +152 -0
  157. package/dist/credentials/secrets.js +118 -0
  158. package/dist/credentials/types.js +30 -0
  159. package/dist/exec/bash.js +279 -0
  160. package/dist/exec/codes.js +37 -0
  161. package/dist/exec/env.js +99 -0
  162. package/dist/exec/environment.js +39 -0
  163. package/dist/exec/git-guard.js +623 -0
  164. package/dist/exec/index.js +24 -0
  165. package/dist/exec/registry.js +120 -0
  166. package/dist/exec/spawn.js +281 -0
  167. package/dist/exec/tail.js +77 -0
  168. package/dist/exec/types.js +5 -0
  169. package/dist/goal/codes.js +33 -0
  170. package/dist/goal/command.js +175 -0
  171. package/dist/goal/fold.js +121 -0
  172. package/dist/goal/gates.js +122 -0
  173. package/dist/goal/index.js +22 -0
  174. package/dist/goal/migration.js +48 -0
  175. package/dist/goal/service.js +501 -0
  176. package/dist/goal/todo-tool.js +174 -0
  177. package/dist/goal/types.js +1 -0
  178. package/dist/goal/update-tool.js +46 -0
  179. package/dist/host/approval-cmd.js +276 -0
  180. package/dist/host/assembly.js +1146 -0
  181. package/dist/host/boot-failures.js +104 -0
  182. package/dist/host/budget-advisory.js +74 -0
  183. package/dist/host/budget-broadcast.js +50 -0
  184. package/dist/host/builtins.js +54 -0
  185. package/dist/host/cli.js +544 -0
  186. package/dist/host/codes.js +120 -0
  187. package/dist/host/config-schema.js +195 -0
  188. package/dist/host/conversation-stack.js +654 -0
  189. package/dist/host/core-plugins.js +1685 -0
  190. package/dist/host/credentials-cmd.js +42 -0
  191. package/dist/host/disclosure.js +30 -0
  192. package/dist/host/dispatch.js +105 -0
  193. package/dist/host/doors-cmd.js +154 -0
  194. package/dist/host/dump-config.js +44 -0
  195. package/dist/host/hook-dispatch-guard.js +27 -0
  196. package/dist/host/import-gate.js +153 -0
  197. package/dist/host/index.js +54 -0
  198. package/dist/host/issue-session.js +241 -0
  199. package/dist/host/loader.js +372 -0
  200. package/dist/host/main.js +161 -0
  201. package/dist/host/manifest.js +381 -0
  202. package/dist/host/mcp-entry.js +53 -0
  203. package/dist/host/plugin-boot.js +952 -0
  204. package/dist/host/plugin-context.js +672 -0
  205. package/dist/host/plugin-install.js +480 -0
  206. package/dist/host/plugin-reload.js +124 -0
  207. package/dist/host/plugin-store.js +462 -0
  208. package/dist/host/plugin-tools.js +359 -0
  209. package/dist/host/plugin-uninstall.js +257 -0
  210. package/dist/host/plugins-cmd.js +302 -0
  211. package/dist/host/plugins-command.js +130 -0
  212. package/dist/host/plugins-config.js +195 -0
  213. package/dist/host/prompt-sections.js +133 -0
  214. package/dist/host/run-entry.js +544 -0
  215. package/dist/host/runtime.js +209 -0
  216. package/dist/host/scheduler-tick.js +294 -0
  217. package/dist/host/serve-daemon.js +501 -0
  218. package/dist/host/serve-entry.js +279 -0
  219. package/dist/host/session-anchor.js +41 -0
  220. package/dist/host/sessions-cmd.js +219 -0
  221. package/dist/host/sessions-face.js +47 -0
  222. package/dist/host/settings-store.js +112 -0
  223. package/dist/host/signals.js +69 -0
  224. package/dist/host/single-instance.js +105 -0
  225. package/dist/host/subagent-factory.js +291 -0
  226. package/dist/host/testkit/harness.js +283 -0
  227. package/dist/host/testkit/index.js +14 -0
  228. package/dist/host/testkit/matrix.js +302 -0
  229. package/dist/host/tool-policy-store.js +177 -0
  230. package/dist/host/triggers.js +212 -0
  231. package/dist/host/tui-entry.js +216 -0
  232. package/dist/host/webui-bridge.js +165 -0
  233. package/dist/issue/codes.js +38 -0
  234. package/dist/issue/filter.js +146 -0
  235. package/dist/issue/github.js +116 -0
  236. package/dist/issue/index.js +17 -0
  237. package/dist/issue/mount.js +70 -0
  238. package/dist/issue/poll.js +58 -0
  239. package/dist/issue/service.js +341 -0
  240. package/dist/issue/tools.js +70 -0
  241. package/dist/issue/types.js +34 -0
  242. package/dist/issue/webhook.js +126 -0
  243. package/dist/llm/codes.js +77 -0
  244. package/dist/llm/complete.js +196 -0
  245. package/dist/llm/events.js +15 -0
  246. package/dist/llm/index.js +24 -0
  247. package/dist/llm/inflight.js +65 -0
  248. package/dist/llm/model-id.js +58 -0
  249. package/dist/llm/provider-face.d.ts +32 -0
  250. package/dist/llm/provider-face.js +34 -0
  251. package/dist/llm/recovery.js +146 -0
  252. package/dist/llm/runtime.js +46 -0
  253. package/dist/llm/stream-fn.js +147 -0
  254. package/dist/lsp/codes.js +31 -0
  255. package/dist/lsp/connection.js +147 -0
  256. package/dist/lsp/frame.js +110 -0
  257. package/dist/lsp/index.js +16 -0
  258. package/dist/lsp/inject.js +58 -0
  259. package/dist/lsp/instance.js +264 -0
  260. package/dist/lsp/service.js +522 -0
  261. package/dist/lsp/tools.js +53 -0
  262. package/dist/lsp/types.js +201 -0
  263. package/dist/mcp/bridge.js +222 -0
  264. package/dist/mcp/codes.js +21 -0
  265. package/dist/mcp/index.js +19 -0
  266. package/dist/mcp/jsonrpc.js +230 -0
  267. package/dist/mcp/service.js +125 -0
  268. package/dist/mcp/tools.js +145 -0
  269. package/dist/mcp/types.js +108 -0
  270. package/dist/memory/cite.js +131 -0
  271. package/dist/memory/codes.js +50 -0
  272. package/dist/memory/command.js +71 -0
  273. package/dist/memory/consolidate.js +219 -0
  274. package/dist/memory/cycle.js +109 -0
  275. package/dist/memory/dao.js +878 -0
  276. package/dist/memory/diff.js +238 -0
  277. package/dist/memory/extract.js +181 -0
  278. package/dist/memory/fts.js +58 -0
  279. package/dist/memory/index.js +42 -0
  280. package/dist/memory/inject.js +354 -0
  281. package/dist/memory/merge.js +178 -0
  282. package/dist/memory/migration.js +132 -0
  283. package/dist/memory/pollution.js +59 -0
  284. package/dist/memory/port.js +385 -0
  285. package/dist/memory/review.js +201 -0
  286. package/dist/memory/scan.js +89 -0
  287. package/dist/memory/tools.js +508 -0
  288. package/dist/memory/types.js +140 -0
  289. package/dist/obs/codes.js +38 -0
  290. package/dist/obs/db.js +115 -0
  291. package/dist/obs/index.js +20 -0
  292. package/dist/obs/rollup.js +61 -0
  293. package/dist/obs/service.js +363 -0
  294. package/dist/obs/session-tools.js +282 -0
  295. package/dist/obs/session-view.js +225 -0
  296. package/dist/obs/tool.js +97 -0
  297. package/dist/obs/types.js +1 -0
  298. package/dist/persist/audit.js +63 -0
  299. package/dist/persist/aux.js +39 -0
  300. package/dist/persist/codes.js +35 -0
  301. package/dist/persist/index.js +30 -0
  302. package/dist/persist/load-history.js +50 -0
  303. package/dist/persist/migrations.js +37 -0
  304. package/dist/persist/paths.js +108 -0
  305. package/dist/persist/persistence.js +229 -0
  306. package/dist/persist/schema.js +99 -0
  307. package/dist/persist/secret-box.js +95 -0
  308. package/dist/persist/store.js +706 -0
  309. package/dist/persist/write-behind.js +238 -0
  310. package/dist/safety/approval.js +171 -0
  311. package/dist/safety/bwrap.js +123 -0
  312. package/dist/safety/codes.js +57 -0
  313. package/dist/safety/danger.js +505 -0
  314. package/dist/safety/gate.js +226 -0
  315. package/dist/safety/index.js +24 -0
  316. package/dist/safety/presets.js +73 -0
  317. package/dist/safety/roots.js +139 -0
  318. package/dist/safety/sandbox.js +214 -0
  319. package/dist/safety/seatbelt.js +88 -0
  320. package/dist/safety/sensitive.js +34 -0
  321. package/dist/safety/tool-policy.js +203 -0
  322. package/dist/safety/types.js +13 -0
  323. package/dist/scheduler/codes.js +54 -0
  324. package/dist/scheduler/cron-backend.js +135 -0
  325. package/dist/scheduler/engine.js +280 -0
  326. package/dist/scheduler/gates.js +60 -0
  327. package/dist/scheduler/index.js +25 -0
  328. package/dist/scheduler/migration.js +22 -0
  329. package/dist/scheduler/runner.js +171 -0
  330. package/dist/scheduler/schedule.js +193 -0
  331. package/dist/scheduler/service.js +283 -0
  332. package/dist/scheduler/tick.js +139 -0
  333. package/dist/scheduler/types.js +1 -0
  334. package/dist/sdk/http.js +865 -0
  335. package/dist/sdk/index.js +22 -0
  336. package/dist/sdk/mcp.js +289 -0
  337. package/dist/sdk/plugin-route-registry.js +126 -0
  338. package/dist/sdk/plugin-routes.js +149 -0
  339. package/dist/sdk/security.js +116 -0
  340. package/dist/sdk/types.js +32 -0
  341. package/dist/session/budget.js +159 -0
  342. package/dist/session/codes.js +40 -0
  343. package/dist/session/derive.js +180 -0
  344. package/dist/session/event-data.js +1 -0
  345. package/dist/session/fork.js +72 -0
  346. package/dist/session/import-gates.js +148 -0
  347. package/dist/session/index.js +18 -0
  348. package/dist/session/recover.js +103 -0
  349. package/dist/session/session.js +279 -0
  350. package/dist/session/snapshot.js +107 -0
  351. package/dist/skills/agents.js +242 -0
  352. package/dist/skills/codes.js +51 -0
  353. package/dist/skills/discovery.js +266 -0
  354. package/dist/skills/frontmatter.js +215 -0
  355. package/dist/skills/index.js +28 -0
  356. package/dist/skills/load.js +118 -0
  357. package/dist/skills/manage.js +201 -0
  358. package/dist/skills/registry.js +138 -0
  359. package/dist/skills/render.js +124 -0
  360. package/dist/skills/sections.js +171 -0
  361. package/dist/skills/types.js +21 -0
  362. package/dist/subagent/codes.js +50 -0
  363. package/dist/subagent/declarative.js +55 -0
  364. package/dist/subagent/index.js +19 -0
  365. package/dist/subagent/notify.js +51 -0
  366. package/dist/subagent/provide.js +7 -0
  367. package/dist/subagent/registry.js +149 -0
  368. package/dist/subagent/service.js +338 -0
  369. package/dist/subagent/surface.js +44 -0
  370. package/dist/subagent/tool.js +153 -0
  371. package/dist/subagent/types.js +25 -0
  372. package/dist/tools/apply-patch.js +164 -0
  373. package/dist/tools/codes.js +109 -0
  374. package/dist/tools/fs.js +501 -0
  375. package/dist/tools/index.js +17 -0
  376. package/dist/tools/observed.js +99 -0
  377. package/dist/tools/pipeline.js +176 -0
  378. package/dist/tools/protected-read.js +54 -0
  379. package/dist/tools/registry.js +250 -0
  380. package/dist/tools/search.js +451 -0
  381. package/dist/tools/worktree.js +327 -0
  382. package/dist/web/codes.js +37 -0
  383. package/dist/web/gate.js +43 -0
  384. package/dist/web/hygiene.js +195 -0
  385. package/dist/web/index.js +16 -0
  386. package/dist/web/service.js +207 -0
  387. package/dist/web/ssrf-guard.js +44 -0
  388. package/dist/web/tool.js +76 -0
  389. package/dist/web/types.js +10 -0
  390. package/dist/webui/assets/index-B-t2O8gh.js +22 -0
  391. package/dist/webui/assets/index-DOlgSZ9Q.css +2 -0
  392. package/dist/webui/index.html +14 -0
  393. package/dist/webui/index.js +14 -0
  394. package/dist/webui/server.js +549 -0
  395. package/dist/webui/types.js +38 -0
  396. package/examples/README.md +26 -0
  397. package/examples/minimal-code-plugin/entry.js +43 -0
  398. package/examples/minimal-code-plugin/package.json +12 -0
  399. package/examples/pure-skill-pack/package.json +14 -0
  400. package/examples/pure-skill-pack/skills/markdown-table/SKILL.md +32 -0
  401. package/package.json +86 -0
@@ -0,0 +1,623 @@
1
+ /**
2
+ * exec 件 git-guard——bash 侧 .git 拦截面(04 §252 桥条款两腿执法,成熟度
3
+ * 缺口 #9 落码批;此前「先有词后有法」挂账的执法批本体)。
4
+ *
5
+ * 腿一(重定向目标扫描):词法扫描 bash 命令串全部重定向目标,命中 .git
6
+ * 树 = `EXEC_GIT_REDIRECT_DENIED` 硬拒(消费位 bash 工具件,升权审批前)。
7
+ * 腿二(白名单分类):静态洁净 git 形判定——非豁免形由调用方叠 workspace
8
+ * `.git` 写 deny(运行时兜底关 tee/python/sed -i/变量间接等全部非重定向
9
+ * 向量);豁免形 + worktree 锚定补 backing gitdir 可写根。
10
+ *
11
+ * 纯逻辑件(零 spawn);fs 面 = statSync/readFileSync(worktree 探测)与
12
+ * canonicalPath(最近存在祖先回退形——符号链/大小写归一判)。词法诚实边界
13
+ * 见各函数注记:静态不可解的目标(`> $X` 形)由腿二运行时 deny 兜底,双层
14
+ * 分层即本件的设计本体。
15
+ */
16
+ import { readFileSync, statSync } from 'node:fs';
17
+ import { join, resolve as resolvePath, sep } from 'node:path';
18
+ import { canonicalPath } from '../safety/index.js';
19
+ import { GIT_METADATA_COMMANDS } from '../tools/index.js';
20
+ /* ------------------------------------------------------------------ */
21
+ /* 词法基元(引号感知 word 读取——两腿共用) */
22
+ /* ------------------------------------------------------------------ */
23
+ /** word 止步字符集(未引号态:空白/算子/括号/换行/反引号——替换域边界) */
24
+ const WORD_STOP = new Set([' ', '\t', '\n', ';', '|', '&', '<', '>', '(', ')', '`']);
25
+ /**
26
+ * 读一个 word(引号感知拼接 unquote——`.g'it'/config` 对抗拼接形归一)。
27
+ * 从 start 起跳过前导空白后积累至止步字符;单引号段内容原样拼接、双引号段
28
+ * 反斜杠转义消费(`$(`/`` ` `` 在双引号内对词法是普通字符——含展开的目标词
29
+ * 静态不可解,交词面判 + 腿二运行时兜底)。
30
+ * @returns word = '' 表示无词(算子紧邻算子形);next = 消费后位置
31
+ */
32
+ function readWord(command, start) {
33
+ let i = start;
34
+ while (i < command.length && (command[i] === ' ' || command[i] === '\t'))
35
+ i++;
36
+ let word = '';
37
+ while (i < command.length) {
38
+ const ch = command[i];
39
+ if (WORD_STOP.has(ch))
40
+ break;
41
+ if (ch === "'") {
42
+ // 单引号段:至闭合单引号原样拼接(无转义语义)
43
+ const close = command.indexOf("'", i + 1);
44
+ if (close === -1) {
45
+ word += command.slice(i + 1);
46
+ i = command.length;
47
+ break;
48
+ }
49
+ word += command.slice(i + 1, close);
50
+ i = close + 1;
51
+ continue;
52
+ }
53
+ if (ch === '"') {
54
+ i++;
55
+ while (i < command.length && command[i] !== '"') {
56
+ if (command[i] === '\\' && i + 1 < command.length) {
57
+ word += command[i + 1];
58
+ i += 2;
59
+ continue;
60
+ }
61
+ word += command[i];
62
+ i++;
63
+ }
64
+ i++; // 闭合双引号(缺席即串尾——未闭合引号形由 bash 自身报错)
65
+ continue;
66
+ }
67
+ if (ch === '\\' && i + 1 < command.length) {
68
+ word += command[i + 1]; // 未引号反斜杠转义(文件名含空格支持)
69
+ i += 2;
70
+ continue;
71
+ }
72
+ word += ch;
73
+ i++;
74
+ }
75
+ return { word, next: i };
76
+ }
77
+ /** 段内 token 化(readWord 循环;算子字符段跳过——`2>&1` 形读出 2/1 不影响词干判) */
78
+ function tokenizeWords(segment) {
79
+ const tokens = [];
80
+ let i = 0;
81
+ while (i < segment.length) {
82
+ const ch = segment[i];
83
+ if (ch === ' ' || ch === '\t' || WORD_STOP.has(ch)) {
84
+ i++;
85
+ continue;
86
+ }
87
+ const { word, next } = readWord(segment, i);
88
+ if (word === '') {
89
+ i++;
90
+ continue;
91
+ }
92
+ tokens.push(word);
93
+ i = next;
94
+ }
95
+ return tokens;
96
+ }
97
+ /** 虚拟 cwd 词法解析(`~` 形不可解保留原词——词面判照走;path.resolve 归一 `..`) */
98
+ function resolveWord(vcwd, word) {
99
+ if (word.startsWith('~'))
100
+ return word;
101
+ if (word.startsWith('/') || word.startsWith(sep))
102
+ return resolvePath(word);
103
+ return resolvePath(vcwd, word);
104
+ }
105
+ /**
106
+ * 扫描 bash 命令串的全部重定向目标(写向量闭算子族)。
107
+ *
108
+ * 覆盖算子:`>` `>>` `>|` `<>` `&>` `&>>`(`N>`/`N>>` 数字前置形被裸 `>`
109
+ * 分支自然覆盖);`2>&1` fd 复制形跳过(数字跟随 = 非文件目标);`<<`/`<<-`
110
+ * heredoc 体整段跳过(体是数据非执行——防体内 `>` 假阳性)、`<<<` here-string
111
+ * 词为数据跳过;`<` 普通输入重定向跳过(读向量不在本面)。
112
+ *
113
+ * 命令替换嵌套域(裸/双引号内 `$(`、反引号)照扫——替换体里的 `>` 是真
114
+ * 算子(`"$(cmd > .git/x)"` 是真写);嵌套域内 heredoc 不识别(体按文本扫
115
+ * 可致保守误拒,方向无害——04 §252 诚实边界)。
116
+ *
117
+ * 段内 `cd <静态词>` 漂移追踪:段边界(未引号 `;` `&&` `||` `|` `&` 换行)
118
+ * 处回看刚完段——`cd .git && > config` 漂移形同捕。漏追边界诚实成文:段内
119
+ * 含重定向等附加 token 的复合 cd 形不追(`cd x 2>/dev/null && > f` 用旧
120
+ * vcwd 解析——该构造下词面判缺席则由腿二 deny 兜底)。
121
+ */
122
+ export function scanRedirectionTargets(command, cwd) {
123
+ const targets = [];
124
+ let vcwd = cwd;
125
+ let segStart = 0;
126
+ let i = 0;
127
+ const n = command.length;
128
+ /** 待跳 heredoc 体(读到 <<[-] delim 后置位;下一个未引号换行起整段跳体) */
129
+ let pendingHeredoc;
130
+ /** 读目标词并记录(从 from 起跳空白) */
131
+ const recordTarget = (from) => {
132
+ const { word, next } = readWord(command, from);
133
+ if (word !== '')
134
+ targets.push({ raw: word, resolved: resolveWord(vcwd, word) });
135
+ return next;
136
+ };
137
+ /** 段边界:回看刚完段是否静态 cd → 词法漂移 vcwd */
138
+ const closeSegment = (boundaryStart, nextStart) => {
139
+ const seg = command.slice(segStart, boundaryStart);
140
+ const tokens = tokenizeWords(seg);
141
+ if (tokens.length === 2 && tokens[0] === 'cd' && !tokens[1].includes('$')) {
142
+ vcwd = resolveWord(vcwd, tokens[1]);
143
+ }
144
+ segStart = nextStart;
145
+ i = nextStart;
146
+ };
147
+ /**
148
+ * heredoc 体跳过(pendingHeredoc 在场时的换行触发):从 from(换行后)起
149
+ * 逐行至 delimiter 行(`<<-` 剥前导 tab);未闭合(delimiter 行缺席)余串
150
+ * 全跳(bash 自身报错不执行——跳过无害)。
151
+ * @returns 消费后位置
152
+ */
153
+ const skipHeredocBody = (from, delim, strip) => {
154
+ let lineStart = from;
155
+ while (lineStart <= n) {
156
+ let lineEnd = command.indexOf('\n', lineStart);
157
+ if (lineEnd === -1)
158
+ lineEnd = n;
159
+ let ls = lineStart;
160
+ if (strip)
161
+ while (ls < lineEnd && command[ls] === '\t')
162
+ ls++;
163
+ if (command.slice(ls, lineEnd) === delim)
164
+ return Math.min(lineEnd + 1, n);
165
+ if (lineEnd >= n)
166
+ return n;
167
+ lineStart = lineEnd + 1;
168
+ }
169
+ return n;
170
+ };
171
+ while (i < n) {
172
+ const ch = command[i];
173
+ // ---- 引号态 ----
174
+ if (ch === "'") {
175
+ const close = command.indexOf("'", i + 1);
176
+ i = close === -1 ? n : close + 1;
177
+ continue;
178
+ }
179
+ if (ch === '"') {
180
+ i++;
181
+ while (i < n && command[i] !== '"') {
182
+ if (command[i] === '$' && command[i + 1] === '(') {
183
+ i = scanNested(command, i + 2, targets, () => vcwd);
184
+ continue;
185
+ }
186
+ if (command[i] === '\\' && i + 1 < n) {
187
+ i += 2;
188
+ continue;
189
+ }
190
+ i++;
191
+ }
192
+ i++;
193
+ continue;
194
+ }
195
+ if (ch === '\\' && i + 1 < n) {
196
+ i += 2;
197
+ continue;
198
+ }
199
+ // ---- 命令替换(裸 $() / 反引号) ----
200
+ if (ch === '$' && command[i + 1] === '(') {
201
+ i = scanNested(command, i + 2, targets, () => vcwd);
202
+ continue;
203
+ }
204
+ if (ch === '`') {
205
+ i = scanNestedBacktick(command, i + 1, targets, () => vcwd);
206
+ continue;
207
+ }
208
+ // ---- heredoc / 输入重定向族 ----
209
+ if (ch === '<') {
210
+ const c1 = command[i + 1];
211
+ if (c1 === '<') {
212
+ const c2 = command[i + 2];
213
+ if (c2 === '<') {
214
+ // here-string:词是数据非文件——跳过(本行其余照扫)
215
+ i += 3;
216
+ i = readWord(command, i).next;
217
+ continue;
218
+ }
219
+ // heredoc:只消费 <<[-] 与 delimiter 词——本行其余照扫(`cat << EOF
220
+ // > .git/x` 同行重定向是真写向量必须捕获);体从下一个未引号换行起跳
221
+ const strip = c2 === '-';
222
+ i += 2 + (strip ? 1 : 0);
223
+ const { word: delim, next } = readWord(command, i);
224
+ i = next;
225
+ if (delim !== '')
226
+ pendingHeredoc = { delim, strip };
227
+ continue;
228
+ }
229
+ if (c1 === '>') {
230
+ // <> 读写重定向——写向量,记录
231
+ i += 2;
232
+ i = recordTarget(i);
233
+ continue;
234
+ }
235
+ // 普通输入重定向:读向量不记录
236
+ i += 1;
237
+ i = readWord(command, i).next;
238
+ continue;
239
+ }
240
+ // ---- 输出重定向族 ----
241
+ if (ch === '>') {
242
+ const c1 = command[i + 1];
243
+ if (c1 === '>' || c1 === '|') {
244
+ i += 2;
245
+ i = recordTarget(i);
246
+ continue;
247
+ }
248
+ if (c1 === '&') {
249
+ // >& 形:后随数字/- = fd 复制(非文件目标)跳过;否则文件目标
250
+ let j = i + 2;
251
+ while (j < n && (command[j] === ' ' || command[j] === '\t'))
252
+ j++;
253
+ const jc = j < n ? command[j] : '';
254
+ if (jc !== '' && ((jc >= '0' && jc <= '9') || jc === '-')) {
255
+ while (j < n && ((command[j] >= '0' && command[j] <= '9') || command[j] === '-'))
256
+ j++;
257
+ i = j;
258
+ continue;
259
+ }
260
+ i += 2;
261
+ i = recordTarget(i);
262
+ continue;
263
+ }
264
+ i += 1;
265
+ i = recordTarget(i);
266
+ continue;
267
+ }
268
+ // ---- &> 族与段边界 ----
269
+ if (ch === '&') {
270
+ if (command[i + 1] === '>') {
271
+ const c2 = command[i + 2];
272
+ i += 2 + (c2 === '>' ? 1 : 0);
273
+ i = recordTarget(i);
274
+ continue;
275
+ }
276
+ closeSegment(i, i + (command[i + 1] === '&' ? 2 : 1)); // && 或后台 &
277
+ continue;
278
+ }
279
+ if (ch === ';' || ch === '|') {
280
+ const w = ch === '|' && command[i + 1] === '|' ? 2 : 1;
281
+ closeSegment(i, i + w);
282
+ continue;
283
+ }
284
+ if (ch === '\n') {
285
+ // 换行:pending heredoc 在场先跳体(体是数据);否则段边界
286
+ if (pendingHeredoc !== undefined) {
287
+ const { delim, strip } = pendingHeredoc;
288
+ pendingHeredoc = undefined;
289
+ i = skipHeredocBody(i + 1, delim, strip);
290
+ segStart = i; // 体后新段(cd 漂移无跨体语义)
291
+ continue;
292
+ }
293
+ closeSegment(i, i + 1);
294
+ continue;
295
+ }
296
+ i++;
297
+ }
298
+ return targets;
299
+ }
300
+ /**
301
+ * 嵌套命令替换域扫描(`$(` 开):至配对 `)` 止(depth 计数、引号感知)。
302
+ * 域内重定向照记(真实执行域);heredoc 不识别(保守诚实边界);反引号域
303
+ * 递归。vcwd 取值用闭包读外层当前值(域内 cd 不回写外层——子壳语义保守)。
304
+ * @returns 消费后位置(配对 `)` 之后)
305
+ */
306
+ function scanNested(command, from, targets, vcwdOf) {
307
+ let depth = 1;
308
+ let i = from;
309
+ const n = command.length;
310
+ const recordTarget = (fromWord) => {
311
+ const { word, next } = readWord(command, fromWord);
312
+ if (word !== '')
313
+ targets.push({ raw: word, resolved: resolveWord(vcwdOf(), word) });
314
+ return next;
315
+ };
316
+ while (i < n) {
317
+ const ch = command[i];
318
+ if (ch === ')') {
319
+ depth--;
320
+ if (depth === 0)
321
+ return i + 1;
322
+ i++;
323
+ continue;
324
+ }
325
+ if (ch === '(') {
326
+ depth++;
327
+ i++;
328
+ continue;
329
+ }
330
+ if (ch === "'") {
331
+ const close = command.indexOf("'", i + 1);
332
+ i = close === -1 ? n : close + 1;
333
+ continue;
334
+ }
335
+ if (ch === '"') {
336
+ i++;
337
+ while (i < n && command[i] !== '"') {
338
+ if (command[i] === '$' && command[i + 1] === '(') {
339
+ i = scanNested(command, i + 2, targets, vcwdOf);
340
+ continue;
341
+ }
342
+ if (command[i] === '\\' && i + 1 < n) {
343
+ i += 2;
344
+ continue;
345
+ }
346
+ i++;
347
+ }
348
+ i++;
349
+ continue;
350
+ }
351
+ if (ch === '\\' && i + 1 < n) {
352
+ i += 2;
353
+ continue;
354
+ }
355
+ if (ch === '`') {
356
+ i = scanNestedBacktick(command, i + 1, targets, vcwdOf);
357
+ continue;
358
+ }
359
+ if (ch === '>') {
360
+ const c1 = command[i + 1];
361
+ if (c1 === '>' || c1 === '|')
362
+ i += 2;
363
+ else if (c1 === '&') {
364
+ let j = i + 2;
365
+ while (j < n && (command[j] === ' ' || command[j] === '\t'))
366
+ j++;
367
+ const jc = j < n ? command[j] : '';
368
+ if (jc !== '' && ((jc >= '0' && jc <= '9') || jc === '-')) {
369
+ while (j < n && ((command[j] >= '0' && command[j] <= '9') || command[j] === '-'))
370
+ j++;
371
+ i = j;
372
+ continue;
373
+ }
374
+ i += 2;
375
+ i = recordTarget(i);
376
+ continue;
377
+ }
378
+ else
379
+ i += 1;
380
+ i = recordTarget(i);
381
+ continue;
382
+ }
383
+ if (ch === '<') {
384
+ // 域内输入重定向/heredoc:读向量跳词(heredoc 不识别——保守边界)
385
+ i += 1;
386
+ i = readWord(command, i).next;
387
+ continue;
388
+ }
389
+ i++;
390
+ }
391
+ return n; // 未闭合(bash 自身会报错)——余串已扫完
392
+ }
393
+ /** 反引号替换域扫描:至闭合反引号止(`$(` 域递归;重定向照记) */
394
+ function scanNestedBacktick(command, from, targets, vcwdOf) {
395
+ let i = from;
396
+ const n = command.length;
397
+ const recordTarget = (fromWord) => {
398
+ const { word, next } = readWord(command, fromWord);
399
+ if (word !== '')
400
+ targets.push({ raw: word, resolved: resolveWord(vcwdOf(), word) });
401
+ return next;
402
+ };
403
+ while (i < n) {
404
+ const ch = command[i];
405
+ if (ch === '`')
406
+ return i + 1;
407
+ if (ch === '\\') {
408
+ if (command[i + 1] === '`' || command[i + 1] === '\\') {
409
+ i += 2;
410
+ continue;
411
+ }
412
+ i++;
413
+ continue;
414
+ }
415
+ if (ch === "'") {
416
+ const close = command.indexOf("'", i + 1);
417
+ i = close === -1 ? n : close + 1;
418
+ continue;
419
+ }
420
+ if (ch === '$' && command[i + 1] === '(') {
421
+ i = scanNested(command, i + 2, targets, vcwdOf);
422
+ continue;
423
+ }
424
+ if (ch === '>') {
425
+ const c1 = command[i + 1];
426
+ if (c1 === '>' || c1 === '|')
427
+ i += 2;
428
+ else
429
+ i += 1;
430
+ i = recordTarget(i);
431
+ continue;
432
+ }
433
+ i++;
434
+ }
435
+ return n;
436
+ }
437
+ /* ------------------------------------------------------------------ */
438
+ /* .git 判定与违例收集 */
439
+ /* ------------------------------------------------------------------ */
440
+ /**
441
+ * 路径 `/` 分段中恰有 `.git` 组件(`.gitignore`/`.github`/`x.git` 不误伤;
442
+ * 裸相对形 `.git` 同命中)。词面判——`$X/.git/config` 形字面命中即真。
443
+ */
444
+ export function pathHasGitComponent(p) {
445
+ return p.split(/[\\/]/).includes('.git');
446
+ }
447
+ /**
448
+ * 腿一判定:命令的全部重定向目标中落在 .git 树内者(返回违例词面清单)。
449
+ * 两重判:虚拟 cwd 词法解析判(resolved 词面组件判——path.resolve 归一
450
+ * `..`(`.git/../out.txt` 逸出形正确放过)、`cd .git && > config` 漂移形、
451
+ * `$X/.git/config` 字面拼接形命中)+ canonical 判(最近存在祖先符号链解
452
+ * 析——macOS 大小写不敏感归一;解析失败静默降级词法判,不虚构路径)。
453
+ */
454
+ export function findGitRedirectViolations(command, cwd) {
455
+ const hits = [];
456
+ for (const target of scanRedirectionTargets(command, cwd)) {
457
+ if (pathHasGitComponent(target.resolved)) {
458
+ hits.push(target.raw);
459
+ continue;
460
+ }
461
+ try {
462
+ if (pathHasGitComponent(canonicalPath(target.resolved)))
463
+ hits.push(target.raw);
464
+ }
465
+ catch {
466
+ // canonical 化失败(病理形态)——词法判已过,不虚构
467
+ }
468
+ }
469
+ return hits;
470
+ }
471
+ /* ------------------------------------------------------------------ */
472
+ /* 腿二:静态洁净白名单分类 */
473
+ /* ------------------------------------------------------------------ */
474
+ /**
475
+ * 静态洁净 git 白名单形判定(04 §252 腿二——豁免 = 策略不携 workspace .git
476
+ * 写 deny + worktree 授予腿)。
477
+ *
478
+ * 洁净形 = 段(未引号 `;` `&&` `||` `|` `&` 换行 切分)全部为:
479
+ * - 空段;或
480
+ * - `cd` 恰一参(静态词);或
481
+ * - 首词 basename = `git` 且次词 ∈ `GIT_METADATA_COMMANDS`(`git -C sub
482
+ * commit` 全局旗形失豁——词干判形不展开旗参,保守边界)。
483
+ * 且全串无**单引号外** `$` 与反引号(展开不可静态判定即失豁免——`git commit
484
+ * -m "$(cat f)"` 保守失豁、`-F` 直传替代)、无未引号 `(`/`)`(子壳失豁)。
485
+ *
486
+ * 重定向允许在豁免形内(`git log > out.txt` 豁免 ✓——.git 目标由腿一执法、
487
+ * 词面判缺席时非 .git 目标无害)。管道段各判(`git log | head` 两段各自词干
488
+ * 判——head 段非 git 形 → 整体非豁免 → deny 在场:只读命令不受写 deny 影响
489
+ * 照常工作)。glob/`~` 允许。
490
+ */
491
+ export function isGitMetadataExempt(command) {
492
+ const boundaries = []; // 段切片 [start, end)
493
+ let expansion = false; // 单引号外 $/反引号在场
494
+ let subshell = false; // 未引号括号在场
495
+ let segStart = 0;
496
+ let i = 0;
497
+ const n = command.length;
498
+ while (i < n) {
499
+ const ch = command[i];
500
+ if (ch === "'") {
501
+ const close = command.indexOf("'", i + 1);
502
+ i = close === -1 ? n : close + 1;
503
+ continue;
504
+ }
505
+ if (ch === '"') {
506
+ i++;
507
+ while (i < n && command[i] !== '"') {
508
+ const c = command[i];
509
+ if (c === '\\' && i + 1 < n) {
510
+ i += 2;
511
+ continue;
512
+ }
513
+ if (c === '$' || c === '`')
514
+ expansion = true; // 双引号内展开生效
515
+ i++;
516
+ }
517
+ i++;
518
+ continue;
519
+ }
520
+ if (ch === '\\') {
521
+ i += 2;
522
+ continue;
523
+ }
524
+ if (ch === '$' || ch === '`') {
525
+ expansion = true;
526
+ i++;
527
+ continue;
528
+ }
529
+ if (ch === '(' || ch === ')') {
530
+ subshell = true;
531
+ i++;
532
+ continue;
533
+ }
534
+ if (ch === '>') {
535
+ // 重定向算子不切段(`git log > out.txt` 豁免面);fd 复制形 `>&N` 的
536
+ // & 在此一并消费——防 `2>&1` 的 & 被下分支误切段边界
537
+ if (command[i + 1] === '&') {
538
+ let j = i + 2;
539
+ while (j < n && command[j] >= '0' && command[j] <= '9')
540
+ j++;
541
+ i = j;
542
+ continue;
543
+ }
544
+ i += command[i + 1] === '>' || command[i + 1] === '|' ? 2 : 1;
545
+ continue;
546
+ }
547
+ if (ch === '<') {
548
+ // 输入重定向/heredoc 算子不切段;`<>` 读写形两字符消费
549
+ i += command[i + 1] === '>' ? 2 : 1;
550
+ continue;
551
+ }
552
+ if (ch === ';' || ch === '\n' || ch === '&' || ch === '|') {
553
+ const w = (ch === '&' || ch === '|') && command[i + 1] === ch ? 2 : 1;
554
+ boundaries.push([segStart, i]);
555
+ i += w;
556
+ segStart = i;
557
+ continue;
558
+ }
559
+ i++;
560
+ }
561
+ boundaries.push([segStart, n]);
562
+ if (expansion || subshell)
563
+ return false;
564
+ for (const [start, end] of boundaries) {
565
+ const tokens = tokenizeWords(command.slice(start, end));
566
+ if (tokens.length === 0)
567
+ continue;
568
+ if (tokens[0] === 'cd') {
569
+ if (tokens.length !== 2)
570
+ return false; // cd 多参/带旗形保守失豁
571
+ continue;
572
+ }
573
+ const exe = tokens[0].split(/[\\/]/).pop();
574
+ if (exe !== 'git')
575
+ return false;
576
+ const verb = tokens[1];
577
+ if (verb === undefined || !GIT_METADATA_COMMANDS.includes(verb))
578
+ return false;
579
+ }
580
+ return true;
581
+ }
582
+ /* ------------------------------------------------------------------ */
583
+ /* worktree 授予腿(backing gitdir 探测) */
584
+ /* ------------------------------------------------------------------ */
585
+ /**
586
+ * worktree 形态探测(04 §252「worktree 绑定会话对锚定 worktree 的 git 命令
587
+ * 豁免」的授予腿):workspaceRoot `.git` 为**文件**(worktree 指针形)时读
588
+ * `gitdir:` 指针,返回**主仓 common git dir**(`<repo>/.git/`)的 canonical
589
+ * 路径。真跑证明返回 backing worktrees 目录不够——worktree 的 git 元数据写
590
+ * 落点两面:backing(HEAD/index/logs)在 common dir 内,而**对象库共享**
591
+ * (`git commit` 的 loose object 写主仓 `.git/objects/`、分支 ref 亦在主仓
592
+ * `.git/refs/`)——缺省可写根推导两者皆不可达(沙箱下 `git commit` 结构性
593
+ * 断链的真缺陷),由调用方将 common dir 并入 writableRoots 修复(backing
594
+ * 是其子路径,单条全覆盖)。主仓形态(`.git` 目录)/非 git 环境/解析失败 =
595
+ * undefined(主仓元数据写落 workspace 根内,本就可达)。
596
+ *
597
+ * common dir 解析序:backing 目录 `commondir` 文件(git worktree add 缺省产
598
+ * 物——相对 backing 的路径)→ 缺席时几何回退(`worktrees/<name>` 上两级)。
599
+ */
600
+ export function worktreeGitDir(workspaceRoot) {
601
+ try {
602
+ const dotGit = join(workspaceRoot, '.git');
603
+ if (!statSync(dotGit).isFile())
604
+ return undefined;
605
+ const content = readFileSync(dotGit, 'utf8');
606
+ const match = /^gitdir:\s*(\S+)/m.exec(content);
607
+ if (!match)
608
+ return undefined;
609
+ const backing = match[1];
610
+ try {
611
+ const common = readFileSync(join(backing, 'commondir'), 'utf8').trim();
612
+ if (common !== '')
613
+ return canonicalPath(resolvePath(backing, common));
614
+ }
615
+ catch {
616
+ // commondir 缺席(病理/手工 fixture 形)——几何回退
617
+ }
618
+ return canonicalPath(resolvePath(backing, '..', '..'));
619
+ }
620
+ catch {
621
+ return undefined; // 缺席/不可读 = 非探测形(fail-soft:授予腿不造值)
622
+ }
623
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * exec 件公开面(02 §4.1 席 16:spawn 管道 + bash 工具件 + environment 披露
3
+ * 段 git 摘要件;deps {contracts, context, safety, tools})。
4
+ *
5
+ * 装载态集成归批 12 装载面后装配批:core:exec 插件装载时经 scope.provide
6
+ * ('exec', { bashTool }) 供给 conversation 的 ExecToolService 结构契约(见
7
+ * conversation/types.ts——契约单源在对端,本件刻意不 import 对端:conversation
8
+ * 边表不可达 exec,反向引用会破 DAG 单向)。
9
+ */
10
+ import './codes.js';
11
+ export { BASH_TIMEOUT_DEFAULT_MS, BASH_TIMEOUT_MAX_MS, OUTPUT_TAIL_BYTES } from './types.js';
12
+ // 输出保尾 + env 白名单
13
+ export { OutputTail } from './tail.js';
14
+ export { buildChildEnv, DEFAULT_ENV_ALLOW } from './env.js';
15
+ // 登记簿与孤儿清扫
16
+ export { createProcessRegistry, sweepOrphans } from './registry.js';
17
+ // spawn 管道与平台真身
18
+ export { createSpawnPipeline, isPidAlive, killProcessTree, readCmdlineSync } from './spawn.js';
19
+ // bash 工具件
20
+ export { assertNoBackgroundCommand, createBashTool, discoverBash } from './bash.js';
21
+ // bash 侧 .git 拦截面(04 §252 桥条款两腿——成熟度缺口 #9 落码批)
22
+ export { findGitRedirectViolations, isGitMetadataExempt, pathHasGitComponent, scanRedirectionTargets, worktreeGitDir, } from './git-guard.js';
23
+ // environment 披露段 git 摘要
24
+ export { gitSummary } from './environment.js';