@reticlehq/server 3.7.0 → 3.8.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 (578) hide show
  1. package/SKILL.md +59 -68
  2. package/bin/reticle.js +9 -2
  3. package/dist/command/cli/adhoc-suite.js +1 -1
  4. package/dist/command/cli/adhoc-verdict.d.ts +12 -0
  5. package/dist/command/cli/adhoc-verdict.js +62 -4
  6. package/dist/command/cli/answers/gate-hook-message.d.ts +2 -0
  7. package/dist/command/cli/answers/gate-hook-message.js +5 -1
  8. package/dist/command/cli/auth/cloud-session.d.ts +11 -19
  9. package/dist/command/cli/auth/cloud-session.js +23 -0
  10. package/dist/command/cli/auth/device-flow.d.ts +3 -37
  11. package/dist/command/cli/cli-doctor.js +66 -21
  12. package/dist/command/cli/cli-flow-commands.js +53 -11
  13. package/dist/command/cli/cli-kill.d.ts +8 -0
  14. package/dist/command/cli/cli-kill.js +43 -3
  15. package/dist/command/cli/cli-parse-feedback.d.ts +4 -0
  16. package/dist/command/cli/cli-parse-feedback.js +13 -1
  17. package/dist/command/cli/cli-parse-grammar.d.ts +1 -2
  18. package/dist/command/cli/cli-parse-grammar.js +1 -6
  19. package/dist/command/cli/cli-parse-serve.d.ts +24 -0
  20. package/dist/command/cli/cli-parse-serve.js +124 -0
  21. package/dist/command/cli/cli-parse.d.ts +12 -11
  22. package/dist/command/cli/cli-parse.js +100 -123
  23. package/dist/command/cli/cli-usage.d.ts +0 -11
  24. package/dist/command/cli/cli-usage.js +43 -16
  25. package/dist/command/cli/cli-verify.d.ts +1 -1
  26. package/dist/command/cli/cli-verify.js +29 -14
  27. package/dist/command/cli/cloud-cli.js +27 -9
  28. package/dist/command/cli/cloud-kit.js +3 -3
  29. package/dist/command/cli/cloud-link.d.ts +1 -20
  30. package/dist/command/cli/cloud-link.js +2 -0
  31. package/dist/command/cli/cloud-login.d.ts +6 -2
  32. package/dist/command/cli/cloud-login.js +13 -41
  33. package/dist/command/cli/config/checkout.d.ts +10 -0
  34. package/dist/command/cli/config/checkout.js +78 -0
  35. package/dist/command/cli/doctor/doctor-agent-link.d.ts +23 -0
  36. package/dist/command/cli/doctor/doctor-agent-link.js +28 -0
  37. package/dist/command/cli/doctor/doctor-coverage-line.js +1 -7
  38. package/dist/command/cli/doctor/doctor-rows.d.ts +4 -0
  39. package/dist/command/cli/doctor/doctor-rows.js +4 -0
  40. package/dist/command/cli/drive/drive-attach.d.ts +5 -0
  41. package/dist/command/cli/drive/drive-attach.js +21 -1
  42. package/dist/command/cli/hook/codex-transcript.d.ts +20 -0
  43. package/dist/command/cli/hook/codex-transcript.js +224 -0
  44. package/dist/command/cli/hook/episode-structure.d.ts +24 -0
  45. package/dist/command/cli/hook/episode-structure.js +138 -0
  46. package/dist/command/cli/hook/hook-command.d.ts +9 -0
  47. package/dist/command/cli/hook/hook-command.js +211 -0
  48. package/dist/command/cli/hook/hook-fast.d.ts +31 -0
  49. package/dist/command/cli/hook/hook-fast.js +136 -0
  50. package/dist/command/cli/hook/prompt-hook.d.ts +30 -0
  51. package/dist/command/cli/hook/prompt-hook.js +199 -0
  52. package/dist/command/cli/hook/tool-hook.d.ts +21 -0
  53. package/dist/command/cli/hook/tool-hook.js +259 -0
  54. package/dist/command/cli/launch/cli-launch.d.ts +2 -0
  55. package/dist/command/cli/launch/cli-launch.js +22 -2
  56. package/dist/command/cli/lifecycle/daemon-lifecycle.js +28 -2
  57. package/dist/command/cli/mcp-command.d.ts +9 -0
  58. package/dist/command/cli/mcp-command.js +127 -41
  59. package/dist/command/cli/open-navigate.d.ts +43 -0
  60. package/dist/command/cli/open-navigate.js +85 -0
  61. package/dist/command/cli/ports/port-holder.d.ts +16 -4
  62. package/dist/command/cli/ports/port-holder.js +58 -12
  63. package/dist/command/cli/ports/resolve/cli-port.js +18 -2
  64. package/dist/command/cli/project-name.d.ts +1 -7
  65. package/dist/command/cli/project-name.js +1 -0
  66. package/dist/command/cli/report-command.d.ts +8 -2
  67. package/dist/command/cli/report-command.js +95 -18
  68. package/dist/command/cli/status-fields.d.ts +9 -2
  69. package/dist/command/cli/status-fields.js +148 -4
  70. package/dist/command/cli/try-command.d.ts +3 -4
  71. package/dist/command/cli/try-command.js +6 -11
  72. package/dist/command/cli/verify-results.d.ts +4 -1
  73. package/dist/command/cli/verify-results.js +3 -1
  74. package/dist/command/cli.d.ts +1 -7
  75. package/dist/command/cli.js +79 -160
  76. package/dist/command/daemon/daemon-resilience.d.ts +6 -1
  77. package/dist/command/daemon/daemon-resilience.js +31 -18
  78. package/dist/command/daemon/daemon.d.ts +11 -0
  79. package/dist/command/daemon/daemon.js +70 -2
  80. package/dist/command/daemon/lifetime/daemon-usefulness.d.ts +2 -0
  81. package/dist/command/daemon/lifetime/daemon-usefulness.js +13 -2
  82. package/dist/command/daemon/wake-decision.js +4 -2
  83. package/dist/command/daemon/win32-escape.d.ts +32 -0
  84. package/dist/command/daemon/win32-escape.js +119 -0
  85. package/dist/command/setup/agent-configs.d.ts +9 -0
  86. package/dist/command/setup/agent-configs.js +9 -0
  87. package/dist/command/setup/auto-approve.d.ts +10 -3
  88. package/dist/command/setup/auto-approve.js +51 -3
  89. package/dist/command/setup/bringup/dev-server-files.d.ts +1 -9
  90. package/dist/command/setup/bringup/dev-server-wait.js +5 -1
  91. package/dist/command/setup/bringup/ensure-daemon.js +4 -7
  92. package/dist/command/setup/init/init-host.js +6 -0
  93. package/dist/command/setup/init/init-port.js +5 -4
  94. package/dist/command/setup/init/init-runtime.js +7 -1
  95. package/dist/command/setup/node-effects.d.ts +8 -0
  96. package/dist/command/setup/node-effects.js +36 -4
  97. package/dist/command/setup/probe/dev-port.d.ts +26 -0
  98. package/dist/command/setup/probe/dev-port.js +117 -0
  99. package/dist/command/setup/probe/page-probe.d.ts +1 -1
  100. package/dist/command/setup/probe/page-probe.js +11 -3
  101. package/dist/command/setup/remaining-steps.d.ts +0 -7
  102. package/dist/command/setup/remaining-steps.js +3 -2
  103. package/dist/command/setup/run-setup.d.ts +11 -0
  104. package/dist/command/setup/run-setup.js +33 -5
  105. package/dist/command/setup/setup-command.d.ts +2 -0
  106. package/dist/command/setup/setup-command.js +22 -4
  107. package/dist/command/update/updater.d.ts +1 -6
  108. package/dist/command/update/updater.js +4 -5
  109. package/dist/command/update/version-seen.d.ts +24 -0
  110. package/dist/command/update/version-seen.js +65 -0
  111. package/dist/command/version/peer-announce.d.ts +9 -1
  112. package/dist/command/version/peer-announce.js +25 -2
  113. package/dist/command/version/sdk-fix.d.ts +5 -6
  114. package/dist/command/version/sdk-fix.js +17 -32
  115. package/dist/command/version/version-skew.d.ts +16 -1
  116. package/dist/command/version/version-skew.js +84 -10
  117. package/dist/features/crawl/crawl-tools.js +2 -2
  118. package/dist/features/crawl/crawl.js +2 -0
  119. package/dist/features/exhaust/ledger.d.ts +15 -96
  120. package/dist/features/exhaust/ledger.js +3 -3
  121. package/dist/features/harness/drive-runs.d.ts +28 -4
  122. package/dist/features/harness/drive-runs.js +99 -7
  123. package/dist/features/harness/drivers.d.ts +1 -1
  124. package/dist/features/harness/drivers.js +1 -1
  125. package/dist/features/harness/harness.d.ts +5 -0
  126. package/dist/features/harness/harness.js +24 -12
  127. package/dist/features/harness/platform/platform-drives.d.ts +7 -1
  128. package/dist/features/harness/platform/platform-drives.js +12 -7
  129. package/dist/features/harness/platform/refusal.d.ts +1 -1
  130. package/dist/features/harness/platform/refusal.js +4 -4
  131. package/dist/features/harness/platform/remote-drive.js +70 -28
  132. package/dist/features/harness/platform/server-driver.js +22 -1
  133. package/dist/features/harness/platform-config.d.ts +1 -10
  134. package/dist/features/harness/platform-config.js +0 -20
  135. package/dist/features/license/license.d.ts +2 -14
  136. package/dist/features/visual/step-shots.d.ts +33 -1
  137. package/dist/features/visual/step-shots.js +155 -1
  138. package/dist/features/visual/visual-tools.js +12 -4
  139. package/dist/hooks/coding-agents.d.ts +6 -0
  140. package/dist/hooks/coding-agents.js +24 -3
  141. package/dist/hooks/episode-stamp.d.ts +32 -0
  142. package/dist/hooks/episode-stamp.js +61 -0
  143. package/dist/hooks/hook-commands.js +19 -9
  144. package/dist/index.d.ts +5 -1
  145. package/dist/index.js +66 -24
  146. package/dist/judgement/app-graph/journal-steps.d.ts +8 -0
  147. package/dist/judgement/app-graph/journal-steps.js +92 -0
  148. package/dist/judgement/capsule/capsule-store.d.ts +2 -39
  149. package/dist/judgement/domain/domain-model.js +1 -10
  150. package/dist/judgement/domain/domain-tools.js +4 -2
  151. package/dist/judgement/runs/artifact/run-store.d.ts +28 -8
  152. package/dist/judgement/runs/artifact/run-store.js +153 -28
  153. package/dist/judgement/runs/default-run-id.d.ts +3 -1
  154. package/dist/judgement/runs/default-run-id.js +15 -3
  155. package/dist/judgement/runs/drive-run.d.ts +3 -3
  156. package/dist/judgement/runs/drive-run.js +11 -7
  157. package/dist/judgement/runs/run-ledger.d.ts +21 -0
  158. package/dist/judgement/runs/run-ledger.js +143 -0
  159. package/dist/judgement/runs/runner-port.d.ts +1 -1
  160. package/dist/judgement/runs/runner-port.js +8 -2
  161. package/dist/judgement/runs/verification-sync.d.ts +4 -3
  162. package/dist/judgement/runs/verification-sync.js +27 -5
  163. package/dist/language/flows/affected-tools.js +2 -1
  164. package/dist/language/flows/annotate-notes/annotate.js +8 -3
  165. package/dist/language/flows/annotate-tools.js +1 -1
  166. package/dist/language/flows/change/affected.d.ts +8 -0
  167. package/dist/language/flows/change/affected.js +20 -7
  168. package/dist/language/flows/decision.js +19 -4
  169. package/dist/language/flows/fields/flow-secret-field.d.ts +9 -0
  170. package/dist/language/flows/fields/flow-secret-field.js +67 -5
  171. package/dist/language/flows/flow-anchor.d.ts +5 -0
  172. package/dist/language/flows/flow-anchor.js +4 -0
  173. package/dist/language/flows/flow-cross-step.d.ts +7 -1
  174. package/dist/language/flows/flow-cross-step.js +9 -0
  175. package/dist/language/flows/flow-expect-grammar.js +15 -10
  176. package/dist/language/flows/flow-intent.js +6 -1
  177. package/dist/language/flows/flow-journey.d.ts +8 -0
  178. package/dist/language/flows/flow-journey.js +13 -0
  179. package/dist/language/flows/flow-lease.d.ts +27 -0
  180. package/dist/language/flows/flow-lease.js +59 -0
  181. package/dist/language/flows/flow-replay-run.d.ts +1 -87
  182. package/dist/language/flows/flow-replay-run.js +111 -310
  183. package/dist/language/flows/flow-replay.js +21 -19
  184. package/dist/language/flows/flow-replayable.d.ts +38 -0
  185. package/dist/language/flows/flow-replayable.js +179 -0
  186. package/dist/language/flows/flow-start-path.d.ts +108 -0
  187. package/dist/language/flows/flow-start-path.js +313 -0
  188. package/dist/language/flows/flow-step-runners.d.ts +23 -2
  189. package/dist/language/flows/flow-step-runners.js +191 -32
  190. package/dist/language/flows/flow-store-for-session.d.ts +8 -1
  191. package/dist/language/flows/flow-store-for-session.js +6 -0
  192. package/dist/language/flows/flow-tools.d.ts +2 -23
  193. package/dist/language/flows/flow-tools.js +63 -113
  194. package/dist/language/flows/flows.d.ts +4 -6
  195. package/dist/language/flows/flows.js +90 -15
  196. package/dist/language/flows/heal-run.d.ts +10 -4
  197. package/dist/language/flows/heal-run.js +26 -9
  198. package/dist/language/flows/recording/flake.d.ts +3 -27
  199. package/dist/language/flows/recording/flake.js +1 -1
  200. package/dist/language/flows/recording/recording-backtrack.js +1 -4
  201. package/dist/language/flows/recording/tape/recordings.d.ts +11 -1
  202. package/dist/language/flows/recording/tape/recordings.js +9 -3
  203. package/dist/language/flows/replay.d.ts +25 -5
  204. package/dist/language/flows/replay.js +83 -16
  205. package/dist/language/flows/stores/assertion-tiers-store.js +1 -1
  206. package/dist/language/flows/suite/coverage.d.ts +9 -0
  207. package/dist/language/flows/suite/coverage.js +12 -1
  208. package/dist/language/flows/suite-selection.d.ts +25 -0
  209. package/dist/language/flows/suite-selection.js +41 -9
  210. package/dist/machine/git-tracks.d.ts +6 -0
  211. package/dist/machine/git-tracks.js +36 -0
  212. package/dist/memory/cloud/background-sync.d.ts +25 -0
  213. package/dist/memory/cloud/background-sync.js +79 -0
  214. package/dist/memory/cloud/cloud-config.d.ts +12 -1
  215. package/dist/memory/cloud/cloud-config.js +39 -13
  216. package/dist/memory/cloud/cloud-sync.d.ts +3 -24
  217. package/dist/memory/cloud/cloud-sync.js +5 -2
  218. package/dist/memory/cloud/harness-config.d.ts +0 -7
  219. package/dist/memory/cloud/memory-scope.d.ts +19 -0
  220. package/dist/memory/cloud/memory-scope.js +48 -0
  221. package/dist/memory/cloud/sync-answer.d.ts +43 -0
  222. package/dist/memory/cloud/sync-answer.js +42 -0
  223. package/dist/memory/cloud/sync-cycle.d.ts +40 -10
  224. package/dist/memory/cloud/sync-cycle.js +97 -144
  225. package/dist/memory/cloud/sync-daemon.d.ts +50 -3
  226. package/dist/memory/cloud/sync-daemon.js +473 -83
  227. package/dist/memory/cloud/sync-describe.d.ts +28 -0
  228. package/dist/memory/cloud/sync-describe.js +92 -0
  229. package/dist/memory/cloud/sync-disk.d.ts +9 -1
  230. package/dist/memory/cloud/sync-disk.js +137 -33
  231. package/dist/memory/cloud/sync-flow-versions.d.ts +26 -0
  232. package/dist/memory/cloud/sync-flow-versions.js +42 -0
  233. package/dist/memory/impact/impact-store.js +32 -15
  234. package/dist/memory/intent/intent-shard.d.ts +41 -266
  235. package/dist/memory/intent/intent-tools.js +20 -61
  236. package/dist/memory/intent/record-request.d.ts +27 -0
  237. package/dist/memory/intent/record-request.js +88 -0
  238. package/dist/memory/intent/settled-words.js +9 -1
  239. package/dist/memory/journal/drive-flow.d.ts +3 -0
  240. package/dist/memory/journal/drive-flow.js +11 -41
  241. package/dist/memory/journal/envelope-store.js +1 -1
  242. package/dist/memory/journal/envelope.d.ts +6 -154
  243. package/dist/memory/journal/episode-log.d.ts +107 -0
  244. package/dist/memory/journal/episode-log.js +302 -0
  245. package/dist/memory/journal/journal-recorder.d.ts +7 -0
  246. package/dist/memory/journal/journal-recorder.js +22 -9
  247. package/dist/memory/journal/on-disk/ambient-file.d.ts +1 -7
  248. package/dist/memory/journal/on-disk/ambient-file.js +1 -1
  249. package/dist/memory/journal/on-disk/workspace-gitignore.d.ts +24 -3
  250. package/dist/memory/journal/on-disk/workspace-gitignore.js +38 -7
  251. package/dist/memory/journal/on-disk/workspace-tiers.js +7 -0
  252. package/dist/memory/journal/self-observation.js +3 -1
  253. package/dist/memory/journal/session-end.js +23 -9
  254. package/dist/memory/journal/session-journal.js +39 -20
  255. package/dist/memory/project/artifact-root-resolver.d.ts +14 -1
  256. package/dist/memory/project/artifact-root-resolver.js +68 -23
  257. package/dist/memory/project/artifact-root.d.ts +17 -3
  258. package/dist/memory/project/artifact-root.js +47 -9
  259. package/dist/memory/project/fs/fs-port.d.ts +5 -0
  260. package/dist/memory/project/fs/fs-port.js +13 -0
  261. package/dist/memory/project/learned-routes.d.ts +3 -4
  262. package/dist/memory/project/learned-routes.js +3 -11
  263. package/dist/memory/project/link-directory.js +5 -2
  264. package/dist/memory/project/root-claims.d.ts +3 -1
  265. package/dist/memory/project/root-claims.js +41 -7
  266. package/dist/memory/project/session-root.d.ts +1 -1
  267. package/dist/memory/project/session-root.js +1 -15
  268. package/dist/memory/project/sync-status.js +5 -6
  269. package/dist/memory/recall/memory-tools.js +2 -2
  270. package/dist/memory/recall/platform-memory.d.ts +3 -1
  271. package/dist/memory/recall/platform-memory.js +9 -1
  272. package/dist/memory/recall/project-memory.js +15 -19
  273. package/dist/portal/bridge/auth-failure-reason.d.ts +13 -0
  274. package/dist/portal/bridge/auth-failure-reason.js +19 -0
  275. package/dist/portal/bridge/bridge.d.ts +19 -4
  276. package/dist/portal/bridge/bridge.js +150 -177
  277. package/dist/portal/bridge/harness-request.d.ts +15 -0
  278. package/dist/portal/bridge/harness-request.js +19 -0
  279. package/dist/portal/bridge/origin/origin-follow.d.ts +46 -0
  280. package/dist/portal/bridge/origin/origin-follow.js +110 -0
  281. package/dist/portal/bridge/pairing-token.d.ts +6 -0
  282. package/dist/portal/bridge/pairing-token.js +47 -16
  283. package/dist/portal/bridge/protocol-skew.d.ts +27 -2
  284. package/dist/portal/bridge/protocol-skew.js +66 -2
  285. package/dist/portal/bridge/session-nonces.d.ts +46 -0
  286. package/dist/portal/bridge/session-nonces.js +72 -0
  287. package/dist/portal/input/viewport-tools.d.ts +31 -0
  288. package/dist/portal/input/viewport-tools.js +106 -15
  289. package/dist/portal/pool/bounded-close.d.ts +23 -0
  290. package/dist/portal/pool/bounded-close.js +38 -0
  291. package/dist/portal/pool/browser-pool.d.ts +44 -39
  292. package/dist/portal/pool/browser-pool.js +247 -95
  293. package/dist/portal/pool/context-permissions.d.ts +9 -1
  294. package/dist/portal/pool/context-permissions.js +26 -0
  295. package/dist/portal/pool/lease-emulation.d.ts +37 -0
  296. package/dist/portal/pool/lease-emulation.js +29 -0
  297. package/dist/portal/pool/playwright-launcher.d.ts +6 -0
  298. package/dist/portal/pool/playwright-launcher.js +74 -7
  299. package/dist/portal/pool/pool-contract.d.ts +23 -1
  300. package/dist/portal/pool/pool-lease.d.ts +128 -0
  301. package/dist/portal/pool/pool-lease.js +49 -0
  302. package/dist/portal/pool/zero-install.d.ts +8 -1
  303. package/dist/portal/pool/zero-install.js +24 -2
  304. package/dist/portal/production/production-activation.d.ts +136 -0
  305. package/dist/portal/production/production-activation.js +173 -0
  306. package/dist/portal/production/production-drive.d.ts +52 -0
  307. package/dist/portal/production/production-drive.js +301 -0
  308. package/dist/portal/production/production-hello.d.ts +23 -0
  309. package/dist/portal/production/production-hello.js +23 -0
  310. package/dist/portal/realm/web-realm.js +42 -15
  311. package/dist/portal/session/control-envelope.d.ts +1 -1
  312. package/dist/portal/session/dev-server/served-document.d.ts +0 -5
  313. package/dist/portal/session/dev-server/served-document.js +6 -8
  314. package/dist/portal/session/facts/handshake-facts.d.ts +8 -1
  315. package/dist/portal/session/first-run-wiring.d.ts +3 -36
  316. package/dist/portal/session/first-run-wiring.js +10 -103
  317. package/dist/portal/session/live-control-tools.js +61 -19
  318. package/dist/portal/session/no-session-next-action.d.ts +11 -0
  319. package/dist/portal/session/no-session-next-action.js +40 -15
  320. package/dist/portal/session/no-session-watch.d.ts +7 -0
  321. package/dist/portal/session/no-session-watch.js +67 -23
  322. package/dist/portal/session/output-budget.d.ts +10 -0
  323. package/dist/portal/session/output-budget.js +16 -0
  324. package/dist/portal/session/presence/session-lease.d.ts +2 -0
  325. package/dist/portal/session/presence/session-lease.js +8 -0
  326. package/dist/portal/session/recorded-gaps.d.ts +15 -11
  327. package/dist/portal/session/recorded-gaps.js +39 -18
  328. package/dist/portal/session/serving-directory.d.ts +24 -2
  329. package/dist/portal/session/serving-directory.js +93 -5
  330. package/dist/portal/session/session-info.d.ts +26 -0
  331. package/dist/portal/session/session-info.js +20 -0
  332. package/dist/portal/session/session-manager.d.ts +35 -3
  333. package/dist/portal/session/session-manager.js +69 -14
  334. package/dist/portal/session/session-successor.js +1 -9
  335. package/dist/portal/session/session-tools.js +22 -4
  336. package/dist/portal/session/session.d.ts +10 -10
  337. package/dist/portal/session/session.js +37 -29
  338. package/dist/portal/session/zero-install-first-call.d.ts +7 -2
  339. package/dist/portal/session/zero-install-first-call.js +43 -20
  340. package/dist/start-wiring.d.ts +2 -2
  341. package/dist/start-wiring.js +17 -3
  342. package/dist/status-payload.d.ts +11 -1
  343. package/dist/status-payload.js +7 -1
  344. package/dist/surface/agent-presence-grace.d.ts +11 -0
  345. package/dist/surface/agent-presence-grace.js +34 -0
  346. package/dist/surface/http-server.d.ts +12 -1
  347. package/dist/surface/http-server.js +64 -15
  348. package/dist/surface/mcp/faults/mcp-outage.d.ts +12 -2
  349. package/dist/surface/mcp/faults/mcp-outage.js +19 -1
  350. package/dist/surface/mcp/mcp-proxy.d.ts +17 -74
  351. package/dist/surface/mcp/mcp-proxy.js +194 -267
  352. package/dist/surface/mcp/mcp.d.ts +20 -3
  353. package/dist/surface/mcp/mcp.js +88 -25
  354. package/dist/surface/mcp/proxy/drive-calls.d.ts +44 -0
  355. package/dist/surface/mcp/proxy/drive-calls.js +114 -5
  356. package/dist/surface/mcp/proxy/proxy-boot.d.ts +11 -0
  357. package/dist/surface/mcp/proxy/proxy-boot.js +1 -0
  358. package/dist/surface/mcp/proxy/proxy-daemon-probe.d.ts +6 -0
  359. package/dist/surface/mcp/proxy/proxy-daemon-probe.js +14 -1
  360. package/dist/surface/mcp/proxy/proxy-handshake.d.ts +34 -2
  361. package/dist/surface/mcp/proxy/proxy-handshake.js +76 -8
  362. package/dist/surface/mcp/server-instructions.js +35 -16
  363. package/dist/surface/mcp/shared-params.d.ts +8 -0
  364. package/dist/surface/mcp/shared-params.js +15 -1
  365. package/dist/surface/mcp/sse-frame-parser.d.ts +11 -0
  366. package/dist/surface/mcp/sse-frame-parser.js +18 -0
  367. package/dist/surface/mcp/surface-vocabulary.d.ts +2 -0
  368. package/dist/surface/mcp/surface-vocabulary.js +13 -0
  369. package/dist/surface/mcp/tool-catalog-cache.d.ts +2 -0
  370. package/dist/surface/mcp/tool-catalog-cache.js +14 -4
  371. package/dist/surface/tools/account-tool.d.ts +4 -3
  372. package/dist/surface/tools/account-tool.js +20 -31
  373. package/dist/surface/tools/act/act-danger.d.ts +2 -0
  374. package/dist/surface/tools/act/act-danger.js +41 -14
  375. package/dist/surface/tools/act/act-observation.d.ts +11 -0
  376. package/dist/surface/tools/act/act-observation.js +33 -0
  377. package/dist/surface/tools/act/act-preflight.js +10 -3
  378. package/dist/surface/tools/act/act-sequence-retry.js +2 -1
  379. package/dist/surface/tools/act/act-target.js +19 -5
  380. package/dist/surface/tools/act/durable.js +1 -1
  381. package/dist/surface/tools/act/navigation/navigate-arrival.d.ts +9 -7
  382. package/dist/surface/tools/act/navigation/navigate-arrival.js +11 -14
  383. package/dist/surface/tools/act/unsettled-find.d.ts +36 -0
  384. package/dist/surface/tools/act/unsettled-find.js +71 -0
  385. package/dist/surface/tools/act-sequence-tool.js +53 -24
  386. package/dist/surface/tools/act-tools.d.ts +2 -3
  387. package/dist/surface/tools/act-tools.js +49 -47
  388. package/dist/surface/tools/agent/compact-envelope.d.ts +2 -2
  389. package/dist/surface/tools/agent/for-the-agent.js +41 -3
  390. package/dist/surface/tools/assert/assert-grade.js +8 -3
  391. package/dist/surface/tools/assert/assert-verdict.js +4 -2
  392. package/dist/surface/tools/browser-tools.js +7 -4
  393. package/dist/surface/tools/chat-drives.js +50 -25
  394. package/dist/surface/tools/coverage-tools.js +1 -1
  395. package/dist/surface/tools/dynamic-tools.d.ts +8 -0
  396. package/dist/surface/tools/dynamic-tools.js +89 -34
  397. package/dist/surface/tools/empty-session-list.d.ts +18 -25
  398. package/dist/surface/tools/empty-session-list.js +67 -38
  399. package/dist/surface/tools/error-recovery.d.ts +17 -7
  400. package/dist/surface/tools/error-recovery.js +53 -20
  401. package/dist/surface/tools/explore-tools.d.ts +10 -0
  402. package/dist/surface/tools/explore-tools.js +72 -19
  403. package/dist/surface/tools/feedback-tools.js +23 -2
  404. package/dist/surface/tools/gap-novelty.d.ts +6 -0
  405. package/dist/surface/tools/gap-novelty.js +9 -0
  406. package/dist/surface/tools/gaps/capability-absences.js +4 -3
  407. package/dist/surface/tools/harness-explore.d.ts +1 -1
  408. package/dist/surface/tools/harness-explore.js +6 -4
  409. package/dist/surface/tools/harness-gate.d.ts +43 -21
  410. package/dist/surface/tools/harness-gate.js +55 -81
  411. package/dist/surface/tools/harness-script.d.ts +2 -0
  412. package/dist/surface/tools/harness-script.js +11 -6
  413. package/dist/surface/tools/harness-toolset.js +3 -4
  414. package/dist/surface/tools/invoke-tool.d.ts +0 -8
  415. package/dist/surface/tools/invoke-tool.js +164 -178
  416. package/dist/surface/tools/lease-hint.d.ts +9 -0
  417. package/dist/surface/tools/lease-hint.js +22 -3
  418. package/dist/surface/tools/lease-permissions.d.ts +1 -1
  419. package/dist/surface/tools/lease-readiness.d.ts +55 -0
  420. package/dist/surface/tools/lease-readiness.js +150 -2
  421. package/dist/surface/tools/lease-tools.d.ts +3 -75
  422. package/dist/surface/tools/lease-tools.js +124 -188
  423. package/dist/surface/tools/map-tools.d.ts +20 -0
  424. package/dist/surface/tools/map-tools.js +145 -0
  425. package/dist/surface/tools/merge-tools.js +11 -7
  426. package/dist/surface/tools/next-step.js +6 -3
  427. package/dist/surface/tools/observe-tools.js +46 -35
  428. package/dist/surface/tools/platform-moment.d.ts +3 -12
  429. package/dist/surface/tools/platform-moment.js +12 -24
  430. package/dist/surface/tools/read/complete-snapshot.d.ts +7 -1
  431. package/dist/surface/tools/read/complete-snapshot.js +13 -2
  432. package/dist/surface/tools/read/query-paginate.js +6 -0
  433. package/dist/surface/tools/read/query-shape.d.ts +9 -0
  434. package/dist/surface/tools/read/query-shape.js +15 -0
  435. package/dist/surface/tools/read/snapshot-unmounted-note.d.ts +1 -0
  436. package/dist/surface/tools/read/snapshot-unmounted-note.js +73 -0
  437. package/dist/surface/tools/read-tools.js +5 -5
  438. package/dist/surface/tools/real-input-attempt.d.ts +11 -0
  439. package/dist/surface/tools/real-input-attempt.js +206 -38
  440. package/dist/surface/tools/reconcile-tools.js +21 -8
  441. package/dist/surface/tools/reticle-result.d.ts +3 -0
  442. package/dist/surface/tools/reticle-result.js +8 -1
  443. package/dist/surface/tools/run-report.d.ts +22 -0
  444. package/dist/surface/tools/run-report.js +141 -0
  445. package/dist/surface/tools/session-ports.d.ts +57 -0
  446. package/dist/surface/tools/session-ports.js +102 -0
  447. package/dist/surface/tools/tool-kit.d.ts +58 -4
  448. package/dist/surface/tools/tool-kit.js +140 -6
  449. package/dist/surface/tools/tool-surface.d.ts +2 -0
  450. package/dist/surface/tools/tool-surface.js +45 -0
  451. package/dist/surface/tools/tools.js +49 -87
  452. package/dist/telemetry/automation-hint.d.ts +5 -0
  453. package/dist/telemetry/automation-hint.js +7 -0
  454. package/dist/telemetry/cli-telemetry.d.ts +22 -0
  455. package/dist/telemetry/cli-telemetry.js +80 -11
  456. package/dist/telemetry/daemon-telemetry.d.ts +3 -1
  457. package/dist/telemetry/daemon-telemetry.js +9 -2
  458. package/dist/telemetry/feedback-cli.d.ts +1 -1
  459. package/dist/telemetry/feedback-cli.js +8 -3
  460. package/dist/telemetry/feedback.d.ts +1 -1
  461. package/dist/telemetry/feedback.js +51 -4
  462. package/dist/telemetry/harness-run.d.ts +3 -3
  463. package/dist/telemetry/harness-run.js +18 -7
  464. package/dist/telemetry/hud-events.d.ts +1 -1
  465. package/dist/telemetry/hud-metrics.d.ts +6 -3
  466. package/dist/telemetry/hud-metrics.js +20 -8
  467. package/dist/telemetry/install-source.js +1 -1
  468. package/dist/telemetry/mcp-connection.d.ts +1 -1
  469. package/dist/telemetry/mcp-connection.js +21 -2
  470. package/dist/telemetry/onboarding-firsts.d.ts +6 -1
  471. package/dist/telemetry/onboarding-firsts.js +41 -1
  472. package/dist/telemetry/proxy-telemetry.d.ts +42 -0
  473. package/dist/telemetry/proxy-telemetry.js +70 -1
  474. package/dist/telemetry/session-metrics.d.ts +1 -3
  475. package/dist/telemetry/session-metrics.js +16 -12
  476. package/dist/telemetry/telemetry.d.ts +9 -1
  477. package/dist/telemetry/telemetry.js +16 -1
  478. package/dist/telemetry/verification-of.d.ts +3 -1
  479. package/dist/telemetry/verification-of.js +7 -1
  480. package/dist/wire-journal.js +12 -2
  481. package/docs/README.md +4 -2
  482. package/docs/actions.mdx +7 -6
  483. package/docs/adapters.md +2 -1
  484. package/docs/agent-cheatsheet.md +19 -5
  485. package/docs/architecture.md +13 -10
  486. package/docs/autodrive.md +6 -5
  487. package/docs/benchmarks.md +2 -2
  488. package/docs/capabilities.mdx +1 -1
  489. package/docs/cli/affected.mdx +1 -1
  490. package/docs/cli/cloud.mdx +6 -6
  491. package/docs/cli/doctor.mdx +3 -1
  492. package/docs/cli/feedback.mdx +11 -7
  493. package/docs/cli/gate.mdx +1 -1
  494. package/docs/cli/help.mdx +1 -1
  495. package/docs/cli/init.mdx +20 -16
  496. package/docs/cli/mcp.mdx +5 -2
  497. package/docs/cli/open.mdx +5 -2
  498. package/docs/cli/report.mdx +1 -1
  499. package/docs/cli/restart.mdx +1 -1
  500. package/docs/cli/rollback.mdx +4 -4
  501. package/docs/cli/serve.mdx +7 -5
  502. package/docs/cli/setup.mdx +1 -1
  503. package/docs/cli/status.mdx +6 -2
  504. package/docs/cli/stop.mdx +3 -3
  505. package/docs/cli/telemetry.mdx +5 -1
  506. package/docs/cli/try.mdx +8 -8
  507. package/docs/cli/update.mdx +8 -5
  508. package/docs/cli/verify.mdx +12 -5
  509. package/docs/cli/version.mdx +1 -1
  510. package/docs/cli.mdx +10 -6
  511. package/docs/debugging.md +1 -1
  512. package/docs/deploy-checks.md +8 -6
  513. package/docs/desktop.mdx +14 -18
  514. package/docs/enterprise.md +6 -6
  515. package/docs/environment.mdx +5 -3
  516. package/docs/faq.mdx +9 -6
  517. package/docs/first-drive.md +19 -9
  518. package/docs/flows.md +3 -4
  519. package/docs/for-agents.md +1 -1
  520. package/docs/frameworks.mdx +34 -11
  521. package/docs/getting-started.md +46 -29
  522. package/docs/http-transport.md +2 -2
  523. package/docs/human-control.md +8 -8
  524. package/docs/index.mdx +2 -2
  525. package/docs/install-agentic.mdx +18 -19
  526. package/docs/install-manual.mdx +8 -4
  527. package/docs/integration-patterns.md +5 -5
  528. package/docs/multi-agent-testing.md +7 -5
  529. package/docs/packages/browser.mdx +34 -2
  530. package/docs/packages/core.mdx +2 -1
  531. package/docs/packages/electron.mdx +15 -3
  532. package/docs/packages/eslint-plugin.mdx +1 -1
  533. package/docs/packages/next.mdx +17 -5
  534. package/docs/packages/react.mdx +8 -1
  535. package/docs/packages/server.mdx +3 -3
  536. package/docs/packages/tauri.mdx +3 -3
  537. package/docs/packages/vite-plugin.mdx +1 -1
  538. package/docs/packages.mdx +19 -6
  539. package/docs/platform-integration.md +12 -4
  540. package/docs/predicates.mdx +18 -7
  541. package/docs/production-mode.md +108 -0
  542. package/docs/prompt-capture.md +99 -0
  543. package/docs/quickstart.mdx +14 -12
  544. package/docs/recipes.mdx +9 -8
  545. package/docs/required-checks.md +3 -2
  546. package/docs/skill-file.mdx +4 -3
  547. package/docs/skill-setup.md +2 -2
  548. package/docs/skill.md +62 -70
  549. package/docs/state-files.md +1 -0
  550. package/docs/state-management.mdx +11 -9
  551. package/docs/system-map.md +14 -13
  552. package/docs/telemetry.md +32 -24
  553. package/docs/testing.md +9 -5
  554. package/docs/tools/act-and-wait.mdx +17 -13
  555. package/docs/tools/act-sequence.mdx +40 -66
  556. package/docs/tools/act.mdx +7 -5
  557. package/docs/tools/assert.mdx +1 -1
  558. package/docs/tools/look.mdx +1 -1
  559. package/docs/tools/navigate.mdx +1 -1
  560. package/docs/tools/network.mdx +13 -11
  561. package/docs/tools/overview.mdx +3 -3
  562. package/docs/tools/query.mdx +3 -3
  563. package/docs/tools/session-and-feedback.mdx +9 -6
  564. package/docs/tools/sessions.mdx +7 -0
  565. package/docs/tools/snapshot.mdx +9 -7
  566. package/docs/tools/tools-and-run.mdx +11 -10
  567. package/docs/tools/verify.mdx +1 -1
  568. package/docs/troubleshooting.mdx +21 -9
  569. package/docs/usage.md +58 -43
  570. package/docs/use-cases.mdx +2 -2
  571. package/docs/vs-chrome-devtools-mcp.mdx +5 -5
  572. package/docs/vs-playwright-mcp.mdx +5 -4
  573. package/docs/what-is-recorded.md +12 -7
  574. package/docs/what-is-reticle.mdx +2 -2
  575. package/docs/why-reticle.mdx +1 -1
  576. package/package.json +9 -12
  577. package/dist/command/dev/stale-issue-guard.d.ts +0 -34
  578. package/dist/command/dev/stale-issue-guard.js +0 -69
@@ -9,6 +9,7 @@
9
9
  * not: `npx reticle` resolves the PACKAGE named `reticle`, which belongs to somebody else, and npx
10
10
  * will happily fetch and run it. So the npx spelling leads every page.
11
11
  */
12
+ import { FEEDBACK_KIND_CHOICES } from '@reticlehq/core';
12
13
  export const HelpGroup = {
13
14
  START: 'Get started',
14
15
  CHECK: 'Check your app',
@@ -21,14 +22,16 @@ export const HelpGroup = {
21
22
  export const HELP_ALL_TOPIC = 'all';
22
23
  const INIT_DETAIL = `init wires the project, boots the app and proves a page connected. It ends with the
23
24
  instrumentation score, what is missing, and a prompt for your coding agent, which proves
24
- the first flow. It never drives: the Harness is opt-in and unlocks at 80% instrumentation.
25
+ the first flow. It never drives: the Harness is opt-in and runs at any instrumentation score.
25
26
 
26
27
  --app <dir> which app in a monorepo: writes its files only (add --start to boot it)
27
28
  --env KEY=VALUE what the app needs to reach a usable state (repeatable)
28
29
  --files-only write the files and stop; do not boot the app
29
30
  --no-install do not install packages
31
+ --new-id give this checkout its own project id (a copied repo keeps the original's)
30
32
  --no-mcp skip the MCP registration, the agent rule files and the /reticle command
31
33
  --hooks also install the agent hooks
34
+ --no-prompt-capture install no hook that saves each prompt to .reticle/
32
35
  --relaunch print the command that restarts this agent conversation with the tools loaded
33
36
  --license <key> write an enterprise key to .env and keep .env out of git
34
37
  --json print the result as one JSON object on stdout
@@ -47,11 +50,15 @@ const VERIFY_DETAIL = `Drives the URL and verifies the saved flows. Exit 0 means
47
50
  --session-id <id>, --timeout N, --headed, --port N
48
51
  --expect '<json predicate>' | --expect-file <path>
49
52
  one verdict, no saved flows needed. Asks a running daemon,
50
- never starts one. Exit 0 only on verified:"yes". For an
53
+ never starts one, and waits up to --timeout ms (default 4000)
54
+ for it to come true. Exit 0 only on verified:"yes". For an
51
55
  action --expect cannot do, use the HTTP MCP transport:
52
56
  https://docs.reticle.sh/http-transport.md`;
53
57
  const BROWSER_NOTE = `The browser Reticle opens for your agent is shown so you can watch the run. --headless hides
54
58
  it; CI, RETICLE_HEADLESS=1, or a Linux machine with no display hides it too.`;
59
+ /** What `--http` starts. MCP is already on the bridge port; this flag is the verify endpoint. */
60
+ const HTTP_HELP = '--http starts the verify endpoint. MCP is already served on the bridge port.';
61
+ const DAEMON_DETAIL = `${BROWSER_NOTE}\n\n${HTTP_HELP}`;
55
62
  const COMMANDS = [
56
63
  {
57
64
  name: 'init',
@@ -72,9 +79,8 @@ const COMMANDS = [
72
79
  ' --project <name|id> the dashboard project to link\n' +
73
80
  ' --workspace <name|id> the workspace to link in, when you belong to more than one\n' +
74
81
  ' --url <origin> the Reticle platform, when it is not the default\n\n' +
75
- 'Without a browser (CI, an agent), in two steps:\n' +
76
- ' reticle login --email <e> [--org <name>] emails a 6-digit code\n' +
77
- ' reticle login --email <e> --code <123456> signs in, then links this repo',
82
+ 'Sign-in is with Google or GitHub, in the browser. Without a browser (CI, an agent), set\n' +
83
+ 'RETICLE_API_KEY to a key from Settings > API keys. Email sign-in is gone.',
78
84
  },
79
85
  {
80
86
  name: 'try',
@@ -110,16 +116,18 @@ const COMMANDS = [
110
116
  },
111
117
  {
112
118
  name: 'open',
113
- synopsis: 'open [url] [--port N]',
119
+ synopsis: 'open [url] [--port N] [--navigate]',
114
120
  summary: 'Open the app in your browser, or reuse a tab that is already connected',
115
121
  group: HelpGroup.MACHINE,
122
+ detail: '--navigate moves a tab already on that origin to the url instead of leaving it.',
116
123
  },
117
124
  {
118
125
  name: 'stop',
119
126
  synopsis: 'stop [--force] [--quiet] [--port N]',
120
127
  summary: 'Stop Reticle; --force frees its port even without a recorded pid',
121
128
  group: HelpGroup.MACHINE,
122
- detail: 'Without --force, stops the daemon Reticle started, by its recorded pid.\n' +
129
+ detail: 'Without --force, asks the daemon to stop (an authenticated request on loopback), and sends\n' +
130
+ 'a signal to its recorded pid only if it does not accept.\n' +
123
131
  "With --force, frees the port by the Reticle daemon LISTENING on it, never the agent's MCP\n" +
124
132
  'proxy, and refuses a listener that is not Reticle.',
125
133
  },
@@ -143,11 +151,12 @@ const COMMANDS = [
143
151
  },
144
152
  {
145
153
  name: 'feedback',
146
- synopsis: 'feedback [--rating 1-5] [--bug] "message"',
154
+ synopsis: 'feedback [--rating 1-5] [--bug] [--contact <email>] "message"',
147
155
  summary: "Tell us what worked and what didn't; prints exactly what it sends",
148
156
  group: HelpGroup.ACCOUNT,
149
- detail: 'Agents: reticle feedback --agent --kind <bug|gap|ambiguity|feature_request|improvement|experience> "message"\n' +
150
- 'works from anywhere, including a setup that never finished.',
157
+ detail: `Agents: reticle feedback --agent --kind ${FEEDBACK_KIND_CHOICES} "message"\n` +
158
+ 'works from anywhere, including a setup that never finished.\n' +
159
+ '--contact <email> adds an address for a reply. It is sent only when you give it.',
151
160
  },
152
161
  {
153
162
  name: 'telemetry',
@@ -177,7 +186,7 @@ const COMMANDS = [
177
186
  },
178
187
  {
179
188
  name: 'config',
180
- synopsis: 'config [--runs on|off] [--memory on|off] [--flows on|off] [--verify local|server]',
189
+ synopsis: 'config [--runs on|off] [--memory on|off] [--flows on|off] [--verify local|server] [--harness on|off]',
181
190
  summary: 'What this project sends to the dashboard',
182
191
  group: HelpGroup.MORE,
183
192
  },
@@ -199,19 +208,34 @@ const COMMANDS = [
199
208
  summary: 'Resume after a compaction: what was proved, failed and is still owed',
200
209
  group: HelpGroup.MORE,
201
210
  },
211
+ {
212
+ name: 'hook',
213
+ synopsis: 'hook [install|uninstall|status|prompt --agent <id>|tool --agent <id>|export --structure]',
214
+ summary: 'Save prompts, tool calls, the model and closing messages to .reticle/, through your agent hooks',
215
+ group: HelpGroup.MORE,
216
+ detail: ' hook install add the prompt-capture hook for the agents used here\n' +
217
+ ' hook uninstall remove exactly the entries hook install added\n' +
218
+ ' hook status which agents capture prompts, and which rely on the MCP instructions\n' +
219
+ ' hook prompt --agent <id> what the agent hook runs; reads the prompt on stdin\n' +
220
+ ' hook tool --agent <id> what the post-tool hook runs; keeps the call in .reticle/episodes/\n' +
221
+ ' hook export --structure each snapshot step in .reticle/episodes/ as text-free page structure (JSONL)\n\n' +
222
+ 'One switch stops prompts, tool calls, the model and the closing message. Off for one project:\n' +
223
+ '"promptCapture": false in .reticle.json. Off on this machine: RETICLE_PROMPT_CAPTURE=0.\n' +
224
+ 'Tool calls also stop with the journal: "journal": false or RETICLE_JOURNAL=0.',
225
+ },
202
226
  {
203
227
  name: 'mcp',
204
228
  synopsis: 'mcp [--port N] [--drive <url>] [--headless] [--headed] [--http] [--http-port N] [--http-token T]',
205
229
  summary: 'The MCP stdio server your agent runs; starts the daemon if needed',
206
230
  group: HelpGroup.MORE,
207
- detail: BROWSER_NOTE,
231
+ detail: DAEMON_DETAIL,
208
232
  },
209
233
  {
210
234
  name: 'serve',
211
235
  synopsis: 'serve [--port N] [--drive <url>] [--headless] [--headed] [--http] [--http-port N] [--http-token T]',
212
236
  summary: 'Start the daemon in the background; returns once it answers',
213
237
  group: HelpGroup.MORE,
214
- detail: BROWSER_NOTE,
238
+ detail: DAEMON_DETAIL,
215
239
  },
216
240
  {
217
241
  name: 'drive',
@@ -268,15 +292,18 @@ export const DOCUMENTED_COMMANDS = COMMANDS.map((c) => c.name);
268
292
  * answers with the page of the command that replaced it, and says so.
269
293
  */
270
294
  export function renderHelp(topic, renamed = new Map()) {
271
- if (topic === undefined)
295
+ if (topic === undefined) {
272
296
  return CLI_USAGE;
273
- if (HELP_ALL_TOPIC === topic)
297
+ }
298
+ if (HELP_ALL_TOPIC === topic) {
274
299
  return CLI_USAGE_ALL;
300
+ }
275
301
  const now = renamed.get(topic);
276
302
  const target = now?.split(' ')[0] ?? topic;
277
303
  const doc = COMMANDS.find((c) => c.name === target);
278
- if (doc === undefined)
304
+ if (doc === undefined) {
279
305
  return CLI_USAGE;
306
+ }
280
307
  return [
281
308
  `usage: reticle ${doc.synopsis}`,
282
309
  ...(now === undefined ? [] : [` (\`reticle ${topic}\` is now \`reticle ${now}\`)`]),
@@ -81,7 +81,7 @@ interface VerifyArgs {
81
81
  resultsJson?: string;
82
82
  }
83
83
  /** Orchestration: boot → wait for a session → replay flows → verdict → exit code. Browser-free. */
84
- export declare function runVerify(args: VerifyArgs, ports: VerifyPorts): Promise<void>;
84
+ export declare function runVerify(args: VerifyArgs, outer: VerifyPorts): Promise<void>;
85
85
  /**
86
86
  * Why the run did not reach the dashboard. A revoked key left CI green with "cloud run sync failed
87
87
  * (401)" in the log as the only trace; a refused key is said as one, loudly.
@@ -9,7 +9,7 @@
9
9
  * so the decision logic — including the two honesty guards (no session, no flows ⇒ never a green pass)
10
10
  * — is unit-tested without launching a real browser.
11
11
  */
12
- import { CLI_CLIENT_NAME } from '../../judgement/runs/verification-sync.js';
12
+ import { cliRunAuthor } from '../../judgement/runs/verification-sync.js';
13
13
  import { join, basename } from 'node:path';
14
14
  import { readFileSync } from 'node:fs';
15
15
  import { runAdhocVerdict } from './adhoc-verdict.js';
@@ -24,7 +24,7 @@ import { resolveSuiteSelection } from '../../language/flows/suite-selection.js';
24
24
  import { projectOnly } from '../../memory/project/session-root.js';
25
25
  import { configuredPairingToken, nodePairingTokenDeps, readOrCreatePairingTokenSync, defaultPairingTokenDir, } from '../../portal/bridge/pairing-token.js';
26
26
  import { randomUUID } from 'node:crypto';
27
- import { MCP_SSE_PATH, MCP_MESSAGE_PATH, RETICLE_DEFAULT_PORT, ReticleEnv, bridgeWsUrl, isLoopbackHostname, ReticleDir, RunAgentKind, RunFramework, RunProfile, RunTrigger, VerdictStatus, VerifyPhase, } from '@reticlehq/core';
27
+ import { MCP_SSE_PATH, MCP_MESSAGE_PATH, RETICLE_DEFAULT_PORT, ReticleEnv, bridgeWsUrl, isLoopbackHostname, ReticleDir, RunFramework, RunProfile, VerdictStatus, VerifyPhase, } from '@reticlehq/core';
28
28
  import { start } from '../../index.js';
29
29
  import { probePresence, describePresence, PortPresence, } from '../daemon/binding/port-presence.js';
30
30
  import { EXPECT_FLAG } from './cli-parse-grammar.js';
@@ -59,7 +59,6 @@ const DEFAULT_PROJECT_NAME = 'app';
59
59
  export function verifyProjectName(linked, configured, folder) {
60
60
  return linked ?? configured ?? (folder || DEFAULT_PROJECT_NAME);
61
61
  }
62
- const VERIFY_AGENT_ID = CLI_CLIENT_NAME;
63
62
  const MSG_NO_SESSION = 'No app connected: Reticle drove the URL but no @reticlehq/browser session dialed back.\n' +
64
63
  ' Make sure the SDK is in the build and reticle.connect() runs on the preview page' +
65
64
  ' (for a non-localhost preview: allowNonLocalhost + a pairing token).';
@@ -85,10 +84,22 @@ async function openConnection(ports) {
85
84
  }
86
85
  }
87
86
  /** Orchestration: boot → wait for a session → replay flows → verdict → exit code. Browser-free. */
88
- export async function runVerify(args, ports) {
89
- const conn = await openConnection(ports);
87
+ export async function runVerify(args, outer) {
88
+ const conn = await openConnection(outer);
90
89
  if (conn === undefined)
91
90
  return;
91
+ /*
92
+ * The exit is held until the connection has closed. The live exit is process.exit, and close()
93
+ * runs the last cloud sync cycle that sends this run's flows and screenshots: exiting first skipped
94
+ * the `finally` below, so with no daemon running the run reached the dashboard without them.
95
+ */
96
+ let exitCode;
97
+ const ports = {
98
+ ...outer,
99
+ exit: (code) => {
100
+ exitCode = code;
101
+ },
102
+ };
92
103
  try {
93
104
  const ready = await conn.sessionReady(args.timeoutMs);
94
105
  if (!ready) {
@@ -160,7 +171,7 @@ export async function runVerify(args, ports) {
160
171
  progress.stop();
161
172
  await progress.flush().catch(() => undefined);
162
173
  }
163
- await pushRunToCloud(run, cloud, ports); // best-effort; opt-in; never changes the verdict or exit code
174
+ const pushed = await pushRunToCloud(run, cloud, ports); // best-effort; opt-in; never changes the verdict or exit code
164
175
  /*
165
176
  * The last event anybody watching is waiting for, sent after the artifact has actually landed —
166
177
  * so "pushed" on a dashboard means the run is really there, not that we were about to try.
@@ -169,7 +180,7 @@ export async function runVerify(args, ports) {
169
180
  // The run's OWN injected timestamp — no wall clock is read in this path (rule 7).
170
181
  [{ phase: VerifyPhase.PUSHED, at: run.createdAt }], cloud, cloudFetch).catch(() => undefined);
171
182
  if (args.resultsJson !== undefined && ports.writeResults !== undefined) {
172
- await ports.writeResults(args.resultsJson, `${JSON.stringify(verifyResults(run), null, 2)}\n`);
183
+ await ports.writeResults(args.resultsJson, `${JSON.stringify(verifyResults(run, pushed), null, 2)}\n`);
173
184
  }
174
185
  ports.out(renderRunReport(run));
175
186
  ports.exit(run.verdict.status === VerdictStatus.PASS ? EXIT_PASS : EXIT_FAIL);
@@ -180,6 +191,9 @@ export async function runVerify(args, ports) {
180
191
  }
181
192
  finally {
182
193
  await conn.close().catch(() => undefined);
194
+ // In the finally, so the early refusals that `return` from the try exit too.
195
+ if (exitCode !== undefined)
196
+ outer.exit(exitCode);
183
197
  }
184
198
  }
185
199
  /**
@@ -222,7 +236,7 @@ async function pushRunToCloud(run, config, ports) {
222
236
  if (null === config) {
223
237
  ports.out(`run ${run.runId} was kept on this computer: this repo is not linked, so nothing was sent ` +
224
238
  `to the dashboard. To save runs for your team: ${CONNECT}`);
225
- return;
239
+ return false;
226
240
  }
227
241
  const result = await syncRunToCloud(run, config, cloudFetch);
228
242
  if (result.outcome === SyncOutcome.SYNCED) {
@@ -230,10 +244,10 @@ async function pushRunToCloud(run, config, ports) {
230
244
  ports.out(dashboard === undefined
231
245
  ? `↑ run ${run.runId} recorded on the Reticle dashboard`
232
246
  : seeRunLine(dashboard, run.runId));
247
+ return true;
233
248
  }
234
- else {
235
- ports.fail(runSyncFailure(result.status, result.error));
236
- }
249
+ ports.fail(runSyncFailure(result.status, result.error));
250
+ return false;
237
251
  }
238
252
  /** The platform answered: the credential was not accepted. */
239
253
  const REFUSED_STATUSES = new Set([401, 403]);
@@ -360,12 +374,11 @@ async function openLiveConnection(opts) {
360
374
  verify: async (onProgress) => {
361
375
  const run = await runner.verify({
362
376
  project: { name: opts.projectName, framework: RunFramework.OTHER, previewUrl: opts.url },
363
- agent: { id: VERIFY_AGENT_ID, kind: RunAgentKind.OEM_PIPELINE },
364
- trigger: { kind: RunTrigger.OEM },
377
+ ...cliRunAuthor(process.env),
365
378
  profile: RunProfile.PROD_PREVIEW,
366
379
  ...(onProgress === undefined ? {} : { onProgress }),
367
380
  });
368
- // Persist the artifact. `reticle gate` decides from RunStore.latest, so without this the
381
+ // Persist the artifact. `reticle gate` decides from the runs in the store, so without this the
369
382
  // documented CI loop is broken end to end: `reticle verify` could pass and the gate would still
370
383
  // block, because it never sees a passing run. (Only the optional `serve --http` endpoint used to
371
384
  // persist.) Best-effort — a disk failure must not turn a passing verification into a failure.
@@ -591,6 +604,8 @@ export function handleVerify(parsed) {
591
604
  port,
592
605
  ...(parsed.url !== undefined && '' !== parsed.url ? { url: parsed.url } : {}),
593
606
  predicate: expectation,
607
+ // How long the expectation may take to come true (#1533).
608
+ ...(parsed.timeoutMs === undefined ? {} : { timeoutMs: parsed.timeoutMs }),
594
609
  // Read fresh, not from `presence`: that probe asks whether a daemon is on the port, and
595
610
  // this asks whether a TAB is — the difference between asserting and opening the url first.
596
611
  sessions: async () => summarizeStatus(await fetchStatus(port)).sessions,
@@ -7,7 +7,8 @@
7
7
  */
8
8
  import { CreatedProjectSchema } from './project-name.js';
9
9
  import { keyRefusalAtLastSync } from '../../memory/project/sync-status.js';
10
- import { apiKeyFrom } from '@reticlehq/core';
10
+ import { ReticleEnv, apiKeyFrom } from '@reticlehq/core';
11
+ import { writeHarnessSwitch } from '../../memory/cloud/harness-switch.js';
11
12
  import { PlatformLinkVia } from '@reticlehq/core/telemetry';
12
13
  import { existsSync } from 'node:fs';
13
14
  import { writeFile } from 'node:fs/promises';
@@ -17,7 +18,7 @@ import { join } from 'node:path';
17
18
  import { z } from 'zod';
18
19
  import { createNodeFileSystem } from '../../memory/project/fs/fs-port.js';
19
20
  import { CLOUD_LINK_FILE, resolveProjectCloud } from '../../memory/cloud/cloud-config.js';
20
- import { memoryReadUrl, MemoryResponseScope, scopeMemoryResponse, scopeOfMemoryResponse, } from '../../memory/cloud/memory-scope.js';
21
+ import { readMemoryPages, MemoryResponseScope, scopeMemoryResponse, scopeOfMemoryResponse, } from '../../memory/cloud/memory-scope.js';
21
22
  import { linkDirectoryFrom } from '../../memory/project/link-directory.js';
22
23
  import { RETICLE_CONFIG_BASENAME } from './ports/resolve/cli-port.js';
23
24
  import { cmdLink, validateKey } from './cloud-link.js';
@@ -33,9 +34,11 @@ import { GateExit, gateExitWith } from './answers/gate-exit.js';
33
34
  import { brokenOf } from './known-regression.js';
34
35
  import { describeUnsyncedBriefly } from '../../memory/cloud/unsynced-roots.js';
35
36
  import { machineUnsyncedRoots } from '../../memory/project/sync-status.js';
36
- import { describeSync, runSyncCycle } from '../../memory/cloud/sync-cycle.js';
37
- import { diskSink, diskSource, readCloudIssues, readCloudState } from '../../memory/cloud/sync-disk.js';
37
+ import { runSyncCycle } from '../../memory/cloud/sync-cycle.js';
38
+ import { describeSync } from '../../memory/cloud/sync-describe.js';
39
+ import { diskSink, diskSource, policyScreenshots, readCloudIssues, readCloudState, } from '../../memory/cloud/sync-disk.js';
38
40
  import { fetchPlatformRun } from '../../features/harness/platform/platform-drives.js';
41
+ import { SERVER_VERSION } from '../version/identity/server-version.js';
39
42
  /**
40
43
  * Where `reticle login` dials when nothing says otherwise: the hosted service.
41
44
  *
@@ -260,6 +263,23 @@ const cmdConnect = async (given) => {
260
263
  /** `reticle config [--runs on|off] [--memory on|off] [--flows on|off] [--verify local|server]`. */
261
264
  const cmdConfig = async (argv) => {
262
265
  const f = flags(argv);
266
+ // The Harness switch lives on the platform, not in cloud.json: every drive spends the workspace's
267
+ // credits, so it is the user's to turn on, with the project's own key.
268
+ if (f['harness'] !== undefined) {
269
+ const enabled = 'on' === f['harness'] ? true : 'off' === f['harness'] ? false : undefined;
270
+ if (enabled === undefined) {
271
+ err('--harness must be on|off');
272
+ return 2;
273
+ }
274
+ const { url, apiKey } = await repoCloud();
275
+ const env = { [ReticleEnv.API_KEY]: apiKey, [ReticleEnv.CLOUD_URL]: url };
276
+ if (!(await writeHarnessSwitch(env, enabled))) {
277
+ err(`the platform did not switch the Harness ${f['harness']} (locked, no access, or offline)`);
278
+ return 1;
279
+ }
280
+ emit({ harness: f['harness'] });
281
+ return 0;
282
+ }
263
283
  const linkPath = join(linkedRoot(), CLOUD_LINK_FILE);
264
284
  const raw = await readJson(linkPath);
265
285
  if (null === raw || typeof raw !== 'object') {
@@ -322,9 +342,7 @@ const cmdSync = async (argv) => {
322
342
  capsules: () => (cloud.policy.capsules ? full.capsules() : []),
323
343
  // `memory` is the project's cross-run history and the derived records that summarise it.
324
344
  derived: (kind) => cloud.policy.memory ? full.derived(kind) : undefined,
325
- ...(cloud.policy.screenshots && full.screenshot !== undefined
326
- ? { screenshot: full.screenshot }
327
- : {}),
345
+ ...policyScreenshots(full.screenshot, cloud.policy),
328
346
  };
329
347
  const once = async () => {
330
348
  const report = await runSyncCycle({
@@ -336,6 +354,7 @@ const cmdSync = async (argv) => {
336
354
  sink: diskSink(reticleRoot),
337
355
  state: readCloudState(reticleRoot),
338
356
  now: () => Date.now(),
357
+ clientVersion: SERVER_VERSION,
339
358
  request: (url, init) => syncRequest(url, init),
340
359
  });
341
360
  emit({
@@ -508,8 +527,7 @@ const cmdMemory = async (argv) => {
508
527
  // `undefined` for anything it cannot show to be this project's, so there is no ordering a future
509
528
  // edit can get wrong — the body it must not print is one it was never handed. The verdict is read
510
529
  // only to say WHICH refusal this is.
511
- const scope = { projectId, subject };
512
- const body = await api('GET', memoryReadUrl(url, scope), apiKey);
530
+ const body = await readMemoryPages(url, { projectId, subject }, (page) => api('GET', page, apiKey));
513
531
  const printable = scopeMemoryResponse(body, projectId);
514
532
  if (printable === undefined) {
515
533
  err(MemoryResponseScope.OTHER === scopeOfMemoryResponse(body, projectId)
@@ -15,9 +15,9 @@ import { homedir } from 'node:os';
15
15
  import { join } from 'node:path';
16
16
  import { z } from 'zod';
17
17
  import { cloudFetch } from '../../memory/cloud/cloud-sync.js';
18
- import { normalizeUrl, readSessionFrom, resolveSessionFor, sessionPath, } from './auth/cloud-session.js';
18
+ import { ACTIVE_SESSION_FILE, normalizeUrl, readSessionFrom, resolveSessionFor, sessionPath, } from './auth/cloud-session.js';
19
19
  export const RETICLE_DIR = '.reticle';
20
- export const SESSION_FILE = 'session.json';
20
+ export const SESSION_FILE = ACTIVE_SESSION_FILE;
21
21
  export const CREDENTIALS_FILE = 'credentials.json';
22
22
  /** The user-level reticle directory, `~/.reticle`. Sessions and credentials live here, not in a repo. */
23
23
  export const home = () => join(homedir(), RETICLE_DIR);
@@ -105,7 +105,7 @@ export class CredentialRefusedError extends Error {
105
105
  : reason.replace(/\.$/, '')}. Mint a new key in Settings → API keys and set RETICLE_API_KEY to it.`
106
106
  : `the platform refused your sign-in (${String(status)}): ${reason === undefined || KEY_REFUSED === reason
107
107
  ? 'it expired, was revoked, or you no longer have access to the workspace'
108
- : reason.replace(/\.$/, '')}. Run \`reticle login\`.`);
108
+ : reason.replace(/\.$/, '')}. Run \`npx @reticlehq/server connect\`.`);
109
109
  this.status = status;
110
110
  this.apiKey = apiKey;
111
111
  }
@@ -10,28 +10,9 @@ export declare const validateKey: (url: string, key: string) => Promise<z.infer<
10
10
  declare const WhoamiSchema: z.ZodObject<{
11
11
  projectId: z.ZodString;
12
12
  projectName: z.ZodString;
13
- /**
14
- * Which tenant the key belongs to. Optional for the same reason `dashboardUrl` is — an older
15
- * cloud does not send it — and when it is missing a stored key cannot be proved to be ours, which
16
- * is why the reuse path below mints instead of guessing.
17
- */
18
13
  orgId: z.ZodOptional<z.ZodString>;
19
- /**
20
- * Where this project's dashboard lives. Optional because an older cloud does not send it, and a
21
- * CLI that refused to link against one would break the thing it is supposed to connect.
22
- */
23
14
  dashboardUrl: z.ZodOptional<z.ZodString>;
24
- }, "strip", z.ZodTypeAny, {
25
- projectId: string;
26
- projectName: string;
27
- orgId?: string | undefined;
28
- dashboardUrl?: string | undefined;
29
- }, {
30
- projectId: string;
31
- projectName: string;
32
- orgId?: string | undefined;
33
- dashboardUrl?: string | undefined;
34
- }>;
15
+ }, z.core.$strip>;
35
16
  export interface PlatformLinked {
36
17
  via: PlatformLinkVia;
37
18
  /** The project this folder was bound to before, if any: a re-run against it is not a new link. */
@@ -280,6 +280,8 @@ via = PlatformLinkVia.LINK) => {
280
280
  * this can be read aloud, pasted into an issue, or left in a terminal without leaking anything.
281
281
  */
282
282
  hint(`bound this repo to project "${projectName}"${inWorkspace(workspace)}`);
283
+ if (dashboardUrl !== undefined)
284
+ hint(`dashboard: ${dashboardUrl}`);
283
285
  if (projectDir !== resolve(cwd))
284
286
  hint(`linked ${relative(cwd, projectDir)}: that is where this app's runs are written`);
285
287
  hint(reusedKey
@@ -1,9 +1,13 @@
1
1
  /** Run `reticle link` with these arguments. Injected; see the note at the top of this file. */
2
2
  type Linker = (argv: readonly string[]) => Promise<number>;
3
3
  /**
4
- * `reticle login` — browser device flow by default; `--email <e>` (or a positional email) keeps the
5
- * headless two-step code path for CI/servers where opening a browser makes no sense.
4
+ * Email sign-in is gone: the platform answers its routes with 410 `email_sign_in_removed`. The sentence
5
+ * starts with the platform's own, so the terminal and the sign-in page agree. `--email`, `--code`
6
+ * and a positional address now stop with it, because the person who typed them wants no browser, and
7
+ * a device flow that waits for an approval nobody can give is worse than a clear refusal.
6
8
  */
9
+ export declare const EMAIL_SIGN_IN_REMOVED: string;
10
+ /** `reticle login` — the browser device flow. Email sign-in is gone (see EMAIL_SIGN_IN_REMOVED). */
7
11
  export declare const cmdLogin: (argv: readonly string[], link: Linker, autoLink?: boolean) => Promise<number>;
8
12
  /** `reticle logout` — forget the cached session token (per-project keys under credentials.json stay). */
9
13
  /**
@@ -19,24 +19,16 @@ import { RETICLE_CONFIG_BASENAME } from './ports/resolve/cli-port.js';
19
19
  import { DevicePollSchema, DeviceStartSchema, LoginOrigin, openBrowser, withLoginOrigin, } from './auth/device-flow.js';
20
20
  import { normalizeUrl, sessionPath, SESSIONS_DIR } from './auth/cloud-session.js';
21
21
  import { api, baseUrl, emit, err, flags, hint, home, readSession, RETICLE_DIR, SESSION_FILE, } from './cloud-kit.js';
22
- const LoginSchema = z.object({
23
- token: z.string(),
24
- org: z.object({ id: z.string().optional(), name: z.string() }),
25
- });
26
22
  /**
27
- * `reticle login --email <e> [--org <name>] [--code <123456>]` — sign in, cache the token under
28
- * ~/.reticle.
29
- *
30
- * TWO STEPS, because the cloud proves you own the inbox before it hands out a session: ask for a code,
31
- * then exchange it. (It used to take an email alone — which meant anyone who knew your address owned your
32
- * org.) `--org` is only consulted when the account is brand new; a returning user never needs it.
33
- *
34
- * Without `--code` we request one and stop, telling the user to re-run with it. The one exception is a
35
- * LOCAL cloud, whose dev mailer cannot actually deliver mail and so echoes the code back in its response
36
- * (`devCode`) — there we complete the login in a single command rather than asking a developer to read a
37
- * code out of a server log they may not even be tailing.
23
+ * Email sign-in is gone: the platform answers its routes with 410 `email_sign_in_removed`. The sentence
24
+ * starts with the platform's own, so the terminal and the sign-in page agree. `--email`, `--code`
25
+ * and a positional address now stop with it, because the person who typed them wants no browser, and
26
+ * a device flow that waits for an approval nobody can give is worse than a clear refusal.
38
27
  */
39
- const RequestCodeSchema = z.object({ devCode: z.string().optional() });
28
+ export const EMAIL_SIGN_IN_REMOVED = 'Email sign-in is gone. Sign in with Google or GitHub: run `reticle connect`, which opens the browser. ' +
29
+ 'Without a browser (CI, an agent), set RETICLE_API_KEY to a key from Settings > API keys.';
30
+ /** The flags the removed email sign-in took. Any one of them gets the sentence above. */
31
+ const EMAIL_FLAGS = ['email', 'code', 'org'];
40
32
  const MeSchema = z.object({ email: z.string().min(1).nullable().optional() });
41
33
  /** The signed-in address, asked of the cloud. Best-effort: a login never fails for want of it. */
42
34
  const signedInEmail = async (url, token) => {
@@ -151,35 +143,15 @@ const cmdLoginDevice = async (explicitUrl, project, link, autoLink, open = openB
151
143
  return 1;
152
144
  }
153
145
  };
154
- /**
155
- * `reticle login` — browser device flow by default; `--email <e>` (or a positional email) keeps the
156
- * headless two-step code path for CI/servers where opening a browser makes no sense.
157
- */
146
+ /** `reticle login` — the browser device flow. Email sign-in is gone (see EMAIL_SIGN_IN_REMOVED). */
158
147
  export const cmdLogin = async (argv, link, autoLink = true) => {
159
148
  const f = flags(argv);
160
149
  const positional = argv[0] !== undefined && !argv[0].startsWith('--') ? argv[0] : undefined;
161
- const email = f['email'] ?? positional;
162
- if (email === undefined)
163
- return cmdLoginDevice(f['url'], f['project'], link, autoLink);
164
- const org = f['org'];
165
- const url = baseUrl(null, f['url']);
166
- let code = f['code'];
167
- if (code === undefined) {
168
- const requested = RequestCodeSchema.parse(await api('POST', `${url}/v1/auth/request-code`, null, {
169
- email,
170
- ...(org !== undefined ? { orgName: org } : {}),
171
- }));
172
- // A real cloud mails the code and never echoes it; a local one cannot mail, so it hands it back.
173
- if (requested.devCode === undefined) {
174
- emit({ codeSent: true, to: email });
175
- hint(`check your inbox, then: \`reticle login --email ${email} --code <the 6-digit code>\``);
176
- return 0;
177
- }
178
- code = requested.devCode;
150
+ if (positional !== undefined || EMAIL_FLAGS.some((name) => f[name] !== undefined)) {
151
+ err(EMAIL_SIGN_IN_REMOVED);
152
+ return 2;
179
153
  }
180
- const parsed = LoginSchema.parse(await api('POST', `${url}/v1/auth/login`, null, { email, code }));
181
- await writeSession(url, parsed.token, parsed.org.name, parsed.org.id, f['project'], link, email, autoLink);
182
- return 0;
154
+ return cmdLoginDevice(f['url'], f['project'], link, autoLink);
183
155
  };
184
156
  /** `reticle logout` — forget the cached session token (per-project keys under credentials.json stay). */
185
157
  /**
@@ -0,0 +1,10 @@
1
+ import { type Checkout } from '@reticlehq/core';
2
+ /** The checkout `dir` is in, or undefined outside git. */
3
+ export declare function checkoutAt(dir: string): Checkout | undefined;
4
+ /**
5
+ * The same folder in every other checkout of the repository that holds `directory`: the main
6
+ * checkout and each linked worktree. Empty outside git and without worktrees.
7
+ */
8
+ export declare function worktreeTwins(directory: string): string[];
9
+ /** True when the repository whose git directory is `commonDir` has a linked worktree. One read. */
10
+ export declare function hasLinkedWorktrees(commonDir: string): boolean;
@@ -0,0 +1,78 @@
1
+ /**
2
+ * The git checkout a directory is in, read from disk. The rule is core's `checkoutOf`; this file
3
+ * only supplies the reads, so every server module asks the same question the same way.
4
+ */
5
+ import { readFileSync, readdirSync, realpathSync } from 'node:fs';
6
+ import { dirname, join, relative, resolve } from 'node:path';
7
+ import { checkoutOf, samePath } from '@reticlehq/core';
8
+ /** Where git keeps one folder for each linked worktree, under the repository's git directory. */
9
+ const WORKTREES_DIR = 'worktrees';
10
+ /** The file in that folder that names the worktree's `.git`. */
11
+ const GITDIR_FILE = 'gitdir';
12
+ /**
13
+ * The long form of a path. Windows can spell one folder two ways (`RUNNER~1` and `runneradmin`), and
14
+ * git writes the long one, so a short-form caller was named as its own twin.
15
+ */
16
+ const longForm = (path) => {
17
+ try {
18
+ return realpathSync.native(path);
19
+ }
20
+ catch {
21
+ return path;
22
+ }
23
+ };
24
+ const readOrUndefined = (path) => {
25
+ try {
26
+ return readFileSync(path, 'utf8');
27
+ }
28
+ catch {
29
+ // Missing, a directory, or unreadable: not the file we look for.
30
+ return undefined;
31
+ }
32
+ };
33
+ /** The checkout `dir` is in, or undefined outside git. */
34
+ export function checkoutAt(dir) {
35
+ return checkoutOf(dir, readOrUndefined);
36
+ }
37
+ /** The top directory of each linked worktree of the repository whose git directory is `commonDir`. */
38
+ function linkedWorktreeRoots(commonDir) {
39
+ const admin = join(commonDir, WORKTREES_DIR);
40
+ let names = [];
41
+ try {
42
+ names = readdirSync(admin);
43
+ }
44
+ catch {
45
+ return [];
46
+ }
47
+ return names.flatMap((name) => {
48
+ // `<worktree>/.git`, absolute, or relative to this file when git writes relative paths.
49
+ const pointer = readOrUndefined(join(admin, name, GITDIR_FILE))?.trim();
50
+ return pointer === undefined || 0 === pointer.length
51
+ ? []
52
+ : [dirname(resolve(admin, name, pointer))];
53
+ });
54
+ }
55
+ /**
56
+ * The same folder in every other checkout of the repository that holds `directory`: the main
57
+ * checkout and each linked worktree. Empty outside git and without worktrees.
58
+ */
59
+ export function worktreeTwins(directory) {
60
+ const from = checkoutAt(directory);
61
+ if (from === undefined)
62
+ return [];
63
+ const rel = relative(from.root, directory);
64
+ const roots = new Set([from.mainRoot, ...linkedWorktreeRoots(from.commonDir)]);
65
+ const self = longForm(from.root);
66
+ return [...roots]
67
+ .filter((root) => !samePath(longForm(root), self))
68
+ .map((root) => join(root, rel));
69
+ }
70
+ /** True when the repository whose git directory is `commonDir` has a linked worktree. One read. */
71
+ export function hasLinkedWorktrees(commonDir) {
72
+ try {
73
+ return readdirSync(join(commonDir, WORKTREES_DIR)).length > 0;
74
+ }
75
+ catch {
76
+ return false;
77
+ }
78
+ }